Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6

* 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (505 commits)
  [media] af9015: Fix max I2C message size when used with tda18271
  [media] IR: initialize ir_raw_event in few more drivers
  [media] Guard a divide in v4l1 compat layer
  [media] imon: fix nomouse modprobe option
  [media] imon: remove redundant change_protocol call
  [media] imon: fix my egregious brown paper bag w/rdev/idev split
  [media] cafe_ccic: Configure ov7670 correctly
  [media] ov7670: allow configuration of image size, clock speed, and I/O method
  [media] af9015: support for DigitalNow TinyTwin v3 [1f4d:9016]
  [media] af9015: map DigitalNow TinyTwin v2 remote
  [media] DigitalNow TinyTwin remote controller
  [media] af9015: RC fixes and improvements
  videodev2.h.xml: Update to reflect the latest changes at videodev2.h
  [media] v4l: document new Bayer and monochrome pixel formats
  [media] DocBook/v4l: Add missing formats used on gspca cpia1 and sn9c2028
  [media] firedtv: add parameter to fake ca_system_ids in CA_INFO
  [media] tm6000: fix a macro coding style issue
  tm6000: Remove some ugly debug code
  [media] Nova-S-Plus audio line input
  [media] [RFC,1/1] V4L2: Use new CAP bits in existing RDS capable drivers
  ...
diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl
index 6ae9715..be34dcb 100644
--- a/Documentation/DocBook/media-entities.tmpl
+++ b/Documentation/DocBook/media-entities.tmpl
@@ -250,6 +250,9 @@
 <!ENTITY sub-yuv422p SYSTEM "v4l/pixfmt-yuv422p.xml">
 <!ENTITY sub-yuyv SYSTEM "v4l/pixfmt-yuyv.xml">
 <!ENTITY sub-yvyu SYSTEM "v4l/pixfmt-yvyu.xml">
+<!ENTITY sub-srggb10 SYSTEM "v4l/pixfmt-srggb10.xml">
+<!ENTITY sub-srggb8 SYSTEM "v4l/pixfmt-srggb8.xml">
+<!ENTITY sub-y10 SYSTEM "v4l/pixfmt-y10.xml">
 <!ENTITY sub-pixfmt SYSTEM "v4l/pixfmt.xml">
 <!ENTITY sub-cropcap SYSTEM "v4l/vidioc-cropcap.xml">
 <!ENTITY sub-dbg-g-register SYSTEM "v4l/vidioc-dbg-g-register.xml">
@@ -347,6 +350,9 @@
 <!ENTITY yuv422p SYSTEM "v4l/pixfmt-yuv422p.xml">
 <!ENTITY yuyv SYSTEM "v4l/pixfmt-yuyv.xml">
 <!ENTITY yvyu SYSTEM "v4l/pixfmt-yvyu.xml">
+<!ENTITY srggb10 SYSTEM "v4l/pixfmt-srggb10.xml">
+<!ENTITY srggb8 SYSTEM "v4l/pixfmt-srggb8.xml">
+<!ENTITY y10 SYSTEM "v4l/pixfmt-y10.xml">
 <!ENTITY cropcap SYSTEM "v4l/vidioc-cropcap.xml">
 <!ENTITY dbg-g-register SYSTEM "v4l/vidioc-dbg-g-register.xml">
 <!ENTITY encoder-cmd SYSTEM "v4l/vidioc-encoder-cmd.xml">
diff --git a/Documentation/DocBook/v4l/compat.xml b/Documentation/DocBook/v4l/compat.xml
index 54447f0..c9ce61d 100644
--- a/Documentation/DocBook/v4l/compat.xml
+++ b/Documentation/DocBook/v4l/compat.xml
@@ -21,11 +21,15 @@
       <title>Opening and Closing Devices</title>
 
       <para>For compatibility reasons the character device file names
-recommended for V4L2 video capture, overlay, radio, teletext and raw
+recommended for V4L2 video capture, overlay, radio and raw
 vbi capture devices did not change from those used by V4L. They are
 listed in <xref linkend="devices" /> and below in <xref
 	  linkend="v4l-dev" />.</para>
 
+      <para>The teletext devices (minor range 192-223) have been removed in
+V4L2 and no longer exist. There is no hardware available anymore for handling
+pure teletext. Instead raw or sliced VBI is used.</para>
+
       <para>The V4L <filename>videodev</filename> module automatically
 assigns minor numbers to drivers in load order, depending on the
 registered device type. We recommend that V4L2 drivers by default
@@ -66,13 +70,6 @@
 	      <entry>64-127</entry>
 	    </row>
 	    <row>
-	      <entry>Teletext decoder</entry>
-	      <entry><para><filename>/dev/vtx</filename>,
-<filename>/dev/vtx0</filename> to
-<filename>/dev/vtx31</filename></para></entry>
-	      <entry>192-223</entry>
-	    </row>
-	    <row>
 	      <entry>Raw VBI capture</entry>
 	      <entry><para><filename>/dev/vbi</filename>,
 <filename>/dev/vbi0</filename> to
@@ -2345,6 +2342,17 @@
 	</listitem>
       </orderedlist>
     </section>
+    <section>
+      <title>V4L2 in Linux 2.6.37</title>
+      <orderedlist>
+	<listitem>
+	  <para>Remove the vtx (videotext/teletext) API. This API was no longer
+used and no hardware exists to verify the API. Nor were any userspace applications found
+that used it. It was originally scheduled for removal in 2.6.35.
+	  </para>
+	</listitem>
+      </orderedlist>
+    </section>
 
     <section id="other">
       <title>Relation of V4L2 to other Linux multimedia APIs</title>
diff --git a/Documentation/DocBook/v4l/controls.xml b/Documentation/DocBook/v4l/controls.xml
index 8408caa..2fae3e8 100644
--- a/Documentation/DocBook/v4l/controls.xml
+++ b/Documentation/DocBook/v4l/controls.xml
@@ -312,10 +312,17 @@
 	    information and bits 24-31 must be zero.</entry>
 	  </row>
 	  <row>
+	    <entry><constant>V4L2_CID_ILLUMINATORS_1</constant>
+		<constant>V4L2_CID_ILLUMINATORS_2</constant></entry>
+	    <entry>boolean</entry>
+	    <entry>Switch on or off the illuminator 1 or 2 of the device
+		(usually a microscope).</entry>
+	  </row>
+	  <row>
 	    <entry><constant>V4L2_CID_LASTP1</constant></entry>
 	    <entry></entry>
 	    <entry>End of the predefined control IDs (currently
-<constant>V4L2_CID_BG_COLOR</constant> + 1).</entry>
+<constant>V4L2_CID_ILLUMINATORS_2</constant> + 1).</entry>
 	  </row>
 	  <row>
 	    <entry><constant>V4L2_CID_PRIVATE_BASE</constant></entry>
@@ -357,9 +364,6 @@
 	      querymenu.index++) {
 		if (0 == ioctl (fd, &VIDIOC-QUERYMENU;, &amp;querymenu)) {
 			printf ("  %s\n", querymenu.name);
-		} else {
-			perror ("VIDIOC_QUERYMENU");
-			exit (EXIT_FAILURE);
 		}
 	}
 }
diff --git a/Documentation/DocBook/v4l/dev-rds.xml b/Documentation/DocBook/v4l/dev-rds.xml
index 0869d70..360d273 100644
--- a/Documentation/DocBook/v4l/dev-rds.xml
+++ b/Documentation/DocBook/v4l/dev-rds.xml
@@ -3,15 +3,16 @@
       <para>The Radio Data System transmits supplementary
 information in binary format, for example the station name or travel
 information, on an inaudible audio subcarrier of a radio program. This
-interface is aimed at devices capable of receiving and decoding RDS
+interface is aimed at devices capable of receiving and/or transmitting RDS
 information.</para>
 
       <para>For more information see the core RDS standard <xref linkend="en50067" />
 and the RBDS standard <xref linkend="nrsc4" />.</para>
 
       <para>Note that the RBDS standard as is used in the USA is almost identical
-to the RDS standard. Any RDS decoder can also handle RBDS. Only some of the fields
-have slightly different meanings. See the RBDS standard for more information.</para>
+to the RDS standard. Any RDS decoder/encoder can also handle RBDS. Only some of the
+fields have slightly different meanings. See the RBDS standard for more
+information.</para>
 
       <para>The RBDS standard also specifies support for MMBS (Modified Mobile Search).
 This is a proprietary format which seems to be discontinued. The RDS interface does not
@@ -21,16 +22,25 @@
   <section>
     <title>Querying Capabilities</title>
 
-    <para>Devices supporting the RDS capturing API
-set the <constant>V4L2_CAP_RDS_CAPTURE</constant> flag in
+    <para>Devices supporting the RDS capturing API set
+the <constant>V4L2_CAP_RDS_CAPTURE</constant> flag in
 the <structfield>capabilities</structfield> field of &v4l2-capability;
-returned by the &VIDIOC-QUERYCAP; ioctl.
-Any tuner that supports RDS will set the
-<constant>V4L2_TUNER_CAP_RDS</constant> flag in the <structfield>capability</structfield>
-field of &v4l2-tuner;.
-Whether an RDS signal is present can be detected by looking at
-the <structfield>rxsubchans</structfield> field of &v4l2-tuner;: the
-<constant>V4L2_TUNER_SUB_RDS</constant> will be set if RDS data was detected.</para>
+returned by the &VIDIOC-QUERYCAP; ioctl.  Any tuner that supports RDS
+will set the <constant>V4L2_TUNER_CAP_RDS</constant> flag in
+the <structfield>capability</structfield> field of &v4l2-tuner;.  If
+the driver only passes RDS blocks without interpreting the data
+the <constant>V4L2_TUNER_SUB_RDS_BLOCK_IO</constant> flag has to be
+set, see <link linkend="reading-rds-data">Reading RDS data</link>.
+For future use the
+flag <constant>V4L2_TUNER_SUB_RDS_CONTROLS</constant> has also been
+defined. However, a driver for a radio tuner with this capability does
+not yet exist, so if you are planning to write such a driver you
+should discuss this on the linux-media mailing list: &v4l-ml;.</para>
+
+    <para> Whether an RDS signal is present can be detected by looking
+at the <structfield>rxsubchans</structfield> field of &v4l2-tuner;:
+the <constant>V4L2_TUNER_SUB_RDS</constant> will be set if RDS data
+was detected.</para>
 
     <para>Devices supporting the RDS output API
 set the <constant>V4L2_CAP_RDS_OUTPUT</constant> flag in
@@ -40,16 +50,31 @@
 <constant>V4L2_TUNER_CAP_RDS</constant> flag in the <structfield>capability</structfield>
 field of &v4l2-modulator;.
 In order to enable the RDS transmission one must set the <constant>V4L2_TUNER_SUB_RDS</constant>
-bit in the <structfield>txsubchans</structfield> field of &v4l2-modulator;.</para>
-
+bit in the <structfield>txsubchans</structfield> field of &v4l2-modulator;.
+If the driver only passes RDS blocks without interpreting the data
+the <constant>V4L2_TUNER_SUB_RDS_BLOCK_IO</constant> flag has to be set. If the
+tuner is capable of handling RDS entities like program identification codes and radio
+text, the flag <constant>V4L2_TUNER_SUB_RDS_CONTROLS</constant> should be set,
+see <link linkend="writing-rds-data">Writing RDS data</link> and
+<link linkend="fm-tx-controls">FM Transmitter Control Reference</link>.</para>
   </section>
 
-  <section>
+  <section  id="reading-rds-data">
     <title>Reading RDS data</title>
 
       <para>RDS data can be read from the radio device
-with the &func-read; function. The data is packed in groups of three bytes,
+with the &func-read; function. The data is packed in groups of three bytes.</para>
+  </section>
+
+  <section  id="writing-rds-data">
+    <title>Writing RDS data</title>
+
+      <para>RDS data can be written to the radio device
+with the &func-write; function. The data is packed in groups of three bytes,
 as follows:</para>
+  </section>
+
+  <section>
     <table frame="none" pgwide="1" id="v4l2-rds-data">
       <title>struct
 <structname>v4l2_rds_data</structname></title>
@@ -111,48 +136,57 @@
 	<tbody valign="top">
 	  <row>
 	    <entry>V4L2_RDS_BLOCK_MSK</entry>
+	    <entry> </entry>
 	    <entry>7</entry>
 	    <entry>Mask for bits 0-2 to get the block ID.</entry>
 	  </row>
 	  <row>
 	    <entry>V4L2_RDS_BLOCK_A</entry>
+	    <entry> </entry>
 	    <entry>0</entry>
 	    <entry>Block A.</entry>
 	  </row>
 	  <row>
 	    <entry>V4L2_RDS_BLOCK_B</entry>
+	    <entry> </entry>
 	    <entry>1</entry>
 	    <entry>Block B.</entry>
 	  </row>
 	  <row>
 	    <entry>V4L2_RDS_BLOCK_C</entry>
+	    <entry> </entry>
 	    <entry>2</entry>
 	    <entry>Block C.</entry>
 	  </row>
 	  <row>
 	    <entry>V4L2_RDS_BLOCK_D</entry>
+	    <entry> </entry>
 	    <entry>3</entry>
 	    <entry>Block D.</entry>
 	  </row>
 	  <row>
 	    <entry>V4L2_RDS_BLOCK_C_ALT</entry>
+	    <entry> </entry>
 	    <entry>4</entry>
 	    <entry>Block C'.</entry>
 	  </row>
 	  <row>
 	    <entry>V4L2_RDS_BLOCK_INVALID</entry>
+	    <entry>read-only</entry>
 	    <entry>7</entry>
 	    <entry>An invalid block.</entry>
 	  </row>
 	  <row>
 	    <entry>V4L2_RDS_BLOCK_CORRECTED</entry>
+	    <entry>read-only</entry>
 	    <entry>0x40</entry>
 	    <entry>A bit error was detected but corrected.</entry>
 	  </row>
 	  <row>
 	    <entry>V4L2_RDS_BLOCK_ERROR</entry>
+	    <entry>read-only</entry>
 	    <entry>0x80</entry>
-	    <entry>An incorrectable error occurred.</entry>
+	    <entry>An uncorrectable error occurred.</entry>
 	  </row>
 	</tbody>
       </tgroup>
diff --git a/Documentation/DocBook/v4l/dev-teletext.xml b/Documentation/DocBook/v4l/dev-teletext.xml
index 76184e8..414b1cf 100644
--- a/Documentation/DocBook/v4l/dev-teletext.xml
+++ b/Documentation/DocBook/v4l/dev-teletext.xml
@@ -1,35 +1,32 @@
   <title>Teletext Interface</title>
 
-  <para>This interface aims at devices receiving and demodulating
+  <para>This interface was aimed at devices receiving and demodulating
 Teletext data [<xref linkend="ets300706" />, <xref linkend="itu653" />], evaluating the
 Teletext packages and storing formatted pages in cache memory. Such
 devices are usually implemented as microcontrollers with serial
-interface (I<superscript>2</superscript>C) and can be found on older
+interface (I<superscript>2</superscript>C) and could be found on old
 TV cards, dedicated Teletext decoding cards and home-brew devices
 connected to the PC parallel port.</para>
 
-  <para>The Teletext API was designed by Martin Buck. It is defined in
+  <para>The Teletext API was designed by Martin Buck. It was defined in
 the kernel header file <filename>linux/videotext.h</filename>, the
 specification is available from <ulink url="ftp://ftp.gwdg.de/pub/linux/misc/videotext/">
 ftp://ftp.gwdg.de/pub/linux/misc/videotext/</ulink>. (Videotext is the name of
-the German public television Teletext service.) Conventional character
-device file names are <filename>/dev/vtx</filename> and
-<filename>/dev/vttuner</filename>, with device number 83, 0 and 83, 16
-respectively. A similar interface exists for the Philips SAA5249
-Teletext decoder [specification?] with character device file names
-<filename>/dev/tlkN</filename>, device number 102, N.</para>
+the German public television Teletext service.)</para>
 
   <para>Eventually the Teletext API was integrated into the V4L API
 with character device file names <filename>/dev/vtx0</filename> to
 <filename>/dev/vtx31</filename>, device major number 81, minor numbers
-192 to 223. For reference the V4L Teletext API specification is
-reproduced here in full: "Teletext interfaces talk the existing VTX
-API." Teletext devices with major number 83 and 102 will be removed in
-Linux 2.6.</para>
+192 to 223.</para>
 
-  <para>There are no plans to replace the Teletext API or to integrate
-it into V4L2. Please write to the linux-media mailing list: &v4l-ml;
-when the need arises.</para>
+  <para>However, teletext decoders were quickly replaced by more
+generic VBI demodulators and those dedicated teletext decoders no longer exist.
+For many years the vtx devices were still around, even though nobody used
+them. So the decision was made to finally remove support for the Teletext API in
+kernel 2.6.37.</para>
+
+  <para>Modern devices all use the <link linkend="raw-vbi">raw</link> or
+<link linkend="sliced">sliced</link> VBI API.</para>
 
   <!--
 Local Variables:
diff --git a/Documentation/DocBook/v4l/pixfmt-packed-rgb.xml b/Documentation/DocBook/v4l/pixfmt-packed-rgb.xml
index 26e8792..4db272b 100644
--- a/Documentation/DocBook/v4l/pixfmt-packed-rgb.xml
+++ b/Documentation/DocBook/v4l/pixfmt-packed-rgb.xml
@@ -739,7 +739,7 @@
 	    <entry>b<subscript>1</subscript></entry>
 	    <entry>b<subscript>0</subscript></entry>
 	  </row>
-	  <row id="V4L2-PIX-FMT-BGR666">
+	  <row><!-- id="V4L2-PIX-FMT-BGR666" -->
 	    <entry><constant>V4L2_PIX_FMT_BGR666</constant></entry>
 	    <entry>'BGRH'</entry>
 	    <entry></entry>
diff --git a/Documentation/DocBook/v4l/pixfmt-srggb10.xml b/Documentation/DocBook/v4l/pixfmt-srggb10.xml
new file mode 100644
index 0000000..7b27409
--- /dev/null
+++ b/Documentation/DocBook/v4l/pixfmt-srggb10.xml
@@ -0,0 +1,90 @@
+    <refentry>
+      <refmeta>
+	<refentrytitle>V4L2_PIX_FMT_SRGGB10 ('RG10'),
+	 V4L2_PIX_FMT_SGRBG10 ('BA10'),
+	 V4L2_PIX_FMT_SGBRG10 ('GB10'),
+	 V4L2_PIX_FMT_SBGGR10 ('BG10'),
+	 </refentrytitle>
+	&manvol;
+      </refmeta>
+      <refnamediv>
+	<refname id="V4L2-PIX-FMT-SRGGB10"><constant>V4L2_PIX_FMT_SRGGB10</constant></refname>
+	<refname id="V4L2-PIX-FMT-SGRBG10"><constant>V4L2_PIX_FMT_SGRBG10</constant></refname>
+	<refname id="V4L2-PIX-FMT-SGBRG10"><constant>V4L2_PIX_FMT_SGBRG10</constant></refname>
+	<refname id="V4L2-PIX-FMT-SBGGR10"><constant>V4L2_PIX_FMT_SBGGR10</constant></refname>
+	<refpurpose>10-bit Bayer formats expanded to 16 bits</refpurpose>
+      </refnamediv>
+      <refsect1>
+	<title>Description</title>
+
+	<para>The following four pixel formats are raw sRGB / Bayer formats with
+10 bits per colour. Each colour component is stored in a 16-bit word, with 6
+unused high bits filled with zeros. Each n-pixel row contains n/2 green samples
+and n/2 blue or red samples, with alternating red and blue rows. Bytes are
+stored in memory in little endian order. They are conventionally described
+as GRGR... BGBG..., RGRG... GBGB..., etc. Below is an example of one of these
+formats</para>
+
+    <example>
+      <title><constant>V4L2_PIX_FMT_SBGGR10</constant> 4 &times; 4
+pixel image</title>
+
+      <formalpara>
+	<title>Byte Order.</title>
+	<para>Each cell is one byte, high 6 bits in high bytes are 0.
+	  <informaltable frame="none">
+	    <tgroup cols="5" align="center">
+	      <colspec align="left" colwidth="2*" />
+	      <tbody valign="top">
+		<row>
+		  <entry>start&nbsp;+&nbsp;0:</entry>
+		  <entry>B<subscript>00low</subscript></entry>
+		  <entry>B<subscript>00high</subscript></entry>
+		  <entry>G<subscript>01low</subscript></entry>
+		  <entry>G<subscript>01high</subscript></entry>
+		  <entry>B<subscript>02low</subscript></entry>
+		  <entry>B<subscript>02high</subscript></entry>
+		  <entry>G<subscript>03low</subscript></entry>
+		  <entry>G<subscript>03high</subscript></entry>
+		</row>
+		<row>
+		  <entry>start&nbsp;+&nbsp;8:</entry>
+		  <entry>G<subscript>10low</subscript></entry>
+		  <entry>G<subscript>10high</subscript></entry>
+		  <entry>R<subscript>11low</subscript></entry>
+		  <entry>R<subscript>11high</subscript></entry>
+		  <entry>G<subscript>12low</subscript></entry>
+		  <entry>G<subscript>12high</subscript></entry>
+		  <entry>R<subscript>13low</subscript></entry>
+		  <entry>R<subscript>13high</subscript></entry>
+		</row>
+		<row>
+		  <entry>start&nbsp;+&nbsp;16:</entry>
+		  <entry>B<subscript>20low</subscript></entry>
+		  <entry>B<subscript>20high</subscript></entry>
+		  <entry>G<subscript>21low</subscript></entry>
+		  <entry>G<subscript>21high</subscript></entry>
+		  <entry>B<subscript>22low</subscript></entry>
+		  <entry>B<subscript>22high</subscript></entry>
+		  <entry>G<subscript>23low</subscript></entry>
+		  <entry>G<subscript>23high</subscript></entry>
+		</row>
+		<row>
+		  <entry>start&nbsp;+&nbsp;24:</entry>
+		  <entry>G<subscript>30low</subscript></entry>
+		  <entry>G<subscript>30high</subscript></entry>
+		  <entry>R<subscript>31low</subscript></entry>
+		  <entry>R<subscript>31high</subscript></entry>
+		  <entry>G<subscript>32low</subscript></entry>
+		  <entry>G<subscript>32high</subscript></entry>
+		  <entry>R<subscript>33low</subscript></entry>
+		  <entry>R<subscript>33high</subscript></entry>
+		</row>
+	      </tbody>
+	    </tgroup>
+	  </informaltable>
+	</para>
+      </formalpara>
+    </example>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/v4l/pixfmt-srggb8.xml b/Documentation/DocBook/v4l/pixfmt-srggb8.xml
new file mode 100644
index 0000000..2570e3b
--- /dev/null
+++ b/Documentation/DocBook/v4l/pixfmt-srggb8.xml
@@ -0,0 +1,67 @@
+    <refentry id="V4L2-PIX-FMT-SRGGB8">
+      <refmeta>
+	<refentrytitle>V4L2_PIX_FMT_SRGGB8 ('RGGB')</refentrytitle>
+	&manvol;
+      </refmeta>
+      <refnamediv>
+	<refname><constant>V4L2_PIX_FMT_SRGGB8</constant></refname>
+	<refpurpose>Bayer RGB format</refpurpose>
+      </refnamediv>
+      <refsect1>
+	<title>Description</title>
+
+	<para>This is commonly the native format of digital cameras,
+reflecting the arrangement of sensors on the CCD device. Only one red,
+green or blue value is given for each pixel. Missing components must
+be interpolated from neighbouring pixels. From left to right the first
+row consists of a red and green value, the second row of a green and
+blue value. This scheme repeats to the right and down for every two
+columns and rows.</para>
+
+	<example>
+	  <title><constant>V4L2_PIX_FMT_SRGGB8</constant> 4 &times; 4
+pixel image</title>
+
+	  <formalpara>
+	    <title>Byte Order.</title>
+	    <para>Each cell is one byte.
+	      <informaltable frame="none">
+		<tgroup cols="5" align="center">
+		  <colspec align="left" colwidth="2*" />
+		  <tbody valign="top">
+		    <row>
+		      <entry>start&nbsp;+&nbsp;0:</entry>
+		      <entry>R<subscript>00</subscript></entry>
+		      <entry>G<subscript>01</subscript></entry>
+		      <entry>R<subscript>02</subscript></entry>
+		      <entry>G<subscript>03</subscript></entry>
+		    </row>
+		    <row>
+		      <entry>start&nbsp;+&nbsp;4:</entry>
+		      <entry>G<subscript>10</subscript></entry>
+		      <entry>B<subscript>11</subscript></entry>
+		      <entry>G<subscript>12</subscript></entry>
+		      <entry>B<subscript>13</subscript></entry>
+		    </row>
+		    <row>
+		      <entry>start&nbsp;+&nbsp;8:</entry>
+		      <entry>R<subscript>20</subscript></entry>
+		      <entry>G<subscript>21</subscript></entry>
+		      <entry>R<subscript>22</subscript></entry>
+		      <entry>G<subscript>23</subscript></entry>
+		    </row>
+		    <row>
+		      <entry>start&nbsp;+&nbsp;12:</entry>
+		      <entry>G<subscript>30</subscript></entry>
+		      <entry>B<subscript>31</subscript></entry>
+		      <entry>G<subscript>32</subscript></entry>
+		      <entry>B<subscript>33</subscript></entry>
+		    </row>
+		  </tbody>
+		</tgroup>
+	      </informaltable>
+	    </para>
+	  </formalpara>
+	</example>
+      </refsect1>
+    </refentry>
diff --git a/Documentation/DocBook/v4l/pixfmt-y10.xml b/Documentation/DocBook/v4l/pixfmt-y10.xml
new file mode 100644
index 0000000..d065043
--- /dev/null
+++ b/Documentation/DocBook/v4l/pixfmt-y10.xml
@@ -0,0 +1,79 @@
+<refentry id="V4L2-PIX-FMT-Y10">
+  <refmeta>
+    <refentrytitle>V4L2_PIX_FMT_Y10 ('Y10 ')</refentrytitle>
+    &manvol;
+  </refmeta>
+  <refnamediv>
+    <refname><constant>V4L2_PIX_FMT_Y10</constant></refname>
+    <refpurpose>Grey-scale image</refpurpose>
+  </refnamediv>
+  <refsect1>
+    <title>Description</title>
+
+    <para>This is a grey-scale image with a depth of 10 bits per pixel. Pixels
+are stored in 16-bit words with unused high bits padded with 0. The least
+significant byte is stored at lower memory addresses (little-endian).</para>
+
+    <example>
+      <title><constant>V4L2_PIX_FMT_Y10</constant> 4 &times; 4
+pixel image</title>
+
+      <formalpara>
+	<title>Byte Order.</title>
+	<para>Each cell is one byte.
+	  <informaltable frame="none">
+	    <tgroup cols="9" align="center">
+	      <colspec align="left" colwidth="2*" />
+	      <tbody valign="top">
+		<row>
+		  <entry>start&nbsp;+&nbsp;0:</entry>
+		  <entry>Y'<subscript>00low</subscript></entry>
+		  <entry>Y'<subscript>00high</subscript></entry>
+		  <entry>Y'<subscript>01low</subscript></entry>
+		  <entry>Y'<subscript>01high</subscript></entry>
+		  <entry>Y'<subscript>02low</subscript></entry>
+		  <entry>Y'<subscript>02high</subscript></entry>
+		  <entry>Y'<subscript>03low</subscript></entry>
+		  <entry>Y'<subscript>03high</subscript></entry>
+		</row>
+		<row>
+		  <entry>start&nbsp;+&nbsp;8:</entry>
+		  <entry>Y'<subscript>10low</subscript></entry>
+		  <entry>Y'<subscript>10high</subscript></entry>
+		  <entry>Y'<subscript>11low</subscript></entry>
+		  <entry>Y'<subscript>11high</subscript></entry>
+		  <entry>Y'<subscript>12low</subscript></entry>
+		  <entry>Y'<subscript>12high</subscript></entry>
+		  <entry>Y'<subscript>13low</subscript></entry>
+		  <entry>Y'<subscript>13high</subscript></entry>
+		</row>
+		<row>
+		  <entry>start&nbsp;+&nbsp;16:</entry>
+		  <entry>Y'<subscript>20low</subscript></entry>
+		  <entry>Y'<subscript>20high</subscript></entry>
+		  <entry>Y'<subscript>21low</subscript></entry>
+		  <entry>Y'<subscript>21high</subscript></entry>
+		  <entry>Y'<subscript>22low</subscript></entry>
+		  <entry>Y'<subscript>22high</subscript></entry>
+		  <entry>Y'<subscript>23low</subscript></entry>
+		  <entry>Y'<subscript>23high</subscript></entry>
+		</row>
+		<row>
+		  <entry>start&nbsp;+&nbsp;24:</entry>
+		  <entry>Y'<subscript>30low</subscript></entry>
+		  <entry>Y'<subscript>30high</subscript></entry>
+		  <entry>Y'<subscript>31low</subscript></entry>
+		  <entry>Y'<subscript>31high</subscript></entry>
+		  <entry>Y'<subscript>32low</subscript></entry>
+		  <entry>Y'<subscript>32high</subscript></entry>
+		  <entry>Y'<subscript>33low</subscript></entry>
+		  <entry>Y'<subscript>33high</subscript></entry>
+		</row>
+	      </tbody>
+	    </tgroup>
+	  </informaltable>
+	</para>
+      </formalpara>
+    </example>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml
index c4ad0a8..d7c4671 100644
--- a/Documentation/DocBook/v4l/pixfmt.xml
+++ b/Documentation/DocBook/v4l/pixfmt.xml
@@ -566,7 +566,9 @@
     &sub-sbggr8;
     &sub-sgbrg8;
     &sub-sgrbg8;
+    &sub-srggb8;
     &sub-sbggr16;
+    &sub-srggb10;
   </section>
 
   <section id="yuv-formats">
@@ -589,6 +591,7 @@
 
     &sub-packed-yuv;
     &sub-grey;
+    &sub-y10;
     &sub-y16;
     &sub-yuyv;
     &sub-uyvy;
@@ -685,6 +688,11 @@
 kernel sources in the file <filename>Documentation/video4linux/cx2341x/README.hm12</filename>
 </para></entry>
 	  </row>
+	  <row id="V4L2-PIX-FMT-CPIA1">
+	    <entry><constant>V4L2_PIX_FMT_CPIA1</constant></entry>
+	    <entry>'CPIA'</entry>
+	    <entry>YUV format used by the gspca cpia1 driver.</entry>
+	  </row>
 	  <row id="V4L2-PIX-FMT-SPCA501">
 	    <entry><constant>V4L2_PIX_FMT_SPCA501</constant></entry>
 	    <entry>'S501'</entry>
@@ -705,11 +713,6 @@
 	    <entry>'S561'</entry>
 	    <entry>Compressed GBRG Bayer format used by the gspca driver.</entry>
 	  </row>
-	  <row id="V4L2-PIX-FMT-SGRBG10">
-	    <entry><constant>V4L2_PIX_FMT_SGRBG10</constant></entry>
-	    <entry>'DA10'</entry>
-	    <entry>10 bit raw Bayer, expanded to 16 bits.</entry>
-	  </row>
 	  <row id="V4L2-PIX-FMT-SGRBG10DPCM8">
 	    <entry><constant>V4L2_PIX_FMT_SGRBG10DPCM8</constant></entry>
 	    <entry>'DB10'</entry>
@@ -770,6 +773,11 @@
 	    <entry>'S920'</entry>
 	    <entry>YUV 4:2:0 format of the gspca sn9c20x driver.</entry>
 	  </row>
+	  <row id="V4L2-PIX-FMT-SN9C2028">
+	    <entry><constant>V4L2_PIX_FMT_SN9C2028</constant></entry>
+	    <entry>'SONX'</entry>
+	    <entry>Compressed GBRG bayer format of the gspca sn9c2028 driver.</entry>
+	  </row>
 	  <row id="V4L2-PIX-FMT-STV0680">
 	    <entry><constant>V4L2_PIX_FMT_STV0680</constant></entry>
 	    <entry>'S680'</entry>
@@ -787,6 +795,20 @@
 	    <entry>'TM60'</entry>
 	    <entry><para>Used by Trident tm6000</para></entry>
 	  </row>
+	  <row id="V4L2-PIX-FMT-CIT-YYVYUY">
+	    <entry><constant>V4L2_PIX_FMT_CIT_YYVYUY</constant></entry>
+	    <entry>'CITV'</entry>
+	    <entry><para>Used by xirlink CIT, found at IBM webcams.</para>
+	           <para>Uses one line of Y then 1 line of VYUY</para>
+	    </entry>
+	  </row>
+	  <row id="V4L2-PIX-FMT-KONICA420">
+	    <entry><constant>V4L2_PIX_FMT_KONICA420</constant></entry>
+	    <entry>'KONI'</entry>
+	    <entry><para>Used by Konica webcams.</para>
+	           <para>YUV420 planar in blocks of 256 pixels.</para>
+	    </entry>
+	  </row>
 	  <row id="V4L2-PIX-FMT-YYUV">
 	    <entry><constant>V4L2_PIX_FMT_YYUV</constant></entry>
 	    <entry>'YYUV'</entry>
diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/v4l/v4l2.xml
index 7c3c098..839e93e 100644
--- a/Documentation/DocBook/v4l/v4l2.xml
+++ b/Documentation/DocBook/v4l/v4l2.xml
@@ -99,6 +99,7 @@
       <year>2007</year>
       <year>2008</year>
       <year>2009</year>
+      <year>2010</year>
       <holder>Bill Dirks, Michael H. Schimek, Hans Verkuil, Martin
 Rubli, Andy Walls, Muralidharan Karicheri, Mauro Carvalho Chehab</holder>
     </copyright>
@@ -110,10 +111,17 @@
       <!-- Put document revisions here, newest first. -->
       <!-- API revisions (changes and additions of defines, enums,
 structs, ioctls) must be noted in more detail in the history chapter
-(compat.sgml), along with the possible impact on existing drivers and
+(compat.xml), along with the possible impact on existing drivers and
 applications. -->
 
       <revision>
+	<revnumber>2.6.37</revnumber>
+	<date>2010-08-06</date>
+	<authorinitials>hv</authorinitials>
+	<revremark>Removed obsolete vtx (videotext) API.</revremark>
+      </revision>
+
+      <revision>
 	<revnumber>2.6.33</revnumber>
 	<date>2009-12-03</date>
 	<authorinitials>mk</authorinitials>
diff --git a/Documentation/DocBook/v4l/videodev2.h.xml b/Documentation/DocBook/v4l/videodev2.h.xml
index 865b06d..325b23b 100644
--- a/Documentation/DocBook/v4l/videodev2.h.xml
+++ b/Documentation/DocBook/v4l/videodev2.h.xml
@@ -154,23 +154,13 @@
         V4L2_BUF_TYPE_VBI_OUTPUT           = 5,
         V4L2_BUF_TYPE_SLICED_VBI_CAPTURE   = 6,
         V4L2_BUF_TYPE_SLICED_VBI_OUTPUT    = 7,
-#if 1 /*KEEP*/
+#if 1
         /* Experimental */
         V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
 #endif
         V4L2_BUF_TYPE_PRIVATE              = 0x80,
 };
 
-enum <link linkend="v4l2-ctrl-type">v4l2_ctrl_type</link> {
-        V4L2_CTRL_TYPE_INTEGER       = 1,
-        V4L2_CTRL_TYPE_BOOLEAN       = 2,
-        V4L2_CTRL_TYPE_MENU          = 3,
-        V4L2_CTRL_TYPE_BUTTON        = 4,
-        V4L2_CTRL_TYPE_INTEGER64     = 5,
-        V4L2_CTRL_TYPE_CTRL_CLASS    = 6,
-        V4L2_CTRL_TYPE_STRING        = 7,
-};
-
 enum <link linkend="v4l2-tuner-type">v4l2_tuner_type</link> {
         V4L2_TUNER_RADIO             = 1,
         V4L2_TUNER_ANALOG_TV         = 2,
@@ -288,6 +278,7 @@
 #define <link linkend="V4L2-PIX-FMT-RGB565">V4L2_PIX_FMT_RGB565</link>  v4l2_fourcc('R', 'G', 'B', 'P') /* 16  RGB-5-6-5     */
 #define <link linkend="V4L2-PIX-FMT-RGB555X">V4L2_PIX_FMT_RGB555X</link> v4l2_fourcc('R', 'G', 'B', 'Q') /* 16  RGB-5-5-5 BE  */
 #define <link linkend="V4L2-PIX-FMT-RGB565X">V4L2_PIX_FMT_RGB565X</link> v4l2_fourcc('R', 'G', 'B', 'R') /* 16  RGB-5-6-5 BE  */
+#define <link linkend="V4L2-PIX-FMT-BGR666">V4L2_PIX_FMT_BGR666</link>  v4l2_fourcc('B', 'G', 'R', 'H') /* 18  BGR-6-6-6     */
 #define <link linkend="V4L2-PIX-FMT-BGR24">V4L2_PIX_FMT_BGR24</link>   v4l2_fourcc('B', 'G', 'R', '3') /* 24  BGR-8-8-8     */
 #define <link linkend="V4L2-PIX-FMT-RGB24">V4L2_PIX_FMT_RGB24</link>   v4l2_fourcc('R', 'G', 'B', '3') /* 24  RGB-8-8-8     */
 #define <link linkend="V4L2-PIX-FMT-BGR32">V4L2_PIX_FMT_BGR32</link>   v4l2_fourcc('B', 'G', 'R', '4') /* 32  BGR-8-8-8-8   */
@@ -295,6 +286,9 @@
 
 /* Grey formats */
 #define <link linkend="V4L2-PIX-FMT-GREY">V4L2_PIX_FMT_GREY</link>    v4l2_fourcc('G', 'R', 'E', 'Y') /*  8  Greyscale     */
+#define <link linkend="V4L2-PIX-FMT-Y4">V4L2_PIX_FMT_Y4</link>      v4l2_fourcc('Y', '0', '4', ' ') /*  4  Greyscale     */
+#define <link linkend="V4L2-PIX-FMT-Y6">V4L2_PIX_FMT_Y6</link>      v4l2_fourcc('Y', '0', '6', ' ') /*  6  Greyscale     */
+#define <link linkend="V4L2-PIX-FMT-Y10">V4L2_PIX_FMT_Y10</link>     v4l2_fourcc('Y', '1', '0', ' ') /* 10  Greyscale     */
 #define <link linkend="V4L2-PIX-FMT-Y16">V4L2_PIX_FMT_Y16</link>     v4l2_fourcc('Y', '1', '6', ' ') /* 16  Greyscale     */
 
 /* Palette formats */
@@ -330,7 +324,11 @@
 #define <link linkend="V4L2-PIX-FMT-SBGGR8">V4L2_PIX_FMT_SBGGR8</link>  v4l2_fourcc('B', 'A', '8', '1') /*  8  BGBG.. GRGR.. */
 #define <link linkend="V4L2-PIX-FMT-SGBRG8">V4L2_PIX_FMT_SGBRG8</link>  v4l2_fourcc('G', 'B', 'R', 'G') /*  8  GBGB.. RGRG.. */
 #define <link linkend="V4L2-PIX-FMT-SGRBG8">V4L2_PIX_FMT_SGRBG8</link>  v4l2_fourcc('G', 'R', 'B', 'G') /*  8  GRGR.. BGBG.. */
-#define <link linkend="V4L2-PIX-FMT-SGRBG10">V4L2_PIX_FMT_SGRBG10</link> v4l2_fourcc('B', 'A', '1', '0') /* 10bit raw bayer */
+#define <link linkend="V4L2-PIX-FMT-SRGGB8">V4L2_PIX_FMT_SRGGB8</link>  v4l2_fourcc('R', 'G', 'G', 'B') /*  8  RGRG.. GBGB.. */
+#define <link linkend="V4L2-PIX-FMT-SBGGR10">V4L2_PIX_FMT_SBGGR10</link> v4l2_fourcc('B', 'G', '1', '0') /* 10  BGBG.. GRGR.. */
+#define <link linkend="V4L2-PIX-FMT-SGBRG10">V4L2_PIX_FMT_SGBRG10</link> v4l2_fourcc('G', 'B', '1', '0') /* 10  GBGB.. RGRG.. */
+#define <link linkend="V4L2-PIX-FMT-SGRBG10">V4L2_PIX_FMT_SGRBG10</link> v4l2_fourcc('B', 'A', '1', '0') /* 10  GRGR.. BGBG.. */
+#define <link linkend="V4L2-PIX-FMT-SRGGB10">V4L2_PIX_FMT_SRGGB10</link> v4l2_fourcc('R', 'G', '1', '0') /* 10  RGRG.. GBGB.. */
         /* 10bit raw bayer DPCM compressed to 8 bits */
 #define <link linkend="V4L2-PIX-FMT-SGRBG10DPCM8">V4L2_PIX_FMT_SGRBG10DPCM8</link> v4l2_fourcc('B', 'D', '1', '0')
         /*
@@ -346,6 +344,7 @@
 #define <link linkend="V4L2-PIX-FMT-MPEG">V4L2_PIX_FMT_MPEG</link>     v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4    */
 
 /*  Vendor-specific formats   */
+#define <link linkend="V4L2-PIX-FMT-CPIA1">V4L2_PIX_FMT_CPIA1</link>    v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
 #define <link linkend="V4L2-PIX-FMT-WNVA">V4L2_PIX_FMT_WNVA</link>     v4l2_fourcc('W', 'N', 'V', 'A') /* Winnov hw compress */
 #define <link linkend="V4L2-PIX-FMT-SN9C10X">V4L2_PIX_FMT_SN9C10X</link>  v4l2_fourcc('S', '9', '1', '0') /* SN9C10x compression */
 #define <link linkend="V4L2-PIX-FMT-SN9C20X-I420">V4L2_PIX_FMT_SN9C20X_I420</link> v4l2_fourcc('S', '9', '2', '0') /* SN9C20x YUV 4:2:0 */
@@ -358,12 +357,15 @@
 #define <link linkend="V4L2-PIX-FMT-SPCA561">V4L2_PIX_FMT_SPCA561</link>  v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */
 #define <link linkend="V4L2-PIX-FMT-PAC207">V4L2_PIX_FMT_PAC207</link>   v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */
 #define <link linkend="V4L2-PIX-FMT-MR97310A">V4L2_PIX_FMT_MR97310A</link> v4l2_fourcc('M', '3', '1', '0') /* compressed BGGR bayer */
+#define <link linkend="V4L2-PIX-FMT-SN9C2028">V4L2_PIX_FMT_SN9C2028</link> v4l2_fourcc('S', 'O', 'N', 'X') /* compressed GBRG bayer */
 #define <link linkend="V4L2-PIX-FMT-SQ905C">V4L2_PIX_FMT_SQ905C</link>   v4l2_fourcc('9', '0', '5', 'C') /* compressed RGGB bayer */
 #define <link linkend="V4L2-PIX-FMT-PJPG">V4L2_PIX_FMT_PJPG</link>     v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */
 #define <link linkend="V4L2-PIX-FMT-OV511">V4L2_PIX_FMT_OV511</link>    v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */
 #define <link linkend="V4L2-PIX-FMT-OV518">V4L2_PIX_FMT_OV518</link>    v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */
-#define <link linkend="V4L2-PIX-FMT-TM6000">V4L2_PIX_FMT_TM6000</link>   v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */
 #define <link linkend="V4L2-PIX-FMT-STV0680">V4L2_PIX_FMT_STV0680</link>  v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */
+#define <link linkend="V4L2-PIX-FMT-TM6000">V4L2_PIX_FMT_TM6000</link>   v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */
+#define <link linkend="V4L2-PIX-FMT-CIT-YYVYUY">V4L2_PIX_FMT_CIT_YYVYUY</link> v4l2_fourcc('C', 'I', 'T', 'V') /* one line of Y then 1 line of VYUY */
+#define <link linkend="V4L2-PIX-FMT-KONICA420">V4L2_PIX_FMT_KONICA420</link>  v4l2_fourcc('K', 'O', 'N', 'I') /* YUV420 planar in blocks of 256 pixels */
 
 /*
  *      F O R M A T   E N U M E R A T I O N
@@ -380,7 +382,7 @@
 #define V4L2_FMT_FLAG_COMPRESSED 0x0001
 #define V4L2_FMT_FLAG_EMULATED   0x0002
 
-#if 1 /*KEEP*/
+#if 1
         /* Experimental Frame Size and frame rate enumeration */
 /*
  *      F R A M E   S I Z E   E N U M E R A T I O N
@@ -544,6 +546,8 @@
 #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 */
+/* Buffer is ready, but the data contained within is corrupted. */
+#define V4L2_BUF_FLAG_ERROR     0x0040
 #define V4L2_BUF_FLAG_TIMECODE  0x0100  /* timecode field is valid */
 #define V4L2_BUF_FLAG_INPUT     0x0200  /* input field is valid */
 
@@ -934,6 +938,16 @@
 #define V4L2_CTRL_ID2CLASS(id)    ((id) &amp; 0x0fff0000UL)
 #define V4L2_CTRL_DRIVER_PRIV(id) (((id) &amp; 0xffff) &gt;= 0x1000)
 
+enum <link linkend="v4l2-ctrl-type">v4l2_ctrl_type</link> {
+        V4L2_CTRL_TYPE_INTEGER       = 1,
+        V4L2_CTRL_TYPE_BOOLEAN       = 2,
+        V4L2_CTRL_TYPE_MENU          = 3,
+        V4L2_CTRL_TYPE_BUTTON        = 4,
+        V4L2_CTRL_TYPE_INTEGER64     = 5,
+        V4L2_CTRL_TYPE_CTRL_CLASS    = 6,
+        V4L2_CTRL_TYPE_STRING        = 7,
+};
+
 /*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
 struct <link linkend="v4l2-queryctrl">v4l2_queryctrl</link> {
         __u32                id;
@@ -1018,21 +1032,27 @@
         V4L2_COLORFX_NONE       = 0,
         V4L2_COLORFX_BW         = 1,
         V4L2_COLORFX_SEPIA      = 2,
-        V4L2_COLORFX_NEGATIVE   = 3,
-        V4L2_COLORFX_EMBOSS     = 4,
-        V4L2_COLORFX_SKETCH     = 5,
-        V4L2_COLORFX_SKY_BLUE   = 6,
+        V4L2_COLORFX_NEGATIVE = 3,
+        V4L2_COLORFX_EMBOSS = 4,
+        V4L2_COLORFX_SKETCH = 5,
+        V4L2_COLORFX_SKY_BLUE = 6,
         V4L2_COLORFX_GRASS_GREEN = 7,
         V4L2_COLORFX_SKIN_WHITEN = 8,
-        V4L2_COLORFX_VIVID      = 9.
+        V4L2_COLORFX_VIVID = 9,
 };
 #define V4L2_CID_AUTOBRIGHTNESS                 (V4L2_CID_BASE+32)
 #define V4L2_CID_BAND_STOP_FILTER               (V4L2_CID_BASE+33)
 
 #define V4L2_CID_ROTATE                         (V4L2_CID_BASE+34)
 #define V4L2_CID_BG_COLOR                       (V4L2_CID_BASE+35)
+
+#define V4L2_CID_CHROMA_GAIN                    (V4L2_CID_BASE+36)
+
+#define V4L2_CID_ILLUMINATORS_1                 (V4L2_CID_BASE+37)
+#define V4L2_CID_ILLUMINATORS_2                 (V4L2_CID_BASE+38)
+
 /* last CID + 1 */
-#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+36)
+#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+39)
 
 /*  MPEG-class control IDs defined by V4L2 */
 #define V4L2_CID_MPEG_BASE                      (V4L2_CTRL_CLASS_MPEG | 0x900)
@@ -1349,6 +1369,8 @@
 #define V4L2_TUNER_CAP_SAP              0x0020
 #define V4L2_TUNER_CAP_LANG1            0x0040
 #define V4L2_TUNER_CAP_RDS              0x0080
+#define V4L2_TUNER_CAP_RDS_BLOCK_IO     0x0100
+#define V4L2_TUNER_CAP_RDS_CONTROLS     0x0200
 
 /*  Flags for the 'rxsubchans' field */
 #define V4L2_TUNER_SUB_MONO             0x0001
@@ -1378,7 +1400,8 @@
         enum <link linkend="v4l2-tuner-type">v4l2_tuner_type</link>  type;
         __u32                 seek_upward;
         __u32                 wrap_around;
-        __u32                 reserved[8];
+        __u32                 spacing;
+        __u32                 reserved[7];
 };
 
 /*
@@ -1433,7 +1456,7 @@
  *
  *      NOTE: EXPERIMENTAL API
  */
-#if 1 /*KEEP*/
+#if 1
 #define V4L2_ENC_IDX_FRAME_I    (0)
 #define V4L2_ENC_IDX_FRAME_P    (1)
 #define V4L2_ENC_IDX_FRAME_B    (2)
@@ -1626,6 +1649,38 @@
 };
 
 /*
+ *      E V E N T S
+ */
+
+#define V4L2_EVENT_ALL                          0
+#define V4L2_EVENT_VSYNC                        1
+#define V4L2_EVENT_EOS                          2
+#define V4L2_EVENT_PRIVATE_START                0x08000000
+
+/* Payload for V4L2_EVENT_VSYNC */
+struct <link linkend="v4l2-event-vsync">v4l2_event_vsync</link> {
+        /* Can be V4L2_FIELD_ANY, _NONE, _TOP or _BOTTOM */
+        __u8 field;
+} __attribute__ ((packed));
+
+struct <link linkend="v4l2-event">v4l2_event</link> {
+        __u32                           type;
+        union {
+                struct <link linkend="v4l2-event-vsync">v4l2_event_vsync</link> vsync;
+                __u8                    data[64];
+        } u;
+        __u32                           pending;
+        __u32                           sequence;
+        struct timespec                 timestamp;
+        __u32                           reserved[9];
+};
+
+struct <link linkend="v4l2-event-subscription">v4l2_event_subscription</link> {
+        __u32                           type;
+        __u32                           reserved[7];
+};
+
+/*
  *      A D V A N C E D   D E B U G G I N G
  *
  *      NOTE: EXPERIMENTAL API, NEVER RELY ON THIS IN APPLICATIONS!
@@ -1720,7 +1775,7 @@
 #define VIDIOC_G_EXT_CTRLS      _IOWR('V', 71, struct <link linkend="v4l2-ext-controls">v4l2_ext_controls</link>)
 #define VIDIOC_S_EXT_CTRLS      _IOWR('V', 72, struct <link linkend="v4l2-ext-controls">v4l2_ext_controls</link>)
 #define VIDIOC_TRY_EXT_CTRLS    _IOWR('V', 73, struct <link linkend="v4l2-ext-controls">v4l2_ext_controls</link>)
-#if 1 /*KEEP*/
+#if 1
 #define VIDIOC_ENUM_FRAMESIZES  _IOWR('V', 74, struct <link linkend="v4l2-frmsizeenum">v4l2_frmsizeenum</link>)
 #define VIDIOC_ENUM_FRAMEINTERVALS _IOWR('V', 75, struct <link linkend="v4l2-frmivalenum">v4l2_frmivalenum</link>)
 #define VIDIOC_G_ENC_INDEX       _IOR('V', 76, struct <link linkend="v4l2-enc-idx">v4l2_enc_idx</link>)
@@ -1728,7 +1783,7 @@
 #define VIDIOC_TRY_ENCODER_CMD  _IOWR('V', 78, struct <link linkend="v4l2-encoder-cmd">v4l2_encoder_cmd</link>)
 #endif
 
-#if 1 /*KEEP*/
+#if 1
 /* Experimental, meant for debugging, testing and internal use.
    Only implemented if CONFIG_VIDEO_ADV_DEBUG is defined.
    You must be root to use these ioctls. Never use these in applications! */
@@ -1747,6 +1802,9 @@
 #define VIDIOC_QUERY_DV_PRESET  _IOR('V',  86, struct <link linkend="v4l2-dv-preset">v4l2_dv_preset</link>)
 #define VIDIOC_S_DV_TIMINGS     _IOWR('V', 87, struct <link linkend="v4l2-dv-timings">v4l2_dv_timings</link>)
 #define VIDIOC_G_DV_TIMINGS     _IOWR('V', 88, struct <link linkend="v4l2-dv-timings">v4l2_dv_timings</link>)
+#define VIDIOC_DQEVENT           _IOR('V', 89, struct <link linkend="v4l2-event">v4l2_event</link>)
+#define VIDIOC_SUBSCRIBE_EVENT   _IOW('V', 90, struct <link linkend="v4l2-event-subscription">v4l2_event_subscription</link>)
+#define VIDIOC_UNSUBSCRIBE_EVENT _IOW('V', 91, struct <link linkend="v4l2-event-subscription">v4l2_event_subscription</link>)
 
 /* Reminder: when adding new ioctls please add support for them to
    drivers/media/video/v4l2-compat-ioctl32.c as well! */
diff --git a/Documentation/DocBook/v4l/vidioc-g-dv-preset.xml b/Documentation/DocBook/v4l/vidioc-g-dv-preset.xml
index 3c6784e..d733721 100644
--- a/Documentation/DocBook/v4l/vidioc-g-dv-preset.xml
+++ b/Documentation/DocBook/v4l/vidioc-g-dv-preset.xml
@@ -16,8 +16,7 @@
 	<funcdef>int <function>ioctl</function></funcdef>
 	<paramdef>int <parameter>fd</parameter></paramdef>
 	<paramdef>int <parameter>request</parameter></paramdef>
-	<paramdef>&v4l2-dv-preset;
-*<parameter>argp</parameter></paramdef>
+	<paramdef>struct v4l2_dv_preset *<parameter>argp</parameter></paramdef>
       </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
diff --git a/Documentation/DocBook/v4l/vidioc-g-dv-timings.xml b/Documentation/DocBook/v4l/vidioc-g-dv-timings.xml
index ecc1957..d5ec6ab 100644
--- a/Documentation/DocBook/v4l/vidioc-g-dv-timings.xml
+++ b/Documentation/DocBook/v4l/vidioc-g-dv-timings.xml
@@ -16,8 +16,7 @@
 	<funcdef>int <function>ioctl</function></funcdef>
 	<paramdef>int <parameter>fd</parameter></paramdef>
 	<paramdef>int <parameter>request</parameter></paramdef>
-	<paramdef>&v4l2-dv-timings;
-*<parameter>argp</parameter></paramdef>
+	<paramdef>struct v4l2_dv_timings *<parameter>argp</parameter></paramdef>
       </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
diff --git a/Documentation/DocBook/v4l/vidioc-query-dv-preset.xml b/Documentation/DocBook/v4l/vidioc-query-dv-preset.xml
index 402229e..d272f7a 100644
--- a/Documentation/DocBook/v4l/vidioc-query-dv-preset.xml
+++ b/Documentation/DocBook/v4l/vidioc-query-dv-preset.xml
@@ -16,7 +16,7 @@
 	<funcdef>int <function>ioctl</function></funcdef>
 	<paramdef>int <parameter>fd</parameter></paramdef>
 	<paramdef>int <parameter>request</parameter></paramdef>
-	<paramdef>&v4l2-dv-preset; *<parameter>argp</parameter></paramdef>
+	<paramdef>struct v4l2_dv_preset *<parameter>argp</parameter></paramdef>
       </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
diff --git a/Documentation/DocBook/v4l/vidioc-querycap.xml b/Documentation/DocBook/v4l/vidioc-querycap.xml
index 6ab7e25..d499da9 100644
--- a/Documentation/DocBook/v4l/vidioc-querycap.xml
+++ b/Documentation/DocBook/v4l/vidioc-querycap.xml
@@ -184,7 +184,7 @@
 	  <row>
 	    <entry><constant>V4L2_CAP_RDS_CAPTURE</constant></entry>
 	    <entry>0x00000100</entry>
-	    <entry>The device supports the <link linkend="rds">RDS</link> interface.</entry>
+	    <entry>The device supports the <link linkend="rds">RDS</link> capture interface.</entry>
 	  </row>
 	  <row>
 	    <entry><constant>V4L2_CAP_VIDEO_OUTPUT_OVERLAY</constant></entry>
@@ -206,6 +206,11 @@
 hardware frequency seeking.</entry>
 	  </row>
 	  <row>
+	    <entry><constant>V4L2_CAP_RDS_OUTPUT</constant></entry>
+	    <entry>0x00000800</entry>
+	    <entry>The device supports the <link linkend="rds">RDS</link> output interface.</entry>
+	  </row>
+	  <row>
 	    <entry><constant>V4L2_CAP_TUNER</constant></entry>
 	    <entry>0x00010000</entry>
 	    <entry>The device has some sort of tuner to
diff --git a/Documentation/DocBook/v4l/vidioc-queryctrl.xml b/Documentation/DocBook/v4l/vidioc-queryctrl.xml
index 8e0e055..0d5e828 100644
--- a/Documentation/DocBook/v4l/vidioc-queryctrl.xml
+++ b/Documentation/DocBook/v4l/vidioc-queryctrl.xml
@@ -103,8 +103,12 @@
 <structfield>index</structfield> is invalid. Menu items are enumerated
 by calling <constant>VIDIOC_QUERYMENU</constant> with successive
 <structfield>index</structfield> values from &v4l2-queryctrl;
-<structfield>minimum</structfield> (0) to
-<structfield>maximum</structfield>, inclusive.</para>
+<structfield>minimum</structfield> to
+<structfield>maximum</structfield>, inclusive. Note that it is possible
+for <constant>VIDIOC_QUERYMENU</constant> to return an &EINVAL; for some
+indices between <structfield>minimum</structfield> and <structfield>maximum</structfield>.
+In that case that particular menu item is not supported by this driver. Also note that
+the <structfield>minimum</structfield> value is not necessarily 0.</para>
 
     <para>See also the examples in <xref linkend="control" />.</para>
 
@@ -139,7 +143,7 @@
 	    <entry><structfield>minimum</structfield></entry>
 	    <entry>Minimum value, inclusive. This field gives a lower
 bound for <constant>V4L2_CTRL_TYPE_INTEGER</constant> controls and the
-lowest valid index (always 0) for <constant>V4L2_CTRL_TYPE_MENU</constant> controls.
+lowest valid index for <constant>V4L2_CTRL_TYPE_MENU</constant> controls.
 For <constant>V4L2_CTRL_TYPE_STRING</constant> controls the minimum value
 gives the minimum length of the string. This length <emphasis>does not include the terminating
 zero</emphasis>. It may not be valid for any other type of control, including
@@ -279,7 +283,7 @@
 	  </row>
 	  <row>
 	    <entry><constant>V4L2_CTRL_TYPE_MENU</constant></entry>
-	    <entry>0</entry>
+	    <entry>&ge; 0</entry>
 	    <entry>1</entry>
 	    <entry>N-1</entry>
 	    <entry>The control has a menu of N choices. The names of
@@ -405,8 +409,10 @@
 	<term><errorcode>EINVAL</errorcode></term>
 	<listitem>
 	  <para>The &v4l2-queryctrl; <structfield>id</structfield>
-is invalid. The &v4l2-querymenu; <structfield>id</structfield> or
-<structfield>index</structfield> is invalid.</para>
+is invalid. The &v4l2-querymenu; <structfield>id</structfield> is
+invalid or <structfield>index</structfield> is out of range (less than
+<structfield>minimum</structfield> or greater than <structfield>maximum</structfield>)
+or this particular menu item is not supported by the driver.</para>
 	</listitem>
       </varlistentry>
       <varlistentry>
diff --git a/Documentation/DocBook/v4l/vidioc-s-hw-freq-seek.xml b/Documentation/DocBook/v4l/vidioc-s-hw-freq-seek.xml
index 14b3ec7..c30dcc4 100644
--- a/Documentation/DocBook/v4l/vidioc-s-hw-freq-seek.xml
+++ b/Documentation/DocBook/v4l/vidioc-s-hw-freq-seek.xml
@@ -51,7 +51,8 @@
 
     <para>Start a hardware frequency seek from the current frequency.
 To do this applications initialize the <structfield>tuner</structfield>,
-<structfield>type</structfield>, <structfield>seek_upward</structfield> and
+<structfield>type</structfield>, <structfield>seek_upward</structfield>,
+<structfield>spacing</structfield> and
 <structfield>wrap_around</structfield> fields, and zero out the
 <structfield>reserved</structfield> array of a &v4l2-hw-freq-seek; and
 call the <constant>VIDIOC_S_HW_FREQ_SEEK</constant> ioctl with a pointer
@@ -89,7 +90,12 @@
 	  </row>
 	  <row>
 	    <entry>__u32</entry>
-	    <entry><structfield>reserved</structfield>[8]</entry>
+	    <entry><structfield>spacing</structfield></entry>
+	    <entry>If non-zero, defines the hardware seek resolution in Hz. The driver selects the nearest value that is supported by the device. If spacing is zero a reasonable default value is used.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>reserved</structfield>[7]</entry>
 	    <entry>Reserved for future extensions. Drivers and
 	    applications must set the array to zero.</entry>
 	  </row>
diff --git a/Documentation/devices.txt b/Documentation/devices.txt
index 170cc1e..eccffe7 100644
--- a/Documentation/devices.txt
+++ b/Documentation/devices.txt
@@ -1496,9 +1496,6 @@
 		 64 = /dev/radio0	Radio device
 		    ...
 		127 = /dev/radio63	Radio device
-		192 = /dev/vtx0		Teletext device
-		    ...
-		223 = /dev/vtx31	Teletext device
 		224 = /dev/vbi0		Vertical blank interrupt
 		    ...
 		255 = /dev/vbi31	Vertical blank interrupt
diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware
index 350959f..59690de 100644
--- a/Documentation/dvb/get_dvb_firmware
+++ b/Documentation/dvb/get_dvb_firmware
@@ -26,7 +26,8 @@
 		"dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004",
 		"or51211", "or51132_qam", "or51132_vsb", "bluebird",
 		"opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718",
-		"af9015", "ngene", "az6027");
+		"af9015", "ngene", "az6027", "lme2510_lg", "lme2510c_s7395",
+		"lme2510c_s7395_old");
 
 # Check args
 syntax() if (scalar(@ARGV) != 1);
@@ -584,6 +585,49 @@
 
     $firmware;
 }
+
+sub lme2510_lg {
+    my $sourcefile = "LMEBDA_DVBS.sys";
+    my $hash = "fc6017ad01e79890a97ec53bea157ed2";
+    my $outfile = "dvb-usb-lme2510-lg.fw";
+    my $hasho = "caa065d5fdbd2c09ad57b399bbf55cad";
+
+    checkstandard();
+
+    verify($sourcefile, $hash);
+    extract($sourcefile, 4168, 3841, $outfile);
+    verify($outfile, $hasho);
+    $outfile;
+}
+
+sub lme2510c_s7395 {
+    my $sourcefile = "US2A0D.sys";
+    my $hash = "b0155a8083fb822a3bd47bc360e74601";
+    my $outfile = "dvb-usb-lme2510c-s7395.fw";
+    my $hasho = "3a3cf1aeebd17b6ddc04cebe131e94cf";
+
+    checkstandard();
+
+    verify($sourcefile, $hash);
+    extract($sourcefile, 37248, 3720, $outfile);
+    verify($outfile, $hasho);
+    $outfile;
+}
+
+sub lme2510c_s7395_old {
+    my $sourcefile = "LMEBDA_DVBS7395C.sys";
+    my $hash = "7572ae0eb9cdf91baabd7c0ba9e09b31";
+    my $outfile = "dvb-usb-lme2510c-s7395.fw";
+    my $hasho = "90430c5b435eb5c6f88fd44a9d950674";
+
+    checkstandard();
+
+    verify($sourcefile, $hash);
+    extract($sourcefile, 4208, 3881, $outfile);
+    verify($outfile, $hasho);
+    $outfile;
+}
+
 # ---------------------------------------------------------------
 # Utilities
 
diff --git a/Documentation/dvb/lmedm04.txt b/Documentation/dvb/lmedm04.txt
new file mode 100644
index 0000000..e175784
--- /dev/null
+++ b/Documentation/dvb/lmedm04.txt
@@ -0,0 +1,58 @@
+To extract firmware for the DM04/QQBOX you need to copy the
+following file(s) to this directory.
+
+for DM04+/QQBOX LME2510C (Sharp 7395 Tuner)
+-------------------------------------------
+
+The Sharp 7395 driver can be found in windows/system32/driver
+
+US2A0D.sys (dated 17 Mar 2009)
+
+
+and run
+./get_dvb_firmware lme2510c_s7395
+
+	will produce
+	dvb-usb-lme2510c-s7395.fw
+
+An alternative but older firmware can be found on the driver
+disk DVB-S_EN_3.5A in BDADriver/driver
+
+LMEBDA_DVBS7395C.sys (dated 18 Jan 2008)
+
+and run
+./get_dvb_firmware lme2510c_s7395_old
+
+	will produce
+	dvb-usb-lme2510c-s7395.fw
+
+--------------------------------------------------------------------
+
+The LG firmware can be found on the driver
+disk DM04+_5.1A[LG] in BDADriver/driver
+
+for DM04 LME2510 (LG Tuner)
+---------------------------
+
+LMEBDA_DVBS.sys (dated 13 Nov 2007)
+
+and run
+./get_dvb_firmware lme2510_lg
+
+	will produce
+	dvb-usb-lme2510-lg.fw
+
+
+Other LG firmware can be extracted manually from US280D.sys
+only found in windows/system32/driver.
+
+dd if=US280D.sys ibs=1 skip=42616 count=3668 of=dvb-usb-lme2510-lg.fw
+
+for DM04 LME2510C (LG Tuner)
+---------------------------
+
+dd if=US280D.sys ibs=1 skip=35200 count=3850 of=dvb-usb-lme2510c-lg.fw
+
+---------------------------------------------------------------------
+
+Copy the firmware file(s) to /lib/firmware
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index f3da8c0..d8f36f9 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -98,7 +98,7 @@
 ---------------------------
 
 What:	Video4Linux API 1 ioctls and from Video devices.
-When:	July 2009
+When:	kernel 2.6.38
 Files:	include/linux/videodev.h
 Check:	include/linux/videodev.h
 Why:	V4L1 AP1 was replaced by V4L2 API during migration from 2.4 to 2.6
@@ -116,6 +116,21 @@
 
 ---------------------------
 
+What:	Video4Linux obsolete drivers using V4L1 API
+When:	kernel 2.6.38
+Files:	drivers/staging/cpia/* drivers/staging/stradis/*
+Check:	drivers/staging/cpia/cpia.c drivers/staging/stradis/stradis.c
+Why:	There are some drivers still using V4L1 API, despite all efforts we've done
+	to migrate. Those drivers are for obsolete hardware that the old maintainer
+	didn't care (or not have the hardware anymore), and that no other developer
+	could find any hardware to buy. They probably have no practical usage today,
+	and people with such old hardware could probably keep using an older version
+	of the kernel. Those drivers will be moved to staging on 2.6.37 and, if nobody
+	care enough to port and test them with V4L2 API, they'll be removed on 2.6.38.
+Who:	Mauro Carvalho Chehab <mchehab@infradead.org>
+
+---------------------------
+
 What:	sys_sysctl
 When:	September 2010
 Option: CONFIG_SYSCTL_SYSCALL
@@ -470,29 +485,6 @@
 Why:	Superseded by xt_CT
 Who:	Netfilter developer team <netfilter-devel@vger.kernel.org>
 
----------------------------
-
-What:	video4linux /dev/vtx teletext API support
-When:	2.6.35
-Files:	drivers/media/video/saa5246a.c drivers/media/video/saa5249.c
-	include/linux/videotext.h
-Why:	The vtx device nodes have been superseded by vbi device nodes
-	for many years. No applications exist that use the vtx support.
-	Of the two i2c drivers that actually support this API the saa5249
-	has been impossible to use for a year now and no known hardware
-	that supports this device exists. The saa5246a is theoretically
-	supported by the old mxb boards, but it never actually worked.
-
-	In summary: there is no hardware that can use this API and there
-	are no applications actually implementing this API.
-
-	The vtx support still reserves minors 192-223 and we would really
-	like to reuse those for upcoming new functionality. In the unlikely
-	event that new hardware appears that wants to use the functionality
-	provided by the vtx API, then that functionality should be build
-	around the sliced VBI API instead.
-Who:	Hans Verkuil <hverkuil@xs4all.nl>
-
 ----------------------------
 
 What:	IRQF_DISABLED
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 33223ff..10f5af8 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -278,7 +278,6 @@
 					<mailto:oe@port.de>
 'z'	10-4F	drivers/s390/crypto/zcrypt_api.h	conflict!
 0x80	00-1F	linux/fb.h
-0x81	00-1F	linux/videotext.h
 0x88	00-3F	media/ovcamchip.h
 0x89	00-06	arch/x86/include/asm/sockios.h
 0x89	0B-DF	linux/sockios.h
diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88
index f251054..42517d9 100644
--- a/Documentation/video4linux/CARDLIST.cx88
+++ b/Documentation/video4linux/CARDLIST.cx88
@@ -83,3 +83,4 @@
  82 -> WinFast DTV2000 H rev. J                            [107d:6f2b]
  83 -> Prof 7301 DVB-S/S2                                  [b034:3034]
  84 -> Samsung SMT 7020 DVB-S                              [18ac:dc00,18ac:dccd]
+ 85 -> Twinhan VP-1027 DVB-S                               [1822:0023]
diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx
index 5c56875..ac2616a 100644
--- a/Documentation/video4linux/CARDLIST.em28xx
+++ b/Documentation/video4linux/CARDLIST.em28xx
@@ -31,6 +31,7 @@
  30 -> Videology 20K14XUSB USB2.0               (em2820/em2840)
  31 -> Usbgear VD204v9                          (em2821)
  32 -> Supercomp USB 2.0 TV                     (em2821)
+ 33 -> Elgato Video Capture                     (em2860)        [0fd9:0033]
  34 -> Terratec Cinergy A Hybrid XS             (em2860)        [0ccd:004f]
  35 -> Typhoon DVD Maker                        (em2860)
  36 -> NetGMBH Cam                              (em2860)
@@ -45,7 +46,7 @@
  45 -> Pinnacle PCTV DVB-T                      (em2870)
  46 -> Compro, VideoMate U3                     (em2870)        [185b:2870]
  47 -> KWorld DVB-T 305U                        (em2880)        [eb1a:e305]
- 48 -> KWorld DVB-T 310U                        (em2880)        [eb1a:e310]
+ 48 -> KWorld DVB-T 310U                        (em2880)
  49 -> MSI DigiVox A/D                          (em2880)        [eb1a:e310]
  50 -> MSI DigiVox A/D II                       (em2880)        [eb1a:e320]
  51 -> Terratec Hybrid XS Secam                 (em2880)        [0ccd:004c]
diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134
index 4000c29..8d9afc7 100644
--- a/Documentation/video4linux/CARDLIST.saa7134
+++ b/Documentation/video4linux/CARDLIST.saa7134
@@ -126,7 +126,7 @@
 125 -> Beholder BeholdTV 409                    [0000:4090]
 126 -> Beholder BeholdTV 505 FM                 [5ace:5050]
 127 -> Beholder BeholdTV 507 FM / BeholdTV 509 FM [5ace:5070,5ace:5090]
-128 -> Beholder BeholdTV Columbus TVFM          [0000:5201]
+128 -> Beholder BeholdTV Columbus TV/FM         [0000:5201]
 129 -> Beholder BeholdTV 607 FM                 [5ace:6070]
 130 -> Beholder BeholdTV M6                     [5ace:6190]
 131 -> Twinhan Hybrid DTV-DVB 3056 PCI          [1822:0022]
diff --git a/Documentation/video4linux/bttv/MAKEDEV b/Documentation/video4linux/bttv/MAKEDEV
index 9d112f7..093c0cd 100644
--- a/Documentation/video4linux/bttv/MAKEDEV
+++ b/Documentation/video4linux/bttv/MAKEDEV
@@ -19,7 +19,6 @@
 echo "*** new device names ***"
 makedev video 0
 makedev radio 64
-makedev vtx 192
 makedev vbi 224
 
 #echo "*** old device names (for compatibility only) ***"
diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt
index 56ba7bb..6a562ee 100644
--- a/Documentation/video4linux/gspca.txt
+++ b/Documentation/video4linux/gspca.txt
@@ -302,12 +302,14 @@
 sonixj		0c45:60fc	LG-LIC300
 sonixj		0c45:60fe	Microdia Audio
 sonixj		0c45:6100	PC Camera (SN9C128)
+sonixj		0c45:6102	PC Camera (SN9C128)
 sonixj		0c45:610a	PC Camera (SN9C128)
 sonixj		0c45:610b	PC Camera (SN9C128)
 sonixj		0c45:610c	PC Camera (SN9C128)
 sonixj		0c45:610e	PC Camera (SN9C128)
 sonixj		0c45:6128	Microdia/Sonix SNP325
 sonixj		0c45:612a	Avant Camera
+sonixj		0c45:612b	Speed-Link REFLECT2
 sonixj		0c45:612c	Typhoon Rasy Cam 1.3MPix
 sonixj		0c45:6130	Sonix Pccam
 sonixj		0c45:6138	Sn9c120 Mo4000
diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt
index e831aac..f22f35c 100644
--- a/Documentation/video4linux/v4l2-framework.txt
+++ b/Documentation/video4linux/v4l2-framework.txt
@@ -44,8 +44,8 @@
 
 2) A way of initializing and commanding sub-devices (if any).
 
-3) Creating V4L2 device nodes (/dev/videoX, /dev/vbiX, /dev/radioX and
-   /dev/vtxX) and keeping track of device-node specific data.
+3) Creating V4L2 device nodes (/dev/videoX, /dev/vbiX and /dev/radioX)
+   and keeping track of device-node specific data.
 
 4) Filehandle-specific structs containing per-filehandle data;
 
@@ -192,6 +192,11 @@
 common i2c_client struct the i2c_set_clientdata() call is used to store a
 v4l2_subdev pointer, for other busses you may have to use other methods.
 
+Bridges might also need to store per-subdev private data, such as a pointer to
+bridge-specific per-subdev private data. The v4l2_subdev structure provides
+host private data for that purpose that can be accessed with
+v4l2_get_subdev_hostdata() and v4l2_set_subdev_hostdata().
+
 From the bridge driver perspective you load the sub-device module and somehow
 obtain the v4l2_subdev pointer. For i2c devices this is easy: you call
 i2c_get_clientdata(). For other busses something similar needs to be done.
@@ -448,6 +453,10 @@
 - ioctl_ops: if you use the v4l2_ioctl_ops to simplify ioctl maintenance
   (highly recommended to use this and it might become compulsory in the
   future!), then set this to your v4l2_ioctl_ops struct.
+- lock: leave to NULL if you want to do all the locking in the driver.
+  Otherwise you give it a pointer to a struct mutex_lock and before any
+  of the v4l2_file_operations is called this lock will be taken by the
+  core and released afterwards.
 - parent: you only set this if v4l2_device was registered with NULL as
   the parent device struct. This only happens in cases where one hardware
   device has multiple PCI devices that all share the same v4l2_device core.
@@ -464,6 +473,22 @@
 The v4l2_file_operations struct is a subset of file_operations. The main
 difference is that the inode argument is omitted since it is never used.
 
+v4l2_file_operations and locking
+--------------------------------
+
+You can set a pointer to a mutex_lock in struct video_device. Usually this
+will be either a top-level mutex or a mutex per device node. If you want
+finer-grained locking then you have to set it to NULL and do you own locking.
+
+If a lock is specified then all file operations will be serialized on that
+lock. If you use videobuf then you must pass the same lock to the videobuf
+queue initialize function: if videobuf has to wait for a frame to arrive, then
+it will temporarily unlock the lock and relock it afterwards. If your driver
+also waits in the code, then you should do the same to allow other processes
+to access the device node while the first process is waiting for something.
+
+The implementation of a hotplug disconnect should also take the lock before
+calling v4l2_device_disconnect.
 
 video_device registration
 -------------------------
@@ -483,7 +508,6 @@
 VFL_TYPE_GRABBER: videoX for video input/output devices
 VFL_TYPE_VBI: vbiX for vertical blank data (i.e. closed captions, teletext)
 VFL_TYPE_RADIO: radioX for radio tuners
-VFL_TYPE_VTX: vtxX for teletext devices (deprecated, don't use)
 
 The last argument gives you a certain amount of control over the device
 device node number used (i.e. the X in videoX). Normally you will pass -1
@@ -547,9 +571,8 @@
 
 After video_unregister_device() returns no new opens can be done. However,
 in the case of USB devices some application might still have one of these
-device nodes open. So after the unregister all file operations will return
-an error as well, except for the ioctl and unlocked_ioctl file operations:
-those will still be passed on since some buffer ioctls may still be needed.
+device nodes open. So after the unregister all file operations (except
+release, of course) will return an error as well.
 
 When the last user of the video device node exits, then the vdev->release()
 callback is called and you can do the final cleanup there.
diff --git a/arch/arm/mach-mx3/mach-pcm037.c b/arch/arm/mach-mx3/mach-pcm037.c
index 86e86c1..2ff3f66 100644
--- a/arch/arm/mach-mx3/mach-pcm037.c
+++ b/arch/arm/mach-mx3/mach-pcm037.c
@@ -311,7 +311,6 @@
 	.bus_id		= 0,		/* Must match with the camera ID */
 	.board_info	= &pcm037_i2c_camera[1],
 	.i2c_adapter_id	= 2,
-	.module_name	= "mt9v022",
 };
 
 static struct soc_camera_link iclink_mt9t031 = {
@@ -319,7 +318,6 @@
 	.power		= pcm037_camera_power,
 	.board_info	= &pcm037_i2c_camera[0],
 	.i2c_adapter_id	= 2,
-	.module_name	= "mt9t031",
 };
 
 static struct i2c_board_info pcm037_i2c_devices[] = {
diff --git a/arch/arm/mach-mx3/mx31moboard-marxbot.c b/arch/arm/mach-mx3/mx31moboard-marxbot.c
index 0551eb3..18069cb 100644
--- a/arch/arm/mach-mx3/mx31moboard-marxbot.c
+++ b/arch/arm/mach-mx3/mx31moboard-marxbot.c
@@ -179,7 +179,6 @@
 	.reset		= marxbot_basecam_reset,
 	.board_info	= &marxbot_i2c_devices[0],
 	.i2c_adapter_id	= 0,
-	.module_name	= "mt9t031",
 };
 
 static struct platform_device marxbot_camera[] = {
diff --git a/arch/arm/mach-mx3/mx31moboard-smartbot.c b/arch/arm/mach-mx3/mx31moboard-smartbot.c
index 417757e..04760a5 100644
--- a/arch/arm/mach-mx3/mx31moboard-smartbot.c
+++ b/arch/arm/mach-mx3/mx31moboard-smartbot.c
@@ -88,7 +88,6 @@
 	.reset		= smartbot_cam_reset,
 	.board_info	= &smartbot_i2c_devices[0],
 	.i2c_adapter_id	= 0,
-	.module_name	= "mt9t031",
 };
 
 static struct platform_device smartbot_camera[] = {
diff --git a/arch/arm/mach-pxa/em-x270.c b/arch/arm/mach-pxa/em-x270.c
index ab48bb8..ed0dbfdb 100644
--- a/arch/arm/mach-pxa/em-x270.c
+++ b/arch/arm/mach-pxa/em-x270.c
@@ -1015,7 +1015,6 @@
 	.power		= em_x270_sensor_power,
 	.board_info	= &em_x270_i2c_cam_info[0],
 	.i2c_adapter_id	= 0,
-	.module_name	= "mt9m111",
 };
 
 static struct platform_device em_x270_camera = {
diff --git a/arch/arm/mach-pxa/ezx.c b/arch/arm/mach-pxa/ezx.c
index 80a9352..142c711 100644
--- a/arch/arm/mach-pxa/ezx.c
+++ b/arch/arm/mach-pxa/ezx.c
@@ -755,7 +755,6 @@
 	.flags          = SOCAM_SENSOR_INVERT_PCLK,
 	.i2c_adapter_id = 0,
 	.board_info     = &a780_camera_i2c_board_info,
-	.module_name    = "mt9m111",
 	.power          = a780_camera_power,
 	.reset          = a780_camera_reset,
 };
@@ -1024,7 +1023,6 @@
 	.bus_id         = 0,
 	.i2c_adapter_id = 0,
 	.board_info     = &a910_camera_i2c_board_info,
-	.module_name    = "mt9m111",
 	.power          = a910_camera_power,
 	.reset          = a910_camera_reset,
 };
diff --git a/arch/arm/mach-pxa/mioa701.c b/arch/arm/mach-pxa/mioa701.c
index 0c31fab..f5fb915 100644
--- a/arch/arm/mach-pxa/mioa701.c
+++ b/arch/arm/mach-pxa/mioa701.c
@@ -711,7 +711,6 @@
 	.bus_id		= 0, /* Match id in pxa27x_device_camera in device.c */
 	.board_info	= &mioa701_i2c_devices[0],
 	.i2c_adapter_id	= 0,
-	.module_name	= "mt9m111",
 };
 
 struct i2c_pxa_platform_data i2c_pdata = {
diff --git a/arch/arm/mach-pxa/pcm990-baseboard.c b/arch/arm/mach-pxa/pcm990-baseboard.c
index f56ae10..f33647a 100644
--- a/arch/arm/mach-pxa/pcm990-baseboard.c
+++ b/arch/arm/mach-pxa/pcm990-baseboard.c
@@ -453,7 +453,6 @@
 		.query_bus_param	= pcm990_camera_query_bus_param,
 		.set_bus_param		= pcm990_camera_set_bus_param,
 		.free_bus		= pcm990_camera_free_bus,
-		.module_name		= "mt9v022",
 	}, {
 		.bus_id			= 0, /* Must match with the camera ID */
 		.board_info		= &pcm990_camera_i2c[1],
@@ -461,7 +460,6 @@
 		.query_bus_param	= pcm990_camera_query_bus_param,
 		.set_bus_param		= pcm990_camera_set_bus_param,
 		.free_bus		= pcm990_camera_free_bus,
-		.module_name		= "mt9m001",
 	},
 };
 
diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c
index 3da116f4..881a3a5 100644
--- a/arch/sh/boards/mach-ap325rxa/setup.c
+++ b/arch/sh/boards/mach-ap325rxa/setup.c
@@ -481,7 +481,6 @@
 	.power		= ov7725_power,
 	.board_info	= &ap325rxa_i2c_camera[0],
 	.i2c_adapter_id	= 0,
-	.module_name	= "ov772x",
 	.priv		= &ov7725_info,
 };
 
diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c
index 71a3368..ddc7e4e 100644
--- a/arch/sh/boards/mach-ecovec24/setup.c
+++ b/arch/sh/boards/mach-ecovec24/setup.c
@@ -620,7 +620,6 @@
 	.bus_id		= 1,
 	.power		= tw9910_power,
 	.board_info	= &i2c_camera[0],
-	.module_name	= "tw9910",
 	.priv		= &tw9910_info,
 };
 
@@ -644,7 +643,6 @@
 	.power		= mt9t112_power1,
 	.bus_id		= 0,
 	.board_info	= &i2c_camera[1],
-	.module_name	= "mt9t112",
 	.priv		= &mt9t112_info1,
 };
 
@@ -667,7 +665,6 @@
 	.power		= mt9t112_power2,
 	.bus_id		= 1,
 	.board_info	= &i2c_camera[2],
-	.module_name	= "mt9t112",
 	.priv		= &mt9t112_info2,
 };
 
@@ -793,7 +790,6 @@
 	.flags		= SH_VOU_HSYNC_LOW | SH_VOU_VSYNC_LOW,
 	.board_info	= &ak8813,
 	.i2c_adap	= 0,
-	.module_name	= "ak881x",
 };
 
 static struct resource sh_vou_resources[] = {
diff --git a/arch/sh/boards/mach-kfr2r09/setup.c b/arch/sh/boards/mach-kfr2r09/setup.c
index 68994a1..1742849 100644
--- a/arch/sh/boards/mach-kfr2r09/setup.c
+++ b/arch/sh/boards/mach-kfr2r09/setup.c
@@ -333,7 +333,6 @@
 	.power		= camera_power,
 	.board_info	= &kfr2r09_i2c_camera,
 	.i2c_adapter_id	= 1,
-	.module_name	= "rj54n1cb0c",
 	.priv		= &rj54n1_priv,
 };
 
diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c
index 662debe..03af848 100644
--- a/arch/sh/boards/mach-migor/setup.c
+++ b/arch/sh/boards/mach-migor/setup.c
@@ -450,7 +450,6 @@
 	.power		= ov7725_power,
 	.board_info	= &migor_i2c_camera[0],
 	.i2c_adapter_id	= 0,
-	.module_name	= "ov772x",
 	.priv		= &ov7725_info,
 };
 
@@ -463,7 +462,6 @@
 	.power		= tw9910_power,
 	.board_info	= &migor_i2c_camera[1],
 	.i2c_adapter_id	= 0,
-	.module_name	= "tw9910",
 	.priv		= &tw9910_info,
 };
 
diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c
index 552ebd9..8cc1d72 100644
--- a/arch/sh/boards/mach-se/7724/setup.c
+++ b/arch/sh/boards/mach-se/7724/setup.c
@@ -550,7 +550,6 @@
 	.flags		= SH_VOU_HSYNC_LOW | SH_VOU_VSYNC_LOW,
 	.board_info	= &ak8813,
 	.i2c_adap	= 0,
-	.module_name	= "ak881x",
 };
 
 static struct resource sh_vou_resources[] = {
diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig
index 490c57c..aa4163e 100644
--- a/drivers/media/IR/Kconfig
+++ b/drivers/media/IR/Kconfig
@@ -79,6 +79,18 @@
 	   Enable this option if you have an infrared remote control which
 	   uses the Sony protocol, and you need software decoding support.
 
+config IR_RC5_SZ_DECODER
+	tristate "Enable IR raw decoder for the RC-5 (streamzap) protocol"
+	depends on IR_CORE
+	select BITREVERSE
+	default y
+
+	---help---
+	   Enable this option if you have IR with RC-5 (streamzap) protocol,
+	   and if the IR is decoded in software. (The Streamzap PC Remote
+	   uses an IR protocol that is almost standard RC-5, but not quite,
+	   as it uses an additional bit).
+
 config IR_LIRC_CODEC
 	tristate "Enable IR to LIRC bridge"
 	depends on IR_CORE
@@ -89,6 +101,20 @@
 	   Enable this option to pass raw IR to and from userspace via
 	   the LIRC interface.
 
+config IR_ENE
+	tristate "ENE eHome Receiver/Transceiver (pnp id: ENE0100/ENE02xxx)"
+	depends on PNP
+	depends on IR_CORE
+	---help---
+	   Say Y here to enable support for integrated infrared receiver
+	   /transceiver made by ENE.
+
+	   You can see if you have it by looking at lspnp output.
+	   Output should include ENE0100 ENE0200 or something similar.
+
+	   To compile this driver as a module, choose M here: the
+	   module will be called ene_ir.
+
 config IR_IMON
 	tristate "SoundGraph iMON Receiver and Display"
 	depends on USB_ARCH_HAS_HCD
@@ -113,19 +139,18 @@
 	   To compile this driver as a module, choose M here: the
 	   module will be called mceusb.
 
-config IR_ENE
-	tristate "ENE eHome Receiver/Transciever (pnp id: ENE0100/ENE02xxx)"
+config IR_NUVOTON
+	tristate "Nuvoton w836x7hg Consumer Infrared Transceiver"
 	depends on PNP
 	depends on IR_CORE
 	---help---
 	   Say Y here to enable support for integrated infrared receiver
-	   /transciever made by ENE.
-
-	   You can see if you have it by looking at lspnp output.
-	   Output should include ENE0100 ENE0200 or something similiar.
+	   /transciever made by Nuvoton (formerly Winbond). This chip is
+	   found in the ASRock ION 330HT, as well as assorted Intel
+	   DP55-series motherboards (and of course, possibly others).
 
 	   To compile this driver as a module, choose M here: the
-	   module will be called ene_ir.
+	   module will be called nuvoton-cir.
 
 config IR_STREAMZAP
 	tristate "Streamzap PC Remote IR Receiver"
diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile
index 5367683..f9574ad 100644
--- a/drivers/media/IR/Makefile
+++ b/drivers/media/IR/Makefile
@@ -11,10 +11,12 @@
 obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
 obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o
 obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o
+obj-$(CONFIG_IR_RC5_SZ_DECODER) += ir-rc5-sz-decoder.o
 obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
 
 # stand-alone IR receivers/transmitters
 obj-$(CONFIG_IR_IMON) += imon.o
 obj-$(CONFIG_IR_MCEUSB) += mceusb.o
+obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o
 obj-$(CONFIG_IR_ENE) += ene_ir.o
 obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
diff --git a/drivers/media/IR/ene_ir.c b/drivers/media/IR/ene_ir.c
index 5447750..7637bab 100644
--- a/drivers/media/IR/ene_ir.c
+++ b/drivers/media/IR/ene_ir.c
@@ -1,5 +1,5 @@
 /*
- * driver for ENE KB3926 B/C/D CIR (pnp id: ENE0XXX)
+ * driver for ENE KB3926 B/C/D/E/F CIR (pnp id: ENE0XXX)
  *
  * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.com>
  *
@@ -17,6 +17,17 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  * USA
+ *
+ * Special thanks to:
+ *   Sami R. <maesesami@gmail.com> for lot of help in debugging and therefore
+ *    bringing to life support for transmission & learning mode.
+ *
+ *   Charlie Andrews <charliethepilot@googlemail.com> for lots of help in
+ *   bringing up the support of new firmware buffer that is popular
+ *   on latest notebooks
+ *
+ *   ENE for partial device documentation
+ *
  */
 
 #include <linux/kernel.h>
@@ -31,51 +42,59 @@
 #include <media/ir-common.h>
 #include "ene_ir.h"
 
-
-static int sample_period = -1;
-static int enable_idle = 1;
-static int input = 1;
+static int sample_period;
+static bool learning_mode_force;
 static int debug;
-static int txsim;
+static bool txsim;
 
-static int ene_irq_status(struct ene_device *dev);
-
-/* read a hardware register */
-static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
+static void ene_set_reg_addr(struct ene_device *dev, u16 reg)
 {
-	u8 retval;
 	outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
 	outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
-	retval = inb(dev->hw_io + ENE_IO);
+}
 
-	ene_dbg_verbose("reg %04x == %02x", reg, retval);
+/* read a hardware register */
+static u8 ene_read_reg(struct ene_device *dev, u16 reg)
+{
+	u8 retval;
+	ene_set_reg_addr(dev, reg);
+	retval = inb(dev->hw_io + ENE_IO);
+	dbg_regs("reg %04x == %02x", reg, retval);
 	return retval;
 }
 
 /* write a hardware register */
-static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value)
+static void ene_write_reg(struct ene_device *dev, u16 reg, u8 value)
 {
-	outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
-	outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+	dbg_regs("reg %04x <- %02x", reg, value);
+	ene_set_reg_addr(dev, reg);
 	outb(value, dev->hw_io + ENE_IO);
-
-	ene_dbg_verbose("reg %04x <- %02x", reg, value);
 }
 
-/* change specific bits in hardware register */
-static void ene_hw_write_reg_mask(struct ene_device *dev,
-				  u16 reg, u8 value, u8 mask)
+/* Set bits in hardware register */
+static void ene_set_reg_mask(struct ene_device *dev, u16 reg, u8 mask)
 {
-	u8 regvalue;
+	dbg_regs("reg %04x |= %02x", reg, mask);
+	ene_set_reg_addr(dev, reg);
+	outb(inb(dev->hw_io + ENE_IO) | mask, dev->hw_io + ENE_IO);
+}
 
-	outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
-	outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+/* Clear bits in hardware register */
+static void ene_clear_reg_mask(struct ene_device *dev, u16 reg, u8 mask)
+{
+	dbg_regs("reg %04x &= ~%02x ", reg, mask);
+	ene_set_reg_addr(dev, reg);
+	outb(inb(dev->hw_io + ENE_IO) & ~mask, dev->hw_io + ENE_IO);
+}
 
-	regvalue = inb(dev->hw_io + ENE_IO) & ~mask;
-	regvalue |= (value & mask);
-	outb(regvalue, dev->hw_io + ENE_IO);
-
-	ene_dbg_verbose("reg %04x <- %02x (mask=%02x)", reg, value, mask);
+/* A helper to set/clear a bit in register according to boolean variable */
+static void ene_set_clear_reg_mask(struct ene_device *dev, u16 reg, u8 mask,
+								bool set)
+{
+	if (set)
+		ene_set_reg_mask(dev, reg, mask);
+	else
+		ene_clear_reg_mask(dev, reg, mask);
 }
 
 /* detect hardware features */
@@ -83,194 +102,378 @@
 {
 	u8 chip_major, chip_minor;
 	u8 hw_revision, old_ver;
-	u8 tmp;
-	u8 fw_capabilities;
-	int pll_freq;
+	u8 fw_reg2, fw_reg1;
 
-	tmp = ene_hw_read_reg(dev, ENE_HW_UNK);
-	ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR);
+	ene_clear_reg_mask(dev, ENE_ECSTS, ENE_ECSTS_RSRVD);
+	chip_major = ene_read_reg(dev, ENE_ECVER_MAJOR);
+	chip_minor = ene_read_reg(dev, ENE_ECVER_MINOR);
+	ene_set_reg_mask(dev, ENE_ECSTS, ENE_ECSTS_RSRVD);
 
-	chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR);
-	chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR);
+	hw_revision = ene_read_reg(dev, ENE_ECHV);
+	old_ver = ene_read_reg(dev, ENE_HW_VER_OLD);
 
-	ene_hw_write_reg(dev, ENE_HW_UNK, tmp);
-	hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION);
-	old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD);
+	dev->pll_freq = (ene_read_reg(dev, ENE_PLLFRH) << 4) +
+		(ene_read_reg(dev, ENE_PLLFRL) >> 4);
 
-	pll_freq = (ene_hw_read_reg(dev, ENE_PLLFRH) << 4) +
-		(ene_hw_read_reg(dev, ENE_PLLFRL) >> 4);
-
-	if (pll_freq != 1000)
-		dev->rx_period_adjust = 4;
-	else
-		dev->rx_period_adjust = 2;
-
-
-	ene_printk(KERN_NOTICE, "PLL freq = %d\n", pll_freq);
+	if (sample_period != ENE_DEFAULT_SAMPLE_PERIOD)
+		dev->rx_period_adjust =
+			dev->pll_freq == ENE_DEFAULT_PLL_FREQ ? 2 : 4;
 
 	if (hw_revision == 0xFF) {
-
-		ene_printk(KERN_WARNING, "device seems to be disabled\n");
-		ene_printk(KERN_WARNING,
-			"send a mail to lirc-list@lists.sourceforge.net\n");
-		ene_printk(KERN_WARNING, "please attach output of acpidump\n");
+		ene_warn("device seems to be disabled");
+		ene_warn("send a mail to lirc-list@lists.sourceforge.net");
+		ene_warn("please attach output of acpidump and dmidecode");
 		return -ENODEV;
 	}
 
+	ene_notice("chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x",
+		chip_major, chip_minor, old_ver, hw_revision);
+
+	ene_notice("PLL freq = %d", dev->pll_freq);
+
 	if (chip_major == 0x33) {
-		ene_printk(KERN_WARNING, "chips 0x33xx aren't supported\n");
+		ene_warn("chips 0x33xx aren't supported");
 		return -ENODEV;
 	}
 
 	if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
 		dev->hw_revision = ENE_HW_C;
+		ene_notice("KB3926C detected");
 	} else if (old_ver == 0x24 && hw_revision == 0xC0) {
 		dev->hw_revision = ENE_HW_B;
-		ene_printk(KERN_NOTICE, "KB3926B detected\n");
+		ene_notice("KB3926B detected");
 	} else {
 		dev->hw_revision = ENE_HW_D;
-		ene_printk(KERN_WARNING,
-			"unknown ENE chip detected, assuming KB3926D\n");
-		ene_printk(KERN_WARNING,
-			"driver support might be not complete");
-
+		ene_notice("KB3926D or higher detected");
 	}
 
-	ene_printk(KERN_DEBUG,
-		"chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x\n",
-			chip_major, chip_minor, old_ver, hw_revision);
-
 	/* detect features hardware supports */
 	if (dev->hw_revision < ENE_HW_C)
 		return 0;
 
-	fw_capabilities = ene_hw_read_reg(dev, ENE_FW2);
-	ene_dbg("Firmware capabilities: %02x", fw_capabilities);
+	fw_reg1 = ene_read_reg(dev, ENE_FW1);
+	fw_reg2 = ene_read_reg(dev, ENE_FW2);
 
-	dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN;
-	dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING;
+	ene_notice("Firmware regs: %02x %02x", fw_reg1, fw_reg2);
 
-	dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable &&
-	    (fw_capabilities & ENE_FW2_FAN_AS_NRML_IN);
+	dev->hw_use_gpio_0a = !!(fw_reg2 & ENE_FW2_GP0A);
+	dev->hw_learning_and_tx_capable = !!(fw_reg2 & ENE_FW2_LEARNING);
+	dev->hw_extra_buffer = !!(fw_reg1 & ENE_FW1_HAS_EXTRA_BUF);
 
-	ene_printk(KERN_NOTICE, "hardware features:\n");
-	ene_printk(KERN_NOTICE,
-		"learning and transmit %s, gpio40_learn %s, fan_in %s\n",
-	       dev->hw_learning_and_tx_capable ? "on" : "off",
-	       dev->hw_gpio40_learning ? "on" : "off",
-	       dev->hw_fan_as_normal_input ? "on" : "off");
+	if (dev->hw_learning_and_tx_capable)
+		dev->hw_fan_input = !!(fw_reg2 & ENE_FW2_FAN_INPUT);
+
+	ene_notice("Hardware features:");
 
 	if (dev->hw_learning_and_tx_capable) {
-		ene_printk(KERN_WARNING,
-		"Device supports transmitting, but that support is\n");
-		ene_printk(KERN_WARNING,
-		"lightly tested. Please test it and mail\n");
-		ene_printk(KERN_WARNING,
-		"lirc-list@lists.sourceforge.net\n");
+		ene_notice("* Supports transmitting & learning mode");
+		ene_notice("   This feature is rare and therefore,");
+		ene_notice("   you are welcome to test it,");
+		ene_notice("   and/or contact the author via:");
+		ene_notice("   lirc-list@lists.sourceforge.net");
+		ene_notice("   or maximlevitsky@gmail.com");
+
+		ene_notice("* Uses GPIO %s for IR raw input",
+			dev->hw_use_gpio_0a ? "40" : "0A");
+
+		if (dev->hw_fan_input)
+			ene_notice("* Uses unused fan feedback input as source"
+					" of demodulated IR data");
 	}
+
+	if (!dev->hw_fan_input)
+		ene_notice("* Uses GPIO %s for IR demodulated input",
+			dev->hw_use_gpio_0a ? "0A" : "40");
+
+	if (dev->hw_extra_buffer)
+		ene_notice("* Uses new style input buffer");
 	return 0;
 }
 
-/* this enables/disables IR input via gpio40*/
-static void ene_enable_gpio40_receive(struct ene_device *dev, int enable)
+/* Read properities of hw sample buffer */
+static void ene_rx_setup_hw_buffer(struct ene_device *dev)
 {
-	ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, enable ?
-			      0 : ENE_CIR_CONF2_GPIO40DIS,
-			      ENE_CIR_CONF2_GPIO40DIS);
-}
+	u16 tmp;
 
-/* this enables/disables IR via standard input */
-static void ene_enable_normal_receive(struct ene_device *dev, int enable)
-{
-	ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_RX_ON : 0);
-}
+	ene_rx_read_hw_pointer(dev);
+	dev->r_pointer = dev->w_pointer;
 
-/* this enables/disables IR input via unused fan tachtometer input */
-static void ene_enable_fan_receive(struct ene_device *dev, int enable)
-{
-	if (!enable)
-		ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0);
-	else {
-		ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
-		ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
+	if (!dev->hw_extra_buffer) {
+		dev->buffer_len = ENE_FW_PACKET_SIZE * 2;
+		return;
 	}
-	dev->rx_fan_input_inuse = enable;
+
+	tmp = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER);
+	tmp |= ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER+1) << 8;
+	dev->extra_buf1_address = tmp;
+
+	dev->extra_buf1_len = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 2);
+
+	tmp = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 3);
+	tmp |= ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 4) << 8;
+	dev->extra_buf2_address = tmp;
+
+	dev->extra_buf2_len = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 5);
+
+	dev->buffer_len = dev->extra_buf1_len + dev->extra_buf2_len + 8;
+
+	ene_notice("Hardware uses 2 extended buffers:");
+	ene_notice("  0x%04x - len : %d", dev->extra_buf1_address,
+						dev->extra_buf1_len);
+	ene_notice("  0x%04x - len : %d", dev->extra_buf2_address,
+						dev->extra_buf2_len);
+
+	ene_notice("Total buffer len = %d", dev->buffer_len);
+
+	if (dev->buffer_len > 64 || dev->buffer_len < 16)
+		goto error;
+
+	if (dev->extra_buf1_address > 0xFBFC ||
+					dev->extra_buf1_address < 0xEC00)
+		goto error;
+
+	if (dev->extra_buf2_address > 0xFBFC ||
+					dev->extra_buf2_address < 0xEC00)
+		goto error;
+
+	if (dev->r_pointer > dev->buffer_len)
+		goto error;
+
+	ene_set_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
+	return;
+error:
+	ene_warn("Error validating extra buffers, device probably won't work");
+	dev->hw_extra_buffer = false;
+	ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
 }
 
 
+/* Restore the pointers to extra buffers - to make module reload work*/
+static void ene_rx_restore_hw_buffer(struct ene_device *dev)
+{
+	if (!dev->hw_extra_buffer)
+		return;
+
+	ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 0,
+				dev->extra_buf1_address & 0xFF);
+	ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 1,
+				dev->extra_buf1_address >> 8);
+	ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 2, dev->extra_buf1_len);
+
+	ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 3,
+				dev->extra_buf2_address & 0xFF);
+	ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 4,
+				dev->extra_buf2_address >> 8);
+	ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 5,
+				dev->extra_buf2_len);
+	ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
+}
+
+/* Read hardware write pointer */
+static void ene_rx_read_hw_pointer(struct ene_device *dev)
+{
+	if (dev->hw_extra_buffer)
+		dev->w_pointer = ene_read_reg(dev, ENE_FW_RX_POINTER);
+	else
+		dev->w_pointer = ene_read_reg(dev, ENE_FW2)
+			& ENE_FW2_BUF_WPTR ? 0 : ENE_FW_PACKET_SIZE;
+
+	dbg_verbose("RB: HW write pointer: %02x, driver read pointer: %02x",
+		dev->w_pointer, dev->r_pointer);
+}
+
+/* Gets address of next sample from HW ring buffer */
+static int ene_rx_get_sample_reg(struct ene_device *dev)
+{
+	int r_pointer;
+
+	if (dev->r_pointer == dev->w_pointer) {
+		dbg_verbose("RB: hit end, try update w_pointer");
+		ene_rx_read_hw_pointer(dev);
+	}
+
+	if (dev->r_pointer == dev->w_pointer) {
+		dbg_verbose("RB: end of data at %d", dev->r_pointer);
+		return 0;
+	}
+
+	dbg_verbose("RB: reading at offset %d", dev->r_pointer);
+	r_pointer = dev->r_pointer;
+
+	dev->r_pointer++;
+	if (dev->r_pointer == dev->buffer_len)
+		dev->r_pointer = 0;
+
+	dbg_verbose("RB: next read will be from offset %d", dev->r_pointer);
+
+	if (r_pointer < 8) {
+		dbg_verbose("RB: read at main buffer at %d", r_pointer);
+		return ENE_FW_SAMPLE_BUFFER + r_pointer;
+	}
+
+	r_pointer -= 8;
+
+	if (r_pointer < dev->extra_buf1_len) {
+		dbg_verbose("RB: read at 1st extra buffer at %d", r_pointer);
+		return dev->extra_buf1_address + r_pointer;
+	}
+
+	r_pointer -= dev->extra_buf1_len;
+
+	if (r_pointer < dev->extra_buf2_len) {
+		dbg_verbose("RB: read at 2nd extra buffer at %d", r_pointer);
+		return dev->extra_buf2_address + r_pointer;
+	}
+
+	dbg("attempt to read beyong ring bufer end");
+	return 0;
+}
+
 /* Sense current received carrier */
-static int ene_rx_sense_carrier(struct ene_device *dev)
+void ene_rx_sense_carrier(struct ene_device *dev)
 {
-	int period = ene_hw_read_reg(dev, ENE_RX_CARRIER);
-	int carrier;
-	ene_dbg("RX: hardware carrier period = %02x", period);
+	DEFINE_IR_RAW_EVENT(ev);
 
-	if (!(period & ENE_RX_CARRIER_VALID))
-		return 0;
+	int carrier, duty_cycle;
+	int period = ene_read_reg(dev, ENE_CIRCAR_PRD);
+	int hperiod = ene_read_reg(dev, ENE_CIRCAR_HPRD);
 
-	period &= ~ENE_RX_CARRIER_VALID;
+	if (!(period & ENE_CIRCAR_PRD_VALID))
+		return;
+
+	period &= ~ENE_CIRCAR_PRD_VALID;
 
 	if (!period)
-		return 0;
+		return;
+
+	dbg("RX: hardware carrier period = %02x", period);
+	dbg("RX: hardware carrier pulse period = %02x", hperiod);
 
 	carrier = 2000000 / period;
-	ene_dbg("RX: sensed carrier = %d Hz", carrier);
-	return carrier;
+	duty_cycle = (hperiod * 100) / period;
+	dbg("RX: sensed carrier = %d Hz, duty cycle %d%%",
+						carrier, duty_cycle);
+	if (dev->carrier_detect_enabled) {
+		ev.carrier_report = true;
+		ev.carrier = carrier;
+		ev.duty_cycle = duty_cycle;
+		ir_raw_event_store(dev->idev, &ev);
+	}
 }
 
-/* determine which input to use*/
-static void ene_rx_set_inputs(struct ene_device *dev)
+/* this enables/disables the CIR RX engine */
+static void ene_rx_enable_cir_engine(struct ene_device *dev, bool enable)
 {
-	int learning_mode = dev->learning_enabled;
+	ene_set_clear_reg_mask(dev, ENE_CIRCFG,
+			ENE_CIRCFG_RX_EN | ENE_CIRCFG_RX_IRQ, enable);
+}
 
-	ene_dbg("RX: setup receiver, learning mode = %d", learning_mode);
+/* this selects input for CIR engine. Ether GPIO 0A or GPIO40*/
+static void ene_rx_select_input(struct ene_device *dev, bool gpio_0a)
+{
+	ene_set_clear_reg_mask(dev, ENE_CIRCFG2, ENE_CIRCFG2_GPIO0A, gpio_0a);
+}
 
-	ene_enable_normal_receive(dev, 1);
-
-	/* old hardware doesn't support learning mode for sure */
-	if (dev->hw_revision <= ENE_HW_B)
+/*
+ * this enables alternative input via fan tachometer sensor and bypasses
+ * the hw CIR engine
+ */
+static void ene_rx_enable_fan_input(struct ene_device *dev, bool enable)
+{
+	if (!dev->hw_fan_input)
 		return;
 
-	/* receiver not learning capable, still set gpio40 correctly */
-	if (!dev->hw_learning_and_tx_capable) {
-		ene_enable_gpio40_receive(dev, !dev->hw_gpio40_learning);
-		return;
+	if (!enable)
+		ene_write_reg(dev, ENE_FAN_AS_IN1, 0);
+	else {
+		ene_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
+		ene_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
 	}
+}
 
-	/* enable learning mode */
+/* setup the receiver for RX*/
+static void ene_rx_setup(struct ene_device *dev)
+{
+	bool learning_mode = dev->learning_mode_enabled ||
+					dev->carrier_detect_enabled;
+	int sample_period_adjust = 0;
+
+	dbg("RX: setup receiver, learning mode = %d", learning_mode);
+
+
+	/* This selects RLC input and clears CFG2 settings */
+	ene_write_reg(dev, ENE_CIRCFG2, 0x00);
+
+	/* set sample period*/
+	if (sample_period == ENE_DEFAULT_SAMPLE_PERIOD)
+		sample_period_adjust =
+			dev->pll_freq == ENE_DEFAULT_PLL_FREQ ? 1 : 2;
+
+	ene_write_reg(dev, ENE_CIRRLC_CFG,
+			(sample_period + sample_period_adjust) |
+						ENE_CIRRLC_CFG_OVERFLOW);
+	/* revB doesn't support inputs */
+	if (dev->hw_revision < ENE_HW_C)
+		goto select_timeout;
+
 	if (learning_mode) {
-		ene_enable_gpio40_receive(dev, dev->hw_gpio40_learning);
 
-		/* fan input is not used for learning */
-		if (dev->hw_fan_as_normal_input)
-			ene_enable_fan_receive(dev, 0);
+		WARN_ON(!dev->hw_learning_and_tx_capable);
 
-	/* disable learning mode */
+		/* Enable the opposite of the normal input
+		That means that if GPIO40 is normally used, use GPIO0A
+		and vice versa.
+		This input will carry non demodulated
+		signal, and we will tell the hw to demodulate it itself */
+		ene_rx_select_input(dev, !dev->hw_use_gpio_0a);
+		dev->rx_fan_input_inuse = false;
+
+		/* Enable carrier demodulation */
+		ene_set_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_CARR_DEMOD);
+
+		/* Enable carrier detection */
+		ene_write_reg(dev, ENE_CIRCAR_PULS, 0x63);
+		ene_set_clear_reg_mask(dev, ENE_CIRCFG2, ENE_CIRCFG2_CARR_DETECT,
+			dev->carrier_detect_enabled || debug);
 	} else {
-		if (dev->hw_fan_as_normal_input) {
-			ene_enable_fan_receive(dev, 1);
-			ene_enable_normal_receive(dev, 0);
-		} else
-			ene_enable_gpio40_receive(dev,
-					!dev->hw_gpio40_learning);
+		if (dev->hw_fan_input)
+			dev->rx_fan_input_inuse = true;
+		else
+			ene_rx_select_input(dev, dev->hw_use_gpio_0a);
+
+		/* Disable carrier detection & demodulation */
+		ene_clear_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_CARR_DEMOD);
+		ene_clear_reg_mask(dev, ENE_CIRCFG2, ENE_CIRCFG2_CARR_DETECT);
 	}
 
-	/* set few additional settings for this mode */
-	ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_mode ?
-			      ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1);
-
-	ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_mode ?
-			      ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2);
-
+select_timeout:
 	if (dev->rx_fan_input_inuse) {
-		dev->props->rx_resolution = ENE_SAMPLE_PERIOD_FAN * 1000;
+		dev->props->rx_resolution = MS_TO_NS(ENE_FW_SAMPLE_PERIOD_FAN);
 
-		dev->props->timeout =
-			ENE_FAN_VALUE_MASK * ENE_SAMPLE_PERIOD_FAN * 1000;
+		/* Fan input doesn't support timeouts, it just ends the
+			input with a maximum sample */
+		dev->props->min_timeout = dev->props->max_timeout =
+			MS_TO_NS(ENE_FW_SMPL_BUF_FAN_MSK *
+				ENE_FW_SAMPLE_PERIOD_FAN);
 	} else {
-		dev->props->rx_resolution = sample_period * 1000;
-		dev->props->timeout = ENE_MAXGAP * 1000;
+		dev->props->rx_resolution = MS_TO_NS(sample_period);
+
+		/* Theoreticly timeout is unlimited, but we cap it
+		 * because it was seen that on one device, it
+		 * would stop sending spaces after around 250 msec.
+		 * Besides, this is close to 2^32 anyway and timeout is u32.
+		 */
+		dev->props->min_timeout = MS_TO_NS(127 * sample_period);
+		dev->props->max_timeout = MS_TO_NS(200000);
 	}
+
+	if (dev->hw_learning_and_tx_capable)
+		dev->props->tx_resolution = MS_TO_NS(sample_period);
+
+	if (dev->props->timeout > dev->props->max_timeout)
+		dev->props->timeout = dev->props->max_timeout;
+	if (dev->props->timeout < dev->props->min_timeout)
+		dev->props->timeout = dev->props->min_timeout;
 }
 
 /* Enable the device for receive */
@@ -278,145 +481,157 @@
 {
 	u8 reg_value;
 
+	/* Enable system interrupt */
 	if (dev->hw_revision < ENE_HW_C) {
-		ene_hw_write_reg(dev, ENEB_IRQ, dev->irq << 1);
-		ene_hw_write_reg(dev, ENEB_IRQ_UNK1, 0x01);
+		ene_write_reg(dev, ENEB_IRQ, dev->irq << 1);
+		ene_write_reg(dev, ENEB_IRQ_UNK1, 0x01);
 	} else {
-		reg_value = ene_hw_read_reg(dev, ENEC_IRQ) & 0xF0;
-		reg_value |= ENEC_IRQ_UNK_EN;
-		reg_value &= ~ENEC_IRQ_STATUS;
-		reg_value |= (dev->irq & ENEC_IRQ_MASK);
-		ene_hw_write_reg(dev, ENEC_IRQ, reg_value);
-		ene_hw_write_reg(dev, ENE_TX_UNK1, 0x63);
+		reg_value = ene_read_reg(dev, ENE_IRQ) & 0xF0;
+		reg_value |= ENE_IRQ_UNK_EN;
+		reg_value &= ~ENE_IRQ_STATUS;
+		reg_value |= (dev->irq & ENE_IRQ_MASK);
+		ene_write_reg(dev, ENE_IRQ, reg_value);
 	}
 
-	ene_hw_write_reg(dev, ENE_CIR_CONF2, 0x00);
-	ene_rx_set_inputs(dev);
-
-	/* set sampling period */
-	ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period);
+	/* Enable inputs */
+	ene_rx_enable_fan_input(dev, dev->rx_fan_input_inuse);
+	ene_rx_enable_cir_engine(dev, !dev->rx_fan_input_inuse);
 
 	/* ack any pending irqs - just in case */
 	ene_irq_status(dev);
 
 	/* enable firmware bits */
-	ene_hw_write_reg_mask(dev, ENE_FW1,
-			      ENE_FW1_ENABLE | ENE_FW1_IRQ,
-			      ENE_FW1_ENABLE | ENE_FW1_IRQ);
+	ene_set_reg_mask(dev, ENE_FW1, ENE_FW1_ENABLE | ENE_FW1_IRQ);
 
 	/* enter idle mode */
-	ir_raw_event_set_idle(dev->idev, 1);
-	ir_raw_event_reset(dev->idev);
-
+	ir_raw_event_set_idle(dev->idev, true);
+	dev->rx_enabled = true;
 }
 
 /* Disable the device receiver */
 static void ene_rx_disable(struct ene_device *dev)
 {
 	/* disable inputs */
-	ene_enable_normal_receive(dev, 0);
-
-	if (dev->hw_fan_as_normal_input)
-		ene_enable_fan_receive(dev, 0);
+	ene_rx_enable_cir_engine(dev, false);
+	ene_rx_enable_fan_input(dev, false);
 
 	/* disable hardware IRQ and firmware flag */
-	ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ);
+	ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_ENABLE | ENE_FW1_IRQ);
 
-	ir_raw_event_set_idle(dev->idev, 1);
-	ir_raw_event_reset(dev->idev);
+	ir_raw_event_set_idle(dev->idev, true);
+	dev->rx_enabled = false;
 }
 
+/* This resets the receiver. Usefull to stop stream of spaces at end of
+ * transmission
+ */
+static void ene_rx_reset(struct ene_device *dev)
+{
+	ene_clear_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_RX_EN);
+	ene_set_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_RX_EN);
+}
+
+/* Set up the TX carrier frequency and duty cycle */
+static void ene_tx_set_carrier(struct ene_device *dev)
+{
+	u8 tx_puls_width;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hw_lock, flags);
+
+	ene_set_clear_reg_mask(dev, ENE_CIRCFG,
+		ENE_CIRCFG_TX_CARR, dev->tx_period > 0);
+
+	if (!dev->tx_period)
+		goto unlock;
+
+	BUG_ON(dev->tx_duty_cycle >= 100 || dev->tx_duty_cycle <= 0);
+
+	tx_puls_width = dev->tx_period / (100 / dev->tx_duty_cycle);
+
+	if (!tx_puls_width)
+		tx_puls_width = 1;
+
+	dbg("TX: pulse distance = %d * 500 ns", dev->tx_period);
+	dbg("TX: pulse width = %d * 500 ns", tx_puls_width);
+
+	ene_write_reg(dev, ENE_CIRMOD_PRD, dev->tx_period | ENE_CIRMOD_PRD_POL);
+	ene_write_reg(dev, ENE_CIRMOD_HPRD, tx_puls_width);
+unlock:
+	spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+/* Enable/disable transmitters */
+static void ene_tx_set_transmitters(struct ene_device *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hw_lock, flags);
+	ene_set_clear_reg_mask(dev, ENE_GPIOFS8, ENE_GPIOFS8_GPIO41,
+					!!(dev->transmitter_mask & 0x01));
+	ene_set_clear_reg_mask(dev, ENE_GPIOFS1, ENE_GPIOFS1_GPIO0D,
+					!!(dev->transmitter_mask & 0x02));
+	spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
 
 /* prepare transmission */
-static void ene_tx_prepare(struct ene_device *dev)
+static void ene_tx_enable(struct ene_device *dev)
 {
-	u8 conf1;
+	u8 conf1 = ene_read_reg(dev, ENE_CIRCFG);
+	u8 fwreg2 = ene_read_reg(dev, ENE_FW2);
 
-	conf1 = ene_hw_read_reg(dev, ENE_CIR_CONF1);
 	dev->saved_conf1 = conf1;
 
+	/* Show information about currently connected transmitter jacks */
+	if (fwreg2 & ENE_FW2_EMMITER1_CONN)
+		dbg("TX: Transmitter #1 is connected");
+
+	if (fwreg2 & ENE_FW2_EMMITER2_CONN)
+		dbg("TX: Transmitter #2 is connected");
+
+	if (!(fwreg2 & (ENE_FW2_EMMITER1_CONN | ENE_FW2_EMMITER2_CONN)))
+		ene_warn("TX: transmitter cable isn't connected!");
+
+	/* disable receive on revc */
 	if (dev->hw_revision == ENE_HW_C)
-		conf1 &= ~ENE_CIR_CONF1_TX_CLEAR;
+		conf1 &= ~ENE_CIRCFG_RX_EN;
 
 	/* Enable TX engine */
-	conf1 |= ENE_CIR_CONF1_TX_ON;
-
-	/* Set carrier */
-	if (dev->tx_period) {
-
-		/* NOTE: duty cycle handling is just a guess, it might
-			not be aviable. Default values were tested */
-		int tx_period_in500ns = dev->tx_period * 2;
-
-		int tx_pulse_width_in_500ns =
-			tx_period_in500ns / (100 / dev->tx_duty_cycle);
-
-		if (!tx_pulse_width_in_500ns)
-			tx_pulse_width_in_500ns = 1;
-
-		ene_dbg("TX: pulse distance = %d * 500 ns", tx_period_in500ns);
-		ene_dbg("TX: pulse width = %d * 500 ns",
-						tx_pulse_width_in_500ns);
-
-		ene_hw_write_reg(dev, ENE_TX_PERIOD, ENE_TX_PERIOD_UNKBIT |
-					tx_period_in500ns);
-
-		ene_hw_write_reg(dev, ENE_TX_PERIOD_PULSE,
-					tx_pulse_width_in_500ns);
-
-		conf1 |= ENE_CIR_CONF1_TX_CARR;
-	} else
-		conf1 &= ~ENE_CIR_CONF1_TX_CARR;
-
-	ene_hw_write_reg(dev, ENE_CIR_CONF1, conf1);
-
+	conf1 |= ENE_CIRCFG_TX_EN | ENE_CIRCFG_TX_IRQ;
+	ene_write_reg(dev, ENE_CIRCFG, conf1);
 }
 
 /* end transmission */
-static void ene_tx_complete(struct ene_device *dev)
+static void ene_tx_disable(struct ene_device *dev)
 {
-	ene_hw_write_reg(dev, ENE_CIR_CONF1, dev->saved_conf1);
+	ene_write_reg(dev, ENE_CIRCFG, dev->saved_conf1);
 	dev->tx_buffer = NULL;
 }
 
-/* set transmit mask */
-static void ene_tx_hw_set_transmiter_mask(struct ene_device *dev)
-{
-	u8 txport1 = ene_hw_read_reg(dev, ENE_TX_PORT1) & ~ENE_TX_PORT1_EN;
-	u8 txport2 = ene_hw_read_reg(dev, ENE_TX_PORT2) & ~ENE_TX_PORT2_EN;
-
-	if (dev->transmitter_mask & 0x01)
-		txport1 |= ENE_TX_PORT1_EN;
-
-	if (dev->transmitter_mask & 0x02)
-		txport2 |= ENE_TX_PORT2_EN;
-
-	ene_hw_write_reg(dev, ENE_TX_PORT1, txport1);
-	ene_hw_write_reg(dev, ENE_TX_PORT2, txport2);
-}
 
 /* TX one sample - must be called with dev->hw_lock*/
 static void ene_tx_sample(struct ene_device *dev)
 {
 	u8 raw_tx;
 	u32 sample;
+	bool pulse = dev->tx_sample_pulse;
 
 	if (!dev->tx_buffer) {
-		ene_dbg("TX: attempt to transmit NULL buffer");
+		ene_warn("TX: BUG: attempt to transmit NULL buffer");
 		return;
 	}
 
 	/* Grab next TX sample */
 	if (!dev->tx_sample) {
-again:
-		if (dev->tx_pos == dev->tx_len + 1) {
+
+		if (dev->tx_pos == dev->tx_len) {
 			if (!dev->tx_done) {
-				ene_dbg("TX: no more data to send");
-				dev->tx_done = 1;
+				dbg("TX: no more data to send");
+				dev->tx_done = true;
 				goto exit;
 			} else {
-				ene_dbg("TX: last sample sent by hardware");
-				ene_tx_complete(dev);
+				dbg("TX: last sample sent by hardware");
+				ene_tx_disable(dev);
 				complete(&dev->tx_complete);
 				return;
 			}
@@ -425,23 +640,23 @@
 		sample = dev->tx_buffer[dev->tx_pos++];
 		dev->tx_sample_pulse = !dev->tx_sample_pulse;
 
-		ene_dbg("TX: sample %8d (%s)", sample, dev->tx_sample_pulse ?
-							"pulse" : "space");
+		dev->tx_sample = DIV_ROUND_CLOSEST(sample, sample_period);
 
-		dev->tx_sample = DIV_ROUND_CLOSEST(sample, ENE_TX_SMPL_PERIOD);
-
-		/* guard against too short samples */
 		if (!dev->tx_sample)
-			goto again;
+			dev->tx_sample = 1;
 	}
 
-	raw_tx = min(dev->tx_sample , (unsigned int)ENE_TX_SMLP_MASK);
+	raw_tx = min(dev->tx_sample , (unsigned int)ENE_CIRRLC_OUT_MASK);
 	dev->tx_sample -= raw_tx;
 
-	if (dev->tx_sample_pulse)
-		raw_tx |= ENE_TX_PULSE_MASK;
+	dbg("TX: sample %8d (%s)", raw_tx * sample_period,
+						pulse ? "pulse" : "space");
+	if (pulse)
+		raw_tx |= ENE_CIRRLC_OUT_PULSE;
 
-	ene_hw_write_reg(dev, ENE_TX_INPUT1 + dev->tx_reg, raw_tx);
+	ene_write_reg(dev,
+		dev->tx_reg ? ENE_CIRRLC_OUT1 : ENE_CIRRLC_OUT0, raw_tx);
+
 	dev->tx_reg = !dev->tx_reg;
 exit:
 	/* simulate TX done interrupt */
@@ -466,76 +681,59 @@
 {
 	u8 irq_status;
 	u8 fw_flags1, fw_flags2;
-	int cur_rx_pointer;
 	int retval = 0;
 
-	fw_flags2 = ene_hw_read_reg(dev, ENE_FW2);
-	cur_rx_pointer = !!(fw_flags2 & ENE_FW2_BUF_HIGH);
+	fw_flags2 = ene_read_reg(dev, ENE_FW2);
 
 	if (dev->hw_revision < ENE_HW_C) {
-		irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS);
+		irq_status = ene_read_reg(dev, ENEB_IRQ_STATUS);
 
 		if (!(irq_status & ENEB_IRQ_STATUS_IR))
 			return 0;
 
-		ene_hw_write_reg(dev, ENEB_IRQ_STATUS,
-				 irq_status & ~ENEB_IRQ_STATUS_IR);
-		dev->rx_pointer = cur_rx_pointer;
+		ene_clear_reg_mask(dev, ENEB_IRQ_STATUS, ENEB_IRQ_STATUS_IR);
 		return ENE_IRQ_RX;
 	}
 
-	irq_status = ene_hw_read_reg(dev, ENEC_IRQ);
-
-	if (!(irq_status & ENEC_IRQ_STATUS))
+	irq_status = ene_read_reg(dev, ENE_IRQ);
+	if (!(irq_status & ENE_IRQ_STATUS))
 		return 0;
 
 	/* original driver does that twice - a workaround ? */
-	ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
-	ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
+	ene_write_reg(dev, ENE_IRQ, irq_status & ~ENE_IRQ_STATUS);
+	ene_write_reg(dev, ENE_IRQ, irq_status & ~ENE_IRQ_STATUS);
 
-	/* clear unknown flag in F8F9 */
-	if (fw_flags2 & ENE_FW2_IRQ_CLR)
-		ene_hw_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_IRQ_CLR);
+	/* check RX interrupt */
+	if (fw_flags2 & ENE_FW2_RXIRQ) {
+		retval |= ENE_IRQ_RX;
+		ene_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_RXIRQ);
+	}
 
-	/* check if this is a TX interrupt */
-	fw_flags1 = ene_hw_read_reg(dev, ENE_FW1);
+	/* check TX interrupt */
+	fw_flags1 = ene_read_reg(dev, ENE_FW1);
 	if (fw_flags1 & ENE_FW1_TXIRQ) {
-		ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ);
+		ene_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ);
 		retval |= ENE_IRQ_TX;
 	}
 
-	/* Check if this is RX interrupt */
-	if (dev->rx_pointer != cur_rx_pointer) {
-		retval |= ENE_IRQ_RX;
-		dev->rx_pointer = cur_rx_pointer;
-
-	} else if (!(retval & ENE_IRQ_TX)) {
-		ene_dbg("RX: interrupt without change in RX pointer(%d)",
-			dev->rx_pointer);
-		retval |= ENE_IRQ_RX;
-	}
-
-	if ((retval & ENE_IRQ_RX) && (retval & ENE_IRQ_TX))
-		ene_dbg("both RX and TX interrupt at same time");
-
 	return retval;
 }
 
 /* interrupt handler */
 static irqreturn_t ene_isr(int irq, void *data)
 {
-	u16 hw_value;
-	int i, hw_sample;
-	int pulse;
-	int irq_status;
+	u16 hw_value, reg;
+	int hw_sample, irq_status;
+	bool pulse;
 	unsigned long flags;
-	int carrier = 0;
 	irqreturn_t retval = IRQ_NONE;
 	struct ene_device *dev = (struct ene_device *)data;
-	struct ir_raw_event ev;
-
+	DEFINE_IR_RAW_EVENT(ev);
 
 	spin_lock_irqsave(&dev->hw_lock, flags);
+
+	dbg_verbose("ISR called");
+	ene_rx_read_hw_pointer(dev);
 	irq_status = ene_irq_status(dev);
 
 	if (!irq_status)
@@ -544,9 +742,9 @@
 	retval = IRQ_HANDLED;
 
 	if (irq_status & ENE_IRQ_TX) {
-
+		dbg_verbose("TX interrupt");
 		if (!dev->hw_learning_and_tx_capable) {
-			ene_dbg("TX interrupt on unsupported device!");
+			dbg("TX interrupt on unsupported device!");
 			goto unlock;
 		}
 		ene_tx_sample(dev);
@@ -555,48 +753,57 @@
 	if (!(irq_status & ENE_IRQ_RX))
 		goto unlock;
 
+	dbg_verbose("RX interrupt");
 
-	if (dev->carrier_detect_enabled || debug)
-		carrier = ene_rx_sense_carrier(dev);
-#if 0
-	/* TODO */
-	if (dev->carrier_detect_enabled && carrier)
-		ir_raw_event_report_frequency(dev->idev, carrier);
-#endif
+	if (dev->hw_learning_and_tx_capable)
+		ene_rx_sense_carrier(dev);
 
-	for (i = 0; i < ENE_SAMPLES_SIZE; i++) {
-		hw_value = ene_hw_read_reg(dev,
-				ENE_SAMPLE_BUFFER + dev->rx_pointer * 4 + i);
+	/* On hardware that don't support extra buffer we need to trust
+		the interrupt and not track the read pointer */
+	if (!dev->hw_extra_buffer)
+		dev->r_pointer = dev->w_pointer == 0 ? ENE_FW_PACKET_SIZE : 0;
+
+	while (1) {
+
+		reg = ene_rx_get_sample_reg(dev);
+
+		dbg_verbose("next sample to read at: %04x", reg);
+		if (!reg)
+			break;
+
+		hw_value = ene_read_reg(dev, reg);
 
 		if (dev->rx_fan_input_inuse) {
+
+			int offset = ENE_FW_SMPL_BUF_FAN - ENE_FW_SAMPLE_BUFFER;
+
 			/* read high part of the sample */
-			hw_value |= ene_hw_read_reg(dev,
-			    ENE_SAMPLE_BUFFER_FAN +
-					dev->rx_pointer * 4 + i) << 8;
-			pulse = hw_value & ENE_FAN_SMPL_PULS_MSK;
+			hw_value |= ene_read_reg(dev, reg + offset) << 8;
+			pulse = hw_value & ENE_FW_SMPL_BUF_FAN_PLS;
 
 			/* clear space bit, and other unused bits */
-			hw_value &= ENE_FAN_VALUE_MASK;
-			hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN;
+			hw_value &= ENE_FW_SMPL_BUF_FAN_MSK;
+			hw_sample = hw_value * ENE_FW_SAMPLE_PERIOD_FAN;
 
 		} else {
-			pulse = !(hw_value & ENE_SAMPLE_SPC_MASK);
-			hw_value &= ENE_SAMPLE_VALUE_MASK;
+			pulse = !(hw_value & ENE_FW_SAMPLE_SPACE);
+			hw_value &= ~ENE_FW_SAMPLE_SPACE;
 			hw_sample = hw_value * sample_period;
 
 			if (dev->rx_period_adjust) {
-				hw_sample *= (100 - dev->rx_period_adjust);
-				hw_sample /= 100;
+				hw_sample *= 100;
+				hw_sample /= (100 + dev->rx_period_adjust);
 			}
 		}
-		/* no more data */
-		if (!(hw_value))
-			break;
 
-		ene_dbg("RX: %d (%s)", hw_sample, pulse ? "pulse" : "space");
+		if (!dev->hw_extra_buffer && !hw_sample) {
+			dev->r_pointer = dev->w_pointer;
+			continue;
+		}
 
+		dbg("RX: %d (%s)", hw_sample, pulse ? "pulse" : "space");
 
-		ev.duration = hw_sample * 1000;
+		ev.duration = MS_TO_NS(hw_sample);
 		ev.pulse = pulse;
 		ir_raw_event_store_with_filter(dev->idev, &ev);
 	}
@@ -608,19 +815,26 @@
 }
 
 /* Initialize default settings */
-static void ene_setup_settings(struct ene_device *dev)
+static void ene_setup_default_settings(struct ene_device *dev)
 {
 	dev->tx_period = 32;
-	dev->tx_duty_cycle = 25; /*%*/
-	dev->transmitter_mask = 3;
+	dev->tx_duty_cycle = 50; /*%*/
+	dev->transmitter_mask = 0x03;
+	dev->learning_mode_enabled = learning_mode_force;
 
-	/* Force learning mode if (input == 2), otherwise
-		let user set it with LIRC_SET_REC_CARRIER */
-	dev->learning_enabled =
-		(input == 2 && dev->hw_learning_and_tx_capable);
+	/* Set reasonable default timeout */
+	dev->props->timeout = MS_TO_NS(150000);
+}
 
-	dev->rx_pointer = -1;
+/* Upload all hardware settings at once. Used at load and resume time */
+static void ene_setup_hw_settings(struct ene_device *dev)
+{
+	if (dev->hw_learning_and_tx_capable) {
+		ene_tx_set_carrier(dev);
+		ene_tx_set_transmitters(dev);
+	}
 
+	ene_rx_setup(dev);
 }
 
 /* outside interface: called on first open*/
@@ -630,8 +844,6 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&dev->hw_lock, flags);
-	dev->in_use = 1;
-	ene_setup_settings(dev);
 	ene_rx_enable(dev);
 	spin_unlock_irqrestore(&dev->hw_lock, flags);
 	return 0;
@@ -645,7 +857,6 @@
 	spin_lock_irqsave(&dev->hw_lock, flags);
 
 	ene_rx_disable(dev);
-	dev->in_use = 0;
 	spin_unlock_irqrestore(&dev->hw_lock, flags);
 }
 
@@ -653,19 +864,17 @@
 static int ene_set_tx_mask(void *data, u32 tx_mask)
 {
 	struct ene_device *dev = (struct ene_device *)data;
-	unsigned long flags;
-	ene_dbg("TX: attempt to set transmitter mask %02x", tx_mask);
+	dbg("TX: attempt to set transmitter mask %02x", tx_mask);
 
 	/* invalid txmask */
-	if (!tx_mask || tx_mask & ~0x3) {
-		ene_dbg("TX: invalid mask");
+	if (!tx_mask || tx_mask & ~0x03) {
+		dbg("TX: invalid mask");
 		/* return count of transmitters */
 		return 2;
 	}
 
-	spin_lock_irqsave(&dev->hw_lock, flags);
 	dev->transmitter_mask = tx_mask;
-	spin_unlock_irqrestore(&dev->hw_lock, flags);
+	ene_tx_set_transmitters(dev);
 	return 0;
 }
 
@@ -673,66 +882,76 @@
 static int ene_set_tx_carrier(void *data, u32 carrier)
 {
 	struct ene_device *dev = (struct ene_device *)data;
-	unsigned long flags;
-	u32 period = 1000000 / carrier; /* (1 / freq) (* # usec in 1 sec) */
+	u32 period = 2000000 / carrier;
 
-	ene_dbg("TX: attempt to set tx carrier to %d kHz", carrier);
+	dbg("TX: attempt to set tx carrier to %d kHz", carrier);
 
-	if (period && (period > ENE_TX_PERIOD_MAX ||
-			period < ENE_TX_PERIOD_MIN)) {
+	if (period && (period > ENE_CIRMOD_PRD_MAX ||
+			period < ENE_CIRMOD_PRD_MIN)) {
 
-		ene_dbg("TX: out of range %d-%d carrier, "
-			"falling back to 32 kHz",
-			1000 / ENE_TX_PERIOD_MIN,
-			1000 / ENE_TX_PERIOD_MAX);
-
-		period = 32; /* this is just a coincidence!!! */
+		dbg("TX: out of range %d-%d kHz carrier",
+			2000 / ENE_CIRMOD_PRD_MIN, 2000 / ENE_CIRMOD_PRD_MAX);
+		return -1;
 	}
-	ene_dbg("TX: set carrier to %d kHz", carrier);
 
-	spin_lock_irqsave(&dev->hw_lock, flags);
 	dev->tx_period = period;
-	spin_unlock_irqrestore(&dev->hw_lock, flags);
+	ene_tx_set_carrier(dev);
 	return 0;
 }
 
+/*outside interface : set tx duty cycle */
+static int ene_set_tx_duty_cycle(void *data, u32 duty_cycle)
+{
+	struct ene_device *dev = (struct ene_device *)data;
+	dbg("TX: setting duty cycle to %d%%", duty_cycle);
+	dev->tx_duty_cycle = duty_cycle;
+	ene_tx_set_carrier(dev);
+	return 0;
+}
 
 /* outside interface: enable learning mode */
 static int ene_set_learning_mode(void *data, int enable)
 {
 	struct ene_device *dev = (struct ene_device *)data;
 	unsigned long flags;
-	if (enable == dev->learning_enabled)
+	if (enable == dev->learning_mode_enabled)
 		return 0;
 
 	spin_lock_irqsave(&dev->hw_lock, flags);
-	dev->learning_enabled = enable;
-	ene_rx_set_inputs(dev);
+	dev->learning_mode_enabled = enable;
+	ene_rx_disable(dev);
+	ene_rx_setup(dev);
+	ene_rx_enable(dev);
 	spin_unlock_irqrestore(&dev->hw_lock, flags);
 	return 0;
 }
 
-/* outside interface: set rec carrier */
-static int ene_set_rec_carrier(void *data, u32 min, u32 max)
+static int ene_set_carrier_report(void *data, int enable)
 {
 	struct ene_device *dev = (struct ene_device *)data;
-	ene_set_learning_mode(dev,
-		max > ENE_NORMAL_RX_HI || min < ENE_NORMAL_RX_LOW);
+	unsigned long flags;
+
+	if (enable == dev->carrier_detect_enabled)
+		return 0;
+
+	spin_lock_irqsave(&dev->hw_lock, flags);
+	dev->carrier_detect_enabled = enable;
+	ene_rx_disable(dev);
+	ene_rx_setup(dev);
+	ene_rx_enable(dev);
+	spin_unlock_irqrestore(&dev->hw_lock, flags);
 	return 0;
 }
 
 /* outside interface: enable or disable idle mode */
-static void ene_rx_set_idle(void *data, int idle)
+static void ene_set_idle(void *data, bool idle)
 {
-	struct ene_device *dev = (struct ene_device *)data;
-	ene_dbg("%sabling idle mode", idle ? "en" : "dis");
-
-	ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD,
-		(enable_idle && idle) ? 0 : ENE_CIR_SAMPLE_OVERFLOW,
-			ENE_CIR_SAMPLE_OVERFLOW);
+	if (idle) {
+		ene_rx_reset((struct ene_device *)data);
+		dbg("RX: end of data");
+	}
 }
 
-
 /* outside interface: transmit */
 static int ene_transmit(void *data, int *buf, u32 n)
 {
@@ -747,12 +966,11 @@
 	dev->tx_sample = 0;
 	dev->tx_sample_pulse = 0;
 
-	ene_dbg("TX: %d samples", dev->tx_len);
+	dbg("TX: %d samples", dev->tx_len);
 
 	spin_lock_irqsave(&dev->hw_lock, flags);
 
-	ene_tx_hw_set_transmiter_mask(dev);
-	ene_tx_prepare(dev);
+	ene_tx_enable(dev);
 
 	/* Transmit first two samples */
 	ene_tx_sample(dev);
@@ -761,16 +979,15 @@
 	spin_unlock_irqrestore(&dev->hw_lock, flags);
 
 	if (wait_for_completion_timeout(&dev->tx_complete, 2 * HZ) == 0) {
-		ene_dbg("TX: timeout");
+		dbg("TX: timeout");
 		spin_lock_irqsave(&dev->hw_lock, flags);
-		ene_tx_complete(dev);
+		ene_tx_disable(dev);
 		spin_unlock_irqrestore(&dev->hw_lock, flags);
 	} else
-		ene_dbg("TX: done");
+		dbg("TX: done");
 	return n;
 }
 
-
 /* probe entry */
 static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 {
@@ -785,121 +1002,103 @@
 	dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
 
 	if (!input_dev || !ir_props || !dev)
-		goto error;
+		goto error1;
 
 	/* validate resources */
 	error = -ENODEV;
 
 	if (!pnp_port_valid(pnp_dev, 0) ||
-	    pnp_port_len(pnp_dev, 0) < ENE_MAX_IO)
+	    pnp_port_len(pnp_dev, 0) < ENE_IO_SIZE)
 		goto error;
 
 	if (!pnp_irq_valid(pnp_dev, 0))
 		goto error;
 
-	dev->hw_io = pnp_port_start(pnp_dev, 0);
-	dev->irq = pnp_irq(pnp_dev, 0);
 	spin_lock_init(&dev->hw_lock);
 
 	/* claim the resources */
 	error = -EBUSY;
-	if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME))
+	dev->hw_io = pnp_port_start(pnp_dev, 0);
+	if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) {
+		dev->hw_io = -1;
+		dev->irq = -1;
 		goto error;
+	}
 
+	dev->irq = pnp_irq(pnp_dev, 0);
 	if (request_irq(dev->irq, ene_isr,
-			IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev))
+			IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) {
+		dev->irq = -1;
 		goto error;
+	}
 
 	pnp_set_drvdata(pnp_dev, dev);
 	dev->pnp_dev = pnp_dev;
 
+	/* don't allow too short/long sample periods */
+	if (sample_period < 5 || sample_period > 0x7F)
+		sample_period = ENE_DEFAULT_SAMPLE_PERIOD;
+
 	/* detect hardware version and features */
 	error = ene_hw_detect(dev);
 	if (error)
 		goto error;
 
-	ene_setup_settings(dev);
-
 	if (!dev->hw_learning_and_tx_capable && txsim) {
-		dev->hw_learning_and_tx_capable = 1;
+		dev->hw_learning_and_tx_capable = true;
 		setup_timer(&dev->tx_sim_timer, ene_tx_irqsim,
 						(long unsigned int)dev);
-		ene_printk(KERN_WARNING,
-			"Simulation of TX activated\n");
+		ene_warn("Simulation of TX activated");
 	}
 
+	if (!dev->hw_learning_and_tx_capable)
+		learning_mode_force = false;
+
 	ir_props->driver_type = RC_DRIVER_IR_RAW;
 	ir_props->allowed_protos = IR_TYPE_ALL;
 	ir_props->priv = dev;
 	ir_props->open = ene_open;
 	ir_props->close = ene_close;
-	ir_props->min_timeout = ENE_MINGAP * 1000;
-	ir_props->max_timeout = ENE_MAXGAP * 1000;
-	ir_props->timeout = ENE_MAXGAP * 1000;
-
-	if (dev->hw_revision == ENE_HW_B)
-		ir_props->s_idle = ene_rx_set_idle;
-
+	ir_props->s_idle = ene_set_idle;
 
 	dev->props = ir_props;
 	dev->idev = input_dev;
 
-	/* don't allow too short/long sample periods */
-	if (sample_period < 5 || sample_period > 0x7F)
-		sample_period = -1;
-
-	/* choose default sample period */
-	if (sample_period == -1) {
-
-		sample_period = 50;
-
-		/* on revB, hardware idle mode eats first sample
-		  if we set too low sample period */
-		if (dev->hw_revision == ENE_HW_B && enable_idle)
-			sample_period = 75;
-	}
-
-	ir_props->rx_resolution = sample_period * 1000;
-
 	if (dev->hw_learning_and_tx_capable) {
-
 		ir_props->s_learning_mode = ene_set_learning_mode;
-
-		if (input == 0)
-			ir_props->s_rx_carrier_range = ene_set_rec_carrier;
-
 		init_completion(&dev->tx_complete);
 		ir_props->tx_ir = ene_transmit;
 		ir_props->s_tx_mask = ene_set_tx_mask;
 		ir_props->s_tx_carrier = ene_set_tx_carrier;
-		ir_props->tx_resolution = ENE_TX_SMPL_PERIOD * 1000;
-		/* ir_props->s_carrier_report = ene_set_carrier_report; */
+		ir_props->s_tx_duty_cycle = ene_set_tx_duty_cycle;
+		ir_props->s_carrier_report = ene_set_carrier_report;
 	}
 
+	ene_rx_setup_hw_buffer(dev);
+	ene_setup_default_settings(dev);
+	ene_setup_hw_settings(dev);
 
-	device_set_wakeup_capable(&pnp_dev->dev, 1);
-	device_set_wakeup_enable(&pnp_dev->dev, 1);
+	device_set_wakeup_capable(&pnp_dev->dev, true);
+	device_set_wakeup_enable(&pnp_dev->dev, true);
 
 	if (dev->hw_learning_and_tx_capable)
 		input_dev->name = "ENE eHome Infrared Remote Transceiver";
 	else
 		input_dev->name = "ENE eHome Infrared Remote Receiver";
 
-
 	error = -ENODEV;
 	if (ir_input_register(input_dev, RC_MAP_RC6_MCE, ir_props,
 							ENE_DRIVER_NAME))
 		goto error;
 
-
-	ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n");
+	ene_notice("driver has been succesfully loaded");
 	return 0;
 error:
-	if (dev->irq)
+	if (dev && dev->irq >= 0)
 		free_irq(dev->irq, dev);
-	if (dev->hw_io)
-		release_region(dev->hw_io, ENE_MAX_IO);
-
+	if (dev && dev->hw_io >= 0)
+		release_region(dev->hw_io, ENE_IO_SIZE);
+error1:
 	input_free_device(input_dev);
 	kfree(ir_props);
 	kfree(dev);
@@ -914,10 +1113,11 @@
 
 	spin_lock_irqsave(&dev->hw_lock, flags);
 	ene_rx_disable(dev);
+	ene_rx_restore_hw_buffer(dev);
 	spin_unlock_irqrestore(&dev->hw_lock, flags);
 
 	free_irq(dev->irq, dev);
-	release_region(dev->hw_io, ENE_MAX_IO);
+	release_region(dev->hw_io, ENE_IO_SIZE);
 	ir_input_unregister(dev->idev);
 	kfree(dev->props);
 	kfree(dev);
@@ -927,28 +1127,29 @@
 static void ene_enable_wake(struct ene_device *dev, int enable)
 {
 	enable = enable && device_may_wakeup(&dev->pnp_dev->dev);
-
-	ene_dbg("wake on IR %s", enable ? "enabled" : "disabled");
-
-	ene_hw_write_reg_mask(dev, ENE_FW1, enable ?
-		ENE_FW1_WAKE : 0, ENE_FW1_WAKE);
+	dbg("wake on IR %s", enable ? "enabled" : "disabled");
+	ene_set_clear_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, enable);
 }
 
 #ifdef CONFIG_PM
 static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
 {
 	struct ene_device *dev = pnp_get_drvdata(pnp_dev);
-	ene_enable_wake(dev, 1);
+	ene_enable_wake(dev, true);
+
+	/* TODO: add support for wake pattern */
 	return 0;
 }
 
 static int ene_resume(struct pnp_dev *pnp_dev)
 {
 	struct ene_device *dev = pnp_get_drvdata(pnp_dev);
-	if (dev->in_use)
+	ene_setup_hw_settings(dev);
+
+	if (dev->rx_enabled)
 		ene_rx_enable(dev);
 
-	ene_enable_wake(dev, 0);
+	ene_enable_wake(dev, false);
 	return 0;
 }
 #endif
@@ -956,7 +1157,7 @@
 static void ene_shutdown(struct pnp_dev *pnp_dev)
 {
 	struct ene_device *dev = pnp_get_drvdata(pnp_dev);
-	ene_enable_wake(dev, 1);
+	ene_enable_wake(dev, true);
 }
 
 static const struct pnp_device_id ene_ids[] = {
@@ -994,18 +1195,11 @@
 module_param(sample_period, int, S_IRUGO);
 MODULE_PARM_DESC(sample_period, "Hardware sample period (50 us default)");
 
-module_param(enable_idle, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(enable_idle,
-	"Enables turning off signal sampling after long inactivity time; "
-	"if disabled might help detecting input signal (default: enabled)"
-	" (KB3926B only)");
-
-module_param(input, bool, S_IRUGO);
-MODULE_PARM_DESC(input, "select which input to use "
-	"0 - auto, 1 - standard, 2 - wideband(KB3926C+)");
+module_param(learning_mode_force, bool, S_IRUGO);
+MODULE_PARM_DESC(learning_mode_force, "Enable learning mode by default");
 
 module_param(debug, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable debug (debug=2 verbose debug output)");
+MODULE_PARM_DESC(debug, "Debug level");
 
 module_param(txsim, bool, S_IRUGO);
 MODULE_PARM_DESC(txsim,
@@ -1013,8 +1207,8 @@
 
 MODULE_DEVICE_TABLE(pnp, ene_ids);
 MODULE_DESCRIPTION
-	("Infrared input driver for KB3926B/KB3926C/KB3926D "
-	"(aka ENE0100/ENE0200/ENE0201) CIR port");
+	("Infrared input driver for KB3926B/C/D/E/F "
+	"(aka ENE0100/ENE0200/ENE0201/ENE0202) CIR port");
 
 MODULE_AUTHOR("Maxim Levitsky");
 MODULE_LICENSE("GPL");
diff --git a/drivers/media/IR/ene_ir.h b/drivers/media/IR/ene_ir.h
index 54c76af..f587066 100644
--- a/drivers/media/IR/ene_ir.h
+++ b/drivers/media/IR/ene_ir.h
@@ -1,5 +1,5 @@
 /*
- * driver for ENE KB3926 B/C/D CIR (also known as ENE0XXX)
+ * driver for ENE KB3926 B/C/D/E/F CIR (also known as ENE0XXX)
  *
  * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.com>
  *
@@ -26,43 +26,50 @@
 #define ENE_ADDR_HI		1	/* hi byte of register address */
 #define ENE_ADDR_LO		2	/* low byte of register address */
 #define ENE_IO			3	/* read/write window */
-#define ENE_MAX_IO		4
+#define ENE_IO_SIZE		4
 
-/* 8 bytes of samples, divided in 2 halfs*/
-#define ENE_SAMPLE_BUFFER	0xF8F0	/* regular sample buffer */
-#define ENE_SAMPLE_SPC_MASK	0x80	/* sample is space */
-#define ENE_SAMPLE_VALUE_MASK	0x7F
-#define ENE_SAMPLE_OVERFLOW	0x7F
-#define ENE_SAMPLES_SIZE	4
+/* 8 bytes of samples, divided in 2 packets*/
+#define ENE_FW_SAMPLE_BUFFER	0xF8F0	/* sample buffer */
+#define ENE_FW_SAMPLE_SPACE	0x80	/* sample is space */
+#define ENE_FW_PACKET_SIZE	4
 
-/* fan input sample buffer */
-#define ENE_SAMPLE_BUFFER_FAN	0xF8FB	/* this buffer holds high byte of */
-					/* each sample of normal buffer */
-#define ENE_FAN_SMPL_PULS_MSK	0x8000	/* this bit of combined sample */
-					/* if set, says that sample is pulse */
-#define ENE_FAN_VALUE_MASK	0x0FFF  /* mask for valid bits of the value */
-
-/* first firmware register */
-#define ENE_FW1			0xF8F8
+/* first firmware flag register */
+#define ENE_FW1			0xF8F8  /* flagr */
 #define	ENE_FW1_ENABLE		0x01	/* enable fw processing */
 #define ENE_FW1_TXIRQ		0x02	/* TX interrupt pending */
+#define ENE_FW1_HAS_EXTRA_BUF	0x04	/* fw uses extra buffer*/
+#define ENE_FW1_EXTRA_BUF_HND	0x08	/* extra buffer handshake bit*/
+#define ENE_FW1_LED_ON		0x10	/* turn on a led */
+
+#define ENE_FW1_WPATTERN	0x20	/* enable wake pattern */
 #define ENE_FW1_WAKE		0x40	/* enable wake from S3 */
 #define ENE_FW1_IRQ		0x80	/* enable interrupt */
 
-/* second firmware register */
-#define ENE_FW2			0xF8F9
-#define ENE_FW2_BUF_HIGH	0x01	/* which half of the buffer to read */
-#define ENE_FW2_IRQ_CLR		0x04	/* clear this on IRQ */
-#define ENE_FW2_GP40_AS_LEARN	0x08	/* normal input is used as */
-					/* learning input */
-#define ENE_FW2_FAN_AS_NRML_IN	0x40	/* fan is used as normal input */
+/* second firmware flag register */
+#define ENE_FW2			0xF8F9  /* flagw */
+#define ENE_FW2_BUF_WPTR	0x01	/* which half of the buffer to read */
+#define ENE_FW2_RXIRQ		0x04	/* RX IRQ pending*/
+#define ENE_FW2_GP0A		0x08	/* Use GPIO0A for demodulated input */
+#define ENE_FW2_EMMITER1_CONN	0x10	/* TX emmiter 1 connected */
+#define ENE_FW2_EMMITER2_CONN	0x20	/* TX emmiter 2 connected */
+
+#define ENE_FW2_FAN_INPUT	0x40	/* fan input used for demodulated data*/
 #define ENE_FW2_LEARNING	0x80	/* hardware supports learning and TX */
 
+/* firmware RX pointer for new style buffer */
+#define ENE_FW_RX_POINTER	0xF8FA
+
+/* high parts of samples for fan input (8 samples)*/
+#define ENE_FW_SMPL_BUF_FAN	0xF8FB
+#define ENE_FW_SMPL_BUF_FAN_PLS	0x8000	/* combined sample is pulse */
+#define ENE_FW_SMPL_BUF_FAN_MSK	0x0FFF  /* combined sample maximum value */
+#define ENE_FW_SAMPLE_PERIOD_FAN 61	/* fan input has fixed sample period */
+
 /* transmitter ports */
-#define ENE_TX_PORT2		0xFC01	/* this enables one or both */
-#define ENE_TX_PORT2_EN		0x20	/* TX ports */
-#define ENE_TX_PORT1		0xFC08
-#define ENE_TX_PORT1_EN		0x02
+#define ENE_GPIOFS1		0xFC01
+#define ENE_GPIOFS1_GPIO0D	0x20	/* enable tx output on GPIO0D */
+#define ENE_GPIOFS8		0xFC08
+#define ENE_GPIOFS8_GPIO41	0x02	/* enable tx output on GPIO40 */
 
 /* IRQ registers block (for revision B) */
 #define ENEB_IRQ		0xFD09	/* IRQ number */
@@ -70,98 +77,100 @@
 #define ENEB_IRQ_STATUS		0xFD80	/* irq status */
 #define ENEB_IRQ_STATUS_IR	0x20	/* IR irq */
 
-/* fan as input settings - only if learning capable */
+/* fan as input settings */
 #define ENE_FAN_AS_IN1		0xFE30  /* fan init reg 1 */
 #define ENE_FAN_AS_IN1_EN	0xCD
 #define ENE_FAN_AS_IN2		0xFE31  /* fan init reg 2 */
 #define ENE_FAN_AS_IN2_EN	0x03
-#define ENE_SAMPLE_PERIOD_FAN   61	/* fan input has fixed sample period */
 
 /* IRQ registers block (for revision C,D) */
-#define ENEC_IRQ		0xFE9B	/* new irq settings register */
-#define ENEC_IRQ_MASK		0x0F	/* irq number mask */
-#define ENEC_IRQ_UNK_EN		0x10	/* always enabled */
-#define ENEC_IRQ_STATUS		0x20	/* irq status and ACK */
+#define ENE_IRQ			0xFE9B	/* new irq settings register */
+#define ENE_IRQ_MASK		0x0F	/* irq number mask */
+#define ENE_IRQ_UNK_EN		0x10	/* always enabled */
+#define ENE_IRQ_STATUS		0x20	/* irq status and ACK */
 
-/* CIR block settings */
-#define ENE_CIR_CONF1		0xFEC0
-#define ENE_CIR_CONF1_TX_CLEAR	0x01	/* clear that on revC */
-					/* while transmitting */
-#define ENE_CIR_CONF1_RX_ON	0x07	/* normal receiver enabled */
-#define ENE_CIR_CONF1_LEARN1	0x08	/* enabled on learning mode */
-#define ENE_CIR_CONF1_TX_ON	0x30	/* enabled on transmit */
-#define ENE_CIR_CONF1_TX_CARR	0x80	/* send TX carrier or not */
+/* CIR Config register #1 */
+#define ENE_CIRCFG		0xFEC0
+#define ENE_CIRCFG_RX_EN	0x01	/* RX enable */
+#define ENE_CIRCFG_RX_IRQ	0x02	/* Enable hardware interrupt */
+#define ENE_CIRCFG_REV_POL	0x04	/* Input polarity reversed */
+#define ENE_CIRCFG_CARR_DEMOD	0x08	/* Enable carrier demodulator */
 
-#define ENE_CIR_CONF2		0xFEC1	/* unknown setting = 0 */
-#define ENE_CIR_CONF2_LEARN2	0x10	/* set on enable learning */
-#define ENE_CIR_CONF2_GPIO40DIS	0x20	/* disable input via gpio40 */
+#define ENE_CIRCFG_TX_EN	0x10	/* TX enable */
+#define ENE_CIRCFG_TX_IRQ	0x20	/* Send interrupt on TX done */
+#define ENE_CIRCFG_TX_POL_REV	0x40	/* TX polarity reversed */
+#define ENE_CIRCFG_TX_CARR	0x80	/* send TX carrier or not */
 
-#define ENE_CIR_SAMPLE_PERIOD	0xFEC8	/* sample period in us */
-#define ENE_CIR_SAMPLE_OVERFLOW	0x80	/* interrupt on overflows if set */
+/* CIR config register #2 */
+#define ENE_CIRCFG2		0xFEC1
+#define ENE_CIRCFG2_RLC		0x00
+#define ENE_CIRCFG2_RC5		0x01
+#define ENE_CIRCFG2_RC6		0x02
+#define ENE_CIRCFG2_NEC		0x03
+#define ENE_CIRCFG2_CARR_DETECT	0x10	/* Enable carrier detection */
+#define ENE_CIRCFG2_GPIO0A	0x20	/* Use GPIO0A instead of GPIO40 for input */
+#define ENE_CIRCFG2_FAST_SAMPL1	0x40	/* Fast leading pulse detection for RC6 */
+#define ENE_CIRCFG2_FAST_SAMPL2	0x80	/* Fast data detection for RC6 */
+
+/* Knobs for protocol decoding - will document when/if will use them */
+#define ENE_CIRPF		0xFEC2
+#define ENE_CIRHIGH		0xFEC3
+#define ENE_CIRBIT		0xFEC4
+#define ENE_CIRSTART		0xFEC5
+#define ENE_CIRSTART2		0xFEC6
+
+/* Actual register which contains RLC RX data - read by firmware */
+#define ENE_CIRDAT_IN		0xFEC7
 
 
-/* Two byte tx buffer */
-#define ENE_TX_INPUT1		0xFEC9
-#define ENE_TX_INPUT2		0xFECA
-#define ENE_TX_PULSE_MASK	0x80	/* Transmitted sample is pulse */
-#define ENE_TX_SMLP_MASK	0x7F
-#define ENE_TX_SMPL_PERIOD	50	/* transmit sample period - fixed */
+/* RLC configuration - sample period (1us resulution) + idle mode */
+#define ENE_CIRRLC_CFG		0xFEC8
+#define ENE_CIRRLC_CFG_OVERFLOW	0x80	/* interrupt on overflows if set */
+#define ENE_DEFAULT_SAMPLE_PERIOD 50
+
+/* Two byte RLC TX buffer */
+#define ENE_CIRRLC_OUT0		0xFEC9
+#define ENE_CIRRLC_OUT1		0xFECA
+#define ENE_CIRRLC_OUT_PULSE	0x80	/* Transmitted sample is pulse */
+#define ENE_CIRRLC_OUT_MASK	0x7F
 
 
-/* Unknown TX setting - TX sample period ??? */
-#define ENE_TX_UNK1		0xFECB	/* set to 0x63 */
+/* Carrier detect setting
+ * Low nibble  - number of carrier pulses to average
+ * High nibble - number of initial carrier pulses to discard
+ */
+#define ENE_CIRCAR_PULS		0xFECB
 
-/* Current received carrier period */
-#define ENE_RX_CARRIER		0xFECC	/* RX period (500 ns) */
-#define ENE_RX_CARRIER_VALID	0x80	/* Register content valid */
+/* detected RX carrier period (resolution: 500 ns) */
+#define ENE_CIRCAR_PRD		0xFECC
+#define ENE_CIRCAR_PRD_VALID	0x80	/* data valid content valid */
 
+/* detected RX carrier pulse width (resolution: 500 ns) */
+#define ENE_CIRCAR_HPRD		0xFECD
 
-/* TX period (1/carrier) */
-#define ENE_TX_PERIOD		0xFECE	/* TX period (500 ns) */
-#define ENE_TX_PERIOD_UNKBIT	0x80	/* This bit set on transmit*/
-#define ENE_TX_PERIOD_PULSE	0xFECF	/* TX pulse period (500 ns)*/
+/* TX period (resolution: 500 ns, minimum 2)*/
+#define ENE_CIRMOD_PRD		0xFECE
+#define ENE_CIRMOD_PRD_POL	0x80	/* TX carrier polarity*/
+
+#define ENE_CIRMOD_PRD_MAX	0x7F	/* 15.87 kHz */
+#define ENE_CIRMOD_PRD_MIN	0x02	/* 1 Mhz */
+
+/* TX pulse width (resolution: 500 ns)*/
+#define ENE_CIRMOD_HPRD		0xFECF
 
 /* Hardware versions */
-#define ENE_HW_VERSION		0xFF00	/* hardware revision */
+#define ENE_ECHV		0xFF00	/* hardware revision */
 #define ENE_PLLFRH		0xFF16
 #define ENE_PLLFRL		0xFF17
+#define ENE_DEFAULT_PLL_FREQ	1000
 
-#define ENE_HW_UNK		0xFF1D
-#define ENE_HW_UNK_CLR		0x04
-#define ENE_HW_VER_MAJOR	0xFF1E	/* chip version */
-#define ENE_HW_VER_MINOR	0xFF1F
+#define ENE_ECSTS		0xFF1D
+#define ENE_ECSTS_RSRVD		0x04
+
+#define ENE_ECVER_MAJOR		0xFF1E	/* chip version */
+#define ENE_ECVER_MINOR		0xFF1F
 #define ENE_HW_VER_OLD		0xFD00
 
-/* Normal/Learning carrier ranges - only valid if we have learning input*/
-/* TODO: test */
-#define ENE_NORMAL_RX_LOW	34
-#define ENE_NORMAL_RX_HI	38
-
-/* Tx carrier range */
-/* Hardware might be able to do more, but this range is enough for
-   all purposes */
-#define ENE_TX_PERIOD_MAX	32	/* corresponds to 29.4 kHz */
-#define ENE_TX_PERIOD_MIN	16	/* corrsponds to 62.5 kHz */
-
-
-
-/* Minimal and maximal gaps */
-
-/* Normal case:
-	Minimal gap is 0x7F * sample period
-	Maximum gap depends on hardware.
-	For KB3926B, it is unlimited, for newer models its around
-	250000, after which HW stops sending samples, and that is
-	not possible to change */
-
-/* Fan case:
-	Both minimal and maximal gaps are same, and equal to 0xFFF * 0x61
-	And there is nothing to change this setting
-*/
-
-#define ENE_MAXGAP		250000
-#define ENE_MINGAP		(127 * sample_period)
-
 /******************************************************************************/
 
 #define ENE_DRIVER_NAME		"ene_ir"
@@ -171,46 +180,60 @@
 
 #define  ENE_HW_B		1	/* 3926B */
 #define  ENE_HW_C		2	/* 3926C */
-#define  ENE_HW_D		3	/* 3926D */
+#define  ENE_HW_D		3	/* 3926D or later */
 
 #define ene_printk(level, text, ...) \
-	printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__)
+	printk(level ENE_DRIVER_NAME ": " text "\n", ## __VA_ARGS__)
 
-#define ene_dbg(text, ...) \
-	if (debug) \
-		printk(KERN_DEBUG \
-			ENE_DRIVER_NAME ": " text "\n" , ## __VA_ARGS__)
+#define ene_notice(text, ...) ene_printk(KERN_NOTICE, text, ## __VA_ARGS__)
+#define ene_warn(text, ...) ene_printk(KERN_WARNING, text, ## __VA_ARGS__)
 
-#define ene_dbg_verbose(text, ...) \
-	if (debug > 1) \
-		printk(KERN_DEBUG \
-			ENE_DRIVER_NAME ": " text "\n" , ## __VA_ARGS__)
 
+#define __dbg(level, format, ...) \
+	do { \
+		if (debug >= level) \
+			printk(KERN_DEBUG ENE_DRIVER_NAME \
+				": " format "\n", ## __VA_ARGS__); \
+	} while (0)
+
+
+#define dbg(format, ...)		__dbg(1, format, ## __VA_ARGS__)
+#define dbg_verbose(format, ...)	__dbg(2, format, ## __VA_ARGS__)
+#define dbg_regs(format, ...)		__dbg(3, format, ## __VA_ARGS__)
+
+#define MS_TO_NS(msec) ((msec) * 1000)
 
 struct ene_device {
 	struct pnp_dev *pnp_dev;
 	struct input_dev *idev;
 	struct ir_dev_props *props;
-	int in_use;
 
 	/* hw IO settings */
-	unsigned long hw_io;
+	long hw_io;
 	int irq;
 	spinlock_t hw_lock;
 
 	/* HW features */
 	int hw_revision;			/* hardware revision */
-	bool hw_learning_and_tx_capable;	/* learning capable */
-	bool hw_gpio40_learning;		/* gpio40 is learning */
-	bool hw_fan_as_normal_input;		/* fan input is used as */
-						/* regular input */
+	bool hw_use_gpio_0a;			/* gpio0a is demodulated input*/
+	bool hw_extra_buffer;			/* hardware has 'extra buffer' */
+	bool hw_fan_input;			/* fan input is IR data source */
+	bool hw_learning_and_tx_capable;	/* learning & tx capable */
+	int  pll_freq;
+	int buffer_len;
+
+	/* Extra RX buffer location */
+	int extra_buf1_address;
+	int extra_buf1_len;
+	int extra_buf2_address;
+	int extra_buf2_len;
+
 	/* HW state*/
-	int rx_pointer;				/* hw pointer to rx buffer */
+	int r_pointer;				/* pointer to next sample to read */
+	int w_pointer;				/* pointer to next sample hw will write */
 	bool rx_fan_input_inuse;		/* is fan input in use for rx*/
 	int tx_reg;				/* current reg used for TX */
 	u8  saved_conf1;			/* saved FEC0 reg */
-
-	/* TX sample handling */
 	unsigned int tx_sample;			/* current sample for TX */
 	bool tx_sample_pulse;			/* current sample is pulse */
 
@@ -229,7 +252,11 @@
 	int transmitter_mask;
 
 	/* RX settings */
-	bool learning_enabled;			/* learning input enabled */
+	bool learning_mode_enabled;		/* learning input enabled */
 	bool carrier_detect_enabled;		/* carrier detect enabled */
 	int rx_period_adjust;
+	bool rx_enabled;
 };
+
+static int ene_irq_status(struct ene_device *dev);
+static void ene_rx_read_hw_pointer(struct ene_device *dev);
diff --git a/drivers/media/IR/imon.c b/drivers/media/IR/imon.c
index faed5a3..bc11806 100644
--- a/drivers/media/IR/imon.c
+++ b/drivers/media/IR/imon.c
@@ -1,7 +1,7 @@
 /*
  *   imon.c:	input and display driver for SoundGraph iMON IR/VFD/LCD
  *
- *   Copyright(C) 2009  Jarod Wilson <jarod@wilsonet.com>
+ *   Copyright(C) 2010  Jarod Wilson <jarod@wilsonet.com>
  *   Portions based on the original lirc_imon driver,
  *	Copyright(C) 2004  Venky Raju(dev@venky.ws)
  *
@@ -26,6 +26,8 @@
  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
+
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -44,7 +46,7 @@
 #define MOD_AUTHOR	"Jarod Wilson <jarod@wilsonet.com>"
 #define MOD_DESC	"Driver for SoundGraph iMON MultiMedia IR/Display"
 #define MOD_NAME	"imon"
-#define MOD_VERSION	"0.9.1"
+#define MOD_VERSION	"0.9.2"
 
 #define DISPLAY_MINOR_BASE	144
 #define DEVICE_NAME	"lcd%d"
@@ -121,21 +123,26 @@
 	u16 vendor;			/* usb vendor ID */
 	u16 product;			/* usb product ID */
 
-	struct input_dev *idev;		/* input device for remote */
+	struct input_dev *rdev;		/* input device for remote */
+	struct input_dev *idev;		/* input device for panel & IR mouse */
 	struct input_dev *touch;	/* input device for touchscreen */
 
+	spinlock_t kc_lock;		/* make sure we get keycodes right */
 	u32 kc;				/* current input keycode */
 	u32 last_keycode;		/* last reported input keycode */
+	u32 rc_scancode;		/* the computed remote scancode */
+	u8 rc_toggle;			/* the computed remote toggle bit */
 	u64 ir_type;			/* iMON or MCE (RC6) IR protocol? */
-	u8 mce_toggle_bit;		/* last mce toggle bit */
 	bool release_code;		/* some keys send a release code */
 
 	u8 display_type;		/* store the display type */
 	bool pad_mouse;			/* toggle kbd(0)/mouse(1) mode */
 
+	char name_rdev[128];		/* rc input device name */
+	char phys_rdev[64];		/* rc input device phys path */
+
 	char name_idev[128];		/* input device name */
 	char phys_idev[64];		/* input device phys path */
-	struct timer_list itimer;	/* input device timer, need for rc6 */
 
 	char name_touch[128];		/* touch screen name */
 	char phys_touch[64];		/* touch screen phys path */
@@ -289,6 +296,9 @@
 	{ 0x000100000000ffeell, KEY_VOLUMEUP },
 	{ 0x010000000000ffeell, KEY_VOLUMEDOWN },
 	{ 0x000000000100ffeell, KEY_MUTE },
+	/* 0xffdc iMON MCE VFD */
+	{ 0x00010000ffffffeell, KEY_VOLUMEUP },
+	{ 0x01000000ffffffeell, KEY_VOLUMEDOWN },
 	/* iMON Knob values */
 	{ 0x000100ffffffffeell, KEY_VOLUMEUP },
 	{ 0x010000ffffffffeell, KEY_VOLUMEDOWN },
@@ -307,7 +317,7 @@
 
 static bool debug;
 module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)");
+MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)");
 
 /* lcd, vfd, vga or none? should be auto-detected, but can be overridden... */
 static int display_type;
@@ -365,15 +375,14 @@
 	subminor = iminor(inode);
 	interface = usb_find_interface(&imon_driver, subminor);
 	if (!interface) {
-		err("%s: could not find interface for minor %d",
-		    __func__, subminor);
+		pr_err("could not find interface for minor %d\n", subminor);
 		retval = -ENODEV;
 		goto exit;
 	}
 	ictx = usb_get_intfdata(interface);
 
 	if (!ictx) {
-		err("%s: no context found for minor %d", __func__, subminor);
+		pr_err("no context found for minor %d\n", subminor);
 		retval = -ENODEV;
 		goto exit;
 	}
@@ -381,10 +390,10 @@
 	mutex_lock(&ictx->lock);
 
 	if (!ictx->display_supported) {
-		err("%s: display not supported by device", __func__);
+		pr_err("display not supported by device\n");
 		retval = -ENODEV;
 	} else if (ictx->display_isopen) {
-		err("%s: display port is already open", __func__);
+		pr_err("display port is already open\n");
 		retval = -EBUSY;
 	} else {
 		ictx->display_isopen = true;
@@ -411,17 +420,17 @@
 	ictx = file->private_data;
 
 	if (!ictx) {
-		err("%s: no context for device", __func__);
+		pr_err("no context for device\n");
 		return -ENODEV;
 	}
 
 	mutex_lock(&ictx->lock);
 
 	if (!ictx->display_supported) {
-		err("%s: display not supported by device", __func__);
+		pr_err("display not supported by device\n");
 		retval = -ENODEV;
 	} else if (!ictx->display_isopen) {
-		err("%s: display is not open", __func__);
+		pr_err("display is not open\n");
 		retval = -EIO;
 	} else {
 		ictx->display_isopen = false;
@@ -500,19 +509,19 @@
 	if (retval) {
 		ictx->tx.busy = false;
 		smp_rmb(); /* ensure later readers know we're not busy */
-		err("%s: error submitting urb(%d)", __func__, retval);
+		pr_err("error submitting urb(%d)\n", retval);
 	} else {
 		/* Wait for transmission to complete (or abort) */
 		mutex_unlock(&ictx->lock);
 		retval = wait_for_completion_interruptible(
 				&ictx->tx.finished);
 		if (retval)
-			err("%s: task interrupted", __func__);
+			pr_err("task interrupted\n");
 		mutex_lock(&ictx->lock);
 
 		retval = ictx->tx.status;
 		if (retval)
-			err("%s: packet tx failed (%d)", __func__, retval);
+			pr_err("packet tx failed (%d)\n", retval);
 	}
 
 	kfree(control_req);
@@ -544,12 +553,12 @@
 					  0x00, 0x00, 0x00, 0x20 };
 
 	if (!ictx) {
-		err("%s: no context for device", __func__);
+		pr_err("no context for device\n");
 		return -ENODEV;
 	}
 
 	if (!ictx->dev_present_intf0) {
-		err("%s: no iMON device present", __func__);
+		pr_err("no iMON device present\n");
 		return -ENODEV;
 	}
 
@@ -577,7 +586,7 @@
 	int i;
 
 	if (!ictx) {
-		err("%s: no context for device", __func__);
+		pr_err("no context for device\n");
 		return -ENODEV;
 	}
 
@@ -638,8 +647,7 @@
 		memcpy(ictx->usb_tx_buf, clock_enable_pkt[i], 8);
 		retval = send_packet(ictx);
 		if (retval) {
-			err("%s: send_packet failed for packet %d",
-			    __func__, i);
+			pr_err("send_packet failed for packet %d\n", i);
 			break;
 		}
 	}
@@ -778,7 +786,7 @@
 	NULL
 };
 
-static struct attribute_group imon_display_attribute_group = {
+static struct attribute_group imon_display_attr_group = {
 	.attrs = imon_display_sysfs_entries
 };
 
@@ -787,7 +795,7 @@
 	NULL
 };
 
-static struct attribute_group imon_rf_attribute_group = {
+static struct attribute_group imon_rf_attr_group = {
 	.attrs = imon_rf_sysfs_entries
 };
 
@@ -815,20 +823,20 @@
 
 	ictx = file->private_data;
 	if (!ictx) {
-		err("%s: no context for device", __func__);
+		pr_err("no context for device\n");
 		return -ENODEV;
 	}
 
 	mutex_lock(&ictx->lock);
 
 	if (!ictx->dev_present_intf0) {
-		err("%s: no iMON device present", __func__);
+		pr_err("no iMON device present\n");
 		retval = -ENODEV;
 		goto exit;
 	}
 
 	if (n_bytes <= 0 || n_bytes > 32) {
-		err("%s: invalid payload size", __func__);
+		pr_err("invalid payload size\n");
 		retval = -EINVAL;
 		goto exit;
 	}
@@ -854,8 +862,7 @@
 
 		retval = send_packet(ictx);
 		if (retval) {
-			err("%s: send packet failed for packet #%d",
-					__func__, seq/2);
+			pr_err("send packet failed for packet #%d\n", seq / 2);
 			goto exit;
 		} else {
 			seq += 2;
@@ -869,8 +876,7 @@
 	ictx->usb_tx_buf[7] = (unsigned char) seq;
 	retval = send_packet(ictx);
 	if (retval)
-		err("%s: send packet failed for packet #%d",
-		    __func__, seq / 2);
+		pr_err("send packet failed for packet #%d\n", seq / 2);
 
 exit:
 	mutex_unlock(&ictx->lock);
@@ -899,21 +905,20 @@
 
 	ictx = file->private_data;
 	if (!ictx) {
-		err("%s: no context for device", __func__);
+		pr_err("no context for device\n");
 		return -ENODEV;
 	}
 
 	mutex_lock(&ictx->lock);
 
 	if (!ictx->display_supported) {
-		err("%s: no iMON display present", __func__);
+		pr_err("no iMON display present\n");
 		retval = -ENODEV;
 		goto exit;
 	}
 
 	if (n_bytes != 8) {
-		err("%s: invalid payload size: %d (expecting 8)",
-		    __func__, (int) n_bytes);
+		pr_err("invalid payload size: %d (expected 8)\n", (int)n_bytes);
 		retval = -EINVAL;
 		goto exit;
 	}
@@ -925,7 +930,7 @@
 
 	retval = send_packet(ictx);
 	if (retval) {
-		err("%s: send packet failed!", __func__);
+		pr_err("send packet failed!\n");
 		goto exit;
 	} else {
 		dev_dbg(ictx->dev, "%s: write %d bytes to LCD\n",
@@ -958,17 +963,6 @@
 }
 
 /**
- * mce/rc6 keypresses have no distinct release code, use timer
- */
-static void imon_mce_timeout(unsigned long data)
-{
-	struct imon_context *ictx = (struct imon_context *)data;
-
-	input_report_key(ictx->idev, ictx->last_keycode, 0);
-	input_sync(ictx->idev);
-}
-
-/**
  * report touchscreen input
  */
 static void imon_touch_display_timeout(unsigned long data)
@@ -1008,14 +1002,11 @@
 		dev_dbg(dev, "Configuring IR receiver for MCE protocol\n");
 		ir_proto_packet[0] = 0x01;
 		pad_mouse = false;
-		init_timer(&ictx->itimer);
-		ictx->itimer.data = (unsigned long)ictx;
-		ictx->itimer.function = imon_mce_timeout;
 		break;
 	case IR_TYPE_UNKNOWN:
 	case IR_TYPE_OTHER:
 		dev_dbg(dev, "Configuring IR receiver for iMON protocol\n");
-		if (pad_stabilize)
+		if (pad_stabilize && !nomouse)
 			pad_mouse = true;
 		else {
 			dev_dbg(dev, "PAD stabilize functionality disabled\n");
@@ -1027,7 +1018,7 @@
 	default:
 		dev_warn(dev, "Unsupported IR protocol specified, overriding "
 			 "to iMON IR protocol\n");
-		if (pad_stabilize)
+		if (pad_stabilize && !nomouse)
 			pad_mouse = true;
 		else {
 			dev_dbg(dev, "PAD stabilize functionality disabled\n");
@@ -1149,20 +1140,21 @@
 	return result;
 }
 
-static u32 imon_remote_key_lookup(struct imon_context *ictx, u32 hw_code)
+static u32 imon_remote_key_lookup(struct imon_context *ictx, u32 scancode)
 {
-	u32 scancode = be32_to_cpu(hw_code);
 	u32 keycode;
 	u32 release;
 	bool is_release_code = false;
 
 	/* Look for the initial press of a button */
-	keycode = ir_g_keycode_from_table(ictx->idev, scancode);
+	keycode = ir_g_keycode_from_table(ictx->rdev, scancode);
+	ictx->rc_toggle = 0x0;
+	ictx->rc_scancode = scancode;
 
 	/* Look for the release of a button */
 	if (keycode == KEY_RESERVED) {
 		release = scancode & ~0x4000;
-		keycode = ir_g_keycode_from_table(ictx->idev, release);
+		keycode = ir_g_keycode_from_table(ictx->rdev, release);
 		if (keycode != KEY_RESERVED)
 			is_release_code = true;
 	}
@@ -1172,9 +1164,8 @@
 	return keycode;
 }
 
-static u32 imon_mce_key_lookup(struct imon_context *ictx, u32 hw_code)
+static u32 imon_mce_key_lookup(struct imon_context *ictx, u32 scancode)
 {
-	u32 scancode = be32_to_cpu(hw_code);
 	u32 keycode;
 
 #define MCE_KEY_MASK 0x7000
@@ -1188,18 +1179,21 @@
 	 * but we can't or them into all codes, as some keys are decoded in
 	 * a different way w/o the same use of the toggle bit...
 	 */
-	if ((scancode >> 24) & 0x80)
+	if (scancode & 0x80000000)
 		scancode = scancode | MCE_KEY_MASK | MCE_TOGGLE_BIT;
 
-	keycode = ir_g_keycode_from_table(ictx->idev, scancode);
+	ictx->rc_scancode = scancode;
+	keycode = ir_g_keycode_from_table(ictx->rdev, scancode);
+
+	/* not used in mce mode, but make sure we know its false */
+	ictx->release_code = false;
 
 	return keycode;
 }
 
-static u32 imon_panel_key_lookup(u64 hw_code)
+static u32 imon_panel_key_lookup(u64 code)
 {
 	int i;
-	u64 code = be64_to_cpu(hw_code);
 	u32 keycode = KEY_RESERVED;
 
 	for (i = 0; i < ARRAY_SIZE(imon_panel_key_table); i++) {
@@ -1219,6 +1213,9 @@
 	u8 right_shift = 1;
 	bool mouse_input = true;
 	int dir = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ictx->kc_lock, flags);
 
 	/* newer iMON device PAD or mouse button */
 	if (ictx->product != 0xffdc && (buf[0] & 0x01) && len == 5) {
@@ -1250,6 +1247,8 @@
 	} else
 		mouse_input = false;
 
+	spin_unlock_irqrestore(&ictx->kc_lock, flags);
+
 	if (mouse_input) {
 		dev_dbg(ictx->dev, "sending mouse data via input subsystem\n");
 
@@ -1264,7 +1263,9 @@
 					 buf[1] >> right_shift & 0x1);
 		}
 		input_sync(ictx->idev);
+		spin_lock_irqsave(&ictx->kc_lock, flags);
 		ictx->last_keycode = ictx->kc;
+		spin_unlock_irqrestore(&ictx->kc_lock, flags);
 	}
 
 	return mouse_input;
@@ -1286,8 +1287,8 @@
 	int dir = 0;
 	char rel_x = 0x00, rel_y = 0x00;
 	u16 timeout, threshold;
-	u64 temp_key;
-	u32 remote_key;
+	u32 scancode = KEY_RESERVED;
+	unsigned long flags;
 
 	/*
 	 * The imon directional pad functions more like a touchpad. Bytes 3 & 4
@@ -1311,26 +1312,36 @@
 				dir = stabilize((int)rel_x, (int)rel_y,
 						timeout, threshold);
 				if (!dir) {
+					spin_lock_irqsave(&ictx->kc_lock,
+							  flags);
 					ictx->kc = KEY_UNKNOWN;
+					spin_unlock_irqrestore(&ictx->kc_lock,
+							       flags);
 					return;
 				}
 				buf[2] = dir & 0xFF;
 				buf[3] = (dir >> 8) & 0xFF;
-				memcpy(&temp_key, buf, sizeof(temp_key));
-				remote_key = (u32) (le64_to_cpu(temp_key)
-						    & 0xffffffff);
-				ictx->kc = imon_remote_key_lookup(ictx,
-								  remote_key);
+				scancode = be32_to_cpu(*((u32 *)buf));
 			}
 		} else {
+			/*
+			 * Hack alert: instead of using keycodes, we have
+			 * to use hard-coded scancodes here...
+			 */
 			if (abs(rel_y) > abs(rel_x)) {
 				buf[2] = (rel_y > 0) ? 0x7F : 0x80;
 				buf[3] = 0;
-				ictx->kc = (rel_y > 0) ? KEY_DOWN : KEY_UP;
+				if (rel_y > 0)
+					scancode = 0x01007f00; /* KEY_DOWN */
+				else
+					scancode = 0x01008000; /* KEY_UP */
 			} else {
 				buf[2] = 0;
 				buf[3] = (rel_x > 0) ? 0x7F : 0x80;
-				ictx->kc = (rel_x > 0) ? KEY_RIGHT : KEY_LEFT;
+				if (rel_x > 0)
+					scancode = 0x0100007f; /* KEY_RIGHT */
+				else
+					scancode = 0x01000080; /* KEY_LEFT */
 			}
 		}
 
@@ -1367,34 +1378,56 @@
 			dir = stabilize((int)rel_x, (int)rel_y,
 					timeout, threshold);
 			if (!dir) {
+				spin_lock_irqsave(&ictx->kc_lock, flags);
 				ictx->kc = KEY_UNKNOWN;
+				spin_unlock_irqrestore(&ictx->kc_lock, flags);
 				return;
 			}
 			buf[2] = dir & 0xFF;
 			buf[3] = (dir >> 8) & 0xFF;
-			memcpy(&temp_key, buf, sizeof(temp_key));
-			remote_key = (u32) (le64_to_cpu(temp_key) & 0xffffffff);
-			ictx->kc = imon_remote_key_lookup(ictx, remote_key);
+			scancode = be32_to_cpu(*((u32 *)buf));
 		} else {
+			/*
+			 * Hack alert: instead of using keycodes, we have
+			 * to use hard-coded scancodes here...
+			 */
 			if (abs(rel_y) > abs(rel_x)) {
 				buf[2] = (rel_y > 0) ? 0x7F : 0x80;
 				buf[3] = 0;
-				ictx->kc = (rel_y > 0) ? KEY_DOWN : KEY_UP;
+				if (rel_y > 0)
+					scancode = 0x01007f00; /* KEY_DOWN */
+				else
+					scancode = 0x01008000; /* KEY_UP */
 			} else {
 				buf[2] = 0;
 				buf[3] = (rel_x > 0) ? 0x7F : 0x80;
-				ictx->kc = (rel_x > 0) ? KEY_RIGHT : KEY_LEFT;
+				if (rel_x > 0)
+					scancode = 0x0100007f; /* KEY_RIGHT */
+				else
+					scancode = 0x01000080; /* KEY_LEFT */
 			}
 		}
 	}
+
+	if (scancode) {
+		spin_lock_irqsave(&ictx->kc_lock, flags);
+		ictx->kc = imon_remote_key_lookup(ictx, scancode);
+		spin_unlock_irqrestore(&ictx->kc_lock, flags);
+	}
 }
 
+/**
+ * figure out if these is a press or a release. We don't actually
+ * care about repeats, as those will be auto-generated within the IR
+ * subsystem for repeating scancodes.
+ */
 static int imon_parse_press_type(struct imon_context *ictx,
 				 unsigned char *buf, u8 ktype)
 {
 	int press_type = 0;
-	int rep_delay = ictx->idev->rep[REP_DELAY];
-	int rep_period = ictx->idev->rep[REP_PERIOD];
+	unsigned long flags;
+
+	spin_lock_irqsave(&ictx->kc_lock, flags);
 
 	/* key release of 0x02XXXXXX key */
 	if (ictx->kc == KEY_RESERVED && buf[0] == 0x02 && buf[3] == 0x00)
@@ -1410,22 +1443,10 @@
 		 buf[2] == 0x81 && buf[3] == 0xb7)
 		ictx->kc = ictx->last_keycode;
 
-	/* mce-specific button handling */
+	/* mce-specific button handling, no keyup events */
 	else if (ktype == IMON_KEY_MCE) {
-		/* initial press */
-		if (ictx->kc != ictx->last_keycode
-		    || buf[2] != ictx->mce_toggle_bit) {
-			ictx->last_keycode = ictx->kc;
-			ictx->mce_toggle_bit = buf[2];
-			press_type = 1;
-			mod_timer(&ictx->itimer,
-				  jiffies + msecs_to_jiffies(rep_delay));
-		/* repeat */
-		} else {
-			press_type = 2;
-			mod_timer(&ictx->itimer,
-				  jiffies + msecs_to_jiffies(rep_period));
-		}
+		ictx->rc_toggle = buf[2];
+		press_type = 1;
 
 	/* incoherent or irrelevant data */
 	} else if (ictx->kc == KEY_RESERVED)
@@ -1439,6 +1460,8 @@
 	else
 		press_type = 1;
 
+	spin_unlock_irqrestore(&ictx->kc_lock, flags);
+
 	return press_type;
 }
 
@@ -1451,41 +1474,45 @@
 	int len = urb->actual_length;
 	unsigned char *buf = urb->transfer_buffer;
 	struct device *dev = ictx->dev;
+	unsigned long flags;
 	u32 kc;
 	bool norelease = false;
 	int i;
-	u64 temp_key;
-	u64 panel_key = 0;
-	u32 remote_key = 0;
-	struct input_dev *idev = NULL;
+	u64 scancode;
+	struct input_dev *rdev = NULL;
+	struct ir_input_dev *irdev = NULL;
 	int press_type = 0;
 	int msec;
 	struct timeval t;
 	static struct timeval prev_time = { 0, 0 };
-	u8 ktype = IMON_KEY_IMON;
+	u8 ktype;
 
-	idev = ictx->idev;
+	rdev = ictx->rdev;
+	irdev = input_get_drvdata(rdev);
 
 	/* filter out junk data on the older 0xffdc imon devices */
 	if ((buf[0] == 0xff) && (buf[1] == 0xff) && (buf[2] == 0xff))
 		return;
 
 	/* Figure out what key was pressed */
-	memcpy(&temp_key, buf, sizeof(temp_key));
 	if (len == 8 && buf[7] == 0xee) {
+		scancode = be64_to_cpu(*((u64 *)buf));
 		ktype = IMON_KEY_PANEL;
-		panel_key = le64_to_cpu(temp_key);
-		kc = imon_panel_key_lookup(panel_key);
+		kc = imon_panel_key_lookup(scancode);
 	} else {
-		remote_key = (u32) (le64_to_cpu(temp_key) & 0xffffffff);
+		scancode = be32_to_cpu(*((u32 *)buf));
 		if (ictx->ir_type == IR_TYPE_RC6) {
+			ktype = IMON_KEY_IMON;
 			if (buf[0] == 0x80)
 				ktype = IMON_KEY_MCE;
-			kc = imon_mce_key_lookup(ictx, remote_key);
-		} else
-			kc = imon_remote_key_lookup(ictx, remote_key);
+			kc = imon_mce_key_lookup(ictx, scancode);
+		} else {
+			ktype = IMON_KEY_IMON;
+			kc = imon_remote_key_lookup(ictx, scancode);
+		}
 	}
 
+	spin_lock_irqsave(&ictx->kc_lock, flags);
 	/* keyboard/mouse mode toggle button */
 	if (kc == KEY_KEYBOARD && !ictx->release_code) {
 		ictx->last_keycode = kc;
@@ -1493,6 +1520,7 @@
 			ictx->pad_mouse = ~(ictx->pad_mouse) & 0x1;
 			dev_dbg(dev, "toggling to %s mode\n",
 				ictx->pad_mouse ? "mouse" : "keyboard");
+			spin_unlock_irqrestore(&ictx->kc_lock, flags);
 			return;
 		} else {
 			ictx->pad_mouse = 0;
@@ -1501,11 +1529,13 @@
 	}
 
 	ictx->kc = kc;
+	spin_unlock_irqrestore(&ictx->kc_lock, flags);
 
 	/* send touchscreen events through input subsystem if touchpad data */
 	if (ictx->display_type == IMON_DISPLAY_TYPE_VGA && len == 8 &&
 	    buf[7] == 0x86) {
 		imon_touch_event(ictx, buf);
+		return;
 
 	/* look for mouse events with pad in mouse mode */
 	} else if (ictx->pad_mouse) {
@@ -1533,36 +1563,55 @@
 	if (press_type < 0)
 		goto not_input_data;
 
+	spin_lock_irqsave(&ictx->kc_lock, flags);
 	if (ictx->kc == KEY_UNKNOWN)
 		goto unknown_key;
+	spin_unlock_irqrestore(&ictx->kc_lock, flags);
 
-	/* KEY_MUTE repeats from MCE and knob need to be suppressed */
-	if ((ictx->kc == KEY_MUTE && ictx->kc == ictx->last_keycode)
-	    && (buf[7] == 0xee || ktype == IMON_KEY_MCE)) {
+	if (ktype != IMON_KEY_PANEL) {
+		if (press_type == 0)
+			ir_keyup(irdev);
+		else {
+			ir_keydown(rdev, ictx->rc_scancode, ictx->rc_toggle);
+			spin_lock_irqsave(&ictx->kc_lock, flags);
+			ictx->last_keycode = ictx->kc;
+			spin_unlock_irqrestore(&ictx->kc_lock, flags);
+		}
+		return;
+	}
+
+	/* Only panel type events left to process now */
+	spin_lock_irqsave(&ictx->kc_lock, flags);
+
+	/* KEY_MUTE repeats from knob need to be suppressed */
+	if (ictx->kc == KEY_MUTE && ictx->kc == ictx->last_keycode) {
 		do_gettimeofday(&t);
 		msec = tv2int(&t, &prev_time);
 		prev_time = t;
-		if (msec < idev->rep[REP_DELAY])
+		if (msec < ictx->idev->rep[REP_DELAY]) {
+			spin_unlock_irqrestore(&ictx->kc_lock, flags);
 			return;
+		}
 	}
+	kc = ictx->kc;
 
-	input_report_key(idev, ictx->kc, press_type);
-	input_sync(idev);
+	spin_unlock_irqrestore(&ictx->kc_lock, flags);
 
-	/* panel keys and some remote keys don't generate a release */
-	if (panel_key || norelease) {
-		input_report_key(idev, ictx->kc, 0);
-		input_sync(idev);
-	}
+	input_report_key(ictx->idev, kc, press_type);
+	input_sync(ictx->idev);
 
-	ictx->last_keycode = ictx->kc;
+	/* panel keys don't generate a release */
+	input_report_key(ictx->idev, kc, 0);
+	input_sync(ictx->idev);
+
+	ictx->last_keycode = kc;
 
 	return;
 
 unknown_key:
+	spin_unlock_irqrestore(&ictx->kc_lock, flags);
 	dev_info(dev, "%s: unknown keypress, code 0x%llx\n", __func__,
-		 (panel_key ? be64_to_cpu(panel_key) :
-			      be32_to_cpu(remote_key)));
+		 (long long)scancode);
 	return;
 
 not_input_data:
@@ -1653,31 +1702,205 @@
 	usb_submit_urb(ictx->rx_urb_intf1, GFP_ATOMIC);
 }
 
+/*
+ * The 0x15c2:0xffdc device ID was used for umpteen different imon
+ * devices, and all of them constantly spew interrupts, even when there
+ * is no actual data to report. However, byte 6 of this buffer looks like
+ * its unique across device variants, so we're trying to key off that to
+ * figure out which display type (if any) and what IR protocol the device
+ * actually supports. These devices have their IR protocol hard-coded into
+ * their firmware, they can't be changed on the fly like the newer hardware.
+ */
+static void imon_get_ffdc_type(struct imon_context *ictx)
+{
+	u8 ffdc_cfg_byte = ictx->usb_rx_buf[6];
+	u8 detected_display_type = IMON_DISPLAY_TYPE_NONE;
+	u64 allowed_protos = IR_TYPE_OTHER;
+
+	switch (ffdc_cfg_byte) {
+	/* iMON Knob, no display, iMON IR + vol knob */
+	case 0x21:
+		dev_info(ictx->dev, "0xffdc iMON Knob, iMON IR");
+		ictx->display_supported = false;
+		break;
+	/* iMON 2.4G LT (usb stick), no display, iMON RF */
+	case 0x4e:
+		dev_info(ictx->dev, "0xffdc iMON 2.4G LT, iMON RF");
+		ictx->display_supported = false;
+		ictx->rf_device = true;
+		break;
+	/* iMON VFD, no IR (does have vol knob tho) */
+	case 0x35:
+		dev_info(ictx->dev, "0xffdc iMON VFD + knob, no IR");
+		detected_display_type = IMON_DISPLAY_TYPE_VFD;
+		break;
+	/* iMON VFD, iMON IR */
+	case 0x24:
+	case 0x85:
+		dev_info(ictx->dev, "0xffdc iMON VFD, iMON IR");
+		detected_display_type = IMON_DISPLAY_TYPE_VFD;
+		break;
+	/* iMON VFD, MCE IR */
+	case 0x9e:
+		dev_info(ictx->dev, "0xffdc iMON VFD, MCE IR");
+		detected_display_type = IMON_DISPLAY_TYPE_VFD;
+		allowed_protos = IR_TYPE_RC6;
+		break;
+	/* iMON LCD, MCE IR */
+	case 0x9f:
+		dev_info(ictx->dev, "0xffdc iMON LCD, MCE IR");
+		detected_display_type = IMON_DISPLAY_TYPE_LCD;
+		allowed_protos = IR_TYPE_RC6;
+		break;
+	default:
+		dev_info(ictx->dev, "Unknown 0xffdc device, "
+			 "defaulting to VFD and iMON IR");
+		detected_display_type = IMON_DISPLAY_TYPE_VFD;
+		break;
+	}
+
+	printk(KERN_CONT " (id 0x%02x)\n", ffdc_cfg_byte);
+
+	ictx->display_type = detected_display_type;
+	ictx->props->allowed_protos = allowed_protos;
+	ictx->ir_type = allowed_protos;
+}
+
+static void imon_set_display_type(struct imon_context *ictx)
+{
+	u8 configured_display_type = IMON_DISPLAY_TYPE_VFD;
+
+	/*
+	 * Try to auto-detect the type of display if the user hasn't set
+	 * it by hand via the display_type modparam. Default is VFD.
+	 */
+
+	if (display_type == IMON_DISPLAY_TYPE_AUTO) {
+		switch (ictx->product) {
+		case 0xffdc:
+			/* set in imon_get_ffdc_type() */
+			configured_display_type = ictx->display_type;
+			break;
+		case 0x0034:
+		case 0x0035:
+			configured_display_type = IMON_DISPLAY_TYPE_VGA;
+			break;
+		case 0x0038:
+		case 0x0039:
+		case 0x0045:
+			configured_display_type = IMON_DISPLAY_TYPE_LCD;
+			break;
+		case 0x003c:
+		case 0x0041:
+		case 0x0042:
+		case 0x0043:
+			configured_display_type = IMON_DISPLAY_TYPE_NONE;
+			ictx->display_supported = false;
+			break;
+		case 0x0036:
+		case 0x0044:
+		default:
+			configured_display_type = IMON_DISPLAY_TYPE_VFD;
+			break;
+		}
+	} else {
+		configured_display_type = display_type;
+		if (display_type == IMON_DISPLAY_TYPE_NONE)
+			ictx->display_supported = false;
+		else
+			ictx->display_supported = true;
+		dev_info(ictx->dev, "%s: overriding display type to %d via "
+			 "modparam\n", __func__, display_type);
+	}
+
+	ictx->display_type = configured_display_type;
+}
+
+static struct input_dev *imon_init_rdev(struct imon_context *ictx)
+{
+	struct input_dev *rdev;
+	struct ir_dev_props *props;
+	int ret;
+	char *ir_codes = NULL;
+	const unsigned char fp_packet[] = { 0x40, 0x00, 0x00, 0x00,
+					    0x00, 0x00, 0x00, 0x88 };
+
+	rdev = input_allocate_device();
+	props = kzalloc(sizeof(*props), GFP_KERNEL);
+	if (!rdev || !props) {
+		dev_err(ictx->dev, "remote control dev allocation failed\n");
+		goto out;
+	}
+
+	snprintf(ictx->name_rdev, sizeof(ictx->name_rdev),
+		 "iMON Remote (%04x:%04x)", ictx->vendor, ictx->product);
+	usb_make_path(ictx->usbdev_intf0, ictx->phys_rdev,
+		      sizeof(ictx->phys_rdev));
+	strlcat(ictx->phys_rdev, "/input0", sizeof(ictx->phys_rdev));
+
+	rdev->name = ictx->name_rdev;
+	rdev->phys = ictx->phys_rdev;
+	usb_to_input_id(ictx->usbdev_intf0, &rdev->id);
+	rdev->dev.parent = ictx->dev;
+	rdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+	input_set_drvdata(rdev, ictx);
+
+	props->priv = ictx;
+	props->driver_type = RC_DRIVER_SCANCODE;
+	props->allowed_protos = IR_TYPE_OTHER | IR_TYPE_RC6; /* iMON PAD or MCE */
+	props->change_protocol = imon_ir_change_protocol;
+	ictx->props = props;
+
+	/* Enable front-panel buttons and/or knobs */
+	memcpy(ictx->usb_tx_buf, &fp_packet, sizeof(fp_packet));
+	ret = send_packet(ictx);
+	/* Not fatal, but warn about it */
+	if (ret)
+		dev_info(ictx->dev, "panel buttons/knobs setup failed\n");
+
+	if (ictx->product == 0xffdc)
+		imon_get_ffdc_type(ictx);
+
+	imon_set_display_type(ictx);
+
+	if (ictx->ir_type == IR_TYPE_RC6)
+		ir_codes = RC_MAP_IMON_MCE;
+	else
+		ir_codes = RC_MAP_IMON_PAD;
+
+	ret = ir_input_register(rdev, ir_codes, props, MOD_NAME);
+	if (ret < 0) {
+		dev_err(ictx->dev, "remote input dev register failed\n");
+		goto out;
+	}
+
+	return rdev;
+
+out:
+	kfree(props);
+	input_free_device(rdev);
+	return NULL;
+}
+
 static struct input_dev *imon_init_idev(struct imon_context *ictx)
 {
 	struct input_dev *idev;
-	struct ir_dev_props *props;
 	int ret, i;
 
 	idev = input_allocate_device();
 	if (!idev) {
-		dev_err(ictx->dev, "remote input dev allocation failed\n");
-		goto idev_alloc_failed;
-	}
-
-	props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL);
-	if (!props) {
-		dev_err(ictx->dev, "remote ir dev props allocation failed\n");
-		goto props_alloc_failed;
+		dev_err(ictx->dev, "input dev allocation failed\n");
+		goto out;
 	}
 
 	snprintf(ictx->name_idev, sizeof(ictx->name_idev),
-		 "iMON Remote (%04x:%04x)", ictx->vendor, ictx->product);
+		 "iMON Panel, Knob and Mouse(%04x:%04x)",
+		 ictx->vendor, ictx->product);
 	idev->name = ictx->name_idev;
 
 	usb_make_path(ictx->usbdev_intf0, ictx->phys_idev,
 		      sizeof(ictx->phys_idev));
-	strlcat(ictx->phys_idev, "/input0", sizeof(ictx->phys_idev));
+	strlcat(ictx->phys_idev, "/input1", sizeof(ictx->phys_idev));
 	idev->phys = ictx->phys_idev;
 
 	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | BIT_MASK(EV_REL);
@@ -1693,30 +1916,20 @@
 		__set_bit(kc, idev->keybit);
 	}
 
-	props->priv = ictx;
-	props->driver_type = RC_DRIVER_SCANCODE;
-	/* IR_TYPE_OTHER maps to iMON PAD remote, IR_TYPE_RC6 to MCE remote */
-	props->allowed_protos = IR_TYPE_OTHER | IR_TYPE_RC6;
-	props->change_protocol = imon_ir_change_protocol;
-	ictx->props = props;
-
 	usb_to_input_id(ictx->usbdev_intf0, &idev->id);
 	idev->dev.parent = ictx->dev;
+	input_set_drvdata(idev, ictx);
 
-	ret = ir_input_register(idev, RC_MAP_IMON_PAD, props, MOD_NAME);
+	ret = input_register_device(idev);
 	if (ret < 0) {
-		dev_err(ictx->dev, "remote input dev register failed\n");
-		goto idev_register_failed;
+		dev_err(ictx->dev, "input dev register failed\n");
+		goto out;
 	}
 
 	return idev;
 
-idev_register_failed:
-	kfree(props);
-props_alloc_failed:
+out:
 	input_free_device(idev);
-idev_alloc_failed:
-
 	return NULL;
 }
 
@@ -1738,7 +1951,7 @@
 
 	usb_make_path(ictx->usbdev_intf1, ictx->phys_touch,
 		      sizeof(ictx->phys_touch));
-	strlcat(ictx->phys_touch, "/input1", sizeof(ictx->phys_touch));
+	strlcat(ictx->phys_touch, "/input2", sizeof(ictx->phys_touch));
 	touch->phys = ictx->phys_touch;
 
 	touch->evbit[0] =
@@ -1850,7 +2063,7 @@
 
 	/* Input endpoint is mandatory */
 	if (!ir_ep_found)
-		err("%s: no valid input (IR) endpoint found.", __func__);
+		pr_err("no valid input (IR) endpoint found\n");
 
 	ictx->tx_control = tx_control;
 
@@ -1888,6 +2101,7 @@
 	}
 
 	mutex_init(&ictx->lock);
+	spin_lock_init(&ictx->kc_lock);
 
 	mutex_lock(&ictx->lock);
 
@@ -1913,6 +2127,12 @@
 		goto idev_setup_failed;
 	}
 
+	ictx->rdev = imon_init_rdev(ictx);
+	if (!ictx->rdev) {
+		dev_err(dev, "%s: rc device setup failed\n", __func__);
+		goto rdev_setup_failed;
+	}
+
 	usb_fill_int_urb(ictx->rx_urb_intf0, ictx->usbdev_intf0,
 		usb_rcvintpipe(ictx->usbdev_intf0,
 			ictx->rx_endpoint_intf0->bEndpointAddress),
@@ -1922,15 +2142,16 @@
 
 	ret = usb_submit_urb(ictx->rx_urb_intf0, GFP_KERNEL);
 	if (ret) {
-		err("%s: usb_submit_urb failed for intf0 (%d)",
-		    __func__, ret);
+		pr_err("usb_submit_urb failed for intf0 (%d)\n", ret);
 		goto urb_submit_failed;
 	}
 
 	return ictx;
 
 urb_submit_failed:
-	ir_input_unregister(ictx->idev);
+	ir_input_unregister(ictx->rdev);
+rdev_setup_failed:
+	input_unregister_device(ictx->idev);
 idev_setup_failed:
 find_endpoint_failed:
 	mutex_unlock(&ictx->lock);
@@ -1954,7 +2175,7 @@
 
 	rx_urb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!rx_urb) {
-		err("%s: usb_alloc_urb failed for IR urb", __func__);
+		pr_err("usb_alloc_urb failed for IR urb\n");
 		goto rx_urb_alloc_failed;
 	}
 
@@ -1992,8 +2213,7 @@
 	ret = usb_submit_urb(ictx->rx_urb_intf1, GFP_KERNEL);
 
 	if (ret) {
-		err("%s: usb_submit_urb failed for intf1 (%d)",
-		    __func__, ret);
+		pr_err("usb_submit_urb failed for intf1 (%d)\n", ret);
 		goto urb_submit_failed;
 	}
 
@@ -2012,116 +2232,6 @@
 	return NULL;
 }
 
-/*
- * The 0x15c2:0xffdc device ID was used for umpteen different imon
- * devices, and all of them constantly spew interrupts, even when there
- * is no actual data to report. However, byte 6 of this buffer looks like
- * its unique across device variants, so we're trying to key off that to
- * figure out which display type (if any) and what IR protocol the device
- * actually supports. These devices have their IR protocol hard-coded into
- * their firmware, they can't be changed on the fly like the newer hardware.
- */
-static void imon_get_ffdc_type(struct imon_context *ictx)
-{
-	u8 ffdc_cfg_byte = ictx->usb_rx_buf[6];
-	u8 detected_display_type = IMON_DISPLAY_TYPE_NONE;
-	u64 allowed_protos = IR_TYPE_OTHER;
-
-	switch (ffdc_cfg_byte) {
-	/* iMON Knob, no display, iMON IR + vol knob */
-	case 0x21:
-		dev_info(ictx->dev, "0xffdc iMON Knob, iMON IR");
-		ictx->display_supported = false;
-		break;
-	/* iMON 2.4G LT (usb stick), no display, iMON RF */
-	case 0x4e:
-		dev_info(ictx->dev, "0xffdc iMON 2.4G LT, iMON RF");
-		ictx->display_supported = false;
-		ictx->rf_device = true;
-		break;
-	/* iMON VFD, no IR (does have vol knob tho) */
-	case 0x35:
-		dev_info(ictx->dev, "0xffdc iMON VFD + knob, no IR");
-		detected_display_type = IMON_DISPLAY_TYPE_VFD;
-		break;
-	/* iMON VFD, iMON IR */
-	case 0x24:
-	case 0x85:
-		dev_info(ictx->dev, "0xffdc iMON VFD, iMON IR");
-		detected_display_type = IMON_DISPLAY_TYPE_VFD;
-		break;
-	/* iMON LCD, MCE IR */
-	case 0x9e:
-	case 0x9f:
-		dev_info(ictx->dev, "0xffdc iMON LCD, MCE IR");
-		detected_display_type = IMON_DISPLAY_TYPE_LCD;
-		allowed_protos = IR_TYPE_RC6;
-		break;
-	default:
-		dev_info(ictx->dev, "Unknown 0xffdc device, "
-			 "defaulting to VFD and iMON IR");
-		detected_display_type = IMON_DISPLAY_TYPE_VFD;
-		break;
-	}
-
-	printk(KERN_CONT " (id 0x%02x)\n", ffdc_cfg_byte);
-
-	ictx->display_type = detected_display_type;
-	ictx->props->allowed_protos = allowed_protos;
-	ictx->ir_type = allowed_protos;
-}
-
-static void imon_set_display_type(struct imon_context *ictx,
-				  struct usb_interface *intf)
-{
-	u8 configured_display_type = IMON_DISPLAY_TYPE_VFD;
-
-	/*
-	 * Try to auto-detect the type of display if the user hasn't set
-	 * it by hand via the display_type modparam. Default is VFD.
-	 */
-
-	if (display_type == IMON_DISPLAY_TYPE_AUTO) {
-		switch (ictx->product) {
-		case 0xffdc:
-			/* set in imon_get_ffdc_type() */
-			configured_display_type = ictx->display_type;
-			break;
-		case 0x0034:
-		case 0x0035:
-			configured_display_type = IMON_DISPLAY_TYPE_VGA;
-			break;
-		case 0x0038:
-		case 0x0039:
-		case 0x0045:
-			configured_display_type = IMON_DISPLAY_TYPE_LCD;
-			break;
-		case 0x003c:
-		case 0x0041:
-		case 0x0042:
-		case 0x0043:
-			configured_display_type = IMON_DISPLAY_TYPE_NONE;
-			ictx->display_supported = false;
-			break;
-		case 0x0036:
-		case 0x0044:
-		default:
-			configured_display_type = IMON_DISPLAY_TYPE_VFD;
-			break;
-		}
-	} else {
-		configured_display_type = display_type;
-		if (display_type == IMON_DISPLAY_TYPE_NONE)
-			ictx->display_supported = false;
-		else
-			ictx->display_supported = true;
-		dev_info(ictx->dev, "%s: overriding display type to %d via "
-			 "modparam\n", __func__, display_type);
-	}
-
-	ictx->display_type = configured_display_type;
-}
-
 static void imon_init_display(struct imon_context *ictx,
 			      struct usb_interface *intf)
 {
@@ -2130,8 +2240,7 @@
 	dev_dbg(ictx->dev, "Registering iMON display with sysfs\n");
 
 	/* set up sysfs entry for built-in clock */
-	ret = sysfs_create_group(&intf->dev.kobj,
-				 &imon_display_attribute_group);
+	ret = sysfs_create_group(&intf->dev.kobj, &imon_display_attr_group);
 	if (ret)
 		dev_err(ictx->dev, "Could not create display sysfs "
 			"entries(%d)", ret);
@@ -2162,8 +2271,6 @@
 	struct imon_context *ictx = NULL;
 	struct imon_context *first_if_ctx = NULL;
 	u16 vendor, product;
-	const unsigned char fp_packet[] = { 0x40, 0x00, 0x00, 0x00,
-					    0x00, 0x00, 0x00, 0x88 };
 
 	code_length = BUF_CHUNK_SIZE * 8;
 
@@ -2185,7 +2292,7 @@
 	if (ifnum == 0) {
 		ictx = imon_init_intf0(interface);
 		if (!ictx) {
-			err("%s: failed to initialize context!\n", __func__);
+			pr_err("failed to initialize context!\n");
 			ret = -ENODEV;
 			goto fail;
 		}
@@ -2194,7 +2301,7 @@
 	/* this is the secondary interface on the device */
 		ictx = imon_init_intf1(interface, first_if_ctx);
 		if (!ictx) {
-			err("%s: failed to attach to context!\n", __func__);
+			pr_err("failed to attach to context!\n");
 			ret = -ENODEV;
 			goto fail;
 		}
@@ -2204,39 +2311,18 @@
 	usb_set_intfdata(interface, ictx);
 
 	if (ifnum == 0) {
-		/* Enable front-panel buttons and/or knobs */
-		memcpy(ictx->usb_tx_buf, &fp_packet, sizeof(fp_packet));
-		ret = send_packet(ictx);
-		/* Not fatal, but warn about it */
-		if (ret)
-			dev_info(dev, "failed to enable panel buttons "
-				 "and/or knobs\n");
-
-		if (product == 0xffdc)
-			imon_get_ffdc_type(ictx);
-
-		imon_set_display_type(ictx, interface);
-
 		if (product == 0xffdc && ictx->rf_device) {
 			sysfs_err = sysfs_create_group(&interface->dev.kobj,
-						       &imon_rf_attribute_group);
+						       &imon_rf_attr_group);
 			if (sysfs_err)
-				err("%s: Could not create RF sysfs entries(%d)",
-				    __func__, sysfs_err);
+				pr_err("Could not create RF sysfs entries(%d)\n",
+				       sysfs_err);
 		}
 
 		if (ictx->display_supported)
 			imon_init_display(ictx, interface);
 	}
 
-	/* set IR protocol/remote type */
-	ret = imon_ir_change_protocol(ictx, ictx->ir_type);
-	if (ret) {
-		dev_warn(dev, "%s: failed to set IR protocol, falling back "
-			 "to standard iMON protocol mode\n", __func__);
-		ictx->ir_type = IR_TYPE_OTHER;
-	}
-
 	dev_info(dev, "iMON device (%04x:%04x, intf%d) on "
 		 "usb<%d:%d> initialized\n", vendor, product, ifnum,
 		 usbdev->bus->busnum, usbdev->devnum);
@@ -2275,10 +2361,8 @@
 	 * sysfs_remove_group is safe to call even if sysfs_create_group
 	 * hasn't been called
 	 */
-	sysfs_remove_group(&interface->dev.kobj,
-			   &imon_display_attribute_group);
-	sysfs_remove_group(&interface->dev.kobj,
-			   &imon_rf_attribute_group);
+	sysfs_remove_group(&interface->dev.kobj, &imon_display_attr_group);
+	sysfs_remove_group(&interface->dev.kobj, &imon_rf_attr_group);
 
 	usb_set_intfdata(interface, NULL);
 
@@ -2291,7 +2375,8 @@
 	if (ifnum == 0) {
 		ictx->dev_present_intf0 = false;
 		usb_kill_urb(ictx->rx_urb_intf0);
-		ir_input_unregister(ictx->idev);
+		input_unregister_device(ictx->idev);
+		ir_input_unregister(ictx->rdev);
 		if (ictx->display_supported) {
 			if (ictx->display_type == IMON_DISPLAY_TYPE_LCD)
 				usb_deregister_dev(interface, &imon_lcd_class);
@@ -2311,11 +2396,8 @@
 		mutex_unlock(&ictx->lock);
 		if (!ictx->display_isopen)
 			free_imon_context(ictx);
-	} else {
-		if (ictx->ir_type == IR_TYPE_RC6)
-			del_timer_sync(&ictx->itimer);
+	} else
 		mutex_unlock(&ictx->lock);
-	}
 
 	mutex_unlock(&driver_lock);
 
@@ -2372,7 +2454,7 @@
 
 	rc = usb_register(&imon_driver);
 	if (rc) {
-		err("%s: usb register failed(%d)", __func__, rc);
+		pr_err("usb register failed(%d)\n", rc);
 		rc = -ENODEV;
 	}
 
diff --git a/drivers/media/IR/ir-core-priv.h b/drivers/media/IR/ir-core-priv.h
index a85a8c7..81c936b 100644
--- a/drivers/media/IR/ir-core-priv.h
+++ b/drivers/media/IR/ir-core-priv.h
@@ -17,6 +17,7 @@
 #define _IR_RAW_EVENT
 
 #include <linux/slab.h>
+#include <linux/spinlock.h>
 #include <media/ir-core.h>
 
 struct ir_raw_handler {
@@ -33,6 +34,7 @@
 struct ir_raw_event_ctrl {
 	struct list_head		list;		/* to keep track of raw clients */
 	struct task_struct		*thread;
+	spinlock_t			lock;
 	struct kfifo			kfifo;		/* fifo for the pulse/space durations */
 	ktime_t				last_event;	/* when last event occurred */
 	enum raw_event_type		last_type;	/* last event type */
@@ -76,10 +78,22 @@
 		bool first;
 		bool toggle;
 	} jvc;
+	struct rc5_sz_dec {
+		int state;
+		u32 bits;
+		unsigned count;
+		unsigned wanted_bits;
+	} rc5_sz;
 	struct lirc_codec {
 		struct ir_input_dev *ir_dev;
 		struct lirc_driver *drv;
 		int carrier_low;
+
+		ktime_t gap_start;
+		u64 gap_duration;
+		bool gap;
+		bool send_timeout_reports;
+
 	} lirc;
 };
 
@@ -107,13 +121,19 @@
 		ev->duration -= duration;
 }
 
+/* Returns true if event is normal pulse/space event */
+static inline bool is_timing_event(struct ir_raw_event ev)
+{
+	return !ev.carrier_report && !ev.reset;
+}
+
 #define TO_US(duration)			DIV_ROUND_CLOSEST((duration), 1000)
 #define TO_STR(is_pulse)		((is_pulse) ? "pulse" : "space")
-#define IS_RESET(ev)			(ev.duration == 0)
 /*
  * Routines from ir-sysfs.c - Meant to be called only internally inside
  * ir-core
  */
+int ir_register_input(struct input_dev *input_dev);
 
 int ir_register_class(struct input_dev *input_dev);
 void ir_unregister_class(struct input_dev *input_dev);
diff --git a/drivers/media/IR/ir-jvc-decoder.c b/drivers/media/IR/ir-jvc-decoder.c
index 77a89c4..63dca6e 100644
--- a/drivers/media/IR/ir-jvc-decoder.c
+++ b/drivers/media/IR/ir-jvc-decoder.c
@@ -50,8 +50,9 @@
 	if (!(ir_dev->raw->enabled_protocols & IR_TYPE_JVC))
 		return 0;
 
-	if (IS_RESET(ev)) {
-		data->state = STATE_INACTIVE;
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
 		return 0;
 	}
 
diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c
index c06b4d5..9186b45 100644
--- a/drivers/media/IR/ir-keytable.c
+++ b/drivers/media/IR/ir-keytable.c
@@ -435,7 +435,7 @@
  * This routine is used to signal that a key has been released on the
  * remote control. It reports a keyup input event via input_report_key().
  */
-static void ir_keyup(struct ir_input_dev *ir)
+void ir_keyup(struct ir_input_dev *ir)
 {
 	if (!ir->keypressed)
 		return;
@@ -445,6 +445,7 @@
 	input_sync(ir->input_dev);
 	ir->keypressed = false;
 }
+EXPORT_SYMBOL_GPL(ir_keyup);
 
 /**
  * ir_timer_keyup() - generates a keyup event after a timeout
@@ -640,6 +641,10 @@
 				goto out_event;
 		}
 
+	rc = ir_register_input(input_dev);
+	if (rc < 0)
+		goto out_event;
+
 	IR_dprintk(1, "Registered input device on %s for %s remote%s.\n",
 		   driver_name, rc_tab->name,
 		   (ir_dev->props && ir_dev->props->driver_type == RC_DRIVER_IR_RAW) ?
diff --git a/drivers/media/IR/ir-lirc-codec.c b/drivers/media/IR/ir-lirc-codec.c
index 1983cd3..9fc0db9 100644
--- a/drivers/media/IR/ir-lirc-codec.c
+++ b/drivers/media/IR/ir-lirc-codec.c
@@ -32,6 +32,7 @@
 static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
 {
 	struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+	struct lirc_codec *lirc = &ir_dev->raw->lirc;
 	int sample;
 
 	if (!(ir_dev->raw->enabled_protocols & IR_TYPE_LIRC))
@@ -40,21 +41,57 @@
 	if (!ir_dev->raw->lirc.drv || !ir_dev->raw->lirc.drv->rbuf)
 		return -EINVAL;
 
-	if (IS_RESET(ev))
+	/* Packet start */
+	if (ev.reset)
 		return 0;
 
-	IR_dprintk(2, "LIRC data transfer started (%uus %s)\n",
-		   TO_US(ev.duration), TO_STR(ev.pulse));
+	/* Carrier reports */
+	if (ev.carrier_report) {
+		sample = LIRC_FREQUENCY(ev.carrier);
 
-	sample = ev.duration / 1000;
-	if (ev.pulse)
-		sample |= PULSE_BIT;
+	/* Packet end */
+	} else if (ev.timeout) {
+
+		if (lirc->gap)
+			return 0;
+
+		lirc->gap_start = ktime_get();
+		lirc->gap = true;
+		lirc->gap_duration = ev.duration;
+
+		if (!lirc->send_timeout_reports)
+			return 0;
+
+		sample = LIRC_TIMEOUT(ev.duration / 1000);
+
+	/* Normal sample */
+	} else {
+
+		if (lirc->gap) {
+			int gap_sample;
+
+			lirc->gap_duration += ktime_to_ns(ktime_sub(ktime_get(),
+				lirc->gap_start));
+
+			/* Convert to ms and cap by LIRC_VALUE_MASK */
+			do_div(lirc->gap_duration, 1000);
+			lirc->gap_duration = min(lirc->gap_duration,
+							(u64)LIRC_VALUE_MASK);
+
+			gap_sample = LIRC_SPACE(lirc->gap_duration);
+			lirc_buffer_write(ir_dev->raw->lirc.drv->rbuf,
+						(unsigned char *) &gap_sample);
+			lirc->gap = false;
+		}
+
+		sample = ev.pulse ? LIRC_PULSE(ev.duration / 1000) :
+					LIRC_SPACE(ev.duration / 1000);
+	}
 
 	lirc_buffer_write(ir_dev->raw->lirc.drv->rbuf,
 			  (unsigned char *) &sample);
 	wake_up(&ir_dev->raw->lirc.drv->rbuf->wait_poll);
 
-
 	return 0;
 }
 
@@ -102,7 +139,7 @@
 	struct ir_input_dev *ir_dev;
 	int ret = 0;
 	void *drv_data;
-	unsigned long val = 0;
+	__u32 val = 0, tmp;
 
 	lirc = lirc_get_pdata(filep);
 	if (!lirc)
@@ -115,7 +152,7 @@
 	drv_data = ir_dev->props->priv;
 
 	if (_IOC_DIR(cmd) & _IOC_WRITE) {
-		ret = get_user(val, (unsigned long *)arg);
+		ret = get_user(val, (__u32 *)arg);
 		if (ret)
 			return ret;
 	}
@@ -130,22 +167,20 @@
 	case LIRC_SET_SEND_MODE:
 		if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))
 			return -EINVAL;
-		break;
+		return 0;
 
 	/* TX settings */
 	case LIRC_SET_TRANSMITTER_MASK:
-		if (ir_dev->props->s_tx_mask)
-			ret = ir_dev->props->s_tx_mask(drv_data, (u32)val);
-		else
+		if (!ir_dev->props->s_tx_mask)
 			return -EINVAL;
-		break;
+
+		return ir_dev->props->s_tx_mask(drv_data, val);
 
 	case LIRC_SET_SEND_CARRIER:
-		if (ir_dev->props->s_tx_carrier)
-			ir_dev->props->s_tx_carrier(drv_data, (u32)val);
-		else
+		if (!ir_dev->props->s_tx_carrier)
 			return -EINVAL;
-		break;
+
+		return ir_dev->props->s_tx_carrier(drv_data, val);
 
 	case LIRC_SET_SEND_DUTY_CYCLE:
 		if (!ir_dev->props->s_tx_duty_cycle)
@@ -154,39 +189,42 @@
 		if (val <= 0 || val >= 100)
 			return -EINVAL;
 
-		ir_dev->props->s_tx_duty_cycle(ir_dev->props->priv, val);
-		break;
+		return ir_dev->props->s_tx_duty_cycle(drv_data, val);
 
 	/* RX settings */
 	case LIRC_SET_REC_CARRIER:
-		if (ir_dev->props->s_rx_carrier_range)
-			ret = ir_dev->props->s_rx_carrier_range(
-				ir_dev->props->priv,
-				ir_dev->raw->lirc.carrier_low, val);
-		else
+		if (!ir_dev->props->s_rx_carrier_range)
 			return -ENOSYS;
 
-		if (!ret)
-			ir_dev->raw->lirc.carrier_low = 0;
-		break;
+		if (val <= 0)
+			return -EINVAL;
+
+		return ir_dev->props->s_rx_carrier_range(drv_data,
+			ir_dev->raw->lirc.carrier_low, val);
 
 	case LIRC_SET_REC_CARRIER_RANGE:
-		if (val >= 0)
-			ir_dev->raw->lirc.carrier_low = val;
-		break;
+		if (val <= 0)
+			return -EINVAL;
 
+		ir_dev->raw->lirc.carrier_low = val;
+		return 0;
 
 	case LIRC_GET_REC_RESOLUTION:
 		val = ir_dev->props->rx_resolution;
 		break;
 
 	case LIRC_SET_WIDEBAND_RECEIVER:
-		if (ir_dev->props->s_learning_mode)
-			return ir_dev->props->s_learning_mode(
-				ir_dev->props->priv, !!val);
-		else
+		if (!ir_dev->props->s_learning_mode)
 			return -ENOSYS;
 
+		return ir_dev->props->s_learning_mode(drv_data, !!val);
+
+	case LIRC_SET_MEASURE_CARRIER_MODE:
+		if (!ir_dev->props->s_carrier_report)
+			return -ENOSYS;
+
+		return ir_dev->props->s_carrier_report(drv_data, !!val);
+
 	/* Generic timeout support */
 	case LIRC_GET_MIN_TIMEOUT:
 		if (!ir_dev->props->max_timeout)
@@ -201,10 +239,20 @@
 		break;
 
 	case LIRC_SET_REC_TIMEOUT:
-		if (val < ir_dev->props->min_timeout ||
-		    val > ir_dev->props->max_timeout)
-			return -EINVAL;
-		ir_dev->props->timeout = val * 1000;
+		if (!ir_dev->props->max_timeout)
+			return -ENOSYS;
+
+		tmp = val * 1000;
+
+		if (tmp < ir_dev->props->min_timeout ||
+			tmp > ir_dev->props->max_timeout)
+				return -EINVAL;
+
+		ir_dev->props->timeout = tmp;
+		break;
+
+	case LIRC_SET_REC_TIMEOUT_REPORTS:
+		lirc->send_timeout_reports = !!val;
 		break;
 
 	default:
@@ -212,7 +260,7 @@
 	}
 
 	if (_IOC_DIR(cmd) & _IOC_READ)
-		ret = put_user(val, (unsigned long *)arg);
+		ret = put_user(val, (__u32 *)arg);
 
 	return ret;
 }
@@ -231,6 +279,9 @@
 	.owner		= THIS_MODULE,
 	.write		= ir_lirc_transmit_ir,
 	.unlocked_ioctl	= ir_lirc_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= ir_lirc_ioctl,
+#endif
 	.read		= lirc_dev_fop_read,
 	.poll		= lirc_dev_fop_poll,
 	.open		= lirc_dev_fop_open,
@@ -278,6 +329,10 @@
 	if (ir_dev->props->s_learning_mode)
 		features |= LIRC_CAN_USE_WIDEBAND_RECEIVER;
 
+	if (ir_dev->props->s_carrier_report)
+		features |= LIRC_CAN_MEASURE_CARRIER;
+
+
 	if (ir_dev->props->max_timeout)
 		features |= LIRC_CAN_SET_REC_TIMEOUT;
 
diff --git a/drivers/media/IR/ir-nec-decoder.c b/drivers/media/IR/ir-nec-decoder.c
index d597421..70993f7 100644
--- a/drivers/media/IR/ir-nec-decoder.c
+++ b/drivers/media/IR/ir-nec-decoder.c
@@ -54,8 +54,9 @@
 	if (!(ir_dev->raw->enabled_protocols & IR_TYPE_NEC))
 		return 0;
 
-	if (IS_RESET(ev)) {
-		data->state = STATE_INACTIVE;
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
 		return 0;
 	}
 
diff --git a/drivers/media/IR/ir-raw-event.c b/drivers/media/IR/ir-raw-event.c
index 8e0e1b1..a06a07e 100644
--- a/drivers/media/IR/ir-raw-event.c
+++ b/drivers/media/IR/ir-raw-event.c
@@ -39,22 +39,34 @@
 	struct ir_raw_event ev;
 	struct ir_raw_handler *handler;
 	struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data;
+	int retval;
 
 	while (!kthread_should_stop()) {
-		try_to_freeze();
 
-		mutex_lock(&ir_raw_handler_lock);
+		spin_lock_irq(&raw->lock);
+		retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev));
 
-		while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) {
-			list_for_each_entry(handler, &ir_raw_handler_list, list)
-				handler->decode(raw->input_dev, ev);
-			raw->prev_ev = ev;
+		if (!retval) {
+			set_current_state(TASK_INTERRUPTIBLE);
+
+			if (kthread_should_stop())
+				set_current_state(TASK_RUNNING);
+
+			spin_unlock_irq(&raw->lock);
+			schedule();
+			continue;
 		}
 
-		mutex_unlock(&ir_raw_handler_lock);
+		spin_unlock_irq(&raw->lock);
 
-		set_current_state(TASK_INTERRUPTIBLE);
-		schedule();
+
+		BUG_ON(retval != sizeof(ev));
+
+		mutex_lock(&ir_raw_handler_lock);
+		list_for_each_entry(handler, &ir_raw_handler_list, list)
+			handler->decode(raw->input_dev, ev);
+		raw->prev_ev = ev;
+		mutex_unlock(&ir_raw_handler_lock);
 	}
 
 	return 0;
@@ -77,7 +89,7 @@
 	if (!ir->raw)
 		return -EINVAL;
 
-	IR_dprintk(2, "sample: (05%dus %s)\n",
+	IR_dprintk(2, "sample: (%05dus %s)\n",
 		TO_US(ev->duration), TO_STR(ev->pulse));
 
 	if (kfifo_in(&ir->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev))
@@ -162,7 +174,7 @@
 	if (ir->idle && !ev->pulse)
 		return 0;
 	else if (ir->idle)
-		ir_raw_event_set_idle(input_dev, 0);
+		ir_raw_event_set_idle(input_dev, false);
 
 	if (!raw->this_ev.duration) {
 		raw->this_ev = *ev;
@@ -175,48 +187,35 @@
 
 	/* Enter idle mode if nessesary */
 	if (!ev->pulse && ir->props->timeout &&
-		raw->this_ev.duration >= ir->props->timeout)
-		ir_raw_event_set_idle(input_dev, 1);
+		raw->this_ev.duration >= ir->props->timeout) {
+		ir_raw_event_set_idle(input_dev, true);
+	}
 	return 0;
 }
 EXPORT_SYMBOL_GPL(ir_raw_event_store_with_filter);
 
-void ir_raw_event_set_idle(struct input_dev *input_dev, int idle)
+/**
+ * ir_raw_event_set_idle() - hint the ir core if device is receiving
+ * IR data or not
+ * @input_dev: the struct input_dev device descriptor
+ * @idle: the hint value
+ */
+void ir_raw_event_set_idle(struct input_dev *input_dev, bool idle)
 {
 	struct ir_input_dev *ir = input_get_drvdata(input_dev);
 	struct ir_raw_event_ctrl *raw = ir->raw;
-	ktime_t now;
-	u64 delta;
 
-	if (!ir->props)
+	if (!ir->props || !ir->raw)
 		return;
 
-	if (!ir->raw)
-		goto out;
+	IR_dprintk(2, "%s idle mode\n", idle ? "enter" : "leave");
 
 	if (idle) {
-		IR_dprintk(2, "enter idle mode\n");
-		raw->last_event = ktime_get();
-	} else {
-		IR_dprintk(2, "exit idle mode\n");
-
-		now = ktime_get();
-		delta = ktime_to_ns(ktime_sub(now, ir->raw->last_event));
-
-		WARN_ON(raw->this_ev.pulse);
-
-		raw->this_ev.duration =
-			min(raw->this_ev.duration + delta,
-						(u64)IR_MAX_DURATION);
-
+		raw->this_ev.timeout = true;
 		ir_raw_event_store(input_dev, &raw->this_ev);
-
-		if (raw->this_ev.duration == IR_MAX_DURATION)
-			ir_raw_event_reset(input_dev);
-
-		raw->this_ev.duration = 0;
+		init_ir_raw_event(&raw->this_ev);
 	}
-out:
+
 	if (ir->props->s_idle)
 		ir->props->s_idle(ir->props->priv, idle);
 	ir->idle = idle;
@@ -232,11 +231,14 @@
 void ir_raw_event_handle(struct input_dev *input_dev)
 {
 	struct ir_input_dev *ir = input_get_drvdata(input_dev);
+	unsigned long flags;
 
 	if (!ir->raw)
 		return;
 
+	spin_lock_irqsave(&ir->raw->lock, flags);
 	wake_up_process(ir->raw->thread);
+	spin_unlock_irqrestore(&ir->raw->lock, flags);
 }
 EXPORT_SYMBOL_GPL(ir_raw_event_handle);
 
@@ -275,6 +277,7 @@
 		return rc;
 	}
 
+	spin_lock_init(&ir->raw->lock);
 	ir->raw->thread = kthread_run(ir_raw_event_thread, ir->raw,
 			"rc%u",  (unsigned int)ir->devno);
 
diff --git a/drivers/media/IR/ir-rc5-decoder.c b/drivers/media/IR/ir-rc5-decoder.c
index df4770d9..572ed4c 100644
--- a/drivers/media/IR/ir-rc5-decoder.c
+++ b/drivers/media/IR/ir-rc5-decoder.c
@@ -55,8 +55,9 @@
         if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC5))
                 return 0;
 
-	if (IS_RESET(ev)) {
-		data->state = STATE_INACTIVE;
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
 		return 0;
 	}
 
diff --git a/drivers/media/IR/ir-rc5-sz-decoder.c b/drivers/media/IR/ir-rc5-sz-decoder.c
new file mode 100644
index 0000000..7c41350
--- /dev/null
+++ b/drivers/media/IR/ir-rc5-sz-decoder.c
@@ -0,0 +1,154 @@
+/* ir-rc5-sz-decoder.c - handle RC5 Streamzap IR Pulse/Space protocol
+ *
+ * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2010 by Jarod Wilson <jarod@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2 of the License.
+ *
+ *  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.
+ */
+
+/*
+ * This code handles the 15 bit RC5-ish protocol used by the Streamzap
+ * PC Remote.
+ * It considers a carrier of 36 kHz, with a total of 15 bits, where
+ * the first two bits are start bits, and a third one is a filing bit
+ */
+
+#include "ir-core-priv.h"
+
+#define RC5_SZ_NBITS		15
+#define RC5_UNIT		888888 /* ns */
+#define RC5_BIT_START		(1 * RC5_UNIT)
+#define RC5_BIT_END		(1 * RC5_UNIT)
+
+enum rc5_sz_state {
+	STATE_INACTIVE,
+	STATE_BIT_START,
+	STATE_BIT_END,
+	STATE_FINISHED,
+};
+
+/**
+ * ir_rc5_sz_decode() - Decode one RC-5 Streamzap pulse or space
+ * @input_dev:	the struct input_dev descriptor of the device
+ * @ev:		the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_rc5_sz_decode(struct input_dev *input_dev, struct ir_raw_event ev)
+{
+	struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+	struct rc5_sz_dec *data = &ir_dev->raw->rc5_sz;
+	u8 toggle, command, system;
+	u32 scancode;
+
+        if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC5_SZ))
+                return 0;
+
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+	if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2))
+		goto out;
+
+again:
+	IR_dprintk(2, "RC5-sz decode started at state %i (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+	if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2))
+		return 0;
+
+	switch (data->state) {
+
+	case STATE_INACTIVE:
+		if (!ev.pulse)
+			break;
+
+		data->state = STATE_BIT_START;
+		data->count = 1;
+		data->wanted_bits = RC5_SZ_NBITS;
+		decrease_duration(&ev, RC5_BIT_START);
+		goto again;
+
+	case STATE_BIT_START:
+		if (!eq_margin(ev.duration, RC5_BIT_START, RC5_UNIT / 2))
+			break;
+
+		data->bits <<= 1;
+		if (!ev.pulse)
+			data->bits |= 1;
+		data->count++;
+		data->state = STATE_BIT_END;
+		return 0;
+
+	case STATE_BIT_END:
+		if (!is_transition(&ev, &ir_dev->raw->prev_ev))
+			break;
+
+		if (data->count == data->wanted_bits)
+			data->state = STATE_FINISHED;
+		else
+			data->state = STATE_BIT_START;
+
+		decrease_duration(&ev, RC5_BIT_END);
+		goto again;
+
+	case STATE_FINISHED:
+		if (ev.pulse)
+			break;
+
+		/* RC5-sz */
+		command  = (data->bits & 0x0003F) >> 0;
+		system   = (data->bits & 0x02FC0) >> 6;
+		toggle   = (data->bits & 0x01000) ? 1 : 0;
+		scancode = system << 6 | command;
+
+		IR_dprintk(1, "RC5-sz scancode 0x%04x (toggle: %u)\n",
+			   scancode, toggle);
+
+		ir_keydown(input_dev, scancode, toggle);
+		data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+out:
+	IR_dprintk(1, "RC5-sz decode failed at state %i (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+	data->state = STATE_INACTIVE;
+	return -EINVAL;
+}
+
+static struct ir_raw_handler rc5_sz_handler = {
+	.protocols	= IR_TYPE_RC5_SZ,
+	.decode		= ir_rc5_sz_decode,
+};
+
+static int __init ir_rc5_sz_decode_init(void)
+{
+	ir_raw_handler_register(&rc5_sz_handler);
+
+	printk(KERN_INFO "IR RC5 (streamzap) protocol handler initialized\n");
+	return 0;
+}
+
+static void __exit ir_rc5_sz_decode_exit(void)
+{
+	ir_raw_handler_unregister(&rc5_sz_handler);
+}
+
+module_init(ir_rc5_sz_decode_init);
+module_exit(ir_rc5_sz_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
+MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
+MODULE_DESCRIPTION("RC5 (streamzap) IR protocol decoder");
diff --git a/drivers/media/IR/ir-rc6-decoder.c b/drivers/media/IR/ir-rc6-decoder.c
index f1624b8..d25da91 100644
--- a/drivers/media/IR/ir-rc6-decoder.c
+++ b/drivers/media/IR/ir-rc6-decoder.c
@@ -85,8 +85,9 @@
 	if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC6))
 		return 0;
 
-	if (IS_RESET(ev)) {
-		data->state = STATE_INACTIVE;
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
 		return 0;
 	}
 
diff --git a/drivers/media/IR/ir-sony-decoder.c b/drivers/media/IR/ir-sony-decoder.c
index b9074f0..2d15730 100644
--- a/drivers/media/IR/ir-sony-decoder.c
+++ b/drivers/media/IR/ir-sony-decoder.c
@@ -48,8 +48,9 @@
 	if (!(ir_dev->raw->enabled_protocols & IR_TYPE_SONY))
 		return 0;
 
-	if (IS_RESET(ev)) {
-		data->state = STATE_INACTIVE;
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
 		return 0;
 	}
 
diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c
index 46d4246..38423a8 100644
--- a/drivers/media/IR/ir-sysfs.c
+++ b/drivers/media/IR/ir-sysfs.c
@@ -43,6 +43,7 @@
 	{ IR_TYPE_RC6,		"rc-6"		},
 	{ IR_TYPE_JVC,		"jvc"		},
 	{ IR_TYPE_SONY,		"sony"		},
+	{ IR_TYPE_RC5_SZ,	"rc-5-sz"	},
 	{ IR_TYPE_LIRC,		"lirc"		},
 };
 
@@ -67,6 +68,10 @@
 	char *tmp = buf;
 	int i;
 
+	/* Device is being removed */
+	if (!ir_dev)
+		return -EINVAL;
+
 	if (ir_dev->props && ir_dev->props->driver_type == RC_DRIVER_SCANCODE) {
 		enabled = ir_dev->rc_tab.ir_type;
 		allowed = ir_dev->props->allowed_protos;
@@ -122,6 +127,10 @@
 	int rc, i, count = 0;
 	unsigned long flags;
 
+	/* Device is being removed */
+	if (!ir_dev)
+		return -EINVAL;
+
 	if (ir_dev->props && ir_dev->props->driver_type == RC_DRIVER_SCANCODE)
 		type = ir_dev->rc_tab.ir_type;
 	else if (ir_dev->raw)
@@ -256,8 +265,6 @@
  */
 int ir_register_class(struct input_dev *input_dev)
 {
-	int rc;
-	const char *path;
 	struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
 	int devno = find_first_zero_bit(&ir_core_dev_number,
 					IRRCV_NUM_DEVICES);
@@ -266,17 +273,28 @@
 		return devno;
 
 	ir_dev->dev.type = &rc_dev_type;
+	ir_dev->devno = devno;
 
 	ir_dev->dev.class = &ir_input_class;
 	ir_dev->dev.parent = input_dev->dev.parent;
+	input_dev->dev.parent = &ir_dev->dev;
 	dev_set_name(&ir_dev->dev, "rc%d", devno);
 	dev_set_drvdata(&ir_dev->dev, ir_dev);
-	rc = device_register(&ir_dev->dev);
-	if (rc)
-		return rc;
+	return  device_register(&ir_dev->dev);
+};
+
+/**
+ * ir_register_input - registers ir input device with input subsystem
+ * @input_dev:	the struct input_dev descriptor of the device
+ */
+
+int ir_register_input(struct input_dev *input_dev)
+{
+	struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+	int rc;
+	const char *path;
 
 
-	input_dev->dev.parent = &ir_dev->dev;
 	rc = input_register_device(input_dev);
 	if (rc < 0) {
 		device_del(&ir_dev->dev);
@@ -292,11 +310,9 @@
 		path ? path : "N/A");
 	kfree(path);
 
-	ir_dev->devno = devno;
-	set_bit(devno, &ir_core_dev_number);
-
+	set_bit(ir_dev->devno, &ir_core_dev_number);
 	return 0;
-};
+}
 
 /**
  * ir_unregister_class() - removes the sysfs for sysfs for
@@ -309,6 +325,7 @@
 {
 	struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
 
+	input_set_drvdata(input_dev, NULL);
 	clear_bit(ir_dev->devno, &ir_core_dev_number);
 	input_unregister_device(input_dev);
 	device_del(&ir_dev->dev);
diff --git a/drivers/media/IR/keymaps/Makefile b/drivers/media/IR/keymaps/Makefile
index 950e5d9..3194d39 100644
--- a/drivers/media/IR/keymaps/Makefile
+++ b/drivers/media/IR/keymaps/Makefile
@@ -1,4 +1,6 @@
 obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
+			rc-alink-dtu-m.o \
+			rc-anysee.o \
 			rc-apac-viewcomp.o \
 			rc-asus-pc39.o \
 			rc-ati-tv-wonder-hd-600.o \
@@ -8,7 +10,9 @@
 			rc-avermedia-dvbt.o \
 			rc-avermedia-m135a.o \
 			rc-avermedia-m733a-rm-k6.o \
+			rc-avermedia-rm-ks.o \
 			rc-avertv-303.o \
+			rc-azurewave-ad-tu700.o \
 			rc-behold.o \
 			rc-behold-columbus.o \
 			rc-budget-ci-old.o \
@@ -16,6 +20,8 @@
 			rc-cinergy.o \
 			rc-dib0700-nec.o \
 			rc-dib0700-rc5.o \
+			rc-digitalnow-tinytwin.o \
+			rc-digittrade.o \
 			rc-dm1105-nec.o \
 			rc-dntv-live-dvb-t.o \
 			rc-dntv-live-dvbt-pro.o \
@@ -38,8 +44,12 @@
 			rc-kaiomy.o \
 			rc-kworld-315u.o \
 			rc-kworld-plus-tv-analog.o \
+			rc-leadtek-y04g0051.o \
 			rc-lirc.o \
+			rc-lme2510.o \
 			rc-manli.o \
+			rc-msi-digivox-ii.o \
+			rc-msi-digivox-iii.o \
 			rc-msi-tvanywhere.o \
 			rc-msi-tvanywhere-plus.o \
 			rc-nebula.o \
@@ -58,14 +68,18 @@
 			rc-purpletv.o \
 			rc-pv951.o \
 			rc-rc5-hauppauge-new.o \
-			rc-rc5-streamzap.o \
 			rc-rc5-tv.o \
 			rc-rc6-mce.o \
 			rc-real-audio-220-32-keys.o \
+			rc-streamzap.o \
 			rc-tbs-nec.o \
 			rc-terratec-cinergy-xs.o \
+			rc-terratec-slim.o \
 			rc-tevii-nec.o \
+			rc-total-media-in-hand.o \
+			rc-trekstor.o \
 			rc-tt-1500.o \
+			rc-twinhan1027.o \
 			rc-videomate-s350.o \
 			rc-videomate-tv-pvr.o \
 			rc-winfast.o \
diff --git a/drivers/media/IR/keymaps/rc-alink-dtu-m.c b/drivers/media/IR/keymaps/rc-alink-dtu-m.c
new file mode 100644
index 0000000..ddfee7f
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-alink-dtu-m.c
@@ -0,0 +1,68 @@
+/*
+ * A-Link DTU(m) remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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 <media/rc-map.h>
+
+/* A-Link DTU(m) slim remote, 6 rows, 3 columns. */
+static struct ir_scancode alink_dtu_m[] = {
+	{ 0x0800, KEY_VOLUMEUP },
+	{ 0x0801, KEY_1 },
+	{ 0x0802, KEY_3 },
+	{ 0x0803, KEY_7 },
+	{ 0x0804, KEY_9 },
+	{ 0x0805, KEY_NEW },             /* symbol: PIP */
+	{ 0x0806, KEY_0 },
+	{ 0x0807, KEY_CHANNEL },         /* JUMP */
+	{ 0x080d, KEY_5 },
+	{ 0x080f, KEY_2 },
+	{ 0x0812, KEY_POWER2 },
+	{ 0x0814, KEY_CHANNELUP },
+	{ 0x0816, KEY_VOLUMEDOWN },
+	{ 0x0818, KEY_6 },
+	{ 0x081a, KEY_MUTE },
+	{ 0x081b, KEY_8 },
+	{ 0x081c, KEY_4 },
+	{ 0x081d, KEY_CHANNELDOWN },
+};
+
+static struct rc_keymap alink_dtu_m_map = {
+	.map = {
+		.scan    = alink_dtu_m,
+		.size    = ARRAY_SIZE(alink_dtu_m),
+		.ir_type = IR_TYPE_NEC,
+		.name    = RC_MAP_ALINK_DTU_M,
+	}
+};
+
+static int __init init_rc_map_alink_dtu_m(void)
+{
+	return ir_register_map(&alink_dtu_m_map);
+}
+
+static void __exit exit_rc_map_alink_dtu_m(void)
+{
+	ir_unregister_map(&alink_dtu_m_map);
+}
+
+module_init(init_rc_map_alink_dtu_m)
+module_exit(exit_rc_map_alink_dtu_m)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/IR/keymaps/rc-anysee.c b/drivers/media/IR/keymaps/rc-anysee.c
new file mode 100644
index 0000000..30d7049
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-anysee.c
@@ -0,0 +1,93 @@
+/*
+ * Anysee remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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 <media/rc-map.h>
+
+static struct ir_scancode anysee[] = {
+	{ 0x0800, KEY_0 },
+	{ 0x0801, KEY_1 },
+	{ 0x0802, KEY_2 },
+	{ 0x0803, KEY_3 },
+	{ 0x0804, KEY_4 },
+	{ 0x0805, KEY_5 },
+	{ 0x0806, KEY_6 },
+	{ 0x0807, KEY_7 },
+	{ 0x0808, KEY_8 },
+	{ 0x0809, KEY_9 },
+	{ 0x080a, KEY_POWER2 },          /* [red power button] */
+	{ 0x080b, KEY_VIDEO },           /* [*] MODE */
+	{ 0x080c, KEY_CHANNEL },         /* [symbol counterclockwise arrow] */
+	{ 0x080d, KEY_NEXT },            /* [>>|] */
+	{ 0x080e, KEY_MENU },            /* MENU */
+	{ 0x080f, KEY_EPG },             /* [EPG] */
+	{ 0x0810, KEY_CLEAR },           /* EXIT */
+	{ 0x0811, KEY_CHANNELUP },
+	{ 0x0812, KEY_VOLUMEDOWN },
+	{ 0x0813, KEY_VOLUMEUP },
+	{ 0x0814, KEY_CHANNELDOWN },
+	{ 0x0815, KEY_OK },
+	{ 0x0816, KEY_RADIO },           /* [symbol TV/radio] */
+	{ 0x0817, KEY_INFO },            /* [i] */
+	{ 0x0818, KEY_PREVIOUS },        /* [|<<] */
+	{ 0x0819, KEY_FAVORITES },       /* FAV. */
+	{ 0x081a, KEY_SUBTITLE },        /* Subtitle */
+	{ 0x081b, KEY_CAMERA },          /* [symbol camera] */
+	{ 0x081c, KEY_YELLOW },
+	{ 0x081d, KEY_RED },
+	{ 0x081e, KEY_LANGUAGE },        /* [symbol Second Audio Program] */
+	{ 0x081f, KEY_GREEN },
+	{ 0x0820, KEY_SLEEP },           /* Sleep */
+	{ 0x0821, KEY_SCREEN },          /* 16:9 / 4:3 */
+	{ 0x0822, KEY_ZOOM },            /* SIZE */
+	{ 0x0824, KEY_FN },              /* [F1] */
+	{ 0x0825, KEY_FN },              /* [F2] */
+	{ 0x0842, KEY_MUTE },            /* symbol mute */
+	{ 0x0844, KEY_BLUE },
+	{ 0x0847, KEY_TEXT },            /* TEXT */
+	{ 0x0848, KEY_STOP },
+	{ 0x0849, KEY_RECORD },
+	{ 0x0850, KEY_PLAY },
+	{ 0x0851, KEY_PAUSE },
+};
+
+static struct rc_keymap anysee_map = {
+	.map = {
+		.scan    = anysee,
+		.size    = ARRAY_SIZE(anysee),
+		.ir_type = IR_TYPE_NEC,
+		.name    = RC_MAP_ANYSEE,
+	}
+};
+
+static int __init init_rc_map_anysee(void)
+{
+	return ir_register_map(&anysee_map);
+}
+
+static void __exit exit_rc_map_anysee(void)
+{
+	ir_unregister_map(&anysee_map);
+}
+
+module_init(init_rc_map_anysee)
+module_exit(exit_rc_map_anysee)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/IR/keymaps/rc-asus-pc39.c b/drivers/media/IR/keymaps/rc-asus-pc39.c
index 2aa068c..2996e0a 100644
--- a/drivers/media/IR/keymaps/rc-asus-pc39.c
+++ b/drivers/media/IR/keymaps/rc-asus-pc39.c
@@ -20,56 +20,56 @@
 
 static struct ir_scancode asus_pc39[] = {
 	/* Keys 0 to 9 */
-	{ 0x15, KEY_0 },
-	{ 0x29, KEY_1 },
-	{ 0x2d, KEY_2 },
-	{ 0x2b, KEY_3 },
-	{ 0x09, KEY_4 },
-	{ 0x0d, KEY_5 },
-	{ 0x0b, KEY_6 },
-	{ 0x31, KEY_7 },
-	{ 0x35, KEY_8 },
-	{ 0x33, KEY_9 },
+	{ 0x082a, KEY_0 },
+	{ 0x0816, KEY_1 },
+	{ 0x0812, KEY_2 },
+	{ 0x0814, KEY_3 },
+	{ 0x0836, KEY_4 },
+	{ 0x0832, KEY_5 },
+	{ 0x0834, KEY_6 },
+	{ 0x080e, KEY_7 },
+	{ 0x080a, KEY_8 },
+	{ 0x080c, KEY_9 },
 
-	{ 0x3e, KEY_RADIO },		/* radio */
-	{ 0x03, KEY_MENU },		/* dvd/menu */
-	{ 0x2a, KEY_VOLUMEUP },
-	{ 0x19, KEY_VOLUMEDOWN },
-	{ 0x37, KEY_UP },
-	{ 0x3b, KEY_DOWN },
-	{ 0x27, KEY_LEFT },
-	{ 0x2f, KEY_RIGHT },
-	{ 0x25, KEY_VIDEO },		/* video */
-	{ 0x39, KEY_AUDIO },		/* music */
+	{ 0x0801, KEY_RADIO },		/* radio */
+	{ 0x083c, KEY_MENU },		/* dvd/menu */
+	{ 0x0815, KEY_VOLUMEUP },
+	{ 0x0826, KEY_VOLUMEDOWN },
+	{ 0x0808, KEY_UP },
+	{ 0x0804, KEY_DOWN },
+	{ 0x0818, KEY_LEFT },
+	{ 0x0810, KEY_RIGHT },
+	{ 0x081a, KEY_VIDEO },		/* video */
+	{ 0x0806, KEY_AUDIO },		/* music */
 
-	{ 0x21, KEY_TV },		/* tv */
-	{ 0x1d, KEY_EXIT },		/* back */
-	{ 0x0a, KEY_CHANNELUP },	/* channel / program + */
-	{ 0x1b, KEY_CHANNELDOWN },	/* channel / program - */
-	{ 0x1a, KEY_ENTER },		/* enter */
+	{ 0x081e, KEY_TV },		/* tv */
+	{ 0x0822, KEY_EXIT },		/* back */
+	{ 0x0835, KEY_CHANNELUP },	/* channel / program + */
+	{ 0x0824, KEY_CHANNELDOWN },	/* channel / program - */
+	{ 0x0825, KEY_ENTER },		/* enter */
 
-	{ 0x06, KEY_PAUSE },		/* play/pause */
-	{ 0x1e, KEY_PREVIOUS },		/* rew */
-	{ 0x26, KEY_NEXT },		/* forward */
-	{ 0x0e, KEY_REWIND },		/* backward << */
-	{ 0x3a, KEY_FASTFORWARD },	/* forward >> */
-	{ 0x36, KEY_STOP },
-	{ 0x2e, KEY_RECORD },		/* recording */
-	{ 0x16, KEY_POWER },		/* the button that reads "close" */
+	{ 0x0839, KEY_PAUSE },		/* play/pause */
+	{ 0x0821, KEY_PREVIOUS },		/* rew */
+	{ 0x0819, KEY_NEXT },		/* forward */
+	{ 0x0831, KEY_REWIND },		/* backward << */
+	{ 0x0805, KEY_FASTFORWARD },	/* forward >> */
+	{ 0x0809, KEY_STOP },
+	{ 0x0811, KEY_RECORD },		/* recording */
+	{ 0x0829, KEY_POWER },		/* the button that reads "close" */
 
-	{ 0x11, KEY_ZOOM },		/* full screen */
-	{ 0x13, KEY_MACRO },		/* recall */
-	{ 0x23, KEY_HOME },		/* home */
-	{ 0x05, KEY_PVR },		/* picture */
-	{ 0x3d, KEY_MUTE },		/* mute */
-	{ 0x01, KEY_DVD },		/* dvd */
+	{ 0x082e, KEY_ZOOM },		/* full screen */
+	{ 0x082c, KEY_MACRO },		/* recall */
+	{ 0x081c, KEY_HOME },		/* home */
+	{ 0x083a, KEY_PVR },		/* picture */
+	{ 0x0802, KEY_MUTE },		/* mute */
+	{ 0x083e, KEY_DVD },		/* dvd */
 };
 
 static struct rc_keymap asus_pc39_map = {
 	.map = {
 		.scan    = asus_pc39,
 		.size    = ARRAY_SIZE(asus_pc39),
-		.ir_type = IR_TYPE_UNKNOWN,	/* Legacy IR type */
+		.ir_type = IR_TYPE_RC5,
 		.name    = RC_MAP_ASUS_PC39,
 	}
 };
diff --git a/drivers/media/IR/keymaps/rc-avermedia-rm-ks.c b/drivers/media/IR/keymaps/rc-avermedia-rm-ks.c
new file mode 100644
index 0000000..9ee6090
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-avermedia-rm-ks.c
@@ -0,0 +1,79 @@
+/*
+ * AverMedia RM-KS remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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 <media/rc-map.h>
+
+/* Initial keytable is from Jose Alberto Reguero <jareguero@telefonica.net>
+   and Felipe Morales Moreno <felipe.morales.moreno@gmail.com> */
+/* FIXME: mappings are not 100% correct? */
+static struct ir_scancode avermedia_rm_ks[] = {
+	{ 0x0501, KEY_POWER2 },
+	{ 0x0502, KEY_CHANNELUP },
+	{ 0x0503, KEY_CHANNELDOWN },
+	{ 0x0504, KEY_VOLUMEUP },
+	{ 0x0505, KEY_VOLUMEDOWN },
+	{ 0x0506, KEY_MUTE },
+	{ 0x0507, KEY_RIGHT },
+	{ 0x0508, KEY_PROG1 },
+	{ 0x0509, KEY_1 },
+	{ 0x050a, KEY_2 },
+	{ 0x050b, KEY_3 },
+	{ 0x050c, KEY_4 },
+	{ 0x050d, KEY_5 },
+	{ 0x050e, KEY_6 },
+	{ 0x050f, KEY_7 },
+	{ 0x0510, KEY_8 },
+	{ 0x0511, KEY_9 },
+	{ 0x0512, KEY_0 },
+	{ 0x0513, KEY_AUDIO },
+	{ 0x0515, KEY_EPG },
+	{ 0x0516, KEY_PLAY },
+	{ 0x0517, KEY_RECORD },
+	{ 0x0518, KEY_STOP },
+	{ 0x051c, KEY_BACK },
+	{ 0x051d, KEY_FORWARD },
+	{ 0x054d, KEY_LEFT },
+	{ 0x0556, KEY_ZOOM },
+};
+
+static struct rc_keymap avermedia_rm_ks_map = {
+	.map = {
+		.scan    = avermedia_rm_ks,
+		.size    = ARRAY_SIZE(avermedia_rm_ks),
+		.ir_type = IR_TYPE_NEC,
+		.name    = RC_MAP_AVERMEDIA_RM_KS,
+	}
+};
+
+static int __init init_rc_map_avermedia_rm_ks(void)
+{
+	return ir_register_map(&avermedia_rm_ks_map);
+}
+
+static void __exit exit_rc_map_avermedia_rm_ks(void)
+{
+	ir_unregister_map(&avermedia_rm_ks_map);
+}
+
+module_init(init_rc_map_avermedia_rm_ks)
+module_exit(exit_rc_map_avermedia_rm_ks)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/IR/keymaps/rc-azurewave-ad-tu700.c b/drivers/media/IR/keymaps/rc-azurewave-ad-tu700.c
new file mode 100644
index 0000000..e087614
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-azurewave-ad-tu700.c
@@ -0,0 +1,102 @@
+/*
+ * TwinHan AzureWave AD-TU700(704J) remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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 <media/rc-map.h>
+
+static struct ir_scancode azurewave_ad_tu700[] = {
+	{ 0x0000, KEY_TAB },             /* Tab */
+	{ 0x0001, KEY_2 },
+	{ 0x0002, KEY_CHANNELDOWN },
+	{ 0x0003, KEY_1 },
+	{ 0x0004, KEY_MENU },            /* Record List */
+	{ 0x0005, KEY_CHANNELUP },
+	{ 0x0006, KEY_3 },
+	{ 0x0007, KEY_SLEEP },           /* Hibernate */
+	{ 0x0008, KEY_VIDEO },           /* A/V */
+	{ 0x0009, KEY_4 },
+	{ 0x000a, KEY_VOLUMEDOWN },
+	{ 0x000c, KEY_CANCEL },          /* Cancel */
+	{ 0x000d, KEY_7 },
+	{ 0x000e, KEY_AGAIN },           /* Recall */
+	{ 0x000f, KEY_TEXT },            /* Teletext */
+	{ 0x0010, KEY_MUTE },
+	{ 0x0011, KEY_RECORD },
+	{ 0x0012, KEY_FASTFORWARD },     /* FF >> */
+	{ 0x0013, KEY_BACK },            /* Back */
+	{ 0x0014, KEY_PLAY },
+	{ 0x0015, KEY_0 },
+	{ 0x0016, KEY_POWER2 },          /* [red power button] */
+	{ 0x0017, KEY_FAVORITES },       /* Favorite List */
+	{ 0x0018, KEY_RED },
+	{ 0x0019, KEY_8 },
+	{ 0x001a, KEY_STOP },
+	{ 0x001b, KEY_9 },
+	{ 0x001c, KEY_EPG },             /* Info/EPG */
+	{ 0x001d, KEY_5 },
+	{ 0x001e, KEY_VOLUMEUP },
+	{ 0x001f, KEY_6 },
+	{ 0x0040, KEY_REWIND },          /* FR << */
+	{ 0x0041, KEY_PREVIOUS },        /* Replay */
+	{ 0x0042, KEY_NEXT },            /* Skip */
+	{ 0x0043, KEY_SUBTITLE },        /* Subtitle / CC */
+	{ 0x0045, KEY_KPPLUS },          /* Zoom+ */
+	{ 0x0046, KEY_KPMINUS },         /* Zoom- */
+	{ 0x0047, KEY_NEW },             /* PIP */
+	{ 0x0048, KEY_INFO },            /* Preview */
+	{ 0x0049, KEY_MODE },            /* L/R */
+	{ 0x004a, KEY_CLEAR },           /* Clear */
+	{ 0x004b, KEY_UP },              /* up arrow */
+	{ 0x004c, KEY_PAUSE },
+	{ 0x004d, KEY_ZOOM },            /* Full Screen */
+	{ 0x004e, KEY_LEFT },            /* left arrow */
+	{ 0x004f, KEY_OK },              /* Enter / ok */
+	{ 0x0050, KEY_LANGUAGE },        /* SAP */
+	{ 0x0051, KEY_DOWN },            /* down arrow */
+	{ 0x0052, KEY_RIGHT },           /* right arrow */
+	{ 0x0053, KEY_GREEN },
+	{ 0x0054, KEY_CAMERA },          /* Capture */
+	{ 0x005e, KEY_YELLOW },
+	{ 0x005f, KEY_BLUE },
+};
+
+static struct rc_keymap azurewave_ad_tu700_map = {
+	.map = {
+		.scan    = azurewave_ad_tu700,
+		.size    = ARRAY_SIZE(azurewave_ad_tu700),
+		.ir_type = IR_TYPE_NEC,
+		.name    = RC_MAP_AZUREWAVE_AD_TU700,
+	}
+};
+
+static int __init init_rc_map_azurewave_ad_tu700(void)
+{
+	return ir_register_map(&azurewave_ad_tu700_map);
+}
+
+static void __exit exit_rc_map_azurewave_ad_tu700(void)
+{
+	ir_unregister_map(&azurewave_ad_tu700_map);
+}
+
+module_init(init_rc_map_azurewave_ad_tu700)
+module_exit(exit_rc_map_azurewave_ad_tu700)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/IR/keymaps/rc-digitalnow-tinytwin.c b/drivers/media/IR/keymaps/rc-digitalnow-tinytwin.c
new file mode 100644
index 0000000..63e469e
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-digitalnow-tinytwin.c
@@ -0,0 +1,98 @@
+/*
+ * DigitalNow TinyTwin remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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 <media/rc-map.h>
+
+static struct ir_scancode digitalnow_tinytwin[] = {
+	{ 0x0000, KEY_MUTE },            /* [symbol speaker] */
+	{ 0x0001, KEY_VOLUMEUP },
+	{ 0x0002, KEY_POWER2 },          /* TV [power button] */
+	{ 0x0003, KEY_2 },
+	{ 0x0004, KEY_3 },
+	{ 0x0005, KEY_4 },
+	{ 0x0006, KEY_6 },
+	{ 0x0007, KEY_7 },
+	{ 0x0008, KEY_8 },
+	{ 0x0009, KEY_NUMERIC_STAR },    /* [*] */
+	{ 0x000a, KEY_0 },
+	{ 0x000b, KEY_NUMERIC_POUND },   /* [#] */
+	{ 0x000c, KEY_RIGHT },           /* [right arrow] */
+	{ 0x000d, KEY_HOMEPAGE },        /* [symbol home] Start */
+	{ 0x000e, KEY_RED },             /* [red] Videos */
+	{ 0x0010, KEY_POWER },           /* PC [power button] */
+	{ 0x0011, KEY_YELLOW },          /* [yellow] Pictures */
+	{ 0x0012, KEY_DOWN },            /* [down arrow] */
+	{ 0x0013, KEY_GREEN },           /* [green] Music */
+	{ 0x0014, KEY_CYCLEWINDOWS },    /* BACK */
+	{ 0x0015, KEY_FAVORITES },       /* MORE */
+	{ 0x0016, KEY_UP },              /* [up arrow] */
+	{ 0x0017, KEY_LEFT },            /* [left arrow] */
+	{ 0x0018, KEY_OK },              /* OK */
+	{ 0x0019, KEY_BLUE },            /* [blue] MyTV */
+	{ 0x001a, KEY_REWIND },          /* REW [<<] */
+	{ 0x001b, KEY_PLAY },            /* PLAY */
+	{ 0x001c, KEY_5 },
+	{ 0x001d, KEY_9 },
+	{ 0x001e, KEY_VOLUMEDOWN },
+	{ 0x001f, KEY_1 },
+	{ 0x0040, KEY_STOP },            /* STOP */
+	{ 0x0042, KEY_PAUSE },           /* PAUSE */
+	{ 0x0043, KEY_SCREEN },          /* Aspect */
+	{ 0x0044, KEY_FORWARD },         /* FWD [>>] */
+	{ 0x0045, KEY_NEXT },            /* SKIP */
+	{ 0x0048, KEY_RECORD },          /* RECORD */
+	{ 0x0049, KEY_VIDEO },           /* RTV */
+	{ 0x004a, KEY_EPG },             /* Guide */
+	{ 0x004b, KEY_CHANNELUP },
+	{ 0x004c, KEY_HELP },            /* Help */
+	{ 0x004d, KEY_RADIO },           /* Radio */
+	{ 0x004f, KEY_CHANNELDOWN },
+	{ 0x0050, KEY_DVD },             /* DVD */
+	{ 0x0051, KEY_AUDIO },           /* Audio */
+	{ 0x0052, KEY_TITLE },           /* Title */
+	{ 0x0053, KEY_NEW },             /* [symbol PIP?] */
+	{ 0x0057, KEY_MENU },            /* Mouse */
+	{ 0x005a, KEY_PREVIOUS },        /* REPLAY */
+};
+
+static struct rc_keymap digitalnow_tinytwin_map = {
+	.map = {
+		.scan    = digitalnow_tinytwin,
+		.size    = ARRAY_SIZE(digitalnow_tinytwin),
+		.ir_type = IR_TYPE_NEC,
+		.name    = RC_MAP_DIGITALNOW_TINYTWIN,
+	}
+};
+
+static int __init init_rc_map_digitalnow_tinytwin(void)
+{
+	return ir_register_map(&digitalnow_tinytwin_map);
+}
+
+static void __exit exit_rc_map_digitalnow_tinytwin(void)
+{
+	ir_unregister_map(&digitalnow_tinytwin_map);
+}
+
+module_init(init_rc_map_digitalnow_tinytwin)
+module_exit(exit_rc_map_digitalnow_tinytwin)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/IR/keymaps/rc-digittrade.c b/drivers/media/IR/keymaps/rc-digittrade.c
new file mode 100644
index 0000000..5dece78
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-digittrade.c
@@ -0,0 +1,82 @@
+/*
+ * Digittrade DVB-T USB Stick remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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 <media/rc-map.h>
+
+/* Digittrade DVB-T USB Stick remote controller. */
+/* Imported from af9015.h.
+   Initial keytable was from Alain Kalker <miki@dds.nl> */
+
+/* Digittrade DVB-T USB Stick */
+static struct ir_scancode digittrade[] = {
+	{ 0x0000, KEY_9 },
+	{ 0x0001, KEY_EPG },             /* EPG */
+	{ 0x0002, KEY_VOLUMEDOWN },      /* Vol Dn */
+	{ 0x0003, KEY_TEXT },            /* TELETEXT */
+	{ 0x0004, KEY_8 },
+	{ 0x0005, KEY_MUTE },            /* MUTE */
+	{ 0x0006, KEY_POWER2 },          /* POWER */
+	{ 0x0009, KEY_ZOOM },            /* FULLSCREEN */
+	{ 0x000a, KEY_RECORD },          /* RECORD */
+	{ 0x000d, KEY_SUBTITLE },        /* SUBTITLE */
+	{ 0x000e, KEY_STOP },            /* STOP */
+	{ 0x0010, KEY_OK },              /* RETURN */
+	{ 0x0011, KEY_2 },
+	{ 0x0012, KEY_4 },
+	{ 0x0015, KEY_3 },
+	{ 0x0016, KEY_5 },
+	{ 0x0017, KEY_CHANNELDOWN },     /* Ch Dn */
+	{ 0x0019, KEY_CHANNELUP },       /* CH Up */
+	{ 0x001a, KEY_PAUSE },           /* PAUSE */
+	{ 0x001b, KEY_1 },
+	{ 0x001d, KEY_AUDIO },           /* DUAL SOUND */
+	{ 0x001e, KEY_PLAY },            /* PLAY */
+	{ 0x001f, KEY_CAMERA },          /* SNAPSHOT */
+	{ 0x0040, KEY_VOLUMEUP },        /* Vol Up */
+	{ 0x0048, KEY_7 },
+	{ 0x004c, KEY_6 },
+	{ 0x004d, KEY_PLAYPAUSE },       /* TIMESHIFT */
+	{ 0x0054, KEY_0 },
+};
+
+static struct rc_keymap digittrade_map = {
+	.map = {
+		.scan    = digittrade,
+		.size    = ARRAY_SIZE(digittrade),
+		.ir_type = IR_TYPE_NEC,
+		.name    = RC_MAP_DIGITTRADE,
+	}
+};
+
+static int __init init_rc_map_digittrade(void)
+{
+	return ir_register_map(&digittrade_map);
+}
+
+static void __exit exit_rc_map_digittrade(void)
+{
+	ir_unregister_map(&digittrade_map);
+}
+
+module_init(init_rc_map_digittrade)
+module_exit(exit_rc_map_digittrade)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/IR/keymaps/rc-leadtek-y04g0051.c b/drivers/media/IR/keymaps/rc-leadtek-y04g0051.c
new file mode 100644
index 0000000..7521315
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-leadtek-y04g0051.c
@@ -0,0 +1,99 @@
+/*
+ * LeadTek Y04G0051 remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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 <media/rc-map.h>
+
+static struct ir_scancode leadtek_y04g0051[] = {
+	{ 0x0300, KEY_POWER2 },
+	{ 0x0303, KEY_SCREEN },
+	{ 0x0304, KEY_RIGHT },
+	{ 0x0305, KEY_1 },
+	{ 0x0306, KEY_2 },
+	{ 0x0307, KEY_3 },
+	{ 0x0308, KEY_LEFT },
+	{ 0x0309, KEY_4 },
+	{ 0x030a, KEY_5 },
+	{ 0x030b, KEY_6 },
+	{ 0x030c, KEY_UP },
+	{ 0x030d, KEY_7 },
+	{ 0x030e, KEY_8 },
+	{ 0x030f, KEY_9 },
+	{ 0x0310, KEY_DOWN },
+	{ 0x0311, KEY_AGAIN },
+	{ 0x0312, KEY_0 },
+	{ 0x0313, KEY_OK },              /* 1st ok */
+	{ 0x0314, KEY_MUTE },
+	{ 0x0316, KEY_OK },              /* 2nd ok */
+	{ 0x031e, KEY_VIDEO },           /* 2nd video */
+	{ 0x031b, KEY_AUDIO },
+	{ 0x031f, KEY_TEXT },
+	{ 0x0340, KEY_SLEEP },
+	{ 0x0341, KEY_DOT },
+	{ 0x0342, KEY_REWIND },
+	{ 0x0343, KEY_PLAY },
+	{ 0x0344, KEY_FASTFORWARD },
+	{ 0x0345, KEY_TIME },
+	{ 0x0346, KEY_STOP },            /* 2nd stop */
+	{ 0x0347, KEY_RECORD },
+	{ 0x0348, KEY_CAMERA },
+	{ 0x0349, KEY_ESC },
+	{ 0x034a, KEY_NEW },
+	{ 0x034b, KEY_RED },
+	{ 0x034c, KEY_GREEN },
+	{ 0x034d, KEY_YELLOW },
+	{ 0x034e, KEY_BLUE },
+	{ 0x034f, KEY_MENU },
+	{ 0x0350, KEY_STOP },            /* 1st stop */
+	{ 0x0351, KEY_CHANNEL },
+	{ 0x0352, KEY_VIDEO },           /* 1st video */
+	{ 0x0353, KEY_EPG },
+	{ 0x0354, KEY_PREVIOUS },
+	{ 0x0355, KEY_NEXT },
+	{ 0x0356, KEY_TV },
+	{ 0x035a, KEY_VOLUMEDOWN },
+	{ 0x035b, KEY_CHANNELUP },
+	{ 0x035e, KEY_VOLUMEUP },
+	{ 0x035f, KEY_CHANNELDOWN },
+};
+
+static struct rc_keymap leadtek_y04g0051_map = {
+	.map = {
+		.scan    = leadtek_y04g0051,
+		.size    = ARRAY_SIZE(leadtek_y04g0051),
+		.ir_type = IR_TYPE_NEC,
+		.name    = RC_MAP_LEADTEK_Y04G0051,
+	}
+};
+
+static int __init init_rc_map_leadtek_y04g0051(void)
+{
+	return ir_register_map(&leadtek_y04g0051_map);
+}
+
+static void __exit exit_rc_map_leadtek_y04g0051(void)
+{
+	ir_unregister_map(&leadtek_y04g0051_map);
+}
+
+module_init(init_rc_map_leadtek_y04g0051)
+module_exit(exit_rc_map_leadtek_y04g0051)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/IR/keymaps/rc-lme2510.c b/drivers/media/IR/keymaps/rc-lme2510.c
new file mode 100644
index 0000000..40dcf0b
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-lme2510.c
@@ -0,0 +1,68 @@
+/* LME2510 remote control
+ *
+ *
+ * Copyright (C) 2010 Malcolm Priestley (tvboxspy@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+
+
+static struct ir_scancode lme2510_rc[] = {
+	{ 0xba45, KEY_0 },
+	{ 0xa05f, KEY_1 },
+	{ 0xaf50, KEY_2 },
+	{ 0xa25d, KEY_3 },
+	{ 0xbe41, KEY_4 },
+	{ 0xf50a, KEY_5 },
+	{ 0xbd42, KEY_6 },
+	{ 0xb847, KEY_7 },
+	{ 0xb649, KEY_8 },
+	{ 0xfa05, KEY_9 },
+	{ 0xbc43, KEY_POWER },
+	{ 0xb946, KEY_SUBTITLE },
+	{ 0xf906, KEY_PAUSE },
+	{ 0xfc03, KEY_MEDIA_REPEAT},
+	{ 0xfd02, KEY_PAUSE },
+	{ 0xa15e, KEY_VOLUMEUP },
+	{ 0xa35c, KEY_VOLUMEDOWN },
+	{ 0xf609, KEY_CHANNELUP },
+	{ 0xe51a, KEY_CHANNELDOWN },
+	{ 0xe11e, KEY_PLAY },
+	{ 0xe41b, KEY_ZOOM },
+	{ 0xa659, KEY_MUTE },
+	{ 0xa55a, KEY_TV },
+	{ 0xe718, KEY_RECORD },
+	{ 0xf807, KEY_EPG },
+	{ 0xfe01, KEY_STOP },
+
+};
+
+static struct rc_keymap lme2510_map = {
+	.map = {
+		.scan    = lme2510_rc,
+		.size    = ARRAY_SIZE(lme2510_rc),
+		.ir_type = IR_TYPE_UNKNOWN,
+		.name    = RC_MAP_LME2510,
+	}
+};
+
+static int __init init_rc_lme2510_map(void)
+{
+	return ir_register_map(&lme2510_map);
+}
+
+static void __exit exit_rc_lme2510_map(void)
+{
+	ir_unregister_map(&lme2510_map);
+}
+
+module_init(init_rc_lme2510_map)
+module_exit(exit_rc_lme2510_map)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
diff --git a/drivers/media/IR/keymaps/rc-msi-digivox-ii.c b/drivers/media/IR/keymaps/rc-msi-digivox-ii.c
new file mode 100644
index 0000000..67237fb
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-msi-digivox-ii.c
@@ -0,0 +1,67 @@
+/*
+ * MSI DIGIVOX mini II remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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 <media/rc-map.h>
+
+static struct ir_scancode msi_digivox_ii[] = {
+	{ 0x0002, KEY_2 },
+	{ 0x0003, KEY_UP },              /* up */
+	{ 0x0004, KEY_3 },
+	{ 0x0005, KEY_CHANNELDOWN },
+	{ 0x0008, KEY_5 },
+	{ 0x0009, KEY_0 },
+	{ 0x000b, KEY_8 },
+	{ 0x000d, KEY_DOWN },            /* down */
+	{ 0x0010, KEY_9 },
+	{ 0x0011, KEY_7 },
+	{ 0x0014, KEY_VOLUMEUP },
+	{ 0x0015, KEY_CHANNELUP },
+	{ 0x0016, KEY_OK },
+	{ 0x0017, KEY_POWER2 },
+	{ 0x001a, KEY_1 },
+	{ 0x001c, KEY_4 },
+	{ 0x001d, KEY_6 },
+	{ 0x001f, KEY_VOLUMEDOWN },
+};
+
+static struct rc_keymap msi_digivox_ii_map = {
+	.map = {
+		.scan    = msi_digivox_ii,
+		.size    = ARRAY_SIZE(msi_digivox_ii),
+		.ir_type = IR_TYPE_NEC,
+		.name    = RC_MAP_MSI_DIGIVOX_II,
+	}
+};
+
+static int __init init_rc_map_msi_digivox_ii(void)
+{
+	return ir_register_map(&msi_digivox_ii_map);
+}
+
+static void __exit exit_rc_map_msi_digivox_ii(void)
+{
+	ir_unregister_map(&msi_digivox_ii_map);
+}
+
+module_init(init_rc_map_msi_digivox_ii)
+module_exit(exit_rc_map_msi_digivox_ii)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/IR/keymaps/rc-msi-digivox-iii.c b/drivers/media/IR/keymaps/rc-msi-digivox-iii.c
new file mode 100644
index 0000000..882056e
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-msi-digivox-iii.c
@@ -0,0 +1,85 @@
+/*
+ * MSI DIGIVOX mini III remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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 <media/rc-map.h>
+
+/* MSI DIGIVOX mini III */
+/* Uses NEC extended 0x61d6. */
+/* This remote seems to be same as rc-kworld-315u.c. Anyhow, add new remote
+   since rc-kworld-315u.c lacks NEC extended address byte. */
+static struct ir_scancode msi_digivox_iii[] = {
+	{ 0x61d601, KEY_VIDEO },           /* Source */
+	{ 0x61d602, KEY_3 },
+	{ 0x61d603, KEY_POWER },           /* ShutDown */
+	{ 0x61d604, KEY_1 },
+	{ 0x61d605, KEY_5 },
+	{ 0x61d606, KEY_6 },
+	{ 0x61d607, KEY_CHANNELDOWN },     /* CH- */
+	{ 0x61d608, KEY_2 },
+	{ 0x61d609, KEY_CHANNELUP },       /* CH+ */
+	{ 0x61d60a, KEY_9 },
+	{ 0x61d60b, KEY_ZOOM },            /* Zoom */
+	{ 0x61d60c, KEY_7 },
+	{ 0x61d60d, KEY_8 },
+	{ 0x61d60e, KEY_VOLUMEUP },        /* Vol+ */
+	{ 0x61d60f, KEY_4 },
+	{ 0x61d610, KEY_ESC },             /* [back up arrow] */
+	{ 0x61d611, KEY_0 },
+	{ 0x61d612, KEY_OK },              /* [enter arrow] */
+	{ 0x61d613, KEY_VOLUMEDOWN },      /* Vol- */
+	{ 0x61d614, KEY_RECORD },          /* Rec */
+	{ 0x61d615, KEY_STOP },            /* Stop */
+	{ 0x61d616, KEY_PLAY },            /* Play */
+	{ 0x61d617, KEY_MUTE },            /* Mute */
+	{ 0x61d618, KEY_UP },
+	{ 0x61d619, KEY_DOWN },
+	{ 0x61d61a, KEY_LEFT },
+	{ 0x61d61b, KEY_RIGHT },
+	{ 0x61d61c, KEY_RED },
+	{ 0x61d61d, KEY_GREEN },
+	{ 0x61d61e, KEY_YELLOW },
+	{ 0x61d61f, KEY_BLUE },
+	{ 0x61d643, KEY_POWER2 },          /* [red power button] */
+};
+
+static struct rc_keymap msi_digivox_iii_map = {
+	.map = {
+		.scan    = msi_digivox_iii,
+		.size    = ARRAY_SIZE(msi_digivox_iii),
+		.ir_type = IR_TYPE_NEC,
+		.name    = RC_MAP_MSI_DIGIVOX_III,
+	}
+};
+
+static int __init init_rc_map_msi_digivox_iii(void)
+{
+	return ir_register_map(&msi_digivox_iii_map);
+}
+
+static void __exit exit_rc_map_msi_digivox_iii(void)
+{
+	ir_unregister_map(&msi_digivox_iii_map);
+}
+
+module_init(init_rc_map_msi_digivox_iii)
+module_exit(exit_rc_map_msi_digivox_iii)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/IR/keymaps/rc-rc5-streamzap.c b/drivers/media/IR/keymaps/rc-rc5-streamzap.c
deleted file mode 100644
index 4c19c58..0000000
--- a/drivers/media/IR/keymaps/rc-rc5-streamzap.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/* rc-rc5-streamzap.c - Keytable for Streamzap PC Remote, for use
- * with the Streamzap PC Remote IR Receiver.
- *
- * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <media/rc-map.h>
-
-static struct ir_scancode rc5_streamzap[] = {
-/*
- * FIXME: The Streamzap remote isn't actually true RC-5, it has an extra
- * bit in it, which presently throws the in-kernel RC-5 decoder for a loop.
- * We either have to enhance the decoder to support it, add a new decoder,
- * or just rely on lirc userspace decoding.
- */
-	{ 0x00, KEY_NUMERIC_0 },
-	{ 0x01, KEY_NUMERIC_1 },
-	{ 0x02, KEY_NUMERIC_2 },
-	{ 0x03, KEY_NUMERIC_3 },
-	{ 0x04, KEY_NUMERIC_4 },
-	{ 0x05, KEY_NUMERIC_5 },
-	{ 0x06, KEY_NUMERIC_6 },
-	{ 0x07, KEY_NUMERIC_7 },
-	{ 0x08, KEY_NUMERIC_8 },
-	{ 0x0a, KEY_POWER },
-	{ 0x0b, KEY_MUTE },
-	{ 0x0c, KEY_CHANNELUP },
-	{ 0x0d, KEY_VOLUMEUP },
-	{ 0x0e, KEY_CHANNELDOWN },
-	{ 0x0f, KEY_VOLUMEDOWN },
-	{ 0x10, KEY_UP },
-	{ 0x11, KEY_LEFT },
-	{ 0x12, KEY_OK },
-	{ 0x13, KEY_RIGHT },
-	{ 0x14, KEY_DOWN },
-	{ 0x15, KEY_MENU },
-	{ 0x16, KEY_EXIT },
-	{ 0x17, KEY_PLAY },
-	{ 0x18, KEY_PAUSE },
-	{ 0x19, KEY_STOP },
-	{ 0x1a, KEY_BACK },
-	{ 0x1b, KEY_FORWARD },
-	{ 0x1c, KEY_RECORD },
-	{ 0x1d, KEY_REWIND },
-	{ 0x1e, KEY_FASTFORWARD },
-	{ 0x20, KEY_RED },
-	{ 0x21, KEY_GREEN },
-	{ 0x22, KEY_YELLOW },
-	{ 0x23, KEY_BLUE },
-
-};
-
-static struct rc_keymap rc5_streamzap_map = {
-	.map = {
-		.scan    = rc5_streamzap,
-		.size    = ARRAY_SIZE(rc5_streamzap),
-		.ir_type = IR_TYPE_RC5,
-		.name    = RC_MAP_RC5_STREAMZAP,
-	}
-};
-
-static int __init init_rc_map_rc5_streamzap(void)
-{
-	return ir_register_map(&rc5_streamzap_map);
-}
-
-static void __exit exit_rc_map_rc5_streamzap(void)
-{
-	ir_unregister_map(&rc5_streamzap_map);
-}
-
-module_init(init_rc_map_rc5_streamzap)
-module_exit(exit_rc_map_rc5_streamzap)
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
diff --git a/drivers/media/IR/keymaps/rc-rc6-mce.c b/drivers/media/IR/keymaps/rc-rc6-mce.c
index 39557ad..1b7adab 100644
--- a/drivers/media/IR/keymaps/rc-rc6-mce.c
+++ b/drivers/media/IR/keymaps/rc-rc6-mce.c
@@ -12,35 +12,8 @@
 #include <media/rc-map.h>
 
 static struct ir_scancode rc6_mce[] = {
-	{ 0x800f0415, KEY_REWIND },
-	{ 0x800f0414, KEY_FASTFORWARD },
-	{ 0x800f041b, KEY_PREVIOUS },
-	{ 0x800f041a, KEY_NEXT },
 
-	{ 0x800f0416, KEY_PLAY },
-	{ 0x800f0418, KEY_PAUSE },
-	{ 0x800f046e, KEY_PLAYPAUSE },
-	{ 0x800f0419, KEY_STOP },
-	{ 0x800f0417, KEY_RECORD },
-
-	{ 0x800f041e, KEY_UP },
-	{ 0x800f041f, KEY_DOWN },
-	{ 0x800f0420, KEY_LEFT },
-	{ 0x800f0421, KEY_RIGHT },
-
-	{ 0x800f040b, KEY_ENTER },
-	{ 0x800f0422, KEY_OK },
-	{ 0x800f0423, KEY_EXIT },
-	{ 0x800f040a, KEY_DELETE },
-
-	{ 0x800f040e, KEY_MUTE },
-	{ 0x800f0410, KEY_VOLUMEUP },
-	{ 0x800f0411, KEY_VOLUMEDOWN },
-	{ 0x800f0412, KEY_CHANNELUP },
-	{ 0x800f0413, KEY_CHANNELDOWN },
-	{ 0x800f043a, KEY_BRIGHTNESSUP },
-	{ 0x800f0480, KEY_BRIGHTNESSDOWN },
-
+	{ 0x800f0400, KEY_NUMERIC_0 },
 	{ 0x800f0401, KEY_NUMERIC_1 },
 	{ 0x800f0402, KEY_NUMERIC_2 },
 	{ 0x800f0403, KEY_NUMERIC_3 },
@@ -50,38 +23,67 @@
 	{ 0x800f0407, KEY_NUMERIC_7 },
 	{ 0x800f0408, KEY_NUMERIC_8 },
 	{ 0x800f0409, KEY_NUMERIC_9 },
-	{ 0x800f0400, KEY_NUMERIC_0 },
 
-	{ 0x800f041d, KEY_NUMERIC_STAR },
+	{ 0x800f040a, KEY_DELETE },
+	{ 0x800f040b, KEY_ENTER },
+	{ 0x800f040c, KEY_POWER },
+	{ 0x800f040d, KEY_PROG1 }, 		/* Windows MCE button */
+	{ 0x800f040e, KEY_MUTE },
+	{ 0x800f040f, KEY_INFO },
+
+	{ 0x800f0410, KEY_VOLUMEUP },
+	{ 0x800f0411, KEY_VOLUMEDOWN },
+	{ 0x800f0412, KEY_CHANNELUP },
+	{ 0x800f0413, KEY_CHANNELDOWN },
+
+	{ 0x800f0414, KEY_FASTFORWARD },
+	{ 0x800f0415, KEY_REWIND },
+	{ 0x800f0416, KEY_PLAY },
+	{ 0x800f0417, KEY_RECORD },
+	{ 0x800f0418, KEY_PAUSE },
+	{ 0x800f046e, KEY_PLAYPAUSE },
+	{ 0x800f0419, KEY_STOP },
+	{ 0x800f041a, KEY_NEXT },
+	{ 0x800f041b, KEY_PREVIOUS },
 	{ 0x800f041c, KEY_NUMERIC_POUND },
+	{ 0x800f041d, KEY_NUMERIC_STAR },
+
+	{ 0x800f041e, KEY_UP },
+	{ 0x800f041f, KEY_DOWN },
+	{ 0x800f0420, KEY_LEFT },
+	{ 0x800f0421, KEY_RIGHT },
+
+	{ 0x800f0422, KEY_OK },
+	{ 0x800f0423, KEY_EXIT },
+	{ 0x800f0424, KEY_DVD },
+	{ 0x800f0425, KEY_TUNER }, 		/* LiveTV */
+	{ 0x800f0426, KEY_EPG }, 		/* Guide */
+	{ 0x800f0427, KEY_ZOOM }, 		/* Aspect */
+
+	{ 0x800f043a, KEY_BRIGHTNESSUP },
 
 	{ 0x800f0446, KEY_TV },
-	{ 0x800f0447, KEY_AUDIO }, /* My Music */
-	{ 0x800f0448, KEY_PVR }, /* RecordedTV */
+	{ 0x800f0447, KEY_AUDIO }, 		/* My Music */
+	{ 0x800f0448, KEY_PVR }, 		/* RecordedTV */
 	{ 0x800f0449, KEY_CAMERA },
 	{ 0x800f044a, KEY_VIDEO },
-	{ 0x800f0424, KEY_DVD },
-	{ 0x800f0425, KEY_TUNER }, /* LiveTV */
+	{ 0x800f044c, KEY_LANGUAGE },
+	{ 0x800f044d, KEY_TITLE },
+	{ 0x800f044e, KEY_PRINT }, 	/* Print - HP OEM version of remote */
+
 	{ 0x800f0450, KEY_RADIO },
 
-	{ 0x800f044c, KEY_LANGUAGE },
-	{ 0x800f0427, KEY_ZOOM }, /* Aspect */
-
+	{ 0x800f045a, KEY_SUBTITLE }, 		/* Caption/Teletext */
 	{ 0x800f045b, KEY_RED },
 	{ 0x800f045c, KEY_GREEN },
 	{ 0x800f045d, KEY_YELLOW },
 	{ 0x800f045e, KEY_BLUE },
 
-	{ 0x800f040f, KEY_INFO },
-	{ 0x800f0426, KEY_EPG }, /* Guide */
-	{ 0x800f045a, KEY_SUBTITLE }, /* Caption/Teletext */
-	{ 0x800f044d, KEY_TITLE },
+	{ 0x800f046e, KEY_PLAYPAUSE },
+	{ 0x800f046f, KEY_MEDIA }, 	/* Start media application (NEW) */
 
-       { 0x800f044e, KEY_PRINT }, /* Print - HP OEM version of remote */
-
-	{ 0x800f040c, KEY_POWER },
-	{ 0x800f040d, KEY_PROG1 }, /* Windows MCE button */
-
+	{ 0x800f0480, KEY_BRIGHTNESSDOWN },
+	{ 0x800f0481, KEY_PLAYPAUSE },
 };
 
 static struct rc_keymap rc6_mce_map = {
diff --git a/drivers/media/IR/keymaps/rc-streamzap.c b/drivers/media/IR/keymaps/rc-streamzap.c
new file mode 100644
index 0000000..df32013
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-streamzap.c
@@ -0,0 +1,82 @@
+/* rc-streamzap.c - Keytable for Streamzap PC Remote, for use
+ * with the Streamzap PC Remote IR Receiver.
+ *
+ * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+
+static struct ir_scancode streamzap[] = {
+/*
+ * The Streamzap remote is almost, but not quite, RC-5, as it has an extra
+ * bit in it, which throws the in-kernel RC-5 decoder for a loop. Currently,
+ * an additional RC-5-sz decoder is being deployed to support it, but it
+ * may be possible to merge it back with the standard RC-5 decoder.
+ */
+	{ 0x28c0, KEY_NUMERIC_0 },
+	{ 0x28c1, KEY_NUMERIC_1 },
+	{ 0x28c2, KEY_NUMERIC_2 },
+	{ 0x28c3, KEY_NUMERIC_3 },
+	{ 0x28c4, KEY_NUMERIC_4 },
+	{ 0x28c5, KEY_NUMERIC_5 },
+	{ 0x28c6, KEY_NUMERIC_6 },
+	{ 0x28c7, KEY_NUMERIC_7 },
+	{ 0x28c8, KEY_NUMERIC_8 },
+	{ 0x28c9, KEY_NUMERIC_9 },
+	{ 0x28ca, KEY_POWER },
+	{ 0x28cb, KEY_MUTE },
+	{ 0x28cc, KEY_CHANNELUP },
+	{ 0x28cd, KEY_VOLUMEUP },
+	{ 0x28ce, KEY_CHANNELDOWN },
+	{ 0x28cf, KEY_VOLUMEDOWN },
+	{ 0x28d0, KEY_UP },
+	{ 0x28d1, KEY_LEFT },
+	{ 0x28d2, KEY_OK },
+	{ 0x28d3, KEY_RIGHT },
+	{ 0x28d4, KEY_DOWN },
+	{ 0x28d5, KEY_MENU },
+	{ 0x28d6, KEY_EXIT },
+	{ 0x28d7, KEY_PLAY },
+	{ 0x28d8, KEY_PAUSE },
+	{ 0x28d9, KEY_STOP },
+	{ 0x28da, KEY_BACK },
+	{ 0x28db, KEY_FORWARD },
+	{ 0x28dc, KEY_RECORD },
+	{ 0x28dd, KEY_REWIND },
+	{ 0x28de, KEY_FASTFORWARD },
+	{ 0x28e0, KEY_RED },
+	{ 0x28e1, KEY_GREEN },
+	{ 0x28e2, KEY_YELLOW },
+	{ 0x28e3, KEY_BLUE },
+
+};
+
+static struct rc_keymap streamzap_map = {
+	.map = {
+		.scan    = streamzap,
+		.size    = ARRAY_SIZE(streamzap),
+		.ir_type = IR_TYPE_RC5_SZ,
+		.name    = RC_MAP_STREAMZAP,
+	}
+};
+
+static int __init init_rc_map_streamzap(void)
+{
+	return ir_register_map(&streamzap_map);
+}
+
+static void __exit exit_rc_map_streamzap(void)
+{
+	ir_unregister_map(&streamzap_map);
+}
+
+module_init(init_rc_map_streamzap)
+module_exit(exit_rc_map_streamzap)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
diff --git a/drivers/media/IR/keymaps/rc-terratec-slim.c b/drivers/media/IR/keymaps/rc-terratec-slim.c
new file mode 100644
index 0000000..10dee4c
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-terratec-slim.c
@@ -0,0 +1,79 @@
+/*
+ * TerraTec remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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 <media/rc-map.h>
+
+/* TerraTec slim remote, 7 rows, 4 columns. */
+/* Uses NEC extended 0x02bd. */
+static struct ir_scancode terratec_slim[] = {
+	{ 0x02bd00, KEY_1 },
+	{ 0x02bd01, KEY_2 },
+	{ 0x02bd02, KEY_3 },
+	{ 0x02bd03, KEY_4 },
+	{ 0x02bd04, KEY_5 },
+	{ 0x02bd05, KEY_6 },
+	{ 0x02bd06, KEY_7 },
+	{ 0x02bd07, KEY_8 },
+	{ 0x02bd08, KEY_9 },
+	{ 0x02bd09, KEY_0 },
+	{ 0x02bd0a, KEY_MUTE },
+	{ 0x02bd0b, KEY_NEW },             /* symbol: PIP */
+	{ 0x02bd0e, KEY_VOLUMEDOWN },
+	{ 0x02bd0f, KEY_PLAYPAUSE },
+	{ 0x02bd10, KEY_RIGHT },
+	{ 0x02bd11, KEY_LEFT },
+	{ 0x02bd12, KEY_UP },
+	{ 0x02bd13, KEY_DOWN },
+	{ 0x02bd15, KEY_OK },
+	{ 0x02bd16, KEY_STOP },
+	{ 0x02bd17, KEY_CAMERA },          /* snapshot */
+	{ 0x02bd18, KEY_CHANNELUP },
+	{ 0x02bd19, KEY_RECORD },
+	{ 0x02bd1a, KEY_CHANNELDOWN },
+	{ 0x02bd1c, KEY_ESC },
+	{ 0x02bd1f, KEY_VOLUMEUP },
+	{ 0x02bd44, KEY_EPG },
+	{ 0x02bd45, KEY_POWER2 },          /* [red power button] */
+};
+
+static struct rc_keymap terratec_slim_map = {
+	.map = {
+		.scan    = terratec_slim,
+		.size    = ARRAY_SIZE(terratec_slim),
+		.ir_type = IR_TYPE_NEC,
+		.name    = RC_MAP_TERRATEC_SLIM,
+	}
+};
+
+static int __init init_rc_map_terratec_slim(void)
+{
+	return ir_register_map(&terratec_slim_map);
+}
+
+static void __exit exit_rc_map_terratec_slim(void)
+{
+	ir_unregister_map(&terratec_slim_map);
+}
+
+module_init(init_rc_map_terratec_slim)
+module_exit(exit_rc_map_terratec_slim)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/IR/keymaps/rc-total-media-in-hand.c b/drivers/media/IR/keymaps/rc-total-media-in-hand.c
new file mode 100644
index 0000000..fd19857
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-total-media-in-hand.c
@@ -0,0 +1,85 @@
+/*
+ * Total Media In Hand remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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 <media/rc-map.h>
+
+/* Uses NEC extended 0x02bd */
+static struct ir_scancode total_media_in_hand[] = {
+	{ 0x02bd00, KEY_1 },
+	{ 0x02bd01, KEY_2 },
+	{ 0x02bd02, KEY_3 },
+	{ 0x02bd03, KEY_4 },
+	{ 0x02bd04, KEY_5 },
+	{ 0x02bd05, KEY_6 },
+	{ 0x02bd06, KEY_7 },
+	{ 0x02bd07, KEY_8 },
+	{ 0x02bd08, KEY_9 },
+	{ 0x02bd09, KEY_0 },
+	{ 0x02bd0a, KEY_MUTE },
+	{ 0x02bd0b, KEY_CYCLEWINDOWS },    /* yellow, [min / max] */
+	{ 0x02bd0c, KEY_VIDEO },           /* TV / AV */
+	{ 0x02bd0e, KEY_VOLUMEDOWN },
+	{ 0x02bd0f, KEY_TIME },            /* TimeShift */
+	{ 0x02bd10, KEY_RIGHT },           /* right arrow */
+	{ 0x02bd11, KEY_LEFT },            /* left arrow */
+	{ 0x02bd12, KEY_UP },              /* up arrow */
+	{ 0x02bd13, KEY_DOWN },            /* down arrow */
+	{ 0x02bd14, KEY_POWER2 },          /* [red] */
+	{ 0x02bd15, KEY_OK },              /* OK */
+	{ 0x02bd16, KEY_STOP },
+	{ 0x02bd17, KEY_CAMERA },          /* Snapshot */
+	{ 0x02bd18, KEY_CHANNELUP },
+	{ 0x02bd19, KEY_RECORD },
+	{ 0x02bd1a, KEY_CHANNELDOWN },
+	{ 0x02bd1c, KEY_ESC },             /* Esc */
+	{ 0x02bd1e, KEY_PLAY },
+	{ 0x02bd1f, KEY_VOLUMEUP },
+	{ 0x02bd40, KEY_PAUSE },
+	{ 0x02bd41, KEY_FASTFORWARD },     /* FF >> */
+	{ 0x02bd42, KEY_REWIND },          /* FR << */
+	{ 0x02bd43, KEY_ZOOM },            /* [window + mouse pointer] */
+	{ 0x02bd44, KEY_SHUFFLE },         /* Shuffle */
+	{ 0x02bd45, KEY_INFO },            /* [red (I)] */
+};
+
+static struct rc_keymap total_media_in_hand_map = {
+	.map = {
+		.scan    = total_media_in_hand,
+		.size    = ARRAY_SIZE(total_media_in_hand),
+		.ir_type = IR_TYPE_NEC,
+		.name    = RC_MAP_TOTAL_MEDIA_IN_HAND,
+	}
+};
+
+static int __init init_rc_map_total_media_in_hand(void)
+{
+	return ir_register_map(&total_media_in_hand_map);
+}
+
+static void __exit exit_rc_map_total_media_in_hand(void)
+{
+	ir_unregister_map(&total_media_in_hand_map);
+}
+
+module_init(init_rc_map_total_media_in_hand)
+module_exit(exit_rc_map_total_media_in_hand)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/IR/keymaps/rc-trekstor.c b/drivers/media/IR/keymaps/rc-trekstor.c
new file mode 100644
index 0000000..91092ca
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-trekstor.c
@@ -0,0 +1,80 @@
+/*
+ * TrekStor remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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 <media/rc-map.h>
+
+/* TrekStor DVB-T USB Stick remote controller. */
+/* Imported from af9015.h.
+   Initial keytable was from Marc Schneider <macke@macke.org> */
+static struct ir_scancode trekstor[] = {
+	{ 0x0084, KEY_0 },
+	{ 0x0085, KEY_MUTE },            /* Mute */
+	{ 0x0086, KEY_HOMEPAGE },        /* Home */
+	{ 0x0087, KEY_UP },              /* Up */
+	{ 0x0088, KEY_OK },              /* OK */
+	{ 0x0089, KEY_RIGHT },           /* Right */
+	{ 0x008a, KEY_FASTFORWARD },     /* Fast forward */
+	{ 0x008b, KEY_VOLUMEUP },        /* Volume + */
+	{ 0x008c, KEY_DOWN },            /* Down */
+	{ 0x008d, KEY_PLAY },            /* Play/Pause */
+	{ 0x008e, KEY_STOP },            /* Stop */
+	{ 0x008f, KEY_EPG },             /* Info/EPG */
+	{ 0x0090, KEY_7 },
+	{ 0x0091, KEY_4 },
+	{ 0x0092, KEY_1 },
+	{ 0x0093, KEY_CHANNELDOWN },     /* Channel - */
+	{ 0x0094, KEY_8 },
+	{ 0x0095, KEY_5 },
+	{ 0x0096, KEY_2 },
+	{ 0x0097, KEY_CHANNELUP },       /* Channel + */
+	{ 0x0098, KEY_9 },
+	{ 0x0099, KEY_6 },
+	{ 0x009a, KEY_3 },
+	{ 0x009b, KEY_VOLUMEDOWN },      /* Volume - */
+	{ 0x009c, KEY_TV },              /* TV */
+	{ 0x009d, KEY_RECORD },          /* Record */
+	{ 0x009e, KEY_REWIND },          /* Rewind */
+	{ 0x009f, KEY_LEFT },            /* Left */
+};
+
+static struct rc_keymap trekstor_map = {
+	.map = {
+		.scan    = trekstor,
+		.size    = ARRAY_SIZE(trekstor),
+		.ir_type = IR_TYPE_NEC,
+		.name    = RC_MAP_TREKSTOR,
+	}
+};
+
+static int __init init_rc_map_trekstor(void)
+{
+	return ir_register_map(&trekstor_map);
+}
+
+static void __exit exit_rc_map_trekstor(void)
+{
+	ir_unregister_map(&trekstor_map);
+}
+
+module_init(init_rc_map_trekstor)
+module_exit(exit_rc_map_trekstor)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/IR/keymaps/rc-twinhan1027.c b/drivers/media/IR/keymaps/rc-twinhan1027.c
new file mode 100644
index 0000000..0b5d356
--- /dev/null
+++ b/drivers/media/IR/keymaps/rc-twinhan1027.c
@@ -0,0 +1,87 @@
+#include <media/rc-map.h>
+
+static struct ir_scancode twinhan_vp1027[] = {
+	{ 0x16, KEY_POWER2 },
+	{ 0x17, KEY_FAVORITES },
+	{ 0x0f, KEY_TEXT },
+	{ 0x48, KEY_INFO},
+	{ 0x1c, KEY_EPG },
+	{ 0x04, KEY_LIST },
+
+	{ 0x03, KEY_1 },
+	{ 0x01, KEY_2 },
+	{ 0x06, KEY_3 },
+	{ 0x09, KEY_4 },
+	{ 0x1d, KEY_5 },
+	{ 0x1f, KEY_6 },
+	{ 0x0d, KEY_7 },
+	{ 0x19, KEY_8 },
+	{ 0x1b, KEY_9 },
+	{ 0x15, KEY_0 },
+
+	{ 0x0c, KEY_CANCEL },
+	{ 0x4a, KEY_CLEAR },
+	{ 0x13, KEY_BACKSPACE },
+	{ 0x00, KEY_TAB },
+
+	{ 0x4b, KEY_UP },
+	{ 0x51, KEY_DOWN },
+	{ 0x4e, KEY_LEFT },
+	{ 0x52, KEY_RIGHT },
+	{ 0x4f, KEY_ENTER },
+
+	{ 0x1e, KEY_VOLUMEUP },
+	{ 0x0a, KEY_VOLUMEDOWN },
+	{ 0x02, KEY_CHANNELDOWN },
+	{ 0x05, KEY_CHANNELUP },
+	{ 0x11, KEY_RECORD },
+
+	{ 0x14, KEY_PLAY },
+	{ 0x4c, KEY_PAUSE },
+	{ 0x1a, KEY_STOP },
+	{ 0x40, KEY_REWIND },
+	{ 0x12, KEY_FASTFORWARD },
+	{ 0x41, KEY_PREVIOUSSONG },
+	{ 0x42, KEY_NEXTSONG },
+	{ 0x54, KEY_SAVE },
+	{ 0x50, KEY_LANGUAGE },
+	{ 0x47, KEY_MEDIA },
+	{ 0x4d, KEY_SCREEN },
+	{ 0x43, KEY_SUBTITLE },
+	{ 0x10, KEY_MUTE },
+	{ 0x49, KEY_AUDIO },
+	{ 0x07, KEY_SLEEP },
+	{ 0x08, KEY_VIDEO },
+	{ 0x0e, KEY_AGAIN },
+	{ 0x45, KEY_EQUAL },
+	{ 0x46, KEY_MINUS },
+	{ 0x18, KEY_RED },
+	{ 0x53, KEY_GREEN },
+	{ 0x5e, KEY_YELLOW },
+	{ 0x5f, KEY_BLUE },
+};
+
+static struct rc_keymap twinhan_vp1027_map = {
+	.map = {
+		.scan    = twinhan_vp1027,
+		.size    = ARRAY_SIZE(twinhan_vp1027),
+		.ir_type = IR_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_TWINHAN_VP1027_DVBS,
+	}
+};
+
+static int __init init_rc_map_twinhan_vp1027(void)
+{
+	return ir_register_map(&twinhan_vp1027_map);
+}
+
+static void __exit exit_rc_map_twinhan_vp1027(void)
+{
+	ir_unregister_map(&twinhan_vp1027_map);
+}
+
+module_init(init_rc_map_twinhan_vp1027)
+module_exit(exit_rc_map_twinhan_vp1027)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sergey Ivanov <123kash@gmail.com>");
diff --git a/drivers/media/IR/lirc_dev.c b/drivers/media/IR/lirc_dev.c
index 2025818..8418b14 100644
--- a/drivers/media/IR/lirc_dev.c
+++ b/drivers/media/IR/lirc_dev.c
@@ -57,13 +57,12 @@
 
 	struct task_struct *task;
 	long jiffies_to_wait;
-
-	struct cdev cdev;
 };
 
 static DEFINE_MUTEX(lirc_dev_lock);
 
 static struct irctl *irctls[MAX_IRCTL_DEVICES];
+static struct cdev cdevs[MAX_IRCTL_DEVICES];
 
 /* Only used for sysfs but defined to void otherwise */
 static struct class *lirc_class;
@@ -71,15 +70,13 @@
 /*  helper function
  *  initializes the irctl structure
  */
-static void init_irctl(struct irctl *ir)
+static void lirc_irctl_init(struct irctl *ir)
 {
-	dev_dbg(ir->d.dev, LOGHEAD "initializing irctl\n",
-		ir->d.name, ir->d.minor);
 	mutex_init(&ir->irctl_lock);
 	ir->d.minor = NOPLUG;
 }
 
-static void cleanup(struct irctl *ir)
+static void lirc_irctl_cleanup(struct irctl *ir)
 {
 	dev_dbg(ir->d.dev, LOGHEAD "cleaning up\n", ir->d.name, ir->d.minor);
 
@@ -96,7 +93,7 @@
  *  reads key codes from driver and puts them into buffer
  *  returns 0 on success
  */
-static int add_to_buf(struct irctl *ir)
+static int lirc_add_to_buf(struct irctl *ir)
 {
 	if (ir->d.add_to_buf) {
 		int res = -ENODATA;
@@ -139,7 +136,7 @@
 			}
 			if (kthread_should_stop())
 				break;
-			if (!add_to_buf(ir))
+			if (!lirc_add_to_buf(ir))
 				wake_up_interruptible(&ir->buf->wait_poll);
 		} else {
 			set_current_state(TASK_INTERRUPTIBLE);
@@ -154,12 +151,15 @@
 }
 
 
-static struct file_operations fops = {
+static struct file_operations lirc_dev_fops = {
 	.owner		= THIS_MODULE,
 	.read		= lirc_dev_fop_read,
 	.write		= lirc_dev_fop_write,
 	.poll		= lirc_dev_fop_poll,
 	.unlocked_ioctl	= lirc_dev_fop_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= lirc_dev_fop_ioctl,
+#endif
 	.open		= lirc_dev_fop_open,
 	.release	= lirc_dev_fop_close,
 	.llseek		= noop_llseek,
@@ -169,19 +169,20 @@
 {
 	int retval;
 	struct lirc_driver *d = &ir->d;
+	struct cdev *cdev = &cdevs[d->minor];
 
 	if (d->fops) {
-		cdev_init(&ir->cdev, d->fops);
-		ir->cdev.owner = d->owner;
+		cdev_init(cdev, d->fops);
+		cdev->owner = d->owner;
 	} else {
-		cdev_init(&ir->cdev, &fops);
-		ir->cdev.owner = THIS_MODULE;
+		cdev_init(cdev, &lirc_dev_fops);
+		cdev->owner = THIS_MODULE;
 	}
-	kobject_set_name(&ir->cdev.kobj, "lirc%d", d->minor);
+	kobject_set_name(&cdev->kobj, "lirc%d", d->minor);
 
-	retval = cdev_add(&ir->cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1);
+	retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1);
 	if (retval)
-		kobject_put(&ir->cdev.kobj);
+		kobject_put(&cdev->kobj);
 
 	return retval;
 }
@@ -202,6 +203,12 @@
 		goto out;
 	}
 
+	if (!d->dev) {
+		printk(KERN_ERR "%s: dev pointer not filled in!\n", __func__);
+		err = -EINVAL;
+		goto out;
+	}
+
 	if (MAX_IRCTL_DEVICES <= d->minor) {
 		dev_err(d->dev, "lirc_dev: lirc_register_driver: "
 			"\"minor\" must be between 0 and %d (%d)!\n",
@@ -277,7 +284,7 @@
 		err = -ENOMEM;
 		goto out_lock;
 	}
-	init_irctl(ir);
+	lirc_irctl_init(ir);
 	irctls[minor] = ir;
 	d->minor = minor;
 
@@ -316,7 +323,6 @@
 		d->features = LIRC_CAN_REC_LIRCCODE;
 
 	ir->d = *d;
-	ir->d.minor = minor;
 
 	device_create(lirc_class, ir->d.dev,
 		      MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL,
@@ -357,21 +363,28 @@
 int lirc_unregister_driver(int minor)
 {
 	struct irctl *ir;
+	struct cdev *cdev;
 
 	if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
-		printk(KERN_ERR "lirc_dev: lirc_unregister_driver: "
-		       "\"minor (%d)\" must be between 0 and %d!\n",
-		       minor, MAX_IRCTL_DEVICES-1);
+		printk(KERN_ERR "lirc_dev: %s: minor (%d) must be between "
+		       "0 and %d!\n", __func__, minor, MAX_IRCTL_DEVICES-1);
 		return -EBADRQC;
 	}
 
 	ir = irctls[minor];
+	if (!ir) {
+		printk(KERN_ERR "lirc_dev: %s: failed to get irctl struct "
+		       "for minor %d!\n", __func__, minor);
+		return -ENOENT;
+	}
+
+	cdev = &cdevs[minor];
 
 	mutex_lock(&lirc_dev_lock);
 
 	if (ir->d.minor != minor) {
-		printk(KERN_ERR "lirc_dev: lirc_unregister_driver: "
-		       "minor (%d) device not registered!", minor);
+		printk(KERN_ERR "lirc_dev: %s: minor (%d) device not "
+		       "registered!\n", __func__, minor);
 		mutex_unlock(&lirc_dev_lock);
 		return -ENOENT;
 	}
@@ -390,12 +403,11 @@
 		wake_up_interruptible(&ir->buf->wait_poll);
 		mutex_lock(&ir->irctl_lock);
 		ir->d.set_use_dec(ir->d.data);
-		module_put(ir->d.owner);
+		module_put(cdev->owner);
 		mutex_unlock(&ir->irctl_lock);
-		cdev_del(&ir->cdev);
 	} else {
-		cleanup(ir);
-		cdev_del(&ir->cdev);
+		lirc_irctl_cleanup(ir);
+		cdev_del(cdev);
 		kfree(ir);
 		irctls[minor] = NULL;
 	}
@@ -409,6 +421,7 @@
 int lirc_dev_fop_open(struct inode *inode, struct file *file)
 {
 	struct irctl *ir;
+	struct cdev *cdev;
 	int retval = 0;
 
 	if (iminor(inode) >= MAX_IRCTL_DEVICES) {
@@ -425,7 +438,6 @@
 		retval = -ENODEV;
 		goto error;
 	}
-	file->private_data = ir;
 
 	dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor);
 
@@ -439,13 +451,14 @@
 		goto error;
 	}
 
-	if (try_module_get(ir->d.owner)) {
-		++ir->open;
+	cdev = &cdevs[iminor(inode)];
+	if (try_module_get(cdev->owner)) {
+		ir->open++;
 		retval = ir->d.set_use_inc(ir->d.data);
 
 		if (retval) {
-			module_put(ir->d.owner);
-			--ir->open;
+			module_put(cdev->owner);
+			ir->open--;
 		} else {
 			lirc_buffer_clear(ir->buf);
 		}
@@ -469,17 +482,24 @@
 int lirc_dev_fop_close(struct inode *inode, struct file *file)
 {
 	struct irctl *ir = irctls[iminor(inode)];
+	struct cdev *cdev = &cdevs[iminor(inode)];
+
+	if (!ir) {
+		printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+		return -EINVAL;
+	}
 
 	dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor);
 
 	WARN_ON(mutex_lock_killable(&lirc_dev_lock));
 
-	--ir->open;
+	ir->open--;
 	if (ir->attached) {
 		ir->d.set_use_dec(ir->d.data);
-		module_put(ir->d.owner);
+		module_put(cdev->owner);
 	} else {
-		cleanup(ir);
+		lirc_irctl_cleanup(ir);
+		cdev_del(cdev);
 		irctls[ir->d.minor] = NULL;
 		kfree(ir);
 	}
@@ -495,6 +515,11 @@
 	struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
 	unsigned int ret;
 
+	if (!ir) {
+		printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+		return POLLERR;
+	}
+
 	dev_dbg(ir->d.dev, LOGHEAD "poll called\n", ir->d.name, ir->d.minor);
 
 	if (!ir->attached) {
@@ -521,9 +546,14 @@
 
 long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-	unsigned long mode;
+	__u32 mode;
 	int result = 0;
-	struct irctl *ir = file->private_data;
+	struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
+
+	if (!ir) {
+		printk(KERN_ERR "lirc_dev: %s: no irctl found!\n", __func__);
+		return -ENODEV;
+	}
 
 	dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n",
 		ir->d.name, ir->d.minor, cmd);
@@ -538,7 +568,7 @@
 
 	switch (cmd) {
 	case LIRC_GET_FEATURES:
-		result = put_user(ir->d.features, (unsigned long *)arg);
+		result = put_user(ir->d.features, (__u32 *)arg);
 		break;
 	case LIRC_GET_REC_MODE:
 		if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
@@ -548,7 +578,7 @@
 
 		result = put_user(LIRC_REC2MODE
 				  (ir->d.features & LIRC_CAN_REC_MASK),
-				  (unsigned long *)arg);
+				  (__u32 *)arg);
 		break;
 	case LIRC_SET_REC_MODE:
 		if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
@@ -556,7 +586,7 @@
 			break;
 		}
 
-		result = get_user(mode, (unsigned long *)arg);
+		result = get_user(mode, (__u32 *)arg);
 		if (!result && !(LIRC_MODE2REC(mode) & ir->d.features))
 			result = -EINVAL;
 		/*
@@ -565,7 +595,7 @@
 		 */
 		break;
 	case LIRC_GET_LENGTH:
-		result = put_user(ir->d.code_length, (unsigned long *)arg);
+		result = put_user(ir->d.code_length, (__u32 *)arg);
 		break;
 	case LIRC_GET_MIN_TIMEOUT:
 		if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
@@ -574,7 +604,7 @@
 			break;
 		}
 
-		result = put_user(ir->d.min_timeout, (unsigned long *)arg);
+		result = put_user(ir->d.min_timeout, (__u32 *)arg);
 		break;
 	case LIRC_GET_MAX_TIMEOUT:
 		if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
@@ -583,7 +613,7 @@
 			break;
 		}
 
-		result = put_user(ir->d.max_timeout, (unsigned long *)arg);
+		result = put_user(ir->d.max_timeout, (__u32 *)arg);
 		break;
 	default:
 		result = -EINVAL;
@@ -604,12 +634,21 @@
 			  loff_t *ppos)
 {
 	struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
-	unsigned char buf[ir->chunk_size];
+	unsigned char *buf;
 	int ret = 0, written = 0;
 	DECLARE_WAITQUEUE(wait, current);
 
+	if (!ir) {
+		printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+		return -ENODEV;
+	}
+
 	dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor);
 
+	buf = kzalloc(ir->chunk_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
 	if (mutex_lock_interruptible(&ir->irctl_lock))
 		return -ERESTARTSYS;
 	if (!ir->attached) {
@@ -681,6 +720,7 @@
 	mutex_unlock(&ir->irctl_lock);
 
 out_unlocked:
+	kfree(buf);
 	dev_dbg(ir->d.dev, LOGHEAD "read result = %s (%d)\n",
 		ir->d.name, ir->d.minor, ret ? "-EFAULT" : "OK", ret);
 
@@ -709,6 +749,11 @@
 {
 	struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
 
+	if (!ir) {
+		printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+		return -ENODEV;
+	}
+
 	dev_dbg(ir->d.dev, LOGHEAD "write called\n", ir->d.name, ir->d.minor);
 
 	if (!ir->attached)
diff --git a/drivers/media/IR/mceusb.c b/drivers/media/IR/mceusb.c
index bc620e1..9dce684 100644
--- a/drivers/media/IR/mceusb.c
+++ b/drivers/media/IR/mceusb.c
@@ -46,24 +46,58 @@
 			"device driver"
 #define DRIVER_NAME	"mceusb"
 
-#define USB_BUFLEN	32	/* USB reception buffer length */
-#define USB_CTRL_MSG_SZ	2	/* Size of usb ctrl msg on gen1 hw */
-#define MCE_G1_INIT_MSGS 40	/* Init messages on gen1 hw to throw out */
+#define USB_BUFLEN		32 /* USB reception buffer length */
+#define USB_CTRL_MSG_SZ		2  /* Size of usb ctrl msg on gen1 hw */
+#define MCE_G1_INIT_MSGS	40 /* Init messages on gen1 hw to throw out */
 
 /* MCE constants */
-#define MCE_CMDBUF_SIZE	384 /* MCE Command buffer length */
-#define MCE_TIME_UNIT	50 /* Approx 50us resolution */
-#define MCE_CODE_LENGTH	5 /* Normal length of packet (with header) */
-#define MCE_PACKET_SIZE	4 /* Normal length of packet (without header) */
-#define MCE_PACKET_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */
-#define MCE_CONTROL_HEADER 0x9F /* MCE status header */
-#define MCE_TX_HEADER_LENGTH 3 /* # of bytes in the initializing tx header */
-#define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */
-#define MCE_DEFAULT_TX_MASK 0x03 /* Val opts: TX1=0x01, TX2=0x02, ALL=0x03 */
-#define MCE_PULSE_BIT	0x80 /* Pulse bit, MSB set == PULSE else SPACE */
-#define MCE_PULSE_MASK	0x7F /* Pulse mask */
-#define MCE_MAX_PULSE_LENGTH 0x7F /* Longest transmittable pulse symbol */
-#define MCE_PACKET_LENGTH_MASK  0x1F /* Packet length mask */
+#define MCE_CMDBUF_SIZE		384  /* MCE Command buffer length */
+#define MCE_TIME_UNIT		50   /* Approx 50us resolution */
+#define MCE_CODE_LENGTH		5    /* Normal length of packet (with header) */
+#define MCE_PACKET_SIZE		4    /* Normal length of packet (without header) */
+#define MCE_IRDATA_HEADER	0x84 /* Actual header format is 0x80 + num_bytes */
+#define MCE_IRDATA_TRAILER	0x80 /* End of IR data */
+#define MCE_TX_HEADER_LENGTH	3    /* # of bytes in the initializing tx header */
+#define MCE_MAX_CHANNELS	2    /* Two transmitters, hardware dependent? */
+#define MCE_DEFAULT_TX_MASK	0x03 /* Vals: TX1=0x01, TX2=0x02, ALL=0x03 */
+#define MCE_PULSE_BIT		0x80 /* Pulse bit, MSB set == PULSE else SPACE */
+#define MCE_PULSE_MASK		0x7f /* Pulse mask */
+#define MCE_MAX_PULSE_LENGTH	0x7f /* Longest transmittable pulse symbol */
+
+#define MCE_HW_CMD_HEADER	0xff	/* MCE hardware command header */
+#define MCE_COMMAND_HEADER	0x9f	/* MCE command header */
+#define MCE_COMMAND_MASK	0xe0	/* Mask out command bits */
+#define MCE_COMMAND_NULL	0x00	/* These show up various places... */
+/* if buf[i] & MCE_COMMAND_MASK == 0x80 and buf[i] != MCE_COMMAND_HEADER,
+ * then we're looking at a raw IR data sample */
+#define MCE_COMMAND_IRDATA	0x80
+#define MCE_PACKET_LENGTH_MASK	0x1f /* Packet length mask */
+
+/* Sub-commands, which follow MCE_COMMAND_HEADER or MCE_HW_CMD_HEADER */
+#define MCE_CMD_PING		0x03	/* Ping device */
+#define MCE_CMD_UNKNOWN		0x04	/* Unknown */
+#define MCE_CMD_UNKNOWN2	0x05	/* Unknown */
+#define MCE_CMD_S_CARRIER	0x06	/* Set TX carrier frequency */
+#define MCE_CMD_G_CARRIER	0x07	/* Get TX carrier frequency */
+#define MCE_CMD_S_TXMASK	0x08	/* Set TX port bitmask */
+#define MCE_CMD_UNKNOWN3	0x09	/* Unknown */
+#define MCE_CMD_UNKNOWN4	0x0a	/* Unknown */
+#define MCE_CMD_G_REVISION	0x0b	/* Get hw/sw revision */
+#define MCE_CMD_S_TIMEOUT	0x0c	/* Set RX timeout value */
+#define MCE_CMD_G_TIMEOUT	0x0d	/* Get RX timeout value */
+#define MCE_CMD_UNKNOWN5	0x0e	/* Unknown */
+#define MCE_CMD_UNKNOWN6	0x0f	/* Unknown */
+#define MCE_CMD_G_RXPORTSTS	0x11	/* Get RX port status */
+#define MCE_CMD_G_TXMASK	0x13	/* Set TX port bitmask */
+#define MCE_CMD_S_RXSENSOR	0x14	/* Set RX sensor (std/learning) */
+#define MCE_CMD_G_RXSENSOR	0x15	/* Get RX sensor (std/learning) */
+#define MCE_CMD_TX_PORTS	0x16	/* Get number of TX ports */
+#define MCE_CMD_G_WAKESRC	0x17	/* Get wake source */
+#define MCE_CMD_UNKNOWN7	0x18	/* Unknown */
+#define MCE_CMD_UNKNOWN8	0x19	/* Unknown */
+#define MCE_CMD_UNKNOWN9	0x1b	/* Unknown */
+#define MCE_CMD_DEVICE_RESET	0xaa	/* Reset the hardware */
+#define MCE_RSP_CMD_INVALID	0xfe	/* Invalid command issued */
 
 
 /* module parameters */
@@ -104,14 +138,64 @@
 #define VENDOR_NORTHSTAR	0x04eb
 #define VENDOR_REALTEK		0x0bda
 #define VENDOR_TIVO		0x105a
+#define VENDOR_CONEXANT		0x0572
+
+enum mceusb_model_type {
+	MCE_GEN2 = 0,		/* Most boards */
+	MCE_GEN1,
+	MCE_GEN3,
+	MCE_GEN2_TX_INV,
+	POLARIS_EVK,
+};
+
+struct mceusb_model {
+	u32 mce_gen1:1;
+	u32 mce_gen2:1;
+	u32 mce_gen3:1;
+	u32 tx_mask_inverted:1;
+	u32 is_polaris:1;
+
+	const char *rc_map;	/* Allow specify a per-board map */
+	const char *name;	/* per-board name */
+};
+
+static const struct mceusb_model mceusb_model[] = {
+	[MCE_GEN1] = {
+		.mce_gen1 = 1,
+		.tx_mask_inverted = 1,
+	},
+	[MCE_GEN2] = {
+		.mce_gen2 = 1,
+	},
+	[MCE_GEN2_TX_INV] = {
+		.mce_gen2 = 1,
+		.tx_mask_inverted = 1,
+	},
+	[MCE_GEN3] = {
+		.mce_gen3 = 1,
+		.tx_mask_inverted = 1,
+	},
+	[POLARIS_EVK] = {
+		.is_polaris = 1,
+		/*
+		 * In fact, the EVK is shipped without
+		 * remotes, but we should have something handy,
+		 * to allow testing it
+		 */
+		.rc_map = RC_MAP_RC5_HAUPPAUGE_NEW,
+		.name = "cx231xx MCE IR",
+	},
+};
 
 static struct usb_device_id mceusb_dev_table[] = {
 	/* Original Microsoft MCE IR Transceiver (often HP-branded) */
-	{ USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
+	{ USB_DEVICE(VENDOR_MICROSOFT, 0x006d),
+	  .driver_info = MCE_GEN1 },
 	/* Philips Infrared Transceiver - Sahara branded */
 	{ USB_DEVICE(VENDOR_PHILIPS, 0x0608) },
 	/* Philips Infrared Transceiver - HP branded */
-	{ USB_DEVICE(VENDOR_PHILIPS, 0x060c) },
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x060c),
+	  .driver_info = MCE_GEN2_TX_INV },
 	/* Philips SRM5100 */
 	{ USB_DEVICE(VENDOR_PHILIPS, 0x060d) },
 	/* Philips Infrared Transceiver - Omaura */
@@ -127,11 +211,14 @@
 	/* Realtek MCE IR Receiver */
 	{ USB_DEVICE(VENDOR_REALTEK, 0x0161) },
 	/* SMK/Toshiba G83C0004D410 */
-	{ USB_DEVICE(VENDOR_SMK, 0x031d) },
+	{ USB_DEVICE(VENDOR_SMK, 0x031d),
+	  .driver_info = MCE_GEN2_TX_INV },
 	/* SMK eHome Infrared Transceiver (Sony VAIO) */
-	{ USB_DEVICE(VENDOR_SMK, 0x0322) },
+	{ USB_DEVICE(VENDOR_SMK, 0x0322),
+	  .driver_info = MCE_GEN2_TX_INV },
 	/* bundled with Hauppauge PVR-150 */
-	{ USB_DEVICE(VENDOR_SMK, 0x0334) },
+	{ USB_DEVICE(VENDOR_SMK, 0x0334),
+	  .driver_info = MCE_GEN2_TX_INV },
 	/* SMK eHome Infrared Transceiver */
 	{ USB_DEVICE(VENDOR_SMK, 0x0338) },
 	/* Tatung eHome Infrared Transceiver */
@@ -145,17 +232,23 @@
 	/* Mitsumi */
 	{ USB_DEVICE(VENDOR_MITSUMI, 0x2501) },
 	/* Topseed eHome Infrared Transceiver */
-	{ USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0001),
+	  .driver_info = MCE_GEN2_TX_INV },
 	/* Topseed HP eHome Infrared Transceiver */
-	{ USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0006),
+	  .driver_info = MCE_GEN2_TX_INV },
 	/* Topseed eHome Infrared Transceiver */
-	{ USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0007),
+	  .driver_info = MCE_GEN2_TX_INV },
 	/* Topseed eHome Infrared Transceiver */
-	{ USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0008),
+	  .driver_info = MCE_GEN3 },
 	/* Topseed eHome Infrared Transceiver */
-	{ USB_DEVICE(VENDOR_TOPSEED, 0x000a) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x000a),
+	  .driver_info = MCE_GEN2_TX_INV },
 	/* Topseed eHome Infrared Transceiver */
-	{ USB_DEVICE(VENDOR_TOPSEED, 0x0011) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0011),
+	  .driver_info = MCE_GEN2_TX_INV },
 	/* Ricavision internal Infrared Transceiver */
 	{ USB_DEVICE(VENDOR_RICAVISION, 0x0010) },
 	/* Itron ione Libra Q-11 */
@@ -185,7 +278,8 @@
 	/* Fintek eHome Infrared Transceiver (in the AOpen MP45) */
 	{ USB_DEVICE(VENDOR_FINTEK, 0x0702) },
 	/* Pinnacle Remote Kit */
-	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225),
+	  .driver_info = MCE_GEN3 },
 	/* Elitegroup Computer Systems IR */
 	{ USB_DEVICE(VENDOR_ECS, 0x0f38) },
 	/* Wistron Corp. eHome Infrared Receiver */
@@ -198,37 +292,13 @@
 	{ USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) },
 	/* TiVo PC IR Receiver */
 	{ USB_DEVICE(VENDOR_TIVO, 0x2000) },
+	/* Conexant SDK */
+	{ USB_DEVICE(VENDOR_CONEXANT, 0x58a1),
+	  .driver_info = POLARIS_EVK },
 	/* Terminating entry */
 	{ }
 };
 
-static struct usb_device_id gen3_list[] = {
-	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
-	{ USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
-	{}
-};
-
-static struct usb_device_id microsoft_gen1_list[] = {
-	{ USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
-	{}
-};
-
-static struct usb_device_id std_tx_mask_list[] = {
-	{ USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
-	{ USB_DEVICE(VENDOR_PHILIPS, 0x060c) },
-	{ USB_DEVICE(VENDOR_SMK, 0x031d) },
-	{ USB_DEVICE(VENDOR_SMK, 0x0322) },
-	{ USB_DEVICE(VENDOR_SMK, 0x0334) },
-	{ USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
-	{ USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
-	{ USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
-	{ USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
-	{ USB_DEVICE(VENDOR_TOPSEED, 0x000a) },
-	{ USB_DEVICE(VENDOR_TOPSEED, 0x0011) },
-	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
-	{}
-};
-
 /* data structure for each usb transceiver */
 struct mceusb_dev {
 	/* ir-core bits */
@@ -248,8 +318,15 @@
 	/* buffers and dma */
 	unsigned char *buf_in;
 	unsigned int len_in;
-	u8 cmd;		/* MCE command type */
-	u8 rem;		/* Remaining IR data bytes in packet */
+
+	enum {
+		CMD_HEADER = 0,
+		SUBCMD,
+		CMD_DATA,
+		PARSE_IRDATA,
+	} parser_state;
+	u8 cmd, rem;		/* Remaining IR data bytes in packet */
+
 	dma_addr_t dma_in;
 	dma_addr_t dma_out;
 
@@ -257,7 +334,6 @@
 		u32 connected:1;
 		u32 tx_mask_inverted:1;
 		u32 microsoft_gen1:1;
-		u32 reserved:29;
 	} flags;
 
 	/* transmit support */
@@ -267,6 +343,7 @@
 
 	char name[128];
 	char phys[64];
+	enum mceusb_model_type model;
 };
 
 /*
@@ -291,43 +368,81 @@
  * - SET_RX_TIMEOUT sets the receiver timeout
  * - SET_RX_SENSOR sets which receiver sensor to use
  */
-static char DEVICE_RESET[]	= {0x00, 0xff, 0xaa};
-static char GET_REVISION[]	= {0xff, 0x0b};
-static char GET_UNKNOWN[]	= {0xff, 0x18};
-static char GET_UNKNOWN2[]	= {0x9f, 0x05};
-static char GET_CARRIER_FREQ[]	= {0x9f, 0x07};
-static char GET_RX_TIMEOUT[]	= {0x9f, 0x0d};
-static char GET_TX_BITMASK[]	= {0x9f, 0x13};
-static char GET_RX_SENSOR[]	= {0x9f, 0x15};
+static char DEVICE_RESET[]	= {MCE_COMMAND_NULL, MCE_HW_CMD_HEADER,
+				   MCE_CMD_DEVICE_RESET};
+static char GET_REVISION[]	= {MCE_HW_CMD_HEADER, MCE_CMD_G_REVISION};
+static char GET_UNKNOWN[]	= {MCE_HW_CMD_HEADER, MCE_CMD_UNKNOWN7};
+static char GET_UNKNOWN2[]	= {MCE_COMMAND_HEADER, MCE_CMD_UNKNOWN2};
+static char GET_CARRIER_FREQ[]	= {MCE_COMMAND_HEADER, MCE_CMD_G_CARRIER};
+static char GET_RX_TIMEOUT[]	= {MCE_COMMAND_HEADER, MCE_CMD_G_TIMEOUT};
+static char GET_TX_BITMASK[]	= {MCE_COMMAND_HEADER, MCE_CMD_G_TXMASK};
+static char GET_RX_SENSOR[]	= {MCE_COMMAND_HEADER, MCE_CMD_G_RXSENSOR};
 /* sub in desired values in lower byte or bytes for full command */
 /* FIXME: make use of these for transmit.
-static char SET_CARRIER_FREQ[]	= {0x9f, 0x06, 0x00, 0x00};
-static char SET_TX_BITMASK[]	= {0x9f, 0x08, 0x00};
-static char SET_RX_TIMEOUT[]	= {0x9f, 0x0c, 0x00, 0x00};
-static char SET_RX_SENSOR[]	= {0x9f, 0x14, 0x00};
+static char SET_CARRIER_FREQ[]	= {MCE_COMMAND_HEADER,
+				   MCE_CMD_S_CARRIER, 0x00, 0x00};
+static char SET_TX_BITMASK[]	= {MCE_COMMAND_HEADER, MCE_CMD_S_TXMASK, 0x00};
+static char SET_RX_TIMEOUT[]	= {MCE_COMMAND_HEADER,
+				   MCE_CMD_S_TIMEOUT, 0x00, 0x00};
+static char SET_RX_SENSOR[]	= {MCE_COMMAND_HEADER,
+				   MCE_CMD_S_RXSENSOR, 0x00};
 */
 
+static int mceusb_cmdsize(u8 cmd, u8 subcmd)
+{
+	int datasize = 0;
+
+	switch (cmd) {
+	case MCE_COMMAND_NULL:
+		if (subcmd == MCE_HW_CMD_HEADER)
+			datasize = 1;
+		break;
+	case MCE_HW_CMD_HEADER:
+		switch (subcmd) {
+		case MCE_CMD_G_REVISION:
+			datasize = 2;
+			break;
+		}
+	case MCE_COMMAND_HEADER:
+		switch (subcmd) {
+		case MCE_CMD_UNKNOWN:
+		case MCE_CMD_S_CARRIER:
+		case MCE_CMD_S_TIMEOUT:
+		case MCE_CMD_G_RXSENSOR:
+			datasize = 2;
+			break;
+		case MCE_CMD_S_TXMASK:
+		case MCE_CMD_S_RXSENSOR:
+			datasize = 1;
+			break;
+		}
+	}
+	return datasize;
+}
+
 static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
-				 int len, bool out)
+				 int offset, int len, bool out)
 {
 	char codes[USB_BUFLEN * 3 + 1];
 	char inout[9];
-	int i;
 	u8 cmd, subcmd, data1, data2;
 	struct device *dev = ir->dev;
-	int idx = 0;
+	int i, start, skip = 0;
+
+	if (!debug)
+		return;
 
 	/* skip meaningless 0xb1 0x60 header bytes on orig receiver */
 	if (ir->flags.microsoft_gen1 && !out)
-		idx = 2;
+		skip = 2;
 
-	if (len <= idx)
+	if (len <= skip)
 		return;
 
 	for (i = 0; i < len && i < USB_BUFLEN; i++)
-		snprintf(codes + i * 3, 4, "%02x ", buf[i] & 0xFF);
+		snprintf(codes + i * 3, 4, "%02x ", buf[i + offset] & 0xff);
 
-	dev_info(dev, "%sx data: %s (length=%d)\n",
+	dev_info(dev, "%sx data: %s(length=%d)\n",
 		 (out ? "t" : "r"), codes, len);
 
 	if (out)
@@ -335,91 +450,93 @@
 	else
 		strcpy(inout, "Got\0");
 
-	cmd    = buf[idx] & 0xff;
-	subcmd = buf[idx + 1] & 0xff;
-	data1  = buf[idx + 2] & 0xff;
-	data2  = buf[idx + 3] & 0xff;
+	start  = offset + skip;
+	cmd    = buf[start] & 0xff;
+	subcmd = buf[start + 1] & 0xff;
+	data1  = buf[start + 2] & 0xff;
+	data2  = buf[start + 3] & 0xff;
 
 	switch (cmd) {
-	case 0x00:
-		if (subcmd == 0xff && data1 == 0xaa)
+	case MCE_COMMAND_NULL:
+		if ((subcmd == MCE_HW_CMD_HEADER) &&
+		    (data1 == MCE_CMD_DEVICE_RESET))
 			dev_info(dev, "Device reset requested\n");
 		else
 			dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
 				 cmd, subcmd);
 		break;
-	case 0xff:
+	case MCE_HW_CMD_HEADER:
 		switch (subcmd) {
-		case 0x0b:
+		case MCE_CMD_G_REVISION:
 			if (len == 2)
 				dev_info(dev, "Get hw/sw rev?\n");
 			else
 				dev_info(dev, "hw/sw rev 0x%02x 0x%02x "
 					 "0x%02x 0x%02x\n", data1, data2,
-					 buf[idx + 4], buf[idx + 5]);
+					 buf[start + 4], buf[start + 5]);
 			break;
-		case 0xaa:
+		case MCE_CMD_DEVICE_RESET:
 			dev_info(dev, "Device reset requested\n");
 			break;
-		case 0xfe:
+		case MCE_RSP_CMD_INVALID:
 			dev_info(dev, "Previous command not supported\n");
 			break;
-		case 0x18:
-		case 0x1b:
+		case MCE_CMD_UNKNOWN7:
+		case MCE_CMD_UNKNOWN9:
 		default:
 			dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
 				 cmd, subcmd);
 			break;
 		}
 		break;
-	case 0x9f:
+	case MCE_COMMAND_HEADER:
 		switch (subcmd) {
-		case 0x03:
+		case MCE_CMD_PING:
 			dev_info(dev, "Ping\n");
 			break;
-		case 0x04:
+		case MCE_CMD_UNKNOWN:
 			dev_info(dev, "Resp to 9f 05 of 0x%02x 0x%02x\n",
 				 data1, data2);
 			break;
-		case 0x06:
+		case MCE_CMD_S_CARRIER:
 			dev_info(dev, "%s carrier mode and freq of "
 				 "0x%02x 0x%02x\n", inout, data1, data2);
 			break;
-		case 0x07:
+		case MCE_CMD_G_CARRIER:
 			dev_info(dev, "Get carrier mode and freq\n");
 			break;
-		case 0x08:
+		case MCE_CMD_S_TXMASK:
 			dev_info(dev, "%s transmit blaster mask of 0x%02x\n",
 				 inout, data1);
 			break;
-		case 0x0c:
+		case MCE_CMD_S_TIMEOUT:
 			/* value is in units of 50us, so x*50/100 or x/2 ms */
 			dev_info(dev, "%s receive timeout of %d ms\n",
 				 inout, ((data1 << 8) | data2) / 2);
 			break;
-		case 0x0d:
+		case MCE_CMD_G_TIMEOUT:
 			dev_info(dev, "Get receive timeout\n");
 			break;
-		case 0x13:
+		case MCE_CMD_G_TXMASK:
 			dev_info(dev, "Get transmit blaster mask\n");
 			break;
-		case 0x14:
+		case MCE_CMD_S_RXSENSOR:
 			dev_info(dev, "%s %s-range receive sensor in use\n",
 				 inout, data1 == 0x02 ? "short" : "long");
 			break;
-		case 0x15:
+		case MCE_CMD_G_RXSENSOR:
 			if (len == 2)
 				dev_info(dev, "Get receive sensor\n");
 			else
 				dev_info(dev, "Received pulse count is %d\n",
 					 ((data1 << 8) | data2));
 			break;
-		case 0xfe:
+		case MCE_RSP_CMD_INVALID:
 			dev_info(dev, "Error! Hardware is likely wedged...\n");
 			break;
-		case 0x05:
-		case 0x09:
-		case 0x0f:
+		case MCE_CMD_UNKNOWN2:
+		case MCE_CMD_UNKNOWN3:
+		case MCE_CMD_UNKNOWN5:
 		default:
 			dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
 				 cmd, subcmd);
@@ -429,6 +546,12 @@
 	default:
 		break;
 	}
+
+	if (cmd == MCE_IRDATA_TRAILER)
+		dev_info(dev, "End of raw IR data\n");
+	else if ((cmd != MCE_COMMAND_HEADER) &&
+		 ((cmd & MCE_COMMAND_MASK) == MCE_COMMAND_IRDATA))
+		dev_info(dev, "Raw IR data, %d pulse/space samples\n", ir->rem);
 }
 
 static void mce_async_callback(struct urb *urb, struct pt_regs *regs)
@@ -446,9 +569,7 @@
 		dev_dbg(ir->dev, "callback called (status=%d len=%d)\n",
 			urb->status, len);
 
-		if (debug)
-			mceusb_dev_printdata(ir, urb->transfer_buffer,
-					     len, true);
+		mceusb_dev_printdata(ir, urb->transfer_buffer, 0, len, true);
 	}
 
 }
@@ -536,8 +657,8 @@
 		return -ENOMEM;
 
 	/* MCE tx init header */
-	cmdbuf[cmdcount++] = MCE_CONTROL_HEADER;
-	cmdbuf[cmdcount++] = 0x08;
+	cmdbuf[cmdcount++] = MCE_COMMAND_HEADER;
+	cmdbuf[cmdcount++] = MCE_CMD_S_TXMASK;
 	cmdbuf[cmdcount++] = ir->tx_mask;
 
 	/* Generate mce packet data */
@@ -551,7 +672,7 @@
 			if ((cmdcount < MCE_CMDBUF_SIZE) &&
 			    (cmdcount - MCE_TX_HEADER_LENGTH) %
 			     MCE_CODE_LENGTH == 0)
-				cmdbuf[cmdcount++] = MCE_PACKET_HEADER;
+				cmdbuf[cmdcount++] = MCE_IRDATA_HEADER;
 
 			/* Insert mce packet data */
 			if (cmdcount < MCE_CMDBUF_SIZE)
@@ -570,7 +691,8 @@
 
 	/* Fix packet length in last header */
 	cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] =
-		0x80 + (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH - 1;
+		MCE_COMMAND_IRDATA + (cmdcount - MCE_TX_HEADER_LENGTH) %
+		MCE_CODE_LENGTH - 1;
 
 	/* Check if we have room for the empty packet at the end */
 	if (cmdcount >= MCE_CMDBUF_SIZE) {
@@ -579,7 +701,7 @@
 	}
 
 	/* All mce commands end with an empty packet (0x80) */
-	cmdbuf[cmdcount++] = 0x80;
+	cmdbuf[cmdcount++] = MCE_IRDATA_TRAILER;
 
 	/* Transmit the command to the mce device */
 	mce_async_out(ir, cmdbuf, cmdcount);
@@ -608,7 +730,8 @@
 	struct mceusb_dev *ir = priv;
 
 	if (ir->flags.tx_mask_inverted)
-		ir->tx_mask = (mask != 0x03 ? mask ^ 0x03 : mask) << 1;
+		ir->tx_mask = (mask != MCE_DEFAULT_TX_MASK ?
+				mask ^ MCE_DEFAULT_TX_MASK : mask) << 1;
 	else
 		ir->tx_mask = mask;
 
@@ -621,7 +744,8 @@
 	struct mceusb_dev *ir = priv;
 	int clk = 10000000;
 	int prescaler = 0, divisor = 0;
-	unsigned char cmdbuf[4] = { 0x9f, 0x06, 0x00, 0x00 };
+	unsigned char cmdbuf[4] = { MCE_COMMAND_HEADER,
+				    MCE_CMD_S_CARRIER, 0x00, 0x00 };
 
 	/* Carrier has changed */
 	if (ir->carrier != carrier) {
@@ -629,7 +753,7 @@
 		if (carrier == 0) {
 			ir->carrier = carrier;
 			cmdbuf[2] = 0x01;
-			cmdbuf[3] = 0x80;
+			cmdbuf[3] = MCE_IRDATA_TRAILER;
 			dev_dbg(ir->dev, "%s: disabling carrier "
 				"modulation\n", __func__);
 			mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
@@ -638,7 +762,7 @@
 
 		for (prescaler = 0; prescaler < 4; ++prescaler) {
 			divisor = (clk >> (2 * prescaler)) / carrier;
-			if (divisor <= 0xFF) {
+			if (divisor <= 0xff) {
 				ir->carrier = carrier;
 				cmdbuf[2] = prescaler;
 				cmdbuf[3] = divisor;
@@ -660,47 +784,36 @@
 
 static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
 {
-	struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
-	int i, start_index = 0;
-	u8 hdr = MCE_CONTROL_HEADER;
+	DEFINE_IR_RAW_EVENT(rawir);
+	int i = 0;
 
 	/* skip meaningless 0xb1 0x60 header bytes on orig receiver */
 	if (ir->flags.microsoft_gen1)
-		start_index = 2;
+		i = 2;
 
-	for (i = start_index; i < buf_len;) {
-		if (ir->rem == 0) {
-			/* decode mce packets of the form (84),AA,BB,CC,DD */
-			/* IR data packets can span USB messages - rem */
-			hdr = ir->buf_in[i];
-			ir->rem = (hdr & MCE_PACKET_LENGTH_MASK);
-			ir->cmd = (hdr & ~MCE_PACKET_LENGTH_MASK);
-			dev_dbg(ir->dev, "New data. rem: 0x%02x, cmd: 0x%02x\n",
-				ir->rem, ir->cmd);
-			i++;
-		}
-
-		/* don't process MCE commands */
-		if (hdr == MCE_CONTROL_HEADER || hdr == 0xff) {
-			ir->rem = 0;
-			return;
-		}
-
-		for (; (ir->rem > 0) && (i < buf_len); i++) {
+	for (; i < buf_len; i++) {
+		switch (ir->parser_state) {
+		case SUBCMD:
+			ir->rem = mceusb_cmdsize(ir->cmd, ir->buf_in[i]);
+			mceusb_dev_printdata(ir, ir->buf_in, i - 1,
+					     ir->rem + 2, false);
+			ir->parser_state = CMD_DATA;
+			break;
+		case PARSE_IRDATA:
 			ir->rem--;
-
 			rawir.pulse = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0);
 			rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK)
 					 * MCE_TIME_UNIT * 1000;
 
 			if ((ir->buf_in[i] & MCE_PULSE_MASK) == 0x7f) {
-				if (ir->rawir.pulse == rawir.pulse)
+				if (ir->rawir.pulse == rawir.pulse) {
 					ir->rawir.duration += rawir.duration;
-				else {
+				} else {
 					ir->rawir.duration = rawir.duration;
 					ir->rawir.pulse = rawir.pulse;
 				}
-				continue;
+				if (ir->rem)
+					break;
 			}
 			rawir.duration += ir->rawir.duration;
 			ir->rawir.duration = 0;
@@ -711,14 +824,40 @@
 				rawir.duration);
 
 			ir_raw_event_store(ir->idev, &rawir);
+			break;
+		case CMD_DATA:
+			ir->rem--;
+			break;
+		case CMD_HEADER:
+			/* decode mce packets of the form (84),AA,BB,CC,DD */
+			/* IR data packets can span USB messages - rem */
+			ir->cmd = ir->buf_in[i];
+			if ((ir->cmd == MCE_COMMAND_HEADER) ||
+			    ((ir->cmd & MCE_COMMAND_MASK) !=
+			     MCE_COMMAND_IRDATA)) {
+				ir->parser_state = SUBCMD;
+				continue;
+			}
+			ir->rem = (ir->cmd & MCE_PACKET_LENGTH_MASK);
+			mceusb_dev_printdata(ir, ir->buf_in, i, ir->rem + 1, false);
+			if (ir->rem) {
+				ir->parser_state = PARSE_IRDATA;
+				break;
+			}
+			/*
+			 * a package with len=0 (e. g. 0x80) means end of
+			 * data. We could use it to do the call to
+			 * ir_raw_event_handle(). For now, we don't need to
+			 * use it.
+			 */
+			break;
 		}
 
-		if (ir->buf_in[i] == 0x80 || ir->buf_in[i] == 0x9f)
-			ir->rem = 0;
-
-		dev_dbg(ir->dev, "calling ir_raw_event_handle\n");
-		ir_raw_event_handle(ir->idev);
+		if (ir->parser_state != CMD_HEADER && !ir->rem)
+			ir->parser_state = CMD_HEADER;
 	}
+	dev_dbg(ir->dev, "processed IR data, calling ir_raw_event_handle\n");
+	ir_raw_event_handle(ir->idev);
 }
 
 static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
@@ -737,9 +876,6 @@
 
 	buf_len = urb->actual_length;
 
-	if (debug)
-		mceusb_dev_printdata(ir, urb->transfer_buffer, buf_len, false);
-
 	if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
 		ir->send_flags = SEND_FLAG_COMPLETE;
 		dev_dbg(ir->dev, "setup answer received %d bytes\n",
@@ -760,6 +896,7 @@
 
 	case -EPIPE:
 	default:
+		dev_dbg(ir->dev, "Error: urb status = %d\n", urb->status);
 		break;
 	}
 
@@ -865,6 +1002,8 @@
 	struct input_dev *idev;
 	struct ir_dev_props *props;
 	struct device *dev = ir->dev;
+	const char *rc_map = RC_MAP_RC6_MCE;
+	const char *name = "Media Center Ed. eHome Infrared Remote Transceiver";
 	int ret = -ENODEV;
 
 	idev = input_allocate_device();
@@ -880,8 +1019,11 @@
 		goto props_alloc_failed;
 	}
 
-	snprintf(ir->name, sizeof(ir->name), "Media Center Ed. eHome "
-		 "Infrared Remote Transceiver (%04x:%04x)",
+	if (mceusb_model[ir->model].name)
+		name = mceusb_model[ir->model].name;
+
+	snprintf(ir->name, sizeof(ir->name), "%s (%04x:%04x)",
+		 name,
 		 le16_to_cpu(ir->usbdev->descriptor.idVendor),
 		 le16_to_cpu(ir->usbdev->descriptor.idProduct));
 
@@ -899,7 +1041,10 @@
 
 	ir->props = props;
 
-	ret = ir_input_register(idev, RC_MAP_RC6_MCE, props, DRIVER_NAME);
+	if (mceusb_model[ir->model].rc_map)
+		rc_map = mceusb_model[ir->model].rc_map;
+
+	ret = ir_input_register(idev, rc_map, props, DRIVER_NAME);
 	if (ret < 0) {
 		dev_err(dev, "remote input device register failed\n");
 		goto irdev_failed;
@@ -926,17 +1071,26 @@
 	struct mceusb_dev *ir = NULL;
 	int pipe, maxp, i;
 	char buf[63], name[128] = "";
+	enum mceusb_model_type model = id->driver_info;
 	bool is_gen3;
 	bool is_microsoft_gen1;
 	bool tx_mask_inverted;
+	bool is_polaris;
 
 	dev_dbg(&intf->dev, ": %s called\n", __func__);
 
 	idesc  = intf->cur_altsetting;
 
-	is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0;
-	is_microsoft_gen1 = usb_match_id(intf, microsoft_gen1_list) ? 1 : 0;
-	tx_mask_inverted = usb_match_id(intf, std_tx_mask_list) ? 0 : 1;
+	is_gen3 = mceusb_model[model].mce_gen3;
+	is_microsoft_gen1 = mceusb_model[model].mce_gen1;
+	tx_mask_inverted = mceusb_model[model].tx_mask_inverted;
+	is_polaris = mceusb_model[model].is_polaris;
+
+	if (is_polaris) {
+		/* Interface 0 is IR */
+		if (idesc->desc.bInterfaceNumber)
+			return -ENODEV;
+	}
 
 	/* step through the endpoints to find first bulk in and out endpoint */
 	for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
@@ -997,6 +1151,9 @@
 	ir->len_in = maxp;
 	ir->flags.microsoft_gen1 = is_microsoft_gen1;
 	ir->flags.tx_mask_inverted = tx_mask_inverted;
+	ir->model = model;
+
+	init_ir_raw_event(&ir->rawir);
 
 	/* Saving usb interface data for use by the transmitter routine */
 	ir->usb_ep_in = ep_in;
diff --git a/drivers/media/IR/nuvoton-cir.c b/drivers/media/IR/nuvoton-cir.c
new file mode 100644
index 0000000..301be53
--- /dev/null
+++ b/drivers/media/IR/nuvoton-cir.c
@@ -0,0 +1,1246 @@
+/*
+ * Driver for Nuvoton Technology Corporation w83667hg/w83677hg-i CIR
+ *
+ * Copyright (C) 2010 Jarod Wilson <jarod@redhat.com>
+ * Copyright (C) 2009 Nuvoton PS Team
+ *
+ * Special thanks to Nuvoton for providing hardware, spec sheets and
+ * sample code upon which portions of this driver are based. Indirect
+ * thanks also to Maxim Levitsky, whose ene_ir driver this driver is
+ * modeled after.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pnp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <media/ir-core.h>
+#include <linux/pci_ids.h>
+
+#include "nuvoton-cir.h"
+
+static char *chip_id = "w836x7hg";
+
+/* write val to config reg */
+static inline void nvt_cr_write(struct nvt_dev *nvt, u8 val, u8 reg)
+{
+	outb(reg, nvt->cr_efir);
+	outb(val, nvt->cr_efdr);
+}
+
+/* read val from config reg */
+static inline u8 nvt_cr_read(struct nvt_dev *nvt, u8 reg)
+{
+	outb(reg, nvt->cr_efir);
+	return inb(nvt->cr_efdr);
+}
+
+/* update config register bit without changing other bits */
+static inline void nvt_set_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg)
+{
+	u8 tmp = nvt_cr_read(nvt, reg) | val;
+	nvt_cr_write(nvt, tmp, reg);
+}
+
+/* clear config register bit without changing other bits */
+static inline void nvt_clear_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg)
+{
+	u8 tmp = nvt_cr_read(nvt, reg) & ~val;
+	nvt_cr_write(nvt, tmp, reg);
+}
+
+/* enter extended function mode */
+static inline void nvt_efm_enable(struct nvt_dev *nvt)
+{
+	/* Enabling Extended Function Mode explicitly requires writing 2x */
+	outb(EFER_EFM_ENABLE, nvt->cr_efir);
+	outb(EFER_EFM_ENABLE, nvt->cr_efir);
+}
+
+/* exit extended function mode */
+static inline void nvt_efm_disable(struct nvt_dev *nvt)
+{
+	outb(EFER_EFM_DISABLE, nvt->cr_efir);
+}
+
+/*
+ * When you want to address a specific logical device, write its logical
+ * device number to CR_LOGICAL_DEV_SEL, then enable/disable by writing
+ * 0x1/0x0 respectively to CR_LOGICAL_DEV_EN.
+ */
+static inline void nvt_select_logical_dev(struct nvt_dev *nvt, u8 ldev)
+{
+	outb(CR_LOGICAL_DEV_SEL, nvt->cr_efir);
+	outb(ldev, nvt->cr_efdr);
+}
+
+/* write val to cir config register */
+static inline void nvt_cir_reg_write(struct nvt_dev *nvt, u8 val, u8 offset)
+{
+	outb(val, nvt->cir_addr + offset);
+}
+
+/* read val from cir config register */
+static u8 nvt_cir_reg_read(struct nvt_dev *nvt, u8 offset)
+{
+	u8 val;
+
+	val = inb(nvt->cir_addr + offset);
+
+	return val;
+}
+
+/* write val to cir wake register */
+static inline void nvt_cir_wake_reg_write(struct nvt_dev *nvt,
+					  u8 val, u8 offset)
+{
+	outb(val, nvt->cir_wake_addr + offset);
+}
+
+/* read val from cir wake config register */
+static u8 nvt_cir_wake_reg_read(struct nvt_dev *nvt, u8 offset)
+{
+	u8 val;
+
+	val = inb(nvt->cir_wake_addr + offset);
+
+	return val;
+}
+
+#define pr_reg(text, ...) \
+	printk(KERN_INFO KBUILD_MODNAME ": " text, ## __VA_ARGS__)
+
+/* dump current cir register contents */
+static void cir_dump_regs(struct nvt_dev *nvt)
+{
+	nvt_efm_enable(nvt);
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
+
+	pr_reg("%s: Dump CIR logical device registers:\n", NVT_DRIVER_NAME);
+	pr_reg(" * CR CIR ACTIVE :   0x%x\n",
+	       nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
+	pr_reg(" * CR CIR BASE ADDR: 0x%x\n",
+	       (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
+		nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO));
+	pr_reg(" * CR CIR IRQ NUM:   0x%x\n",
+	       nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
+
+	nvt_efm_disable(nvt);
+
+	pr_reg("%s: Dump CIR registers:\n", NVT_DRIVER_NAME);
+	pr_reg(" * IRCON:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRCON));
+	pr_reg(" * IRSTS:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRSTS));
+	pr_reg(" * IREN:      0x%x\n", nvt_cir_reg_read(nvt, CIR_IREN));
+	pr_reg(" * RXFCONT:   0x%x\n", nvt_cir_reg_read(nvt, CIR_RXFCONT));
+	pr_reg(" * CP:        0x%x\n", nvt_cir_reg_read(nvt, CIR_CP));
+	pr_reg(" * CC:        0x%x\n", nvt_cir_reg_read(nvt, CIR_CC));
+	pr_reg(" * SLCH:      0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCH));
+	pr_reg(" * SLCL:      0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCL));
+	pr_reg(" * FIFOCON:   0x%x\n", nvt_cir_reg_read(nvt, CIR_FIFOCON));
+	pr_reg(" * IRFIFOSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFIFOSTS));
+	pr_reg(" * SRXFIFO:   0x%x\n", nvt_cir_reg_read(nvt, CIR_SRXFIFO));
+	pr_reg(" * TXFCONT:   0x%x\n", nvt_cir_reg_read(nvt, CIR_TXFCONT));
+	pr_reg(" * STXFIFO:   0x%x\n", nvt_cir_reg_read(nvt, CIR_STXFIFO));
+	pr_reg(" * FCCH:      0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCH));
+	pr_reg(" * FCCL:      0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCL));
+	pr_reg(" * IRFSM:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFSM));
+}
+
+/* dump current cir wake register contents */
+static void cir_wake_dump_regs(struct nvt_dev *nvt)
+{
+	u8 i, fifo_len;
+
+	nvt_efm_enable(nvt);
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
+
+	pr_reg("%s: Dump CIR WAKE logical device registers:\n",
+	       NVT_DRIVER_NAME);
+	pr_reg(" * CR CIR WAKE ACTIVE :   0x%x\n",
+	       nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
+	pr_reg(" * CR CIR WAKE BASE ADDR: 0x%x\n",
+	       (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
+		nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO));
+	pr_reg(" * CR CIR WAKE IRQ NUM:   0x%x\n",
+	       nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
+
+	nvt_efm_disable(nvt);
+
+	pr_reg("%s: Dump CIR WAKE registers\n", NVT_DRIVER_NAME);
+	pr_reg(" * IRCON:          0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON));
+	pr_reg(" * IRSTS:          0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS));
+	pr_reg(" * IREN:           0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN));
+	pr_reg(" * FIFO CMP DEEP:  0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_DEEP));
+	pr_reg(" * FIFO CMP TOL:   0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_TOL));
+	pr_reg(" * FIFO COUNT:     0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT));
+	pr_reg(" * SLCH:           0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCH));
+	pr_reg(" * SLCL:           0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCL));
+	pr_reg(" * FIFOCON:        0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON));
+	pr_reg(" * SRXFSTS:        0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_SRXFSTS));
+	pr_reg(" * SAMPLE RX FIFO: 0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_SAMPLE_RX_FIFO));
+	pr_reg(" * WR FIFO DATA:   0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_WR_FIFO_DATA));
+	pr_reg(" * RD FIFO ONLY:   0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
+	pr_reg(" * RD FIFO ONLY IDX: 0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX));
+	pr_reg(" * FIFO IGNORE:    0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_IGNORE));
+	pr_reg(" * IRFSM:          0x%x\n",
+	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRFSM));
+
+	fifo_len = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT);
+	pr_reg("%s: Dump CIR WAKE FIFO (len %d)\n", NVT_DRIVER_NAME, fifo_len);
+	pr_reg("* Contents = ");
+	for (i = 0; i < fifo_len; i++)
+		printk(KERN_CONT "%02x ",
+		       nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
+	printk(KERN_CONT "\n");
+}
+
+/* detect hardware features */
+static int nvt_hw_detect(struct nvt_dev *nvt)
+{
+	unsigned long flags;
+	u8 chip_major, chip_minor;
+	int ret = 0;
+
+	nvt_efm_enable(nvt);
+
+	/* Check if we're wired for the alternate EFER setup */
+	chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI);
+	if (chip_major == 0xff) {
+		nvt->cr_efir = CR_EFIR2;
+		nvt->cr_efdr = CR_EFDR2;
+		nvt_efm_enable(nvt);
+		chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI);
+	}
+
+	chip_minor = nvt_cr_read(nvt, CR_CHIP_ID_LO);
+	nvt_dbg("%s: chip id: 0x%02x 0x%02x", chip_id, chip_major, chip_minor);
+
+	if (chip_major != CHIP_ID_HIGH &&
+	    (chip_minor != CHIP_ID_LOW || chip_minor != CHIP_ID_LOW2))
+		ret = -ENODEV;
+
+	nvt_efm_disable(nvt);
+
+	spin_lock_irqsave(&nvt->nvt_lock, flags);
+	nvt->chip_major = chip_major;
+	nvt->chip_minor = chip_minor;
+	spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
+	return ret;
+}
+
+static void nvt_cir_ldev_init(struct nvt_dev *nvt)
+{
+	u8 val;
+
+	/* output pin selection (Pin95=CIRRX, Pin96=CIRTX1, WB enabled */
+	val = nvt_cr_read(nvt, CR_OUTPUT_PIN_SEL);
+	val &= OUTPUT_PIN_SEL_MASK;
+	val |= (OUTPUT_ENABLE_CIR | OUTPUT_ENABLE_CIRWB);
+	nvt_cr_write(nvt, val, CR_OUTPUT_PIN_SEL);
+
+	/* Select CIR logical device and enable */
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
+	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
+
+	nvt_cr_write(nvt, nvt->cir_addr >> 8, CR_CIR_BASE_ADDR_HI);
+	nvt_cr_write(nvt, nvt->cir_addr & 0xff, CR_CIR_BASE_ADDR_LO);
+
+	nvt_cr_write(nvt, nvt->cir_irq, CR_CIR_IRQ_RSRC);
+
+	nvt_dbg("CIR initialized, base io port address: 0x%lx, irq: %d",
+		nvt->cir_addr, nvt->cir_irq);
+}
+
+static void nvt_cir_wake_ldev_init(struct nvt_dev *nvt)
+{
+	/* Select ACPI logical device, enable it and CIR Wake */
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_ACPI);
+	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
+
+	/* Enable CIR Wake via PSOUT# (Pin60) */
+	nvt_set_reg_bit(nvt, CIR_WAKE_ENABLE_BIT, CR_ACPI_CIR_WAKE);
+
+	/* enable cir interrupt of mouse/keyboard IRQ event */
+	nvt_set_reg_bit(nvt, CIR_INTR_MOUSE_IRQ_BIT, CR_ACPI_IRQ_EVENTS);
+
+	/* enable pme interrupt of cir wakeup event */
+	nvt_set_reg_bit(nvt, PME_INTR_CIR_PASS_BIT, CR_ACPI_IRQ_EVENTS2);
+
+	/* Select CIR Wake logical device and enable */
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
+	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
+
+	nvt_cr_write(nvt, nvt->cir_wake_addr >> 8, CR_CIR_BASE_ADDR_HI);
+	nvt_cr_write(nvt, nvt->cir_wake_addr & 0xff, CR_CIR_BASE_ADDR_LO);
+
+	nvt_cr_write(nvt, nvt->cir_wake_irq, CR_CIR_IRQ_RSRC);
+
+	nvt_dbg("CIR Wake initialized, base io port address: 0x%lx, irq: %d",
+		nvt->cir_wake_addr, nvt->cir_wake_irq);
+}
+
+/* clear out the hardware's cir rx fifo */
+static void nvt_clear_cir_fifo(struct nvt_dev *nvt)
+{
+	u8 val;
+
+	val = nvt_cir_reg_read(nvt, CIR_FIFOCON);
+	nvt_cir_reg_write(nvt, val | CIR_FIFOCON_RXFIFOCLR, CIR_FIFOCON);
+}
+
+/* clear out the hardware's cir wake rx fifo */
+static void nvt_clear_cir_wake_fifo(struct nvt_dev *nvt)
+{
+	u8 val;
+
+	val = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON);
+	nvt_cir_wake_reg_write(nvt, val | CIR_WAKE_FIFOCON_RXFIFOCLR,
+			       CIR_WAKE_FIFOCON);
+}
+
+/* clear out the hardware's cir tx fifo */
+static void nvt_clear_tx_fifo(struct nvt_dev *nvt)
+{
+	u8 val;
+
+	val = nvt_cir_reg_read(nvt, CIR_FIFOCON);
+	nvt_cir_reg_write(nvt, val | CIR_FIFOCON_TXFIFOCLR, CIR_FIFOCON);
+}
+
+/* enable RX Trigger Level Reach and Packet End interrupts */
+static void nvt_set_cir_iren(struct nvt_dev *nvt)
+{
+	u8 iren;
+
+	iren = CIR_IREN_RTR | CIR_IREN_PE;
+	nvt_cir_reg_write(nvt, iren, CIR_IREN);
+}
+
+static void nvt_cir_regs_init(struct nvt_dev *nvt)
+{
+	/* set sample limit count (PE interrupt raised when reached) */
+	nvt_cir_reg_write(nvt, CIR_RX_LIMIT_COUNT >> 8, CIR_SLCH);
+	nvt_cir_reg_write(nvt, CIR_RX_LIMIT_COUNT & 0xff, CIR_SLCL);
+
+	/* set fifo irq trigger levels */
+	nvt_cir_reg_write(nvt, CIR_FIFOCON_TX_TRIGGER_LEV |
+			  CIR_FIFOCON_RX_TRIGGER_LEV, CIR_FIFOCON);
+
+	/*
+	 * Enable TX and RX, specify carrier on = low, off = high, and set
+	 * sample period (currently 50us)
+	 */
+	nvt_cir_reg_write(nvt,
+			  CIR_IRCON_TXEN | CIR_IRCON_RXEN |
+			  CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL,
+			  CIR_IRCON);
+
+	/* clear hardware rx and tx fifos */
+	nvt_clear_cir_fifo(nvt);
+	nvt_clear_tx_fifo(nvt);
+
+	/* clear any and all stray interrupts */
+	nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
+
+	/* and finally, enable interrupts */
+	nvt_set_cir_iren(nvt);
+}
+
+static void nvt_cir_wake_regs_init(struct nvt_dev *nvt)
+{
+	/* set number of bytes needed for wake key comparison (default 67) */
+	nvt_cir_wake_reg_write(nvt, CIR_WAKE_FIFO_LEN, CIR_WAKE_FIFO_CMP_DEEP);
+
+	/* set tolerance/variance allowed per byte during wake compare */
+	nvt_cir_wake_reg_write(nvt, CIR_WAKE_CMP_TOLERANCE,
+			       CIR_WAKE_FIFO_CMP_TOL);
+
+	/* set sample limit count (PE interrupt raised when reached) */
+	nvt_cir_wake_reg_write(nvt, CIR_RX_LIMIT_COUNT >> 8, CIR_WAKE_SLCH);
+	nvt_cir_wake_reg_write(nvt, CIR_RX_LIMIT_COUNT & 0xff, CIR_WAKE_SLCL);
+
+	/* set cir wake fifo rx trigger level (currently 67) */
+	nvt_cir_wake_reg_write(nvt, CIR_WAKE_FIFOCON_RX_TRIGGER_LEV,
+			       CIR_WAKE_FIFOCON);
+
+	/*
+	 * Enable TX and RX, specific carrier on = low, off = high, and set
+	 * sample period (currently 50us)
+	 */
+	nvt_cir_wake_reg_write(nvt, CIR_WAKE_IRCON_MODE0 | CIR_WAKE_IRCON_RXEN |
+			       CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV |
+			       CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL,
+			       CIR_WAKE_IRCON);
+
+	/* clear cir wake rx fifo */
+	nvt_clear_cir_wake_fifo(nvt);
+
+	/* clear any and all stray interrupts */
+	nvt_cir_wake_reg_write(nvt, 0xff, CIR_WAKE_IRSTS);
+}
+
+static void nvt_enable_wake(struct nvt_dev *nvt)
+{
+	nvt_efm_enable(nvt);
+
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_ACPI);
+	nvt_set_reg_bit(nvt, CIR_WAKE_ENABLE_BIT, CR_ACPI_CIR_WAKE);
+	nvt_set_reg_bit(nvt, CIR_INTR_MOUSE_IRQ_BIT, CR_ACPI_IRQ_EVENTS);
+	nvt_set_reg_bit(nvt, PME_INTR_CIR_PASS_BIT, CR_ACPI_IRQ_EVENTS2);
+
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
+	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
+
+	nvt_efm_disable(nvt);
+
+	nvt_cir_wake_reg_write(nvt, CIR_WAKE_IRCON_MODE0 | CIR_WAKE_IRCON_RXEN |
+			       CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV |
+			       CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL,
+			       CIR_WAKE_IRCON);
+	nvt_cir_wake_reg_write(nvt, 0xff, CIR_WAKE_IRSTS);
+	nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IREN);
+}
+
+/* rx carrier detect only works in learning mode, must be called w/nvt_lock */
+static u32 nvt_rx_carrier_detect(struct nvt_dev *nvt)
+{
+	u32 count, carrier, duration = 0;
+	int i;
+
+	count = nvt_cir_reg_read(nvt, CIR_FCCL) |
+		nvt_cir_reg_read(nvt, CIR_FCCH) << 8;
+
+	for (i = 0; i < nvt->pkts; i++) {
+		if (nvt->buf[i] & BUF_PULSE_BIT)
+			duration += nvt->buf[i] & BUF_LEN_MASK;
+	}
+
+	duration *= SAMPLE_PERIOD;
+
+	if (!count || !duration) {
+		nvt_pr(KERN_NOTICE, "Unable to determine carrier! (c:%u, d:%u)",
+		       count, duration);
+		return 0;
+	}
+
+	carrier = (count * 1000000) / duration;
+
+	if ((carrier > MAX_CARRIER) || (carrier < MIN_CARRIER))
+		nvt_dbg("WTF? Carrier frequency out of range!");
+
+	nvt_dbg("Carrier frequency: %u (count %u, duration %u)",
+		carrier, count, duration);
+
+	return carrier;
+}
+
+/*
+ * set carrier frequency
+ *
+ * set carrier on 2 registers: CP & CC
+ * always set CP as 0x81
+ * set CC by SPEC, CC = 3MHz/carrier - 1
+ */
+static int nvt_set_tx_carrier(void *data, u32 carrier)
+{
+	struct nvt_dev *nvt = data;
+	u16 val;
+
+	nvt_cir_reg_write(nvt, 1, CIR_CP);
+	val = 3000000 / (carrier) - 1;
+	nvt_cir_reg_write(nvt, val & 0xff, CIR_CC);
+
+	nvt_dbg("cp: 0x%x cc: 0x%x\n",
+		nvt_cir_reg_read(nvt, CIR_CP), nvt_cir_reg_read(nvt, CIR_CC));
+
+	return 0;
+}
+
+/*
+ * nvt_tx_ir
+ *
+ * 1) clean TX fifo first (handled by AP)
+ * 2) copy data from user space
+ * 3) disable RX interrupts, enable TX interrupts: TTR & TFU
+ * 4) send 9 packets to TX FIFO to open TTR
+ * in interrupt_handler:
+ * 5) send all data out
+ * go back to write():
+ * 6) disable TX interrupts, re-enable RX interupts
+ *
+ * The key problem of this function is user space data may larger than
+ * driver's data buf length. So nvt_tx_ir() will only copy TX_BUF_LEN data to
+ * buf, and keep current copied data buf num in cur_buf_num. But driver's buf
+ * number may larger than TXFCONT (0xff). So in interrupt_handler, it has to
+ * set TXFCONT as 0xff, until buf_count less than 0xff.
+ */
+static int nvt_tx_ir(void *priv, int *txbuf, u32 n)
+{
+	struct nvt_dev *nvt = priv;
+	unsigned long flags;
+	size_t cur_count;
+	unsigned int i;
+	u8 iren;
+	int ret;
+
+	spin_lock_irqsave(&nvt->tx.lock, flags);
+
+	if (n >= TX_BUF_LEN) {
+		nvt->tx.buf_count = cur_count = TX_BUF_LEN;
+		ret = TX_BUF_LEN;
+	} else {
+		nvt->tx.buf_count = cur_count = n;
+		ret = n;
+	}
+
+	memcpy(nvt->tx.buf, txbuf, nvt->tx.buf_count);
+
+	nvt->tx.cur_buf_num = 0;
+
+	/* save currently enabled interrupts */
+	iren = nvt_cir_reg_read(nvt, CIR_IREN);
+
+	/* now disable all interrupts, save TFU & TTR */
+	nvt_cir_reg_write(nvt, CIR_IREN_TFU | CIR_IREN_TTR, CIR_IREN);
+
+	nvt->tx.tx_state = ST_TX_REPLY;
+
+	nvt_cir_reg_write(nvt, CIR_FIFOCON_TX_TRIGGER_LEV_8 |
+			  CIR_FIFOCON_RXFIFOCLR, CIR_FIFOCON);
+
+	/* trigger TTR interrupt by writing out ones, (yes, it's ugly) */
+	for (i = 0; i < 9; i++)
+		nvt_cir_reg_write(nvt, 0x01, CIR_STXFIFO);
+
+	spin_unlock_irqrestore(&nvt->tx.lock, flags);
+
+	wait_event(nvt->tx.queue, nvt->tx.tx_state == ST_TX_REQUEST);
+
+	spin_lock_irqsave(&nvt->tx.lock, flags);
+	nvt->tx.tx_state = ST_TX_NONE;
+	spin_unlock_irqrestore(&nvt->tx.lock, flags);
+
+	/* restore enabled interrupts to prior state */
+	nvt_cir_reg_write(nvt, iren, CIR_IREN);
+
+	return ret;
+}
+
+/* dump contents of the last rx buffer we got from the hw rx fifo */
+static void nvt_dump_rx_buf(struct nvt_dev *nvt)
+{
+	int i;
+
+	printk(KERN_DEBUG "%s (len %d): ", __func__, nvt->pkts);
+	for (i = 0; (i < nvt->pkts) && (i < RX_BUF_LEN); i++)
+		printk(KERN_CONT "0x%02x ", nvt->buf[i]);
+	printk(KERN_CONT "\n");
+}
+
+/*
+ * Process raw data in rx driver buffer, store it in raw IR event kfifo,
+ * trigger decode when appropriate.
+ *
+ * We get IR data samples one byte at a time. If the msb is set, its a pulse,
+ * otherwise its a space. The lower 7 bits are the count of SAMPLE_PERIOD
+ * (default 50us) intervals for that pulse/space. A discrete signal is
+ * followed by a series of 0x7f packets, then either 0x7<something> or 0x80
+ * to signal more IR coming (repeats) or end of IR, respectively. We store
+ * sample data in the raw event kfifo until we see 0x7<something> (except f)
+ * or 0x80, at which time, we trigger a decode operation.
+ */
+static void nvt_process_rx_ir_data(struct nvt_dev *nvt)
+{
+	DEFINE_IR_RAW_EVENT(rawir);
+	unsigned int count;
+	u32 carrier;
+	u8 sample;
+	int i;
+
+	nvt_dbg_verbose("%s firing", __func__);
+
+	if (debug)
+		nvt_dump_rx_buf(nvt);
+
+	if (nvt->carrier_detect_enabled)
+		carrier = nvt_rx_carrier_detect(nvt);
+
+	count = nvt->pkts;
+	nvt_dbg_verbose("Processing buffer of len %d", count);
+
+	for (i = 0; i < count; i++) {
+		nvt->pkts--;
+		sample = nvt->buf[i];
+
+		rawir.pulse = ((sample & BUF_PULSE_BIT) != 0);
+		rawir.duration = (sample & BUF_LEN_MASK)
+					* SAMPLE_PERIOD * 1000;
+
+		if ((sample & BUF_LEN_MASK) == BUF_LEN_MASK) {
+			if (nvt->rawir.pulse == rawir.pulse)
+				nvt->rawir.duration += rawir.duration;
+			else {
+				nvt->rawir.duration = rawir.duration;
+				nvt->rawir.pulse = rawir.pulse;
+			}
+			continue;
+		}
+
+		rawir.duration += nvt->rawir.duration;
+
+		init_ir_raw_event(&nvt->rawir);
+		nvt->rawir.duration = 0;
+		nvt->rawir.pulse = rawir.pulse;
+
+		if (sample == BUF_PULSE_BIT)
+			rawir.pulse = false;
+
+		if (rawir.duration) {
+			nvt_dbg("Storing %s with duration %d",
+				rawir.pulse ? "pulse" : "space",
+				rawir.duration);
+
+			ir_raw_event_store(nvt->rdev, &rawir);
+		}
+
+		/*
+		 * BUF_PULSE_BIT indicates end of IR data, BUF_REPEAT_BYTE
+		 * indicates end of IR signal, but new data incoming. In both
+		 * cases, it means we're ready to call ir_raw_event_handle
+		 */
+		if (sample == BUF_PULSE_BIT || ((sample != BUF_LEN_MASK) &&
+		    (sample & BUF_REPEAT_MASK) == BUF_REPEAT_BYTE))
+			ir_raw_event_handle(nvt->rdev);
+	}
+
+	if (nvt->pkts) {
+		nvt_dbg("Odd, pkts should be 0 now... (its %u)", nvt->pkts);
+		nvt->pkts = 0;
+	}
+
+	nvt_dbg_verbose("%s done", __func__);
+}
+
+static void nvt_handle_rx_fifo_overrun(struct nvt_dev *nvt)
+{
+	nvt_pr(KERN_WARNING, "RX FIFO overrun detected, flushing data!");
+
+	nvt->pkts = 0;
+	nvt_clear_cir_fifo(nvt);
+	ir_raw_event_reset(nvt->rdev);
+}
+
+/* copy data from hardware rx fifo into driver buffer */
+static void nvt_get_rx_ir_data(struct nvt_dev *nvt)
+{
+	unsigned long flags;
+	u8 fifocount, val;
+	unsigned int b_idx;
+	bool overrun = false;
+	int i;
+
+	/* Get count of how many bytes to read from RX FIFO */
+	fifocount = nvt_cir_reg_read(nvt, CIR_RXFCONT);
+	/* if we get 0xff, probably means the logical dev is disabled */
+	if (fifocount == 0xff)
+		return;
+	/* watch out for a fifo overrun condition */
+	else if (fifocount > RX_BUF_LEN) {
+		overrun = true;
+		fifocount = RX_BUF_LEN;
+	}
+
+	nvt_dbg("attempting to fetch %u bytes from hw rx fifo", fifocount);
+
+	spin_lock_irqsave(&nvt->nvt_lock, flags);
+
+	b_idx = nvt->pkts;
+
+	/* This should never happen, but lets check anyway... */
+	if (b_idx + fifocount > RX_BUF_LEN) {
+		nvt_process_rx_ir_data(nvt);
+		b_idx = 0;
+	}
+
+	/* Read fifocount bytes from CIR Sample RX FIFO register */
+	for (i = 0; i < fifocount; i++) {
+		val = nvt_cir_reg_read(nvt, CIR_SRXFIFO);
+		nvt->buf[b_idx + i] = val;
+	}
+
+	nvt->pkts += fifocount;
+	nvt_dbg("%s: pkts now %d", __func__, nvt->pkts);
+
+	nvt_process_rx_ir_data(nvt);
+
+	if (overrun)
+		nvt_handle_rx_fifo_overrun(nvt);
+
+	spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+}
+
+static void nvt_cir_log_irqs(u8 status, u8 iren)
+{
+	nvt_pr(KERN_INFO, "IRQ 0x%02x (IREN 0x%02x) :%s%s%s%s%s%s%s%s%s",
+		status, iren,
+		status & CIR_IRSTS_RDR	? " RDR"	: "",
+		status & CIR_IRSTS_RTR	? " RTR"	: "",
+		status & CIR_IRSTS_PE	? " PE"		: "",
+		status & CIR_IRSTS_RFO	? " RFO"	: "",
+		status & CIR_IRSTS_TE	? " TE"		: "",
+		status & CIR_IRSTS_TTR	? " TTR"	: "",
+		status & CIR_IRSTS_TFU	? " TFU"	: "",
+		status & CIR_IRSTS_GH	? " GH"		: "",
+		status & ~(CIR_IRSTS_RDR | CIR_IRSTS_RTR | CIR_IRSTS_PE |
+			   CIR_IRSTS_RFO | CIR_IRSTS_TE | CIR_IRSTS_TTR |
+			   CIR_IRSTS_TFU | CIR_IRSTS_GH) ? " ?" : "");
+}
+
+static bool nvt_cir_tx_inactive(struct nvt_dev *nvt)
+{
+	unsigned long flags;
+	bool tx_inactive;
+	u8 tx_state;
+
+	spin_lock_irqsave(&nvt->tx.lock, flags);
+	tx_state = nvt->tx.tx_state;
+	spin_unlock_irqrestore(&nvt->tx.lock, flags);
+
+	tx_inactive = (tx_state == ST_TX_NONE);
+
+	return tx_inactive;
+}
+
+/* interrupt service routine for incoming and outgoing CIR data */
+static irqreturn_t nvt_cir_isr(int irq, void *data)
+{
+	struct nvt_dev *nvt = data;
+	u8 status, iren, cur_state;
+	unsigned long flags;
+
+	nvt_dbg_verbose("%s firing", __func__);
+
+	nvt_efm_enable(nvt);
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
+	nvt_efm_disable(nvt);
+
+	/*
+	 * Get IR Status register contents. Write 1 to ack/clear
+	 *
+	 * bit: reg name      - description
+	 *   7: CIR_IRSTS_RDR - RX Data Ready
+	 *   6: CIR_IRSTS_RTR - RX FIFO Trigger Level Reach
+	 *   5: CIR_IRSTS_PE  - Packet End
+	 *   4: CIR_IRSTS_RFO - RX FIFO Overrun (RDR will also be set)
+	 *   3: CIR_IRSTS_TE  - TX FIFO Empty
+	 *   2: CIR_IRSTS_TTR - TX FIFO Trigger Level Reach
+	 *   1: CIR_IRSTS_TFU - TX FIFO Underrun
+	 *   0: CIR_IRSTS_GH  - Min Length Detected
+	 */
+	status = nvt_cir_reg_read(nvt, CIR_IRSTS);
+	if (!status) {
+		nvt_dbg_verbose("%s exiting, IRSTS 0x0", __func__);
+		nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
+		return IRQ_RETVAL(IRQ_NONE);
+	}
+
+	/* ack/clear all irq flags we've got */
+	nvt_cir_reg_write(nvt, status, CIR_IRSTS);
+	nvt_cir_reg_write(nvt, 0, CIR_IRSTS);
+
+	/* Interrupt may be shared with CIR Wake, bail if CIR not enabled */
+	iren = nvt_cir_reg_read(nvt, CIR_IREN);
+	if (!iren) {
+		nvt_dbg_verbose("%s exiting, CIR not enabled", __func__);
+		return IRQ_RETVAL(IRQ_NONE);
+	}
+
+	if (debug)
+		nvt_cir_log_irqs(status, iren);
+
+	if (status & CIR_IRSTS_RTR) {
+		/* FIXME: add code for study/learn mode */
+		/* We only do rx if not tx'ing */
+		if (nvt_cir_tx_inactive(nvt))
+			nvt_get_rx_ir_data(nvt);
+	}
+
+	if (status & CIR_IRSTS_PE) {
+		if (nvt_cir_tx_inactive(nvt))
+			nvt_get_rx_ir_data(nvt);
+
+		spin_lock_irqsave(&nvt->nvt_lock, flags);
+
+		cur_state = nvt->study_state;
+
+		spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
+		if (cur_state == ST_STUDY_NONE)
+			nvt_clear_cir_fifo(nvt);
+	}
+
+	if (status & CIR_IRSTS_TE)
+		nvt_clear_tx_fifo(nvt);
+
+	if (status & CIR_IRSTS_TTR) {
+		unsigned int pos, count;
+		u8 tmp;
+
+		spin_lock_irqsave(&nvt->tx.lock, flags);
+
+		pos = nvt->tx.cur_buf_num;
+		count = nvt->tx.buf_count;
+
+		/* Write data into the hardware tx fifo while pos < count */
+		if (pos < count) {
+			nvt_cir_reg_write(nvt, nvt->tx.buf[pos], CIR_STXFIFO);
+			nvt->tx.cur_buf_num++;
+		/* Disable TX FIFO Trigger Level Reach (TTR) interrupt */
+		} else {
+			tmp = nvt_cir_reg_read(nvt, CIR_IREN);
+			nvt_cir_reg_write(nvt, tmp & ~CIR_IREN_TTR, CIR_IREN);
+		}
+
+		spin_unlock_irqrestore(&nvt->tx.lock, flags);
+
+	}
+
+	if (status & CIR_IRSTS_TFU) {
+		spin_lock_irqsave(&nvt->tx.lock, flags);
+		if (nvt->tx.tx_state == ST_TX_REPLY) {
+			nvt->tx.tx_state = ST_TX_REQUEST;
+			wake_up(&nvt->tx.queue);
+		}
+		spin_unlock_irqrestore(&nvt->tx.lock, flags);
+	}
+
+	nvt_dbg_verbose("%s done", __func__);
+	return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+/* Interrupt service routine for CIR Wake */
+static irqreturn_t nvt_cir_wake_isr(int irq, void *data)
+{
+	u8 status, iren, val;
+	struct nvt_dev *nvt = data;
+	unsigned long flags;
+
+	nvt_dbg_wake("%s firing", __func__);
+
+	status = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS);
+	if (!status)
+		return IRQ_RETVAL(IRQ_NONE);
+
+	if (status & CIR_WAKE_IRSTS_IR_PENDING)
+		nvt_clear_cir_wake_fifo(nvt);
+
+	nvt_cir_wake_reg_write(nvt, status, CIR_WAKE_IRSTS);
+	nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IRSTS);
+
+	/* Interrupt may be shared with CIR, bail if Wake not enabled */
+	iren = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN);
+	if (!iren) {
+		nvt_dbg_wake("%s exiting, wake not enabled", __func__);
+		return IRQ_RETVAL(IRQ_HANDLED);
+	}
+
+	if ((status & CIR_WAKE_IRSTS_PE) &&
+	    (nvt->wake_state == ST_WAKE_START)) {
+		while (nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX)) {
+			val = nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY);
+			nvt_dbg("setting wake up key: 0x%x", val);
+		}
+
+		nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IREN);
+		spin_lock_irqsave(&nvt->nvt_lock, flags);
+		nvt->wake_state = ST_WAKE_FINISH;
+		spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+	}
+
+	nvt_dbg_wake("%s done", __func__);
+	return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+static void nvt_enable_cir(struct nvt_dev *nvt)
+{
+	/* set function enable flags */
+	nvt_cir_reg_write(nvt, CIR_IRCON_TXEN | CIR_IRCON_RXEN |
+			  CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL,
+			  CIR_IRCON);
+
+	nvt_efm_enable(nvt);
+
+	/* enable the CIR logical device */
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
+	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
+
+	nvt_efm_disable(nvt);
+
+	/* clear all pending interrupts */
+	nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
+
+	/* enable interrupts */
+	nvt_set_cir_iren(nvt);
+}
+
+static void nvt_disable_cir(struct nvt_dev *nvt)
+{
+	/* disable CIR interrupts */
+	nvt_cir_reg_write(nvt, 0, CIR_IREN);
+
+	/* clear any and all pending interrupts */
+	nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
+
+	/* clear all function enable flags */
+	nvt_cir_reg_write(nvt, 0, CIR_IRCON);
+
+	/* clear hardware rx and tx fifos */
+	nvt_clear_cir_fifo(nvt);
+	nvt_clear_tx_fifo(nvt);
+
+	nvt_efm_enable(nvt);
+
+	/* disable the CIR logical device */
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
+	nvt_cr_write(nvt, LOGICAL_DEV_DISABLE, CR_LOGICAL_DEV_EN);
+
+	nvt_efm_disable(nvt);
+}
+
+static int nvt_open(void *data)
+{
+	struct nvt_dev *nvt = (struct nvt_dev *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&nvt->nvt_lock, flags);
+	nvt->in_use = true;
+	nvt_enable_cir(nvt);
+	spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
+	return 0;
+}
+
+static void nvt_close(void *data)
+{
+	struct nvt_dev *nvt = (struct nvt_dev *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&nvt->nvt_lock, flags);
+	nvt->in_use = false;
+	nvt_disable_cir(nvt);
+	spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+}
+
+/* Allocate memory, probe hardware, and initialize everything */
+static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
+{
+	struct nvt_dev *nvt = NULL;
+	struct input_dev *rdev = NULL;
+	struct ir_dev_props *props = NULL;
+	int ret = -ENOMEM;
+
+	nvt = kzalloc(sizeof(struct nvt_dev), GFP_KERNEL);
+	if (!nvt)
+		return ret;
+
+	props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL);
+	if (!props)
+		goto failure;
+
+	/* input device for IR remote (and tx) */
+	rdev = input_allocate_device();
+	if (!rdev)
+		goto failure;
+
+	ret = -ENODEV;
+	/* validate pnp resources */
+	if (!pnp_port_valid(pdev, 0) ||
+	    pnp_port_len(pdev, 0) < CIR_IOREG_LENGTH) {
+		dev_err(&pdev->dev, "IR PNP Port not valid!\n");
+		goto failure;
+	}
+
+	if (!pnp_irq_valid(pdev, 0)) {
+		dev_err(&pdev->dev, "PNP IRQ not valid!\n");
+		goto failure;
+	}
+
+	if (!pnp_port_valid(pdev, 1) ||
+	    pnp_port_len(pdev, 1) < CIR_IOREG_LENGTH) {
+		dev_err(&pdev->dev, "Wake PNP Port not valid!\n");
+		goto failure;
+	}
+
+	nvt->cir_addr = pnp_port_start(pdev, 0);
+	nvt->cir_irq  = pnp_irq(pdev, 0);
+
+	nvt->cir_wake_addr = pnp_port_start(pdev, 1);
+	/* irq is always shared between cir and cir wake */
+	nvt->cir_wake_irq  = nvt->cir_irq;
+
+	nvt->cr_efir = CR_EFIR;
+	nvt->cr_efdr = CR_EFDR;
+
+	spin_lock_init(&nvt->nvt_lock);
+	spin_lock_init(&nvt->tx.lock);
+	init_ir_raw_event(&nvt->rawir);
+
+	ret = -EBUSY;
+	/* now claim resources */
+	if (!request_region(nvt->cir_addr,
+			    CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
+		goto failure;
+
+	if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED,
+			NVT_DRIVER_NAME, (void *)nvt))
+		goto failure;
+
+	if (!request_region(nvt->cir_wake_addr,
+			    CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
+		goto failure;
+
+	if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED,
+			NVT_DRIVER_NAME, (void *)nvt))
+		goto failure;
+
+	pnp_set_drvdata(pdev, nvt);
+	nvt->pdev = pdev;
+
+	init_waitqueue_head(&nvt->tx.queue);
+
+	ret = nvt_hw_detect(nvt);
+	if (ret)
+		goto failure;
+
+	/* Initialize CIR & CIR Wake Logical Devices */
+	nvt_efm_enable(nvt);
+	nvt_cir_ldev_init(nvt);
+	nvt_cir_wake_ldev_init(nvt);
+	nvt_efm_disable(nvt);
+
+	/* Initialize CIR & CIR Wake Config Registers */
+	nvt_cir_regs_init(nvt);
+	nvt_cir_wake_regs_init(nvt);
+
+	/* Set up ir-core props */
+	props->priv = nvt;
+	props->driver_type = RC_DRIVER_IR_RAW;
+	props->allowed_protos = IR_TYPE_ALL;
+	props->open = nvt_open;
+	props->close = nvt_close;
+#if 0
+	props->min_timeout = XYZ;
+	props->max_timeout = XYZ;
+	props->timeout = XYZ;
+	/* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
+	props->rx_resolution = XYZ;
+
+	/* tx bits */
+	props->tx_resolution = XYZ;
+#endif
+	props->tx_ir = nvt_tx_ir;
+	props->s_tx_carrier = nvt_set_tx_carrier;
+
+	rdev->name = "Nuvoton w836x7hg Infrared Remote Transceiver";
+	rdev->id.bustype = BUS_HOST;
+	rdev->id.vendor = PCI_VENDOR_ID_WINBOND2;
+	rdev->id.product = nvt->chip_major;
+	rdev->id.version = nvt->chip_minor;
+
+	nvt->props = props;
+	nvt->rdev = rdev;
+
+	device_set_wakeup_capable(&pdev->dev, 1);
+	device_set_wakeup_enable(&pdev->dev, 1);
+
+	ret = ir_input_register(rdev, RC_MAP_RC6_MCE, props, NVT_DRIVER_NAME);
+	if (ret)
+		goto failure;
+
+	nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n");
+	if (debug) {
+		cir_dump_regs(nvt);
+		cir_wake_dump_regs(nvt);
+	}
+
+	return 0;
+
+failure:
+	if (nvt->cir_irq)
+		free_irq(nvt->cir_irq, nvt);
+	if (nvt->cir_addr)
+		release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
+
+	if (nvt->cir_wake_irq)
+		free_irq(nvt->cir_wake_irq, nvt);
+	if (nvt->cir_wake_addr)
+		release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
+
+	input_free_device(rdev);
+	kfree(props);
+	kfree(nvt);
+
+	return ret;
+}
+
+static void __devexit nvt_remove(struct pnp_dev *pdev)
+{
+	struct nvt_dev *nvt = pnp_get_drvdata(pdev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&nvt->nvt_lock, flags);
+	/* disable CIR */
+	nvt_cir_reg_write(nvt, 0, CIR_IREN);
+	nvt_disable_cir(nvt);
+	/* enable CIR Wake (for IR power-on) */
+	nvt_enable_wake(nvt);
+	spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
+	/* free resources */
+	free_irq(nvt->cir_irq, nvt);
+	free_irq(nvt->cir_wake_irq, nvt);
+	release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
+	release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
+
+	ir_input_unregister(nvt->rdev);
+
+	kfree(nvt->props);
+	kfree(nvt);
+}
+
+static int nvt_suspend(struct pnp_dev *pdev, pm_message_t state)
+{
+	struct nvt_dev *nvt = pnp_get_drvdata(pdev);
+	unsigned long flags;
+
+	nvt_dbg("%s called", __func__);
+
+	/* zero out misc state tracking */
+	spin_lock_irqsave(&nvt->nvt_lock, flags);
+	nvt->study_state = ST_STUDY_NONE;
+	nvt->wake_state = ST_WAKE_NONE;
+	spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
+	spin_lock_irqsave(&nvt->tx.lock, flags);
+	nvt->tx.tx_state = ST_TX_NONE;
+	spin_unlock_irqrestore(&nvt->tx.lock, flags);
+
+	/* disable all CIR interrupts */
+	nvt_cir_reg_write(nvt, 0, CIR_IREN);
+
+	nvt_efm_enable(nvt);
+
+	/* disable cir logical dev */
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
+	nvt_cr_write(nvt, LOGICAL_DEV_DISABLE, CR_LOGICAL_DEV_EN);
+
+	nvt_efm_disable(nvt);
+
+	/* make sure wake is enabled */
+	nvt_enable_wake(nvt);
+
+	return 0;
+}
+
+static int nvt_resume(struct pnp_dev *pdev)
+{
+	int ret = 0;
+	struct nvt_dev *nvt = pnp_get_drvdata(pdev);
+
+	nvt_dbg("%s called", __func__);
+
+	/* open interrupt */
+	nvt_set_cir_iren(nvt);
+
+	/* Enable CIR logical device */
+	nvt_efm_enable(nvt);
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
+	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
+
+	nvt_efm_disable(nvt);
+
+	nvt_cir_regs_init(nvt);
+	nvt_cir_wake_regs_init(nvt);
+
+	return ret;
+}
+
+static void nvt_shutdown(struct pnp_dev *pdev)
+{
+	struct nvt_dev *nvt = pnp_get_drvdata(pdev);
+	nvt_enable_wake(nvt);
+}
+
+static const struct pnp_device_id nvt_ids[] = {
+	{ "WEC0530", 0 },   /* CIR */
+	{ "NTN0530", 0 },   /* CIR for new chip's pnp id*/
+	{ "", 0 },
+};
+
+static struct pnp_driver nvt_driver = {
+	.name		= NVT_DRIVER_NAME,
+	.id_table	= nvt_ids,
+	.flags		= PNP_DRIVER_RES_DO_NOT_CHANGE,
+	.probe		= nvt_probe,
+	.remove		= __devexit_p(nvt_remove),
+	.suspend	= nvt_suspend,
+	.resume		= nvt_resume,
+	.shutdown	= nvt_shutdown,
+};
+
+int nvt_init(void)
+{
+	return pnp_register_driver(&nvt_driver);
+}
+
+void nvt_exit(void)
+{
+	pnp_unregister_driver(&nvt_driver);
+}
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging output");
+
+MODULE_DEVICE_TABLE(pnp, nvt_ids);
+MODULE_DESCRIPTION("Nuvoton W83667HG-A & W83677HG-I CIR driver");
+
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
+MODULE_LICENSE("GPL");
+
+module_init(nvt_init);
+module_exit(nvt_exit);
diff --git a/drivers/media/IR/nuvoton-cir.h b/drivers/media/IR/nuvoton-cir.h
new file mode 100644
index 0000000..62dc530
--- /dev/null
+++ b/drivers/media/IR/nuvoton-cir.h
@@ -0,0 +1,408 @@
+/*
+ * Driver for Nuvoton Technology Corporation w83667hg/w83677hg-i CIR
+ *
+ * Copyright (C) 2010 Jarod Wilson <jarod@redhat.com>
+ * Copyright (C) 2009 Nuvoton PS Team
+ *
+ * Special thanks to Nuvoton for providing hardware, spec sheets and
+ * sample code upon which portions of this driver are based. Indirect
+ * thanks also to Maxim Levitsky, whose ene_ir driver this driver is
+ * modeled after.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <linux/spinlock.h>
+#include <linux/ioctl.h>
+
+/* platform driver name to register */
+#define NVT_DRIVER_NAME "nuvoton-cir"
+
+/* debugging module parameter */
+static int debug;
+
+
+#define nvt_pr(level, text, ...) \
+	printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__)
+
+#define nvt_dbg(text, ...) \
+	if (debug) \
+		printk(KERN_DEBUG \
+			KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+#define nvt_dbg_verbose(text, ...) \
+	if (debug > 1) \
+		printk(KERN_DEBUG \
+			KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+#define nvt_dbg_wake(text, ...) \
+	if (debug > 2) \
+		printk(KERN_DEBUG \
+			KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+
+/*
+ * Original lirc driver said min value of 76, and recommended value of 256
+ * for the buffer length, but then used 2048. Never mind that the size of the
+ * RX FIFO is 32 bytes... So I'm using 32 for RX and 256 for TX atm, but I'm
+ * not sure if maybe that TX value is off by a factor of 8 (bits vs. bytes),
+ * and I don't have TX-capable hardware to test/debug on...
+ */
+#define TX_BUF_LEN 256
+#define RX_BUF_LEN 32
+
+struct nvt_dev {
+	struct pnp_dev *pdev;
+	struct input_dev *rdev;
+	struct ir_dev_props *props;
+	struct ir_raw_event rawir;
+
+	spinlock_t nvt_lock;
+	bool in_use;
+
+	/* for rx */
+	u8 buf[RX_BUF_LEN];
+	unsigned int pkts;
+
+	struct {
+		spinlock_t lock;
+		u8 buf[TX_BUF_LEN];
+		unsigned int buf_count;
+		unsigned int cur_buf_num;
+		wait_queue_head_t queue;
+		u8 tx_state;
+	} tx;
+
+	/* EFER Config register index/data pair */
+	u8 cr_efir;
+	u8 cr_efdr;
+
+	/* hardware I/O settings */
+	unsigned long cir_addr;
+	unsigned long cir_wake_addr;
+	int cir_irq;
+	int cir_wake_irq;
+
+	/* hardware id */
+	u8 chip_major;
+	u8 chip_minor;
+
+	/* hardware features */
+	bool hw_learning_capable;
+	bool hw_tx_capable;
+
+	/* rx settings */
+	bool learning_enabled;
+	bool carrier_detect_enabled;
+
+	/* track cir wake state */
+	u8 wake_state;
+	/* for study */
+	u8 study_state;
+	/* carrier period = 1 / frequency */
+	u32 carrier;
+};
+
+/* study states */
+#define ST_STUDY_NONE      0x0
+#define ST_STUDY_START     0x1
+#define ST_STUDY_CARRIER   0x2
+#define ST_STUDY_ALL_RECV  0x4
+
+/* wake states */
+#define ST_WAKE_NONE	0x0
+#define ST_WAKE_START	0x1
+#define ST_WAKE_FINISH	0x2
+
+/* receive states */
+#define ST_RX_WAIT_7F		0x1
+#define ST_RX_WAIT_HEAD		0x2
+#define ST_RX_WAIT_SILENT_END	0x4
+
+/* send states */
+#define ST_TX_NONE	0x0
+#define ST_TX_REQUEST	0x2
+#define ST_TX_REPLY	0x4
+
+/* buffer packet constants */
+#define BUF_PULSE_BIT	0x80
+#define BUF_LEN_MASK	0x7f
+#define BUF_REPEAT_BYTE	0x70
+#define BUF_REPEAT_MASK	0xf0
+
+/* CIR settings */
+
+/* total length of CIR and CIR WAKE */
+#define CIR_IOREG_LENGTH	0x0f
+
+/* RX limit length, 8 high bits for SLCH, 8 low bits for SLCL (0x7d0 = 2000) */
+#define CIR_RX_LIMIT_COUNT	0x7d0
+
+/* CIR Regs */
+#define CIR_IRCON	0x00
+#define CIR_IRSTS	0x01
+#define CIR_IREN	0x02
+#define CIR_RXFCONT	0x03
+#define CIR_CP		0x04
+#define CIR_CC		0x05
+#define CIR_SLCH	0x06
+#define CIR_SLCL	0x07
+#define CIR_FIFOCON	0x08
+#define CIR_IRFIFOSTS	0x09
+#define CIR_SRXFIFO	0x0a
+#define CIR_TXFCONT	0x0b
+#define CIR_STXFIFO	0x0c
+#define CIR_FCCH	0x0d
+#define CIR_FCCL	0x0e
+#define CIR_IRFSM	0x0f
+
+/* CIR IRCON settings */
+#define CIR_IRCON_RECV	 0x80
+#define CIR_IRCON_WIREN	 0x40
+#define CIR_IRCON_TXEN	 0x20
+#define CIR_IRCON_RXEN	 0x10
+#define CIR_IRCON_WRXINV 0x08
+#define CIR_IRCON_RXINV	 0x04
+
+#define CIR_IRCON_SAMPLE_PERIOD_SEL_1	0x00
+#define CIR_IRCON_SAMPLE_PERIOD_SEL_25	0x01
+#define CIR_IRCON_SAMPLE_PERIOD_SEL_50	0x02
+#define CIR_IRCON_SAMPLE_PERIOD_SEL_100	0x03
+
+/* FIXME: make this a runtime option */
+/* select sample period as 50us */
+#define CIR_IRCON_SAMPLE_PERIOD_SEL	CIR_IRCON_SAMPLE_PERIOD_SEL_50
+
+/* CIR IRSTS settings */
+#define CIR_IRSTS_RDR	0x80
+#define CIR_IRSTS_RTR	0x40
+#define CIR_IRSTS_PE	0x20
+#define CIR_IRSTS_RFO	0x10
+#define CIR_IRSTS_TE	0x08
+#define CIR_IRSTS_TTR	0x04
+#define CIR_IRSTS_TFU	0x02
+#define CIR_IRSTS_GH	0x01
+
+/* CIR IREN settings */
+#define CIR_IREN_RDR	0x80
+#define CIR_IREN_RTR	0x40
+#define CIR_IREN_PE	0x20
+#define CIR_IREN_RFO	0x10
+#define CIR_IREN_TE	0x08
+#define CIR_IREN_TTR	0x04
+#define CIR_IREN_TFU	0x02
+#define CIR_IREN_GH	0x01
+
+/* CIR FIFOCON settings */
+#define CIR_FIFOCON_TXFIFOCLR		0x80
+
+#define CIR_FIFOCON_TX_TRIGGER_LEV_31	0x00
+#define CIR_FIFOCON_TX_TRIGGER_LEV_24	0x10
+#define CIR_FIFOCON_TX_TRIGGER_LEV_16	0x20
+#define CIR_FIFOCON_TX_TRIGGER_LEV_8	0x30
+
+/* FIXME: make this a runtime option */
+/* select TX trigger level as 16 */
+#define CIR_FIFOCON_TX_TRIGGER_LEV	CIR_FIFOCON_TX_TRIGGER_LEV_16
+
+#define CIR_FIFOCON_RXFIFOCLR		0x08
+
+#define CIR_FIFOCON_RX_TRIGGER_LEV_1	0x00
+#define CIR_FIFOCON_RX_TRIGGER_LEV_8	0x01
+#define CIR_FIFOCON_RX_TRIGGER_LEV_16	0x02
+#define CIR_FIFOCON_RX_TRIGGER_LEV_24	0x03
+
+/* FIXME: make this a runtime option */
+/* select RX trigger level as 24 */
+#define CIR_FIFOCON_RX_TRIGGER_LEV	CIR_FIFOCON_RX_TRIGGER_LEV_24
+
+/* CIR IRFIFOSTS settings */
+#define CIR_IRFIFOSTS_IR_PENDING	0x80
+#define CIR_IRFIFOSTS_RX_GS		0x40
+#define CIR_IRFIFOSTS_RX_FTA		0x20
+#define CIR_IRFIFOSTS_RX_EMPTY		0x10
+#define CIR_IRFIFOSTS_RX_FULL		0x08
+#define CIR_IRFIFOSTS_TX_FTA		0x04
+#define CIR_IRFIFOSTS_TX_EMPTY		0x02
+#define CIR_IRFIFOSTS_TX_FULL		0x01
+
+
+/* CIR WAKE UP Regs */
+#define CIR_WAKE_IRCON			0x00
+#define CIR_WAKE_IRSTS			0x01
+#define CIR_WAKE_IREN			0x02
+#define CIR_WAKE_FIFO_CMP_DEEP		0x03
+#define CIR_WAKE_FIFO_CMP_TOL		0x04
+#define CIR_WAKE_FIFO_COUNT		0x05
+#define CIR_WAKE_SLCH			0x06
+#define CIR_WAKE_SLCL			0x07
+#define CIR_WAKE_FIFOCON		0x08
+#define CIR_WAKE_SRXFSTS		0x09
+#define CIR_WAKE_SAMPLE_RX_FIFO		0x0a
+#define CIR_WAKE_WR_FIFO_DATA		0x0b
+#define CIR_WAKE_RD_FIFO_ONLY		0x0c
+#define CIR_WAKE_RD_FIFO_ONLY_IDX	0x0d
+#define CIR_WAKE_FIFO_IGNORE		0x0e
+#define CIR_WAKE_IRFSM			0x0f
+
+/* CIR WAKE UP IRCON settings */
+#define CIR_WAKE_IRCON_DEC_RST		0x80
+#define CIR_WAKE_IRCON_MODE1		0x40
+#define CIR_WAKE_IRCON_MODE0		0x20
+#define CIR_WAKE_IRCON_RXEN		0x10
+#define CIR_WAKE_IRCON_R		0x08
+#define CIR_WAKE_IRCON_RXINV		0x04
+
+/* FIXME/jarod: make this a runtime option */
+/* select a same sample period like cir register */
+#define CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL	CIR_IRCON_SAMPLE_PERIOD_SEL_50
+
+/* CIR WAKE IRSTS Bits */
+#define CIR_WAKE_IRSTS_RDR		0x80
+#define CIR_WAKE_IRSTS_RTR		0x40
+#define CIR_WAKE_IRSTS_PE		0x20
+#define CIR_WAKE_IRSTS_RFO		0x10
+#define CIR_WAKE_IRSTS_GH		0x08
+#define CIR_WAKE_IRSTS_IR_PENDING	0x01
+
+/* CIR WAKE UP IREN Bits */
+#define CIR_WAKE_IREN_RDR		0x80
+#define CIR_WAKE_IREN_RTR		0x40
+#define CIR_WAKE_IREN_PE		0x20
+#define CIR_WAKE_IREN_RFO		0x10
+#define CIR_WAKE_IREN_TE		0x08
+#define CIR_WAKE_IREN_TTR		0x04
+#define CIR_WAKE_IREN_TFU		0x02
+#define CIR_WAKE_IREN_GH		0x01
+
+/* CIR WAKE FIFOCON settings */
+#define CIR_WAKE_FIFOCON_RXFIFOCLR	0x08
+
+#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_67	0x00
+#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_66	0x01
+#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_65	0x02
+#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_64	0x03
+
+/* FIXME: make this a runtime option */
+/* select WAKE UP RX trigger level as 67 */
+#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV	CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_67
+
+/* CIR WAKE SRXFSTS settings */
+#define CIR_WAKE_IRFIFOSTS_RX_GS	0x80
+#define CIR_WAKE_IRFIFOSTS_RX_FTA	0x40
+#define CIR_WAKE_IRFIFOSTS_RX_EMPTY	0x20
+#define CIR_WAKE_IRFIFOSTS_RX_FULL	0x10
+
+/* CIR Wake FIFO buffer is 67 bytes long */
+#define CIR_WAKE_FIFO_LEN		67
+/* CIR Wake byte comparison tolerance */
+#define CIR_WAKE_CMP_TOLERANCE		5
+
+/*
+ * Extended Function Enable Registers:
+ *  Extended Function Index Register
+ *  Extended Function Data Register
+ */
+#define CR_EFIR			0x2e
+#define CR_EFDR			0x2f
+
+/* Possible alternate EFER values, depends on how the chip is wired */
+#define CR_EFIR2		0x4e
+#define CR_EFDR2		0x4f
+
+/* Extended Function Mode enable/disable magic values */
+#define EFER_EFM_ENABLE		0x87
+#define EFER_EFM_DISABLE	0xaa
+
+/* Chip IDs found in CR_CHIP_ID_{HI,LO} */
+#define CHIP_ID_HIGH		0xb4
+#define CHIP_ID_LOW		0x72
+#define CHIP_ID_LOW2		0x73
+
+/* Config regs we need to care about */
+#define CR_SOFTWARE_RESET	0x02
+#define CR_LOGICAL_DEV_SEL	0x07
+#define CR_CHIP_ID_HI		0x20
+#define CR_CHIP_ID_LO		0x21
+#define CR_DEV_POWER_DOWN	0x22 /* bit 2 is CIR power, default power on */
+#define CR_OUTPUT_PIN_SEL	0x27
+#define CR_LOGICAL_DEV_EN	0x30 /* valid for all logical devices */
+/* next three regs valid for both the CIR and CIR_WAKE logical devices */
+#define CR_CIR_BASE_ADDR_HI	0x60
+#define CR_CIR_BASE_ADDR_LO	0x61
+#define CR_CIR_IRQ_RSRC		0x70
+/* next three regs valid only for ACPI logical dev */
+#define CR_ACPI_CIR_WAKE	0xe0
+#define CR_ACPI_IRQ_EVENTS	0xf6
+#define CR_ACPI_IRQ_EVENTS2	0xf7
+
+/* Logical devices that we need to care about */
+#define LOGICAL_DEV_LPT		0x01
+#define LOGICAL_DEV_CIR		0x06
+#define LOGICAL_DEV_ACPI	0x0a
+#define LOGICAL_DEV_CIR_WAKE	0x0e
+
+#define LOGICAL_DEV_DISABLE	0x00
+#define LOGICAL_DEV_ENABLE	0x01
+
+#define CIR_WAKE_ENABLE_BIT	0x08
+#define CIR_INTR_MOUSE_IRQ_BIT	0x80
+#define PME_INTR_CIR_PASS_BIT	0x08
+
+#define OUTPUT_PIN_SEL_MASK	0xbc
+#define OUTPUT_ENABLE_CIR	0x01 /* Pin95=CIRRX, Pin96=CIRTX1 */
+#define OUTPUT_ENABLE_CIRWB	0x40 /* enable wide-band sensor */
+
+/* MCE CIR signal length, related on sample period */
+
+/* MCE CIR controller signal length: about 43ms
+ * 43ms / 50us (sample period) * 0.85 (inaccuracy)
+ */
+#define CONTROLLER_BUF_LEN_MIN 830
+
+/* MCE CIR keyboard signal length: about 26ms
+ * 26ms / 50us (sample period) * 0.85 (inaccuracy)
+ */
+#define KEYBOARD_BUF_LEN_MAX 650
+#define KEYBOARD_BUF_LEN_MIN 610
+
+/* MCE CIR mouse signal length: about 24ms
+ * 24ms / 50us (sample period) * 0.85 (inaccuracy)
+ */
+#define MOUSE_BUF_LEN_MIN 565
+
+#define CIR_SAMPLE_PERIOD 50
+#define CIR_SAMPLE_LOW_INACCURACY 0.85
+
+/* MAX silence time that driver will sent to lirc */
+#define MAX_SILENCE_TIME 60000
+
+#if CIR_IRCON_SAMPLE_PERIOD_SEL == CIR_IRCON_SAMPLE_PERIOD_SEL_100
+#define SAMPLE_PERIOD 100
+
+#elif CIR_IRCON_SAMPLE_PERIOD_SEL == CIR_IRCON_SAMPLE_PERIOD_SEL_50
+#define SAMPLE_PERIOD 50
+
+#elif CIR_IRCON_SAMPLE_PERIOD_SEL == CIR_IRCON_SAMPLE_PERIOD_SEL_25
+#define SAMPLE_PERIOD 25
+
+#else
+#define SAMPLE_PERIOD 1
+#endif
+
+/* as VISTA MCE definition, valid carrier value */
+#define MAX_CARRIER 60000
+#define MIN_CARRIER 30000
diff --git a/drivers/media/IR/streamzap.c b/drivers/media/IR/streamzap.c
index 058e29f..548381c 100644
--- a/drivers/media/IR/streamzap.c
+++ b/drivers/media/IR/streamzap.c
@@ -38,7 +38,7 @@
 #include <linux/input.h>
 #include <media/ir-core.h>
 
-#define DRIVER_VERSION	"1.60"
+#define DRIVER_VERSION	"1.61"
 #define DRIVER_NAME	"streamzap"
 #define DRIVER_DESC	"Streamzap Remote Control driver"
 
@@ -61,14 +61,21 @@
 
 MODULE_DEVICE_TABLE(usb, streamzap_table);
 
-#define STREAMZAP_PULSE_MASK 0xf0
-#define STREAMZAP_SPACE_MASK 0x0f
-#define STREAMZAP_TIMEOUT    0xff
-#define STREAMZAP_RESOLUTION 256
+#define SZ_PULSE_MASK 0xf0
+#define SZ_SPACE_MASK 0x0f
+#define SZ_TIMEOUT    0xff
+#define SZ_RESOLUTION 256
 
 /* number of samples buffered */
 #define SZ_BUF_LEN 128
 
+/* from ir-rc5-sz-decoder.c */
+#ifdef CONFIG_IR_RC5_SZ_DECODER_MODULE
+#define load_rc5_sz_decode()    request_module("ir-rc5-sz-decoder")
+#else
+#define load_rc5_sz_decode()    0
+#endif
+
 enum StreamzapDecoderState {
 	PulseSpace,
 	FullPulse,
@@ -81,7 +88,6 @@
 
 	/* ir-core */
 	struct ir_dev_props *props;
-	struct ir_raw_event rawir;
 
 	/* core device info */
 	struct device *dev;
@@ -98,17 +104,6 @@
 	dma_addr_t		dma_in;
 	unsigned int		buf_in_len;
 
-	/* timer used to support delay buffering */
-	struct timer_list	delay_timer;
-	bool			timer_running;
-	spinlock_t		timer_lock;
-	struct timer_list	flush_timer;
-	bool			flush;
-
-	/* delay buffer */
-	struct kfifo fifo;
-	bool fifo_initialized;
-
 	/* track what state we're in */
 	enum StreamzapDecoderState decoder_state;
 	/* tracks whether we are currently receiving some signal */
@@ -118,7 +113,7 @@
 	/* start time of signal; necessary for gap tracking */
 	struct timeval		signal_last;
 	struct timeval		signal_start;
-	/* bool			timeout_enabled; */
+	bool			timeout_enabled;
 
 	char			name[128];
 	char			phys[64];
@@ -143,122 +138,16 @@
 	.id_table =	streamzap_table,
 };
 
-static void streamzap_stop_timer(struct streamzap_ir *sz)
+static void sz_push(struct streamzap_ir *sz, struct ir_raw_event rawir)
 {
-	unsigned long flags;
-
-	spin_lock_irqsave(&sz->timer_lock, flags);
-	if (sz->timer_running) {
-		sz->timer_running = false;
-		spin_unlock_irqrestore(&sz->timer_lock, flags);
-		del_timer_sync(&sz->delay_timer);
-	} else {
-		spin_unlock_irqrestore(&sz->timer_lock, flags);
-	}
-}
-
-static void streamzap_flush_timeout(unsigned long arg)
-{
-	struct streamzap_ir *sz = (struct streamzap_ir *)arg;
-
-	dev_info(sz->dev, "%s: callback firing\n", __func__);
-
-	/* finally start accepting data */
-	sz->flush = false;
-}
-
-static void streamzap_delay_timeout(unsigned long arg)
-{
-	struct streamzap_ir *sz = (struct streamzap_ir *)arg;
-	struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
-	unsigned long flags;
-	int len, ret;
-	static unsigned long delay;
-	bool wake = false;
-
-	/* deliver data every 10 ms */
-	delay = msecs_to_jiffies(10);
-
-	spin_lock_irqsave(&sz->timer_lock, flags);
-
-	if (kfifo_len(&sz->fifo) > 0) {
-		ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
-		if (ret != sizeof(rawir))
-			dev_err(sz->dev, "Problem w/kfifo_out...\n");
-		ir_raw_event_store(sz->idev, &rawir);
-		wake = true;
-	}
-
-	len = kfifo_len(&sz->fifo);
-	if (len > 0) {
-		while ((len < SZ_BUF_LEN / 2) &&
-		       (len < SZ_BUF_LEN * sizeof(int))) {
-			ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
-			if (ret != sizeof(rawir))
-				dev_err(sz->dev, "Problem w/kfifo_out...\n");
-			ir_raw_event_store(sz->idev, &rawir);
-			wake = true;
-			len = kfifo_len(&sz->fifo);
-		}
-		if (sz->timer_running)
-			mod_timer(&sz->delay_timer, jiffies + delay);
-
-	} else {
-		sz->timer_running = false;
-	}
-
-	if (wake)
-		ir_raw_event_handle(sz->idev);
-
-	spin_unlock_irqrestore(&sz->timer_lock, flags);
-}
-
-static void streamzap_flush_delay_buffer(struct streamzap_ir *sz)
-{
-	struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
-	bool wake = false;
-	int ret;
-
-	while (kfifo_len(&sz->fifo) > 0) {
-		ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
-		if (ret != sizeof(rawir))
-			dev_err(sz->dev, "Problem w/kfifo_out...\n");
-		ir_raw_event_store(sz->idev, &rawir);
-		wake = true;
-	}
-
-	if (wake)
-		ir_raw_event_handle(sz->idev);
-}
-
-static void sz_push(struct streamzap_ir *sz)
-{
-	struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
-	unsigned long flags;
-	int ret;
-
-	spin_lock_irqsave(&sz->timer_lock, flags);
-	if (kfifo_len(&sz->fifo) >= sizeof(int) * SZ_BUF_LEN) {
-		ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
-		if (ret != sizeof(rawir))
-			dev_err(sz->dev, "Problem w/kfifo_out...\n");
-		ir_raw_event_store(sz->idev, &rawir);
-	}
-
-	kfifo_in(&sz->fifo, &sz->rawir, sizeof(rawir));
-
-	if (!sz->timer_running) {
-		sz->delay_timer.expires = jiffies + (HZ / 10);
-		add_timer(&sz->delay_timer);
-		sz->timer_running = true;
-	}
-
-	spin_unlock_irqrestore(&sz->timer_lock, flags);
+	ir_raw_event_store(sz->idev, &rawir);
 }
 
 static void sz_push_full_pulse(struct streamzap_ir *sz,
 			       unsigned char value)
 {
+	DEFINE_IR_RAW_EVENT(rawir);
+
 	if (sz->idle) {
 		long deltv;
 
@@ -266,57 +155,59 @@
 		do_gettimeofday(&sz->signal_start);
 
 		deltv = sz->signal_start.tv_sec - sz->signal_last.tv_sec;
-		sz->rawir.pulse = false;
+		rawir.pulse = false;
 		if (deltv > 15) {
 			/* really long time */
-			sz->rawir.duration = IR_MAX_DURATION;
+			rawir.duration = IR_MAX_DURATION;
 		} else {
-			sz->rawir.duration = (int)(deltv * 1000000 +
+			rawir.duration = (int)(deltv * 1000000 +
 				sz->signal_start.tv_usec -
 				sz->signal_last.tv_usec);
-			sz->rawir.duration -= sz->sum;
-			sz->rawir.duration *= 1000;
-			sz->rawir.duration &= IR_MAX_DURATION;
+			rawir.duration -= sz->sum;
+			rawir.duration *= 1000;
+			rawir.duration &= IR_MAX_DURATION;
 		}
-		dev_dbg(sz->dev, "ls %u\n", sz->rawir.duration);
-		sz_push(sz);
+		dev_dbg(sz->dev, "ls %u\n", rawir.duration);
+		sz_push(sz, rawir);
 
-		sz->idle = 0;
+		sz->idle = false;
 		sz->sum = 0;
 	}
 
-	sz->rawir.pulse = true;
-	sz->rawir.duration = ((int) value) * STREAMZAP_RESOLUTION;
-	sz->rawir.duration += STREAMZAP_RESOLUTION / 2;
-	sz->sum += sz->rawir.duration;
-	sz->rawir.duration *= 1000;
-	sz->rawir.duration &= IR_MAX_DURATION;
-	dev_dbg(sz->dev, "p %u\n", sz->rawir.duration);
-	sz_push(sz);
+	rawir.pulse = true;
+	rawir.duration = ((int) value) * SZ_RESOLUTION;
+	rawir.duration += SZ_RESOLUTION / 2;
+	sz->sum += rawir.duration;
+	rawir.duration *= 1000;
+	rawir.duration &= IR_MAX_DURATION;
+	dev_dbg(sz->dev, "p %u\n", rawir.duration);
+	sz_push(sz, rawir);
 }
 
 static void sz_push_half_pulse(struct streamzap_ir *sz,
 			       unsigned char value)
 {
-	sz_push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK) >> 4);
+	sz_push_full_pulse(sz, (value & SZ_PULSE_MASK) >> 4);
 }
 
 static void sz_push_full_space(struct streamzap_ir *sz,
 			       unsigned char value)
 {
-	sz->rawir.pulse = false;
-	sz->rawir.duration = ((int) value) * STREAMZAP_RESOLUTION;
-	sz->rawir.duration += STREAMZAP_RESOLUTION / 2;
-	sz->sum += sz->rawir.duration;
-	sz->rawir.duration *= 1000;
-	dev_dbg(sz->dev, "s %u\n", sz->rawir.duration);
-	sz_push(sz);
+	DEFINE_IR_RAW_EVENT(rawir);
+
+	rawir.pulse = false;
+	rawir.duration = ((int) value) * SZ_RESOLUTION;
+	rawir.duration += SZ_RESOLUTION / 2;
+	sz->sum += rawir.duration;
+	rawir.duration *= 1000;
+	dev_dbg(sz->dev, "s %u\n", rawir.duration);
+	sz_push(sz, rawir);
 }
 
 static void sz_push_half_space(struct streamzap_ir *sz,
 			       unsigned long value)
 {
-	sz_push_full_space(sz, value & STREAMZAP_SPACE_MASK);
+	sz_push_full_space(sz, value & SZ_SPACE_MASK);
 }
 
 /**
@@ -330,10 +221,8 @@
 	struct streamzap_ir *sz;
 	unsigned int i;
 	int len;
-	#if 0
-	static int timeout = (((STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION) &
+	static int timeout = (((SZ_TIMEOUT * SZ_RESOLUTION * 1000) &
 				IR_MAX_DURATION) | 0x03000000);
-	#endif
 
 	if (!urb)
 		return;
@@ -356,57 +245,53 @@
 	}
 
 	dev_dbg(sz->dev, "%s: received urb, len %d\n", __func__, len);
-	if (!sz->flush) {
-		for (i = 0; i < urb->actual_length; i++) {
-			dev_dbg(sz->dev, "%d: %x\n", i,
-				(unsigned char)sz->buf_in[i]);
-			switch (sz->decoder_state) {
-			case PulseSpace:
-				if ((sz->buf_in[i] & STREAMZAP_PULSE_MASK) ==
-				    STREAMZAP_PULSE_MASK) {
-					sz->decoder_state = FullPulse;
-					continue;
-				} else if ((sz->buf_in[i] & STREAMZAP_SPACE_MASK)
-					   == STREAMZAP_SPACE_MASK) {
-					sz_push_half_pulse(sz, sz->buf_in[i]);
-					sz->decoder_state = FullSpace;
-					continue;
-				} else {
-					sz_push_half_pulse(sz, sz->buf_in[i]);
-					sz_push_half_space(sz, sz->buf_in[i]);
-				}
-				break;
-			case FullPulse:
-				sz_push_full_pulse(sz, sz->buf_in[i]);
-				sz->decoder_state = IgnorePulse;
-				break;
-			case FullSpace:
-				if (sz->buf_in[i] == STREAMZAP_TIMEOUT) {
-					sz->idle = 1;
-					streamzap_stop_timer(sz);
-					#if 0
-					if (sz->timeout_enabled) {
-						sz->rawir.pulse = false;
-						sz->rawir.duration = timeout;
-						sz->rawir.duration *= 1000;
-						sz_push(sz);
-					}
-					#endif
-					streamzap_flush_delay_buffer(sz);
-				} else
-					sz_push_full_space(sz, sz->buf_in[i]);
-				sz->decoder_state = PulseSpace;
-				break;
-			case IgnorePulse:
-				if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
-				    STREAMZAP_SPACE_MASK) {
-					sz->decoder_state = FullSpace;
-					continue;
-				}
+	for (i = 0; i < len; i++) {
+		dev_dbg(sz->dev, "sz idx %d: %x\n",
+			i, (unsigned char)sz->buf_in[i]);
+		switch (sz->decoder_state) {
+		case PulseSpace:
+			if ((sz->buf_in[i] & SZ_PULSE_MASK) ==
+				SZ_PULSE_MASK) {
+				sz->decoder_state = FullPulse;
+				continue;
+			} else if ((sz->buf_in[i] & SZ_SPACE_MASK)
+					== SZ_SPACE_MASK) {
+				sz_push_half_pulse(sz, sz->buf_in[i]);
+				sz->decoder_state = FullSpace;
+				continue;
+			} else {
+				sz_push_half_pulse(sz, sz->buf_in[i]);
 				sz_push_half_space(sz, sz->buf_in[i]);
-				sz->decoder_state = PulseSpace;
-				break;
 			}
+			break;
+		case FullPulse:
+			sz_push_full_pulse(sz, sz->buf_in[i]);
+			sz->decoder_state = IgnorePulse;
+			break;
+		case FullSpace:
+			if (sz->buf_in[i] == SZ_TIMEOUT) {
+				DEFINE_IR_RAW_EVENT(rawir);
+
+				rawir.pulse = false;
+				rawir.duration = timeout;
+				sz->idle = true;
+				if (sz->timeout_enabled)
+					sz_push(sz, rawir);
+				ir_raw_event_handle(sz->idev);
+			} else {
+				sz_push_full_space(sz, sz->buf_in[i]);
+			}
+			sz->decoder_state = PulseSpace;
+			break;
+		case IgnorePulse:
+			if ((sz->buf_in[i] & SZ_SPACE_MASK) ==
+				SZ_SPACE_MASK) {
+				sz->decoder_state = FullSpace;
+				continue;
+			}
+			sz_push_half_space(sz, sz->buf_in[i]);
+			sz->decoder_state = PulseSpace;
+			break;
 		}
 	}
 
@@ -446,12 +331,11 @@
 
 	props->priv = sz;
 	props->driver_type = RC_DRIVER_IR_RAW;
-	/* FIXME: not sure about supported protocols, check on this */
-	props->allowed_protos = IR_TYPE_RC5 | IR_TYPE_RC6;
+	props->allowed_protos = IR_TYPE_ALL;
 
 	sz->props = props;
 
-	ret = ir_input_register(idev, RC_MAP_RC5_STREAMZAP, props, DRIVER_NAME);
+	ret = ir_input_register(idev, RC_MAP_STREAMZAP, props, DRIVER_NAME);
 	if (ret < 0) {
 		dev_err(dev, "remote input device register failed\n");
 		goto irdev_failed;
@@ -467,29 +351,6 @@
 	return NULL;
 }
 
-static int streamzap_delay_buf_init(struct streamzap_ir *sz)
-{
-	int ret;
-
-	ret = kfifo_alloc(&sz->fifo, sizeof(int) * SZ_BUF_LEN,
-			  GFP_KERNEL);
-	if (ret == 0)
-		sz->fifo_initialized = 1;
-
-	return ret;
-}
-
-static void streamzap_start_flush_timer(struct streamzap_ir *sz)
-{
-	sz->flush_timer.expires = jiffies + HZ;
-	sz->flush = true;
-	add_timer(&sz->flush_timer);
-
-	sz->urb_in->dev = sz->usbdev;
-	if (usb_submit_urb(sz->urb_in, GFP_ATOMIC))
-		dev_err(sz->dev, "urb submit failed\n");
-}
-
 /**
  *	streamzap_probe
  *
@@ -575,35 +436,21 @@
 		snprintf(name + strlen(name), sizeof(name) - strlen(name),
 			 " %s", buf);
 
-	retval = streamzap_delay_buf_init(sz);
-	if (retval) {
-		dev_err(&intf->dev, "%s: delay buffer init failed\n", __func__);
-		goto free_urb_in;
-	}
-
 	sz->idev = streamzap_init_input_dev(sz);
 	if (!sz->idev)
 		goto input_dev_fail;
 
 	sz->idle = true;
 	sz->decoder_state = PulseSpace;
+	/* FIXME: don't yet have a way to set this */
+	sz->timeout_enabled = true;
 	#if 0
 	/* not yet supported, depends on patches from maxim */
 	/* see also: LIRC_GET_REC_RESOLUTION and LIRC_SET_REC_TIMEOUT */
-	sz->timeout_enabled = false;
-	sz->min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION * 1000;
-	sz->max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION * 1000;
+	sz->min_timeout = SZ_TIMEOUT * SZ_RESOLUTION * 1000;
+	sz->max_timeout = SZ_TIMEOUT * SZ_RESOLUTION * 1000;
 	#endif
 
-	init_timer(&sz->delay_timer);
-	sz->delay_timer.function = streamzap_delay_timeout;
-	sz->delay_timer.data = (unsigned long)sz;
-	spin_lock_init(&sz->timer_lock);
-
-	init_timer(&sz->flush_timer);
-	sz->flush_timer.function = streamzap_flush_timeout;
-	sz->flush_timer.data = (unsigned long)sz;
-
 	do_gettimeofday(&sz->signal_start);
 
 	/* Complete final initialisations */
@@ -615,16 +462,18 @@
 
 	usb_set_intfdata(intf, sz);
 
-	streamzap_start_flush_timer(sz);
+	if (usb_submit_urb(sz->urb_in, GFP_ATOMIC))
+		dev_err(sz->dev, "urb submit failed\n");
 
 	dev_info(sz->dev, "Registered %s on usb%d:%d\n", name,
 		 usbdev->bus->busnum, usbdev->devnum);
 
+	/* Load the streamzap not-quite-rc5 decoder too */
+	load_rc5_sz_decode();
+
 	return 0;
 
 input_dev_fail:
-	kfifo_free(&sz->fifo);
-free_urb_in:
 	usb_free_urb(sz->urb_in);
 free_buf_in:
 	usb_free_coherent(usbdev, maxp, sz->buf_in, sz->dma_in);
@@ -654,13 +503,6 @@
 	if (!sz)
 		return;
 
-	if (sz->flush) {
-		sz->flush = false;
-		del_timer_sync(&sz->flush_timer);
-	}
-
-	streamzap_stop_timer(sz);
-
 	sz->usbdev = NULL;
 	ir_input_unregister(sz->idev);
 	usb_kill_urb(sz->urb_in);
@@ -674,13 +516,6 @@
 {
 	struct streamzap_ir *sz = usb_get_intfdata(intf);
 
-	if (sz->flush) {
-		sz->flush = false;
-		del_timer_sync(&sz->flush_timer);
-	}
-
-	streamzap_stop_timer(sz);
-
 	usb_kill_urb(sz->urb_in);
 
 	return 0;
@@ -690,13 +525,6 @@
 {
 	struct streamzap_ir *sz = usb_get_intfdata(intf);
 
-	if (sz->fifo_initialized)
-		kfifo_reset(&sz->fifo);
-
-	sz->flush_timer.expires = jiffies + HZ;
-	sz->flush = true;
-	add_timer(&sz->flush_timer);
-
 	if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
 		dev_err(sz->dev, "Error sumbiting urb\n");
 		return -EIO;
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
index 4da2a54c..e3fedc6 100644
--- a/drivers/media/common/saa7146_fops.c
+++ b/drivers/media/common/saa7146_fops.c
@@ -56,7 +56,7 @@
 
 	BUG_ON(in_interrupt());
 
-	videobuf_waiton(&buf->vb,0,0);
+	videobuf_waiton(q, &buf->vb, 0, 0);
 	videobuf_dma_unmap(q->dev, dma);
 	videobuf_dma_free(dma);
 	buf->vb.state = VIDEOBUF_NEEDS_INIT;
diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c
index 48cb154..3d88542 100644
--- a/drivers/media/common/saa7146_i2c.c
+++ b/drivers/media/common/saa7146_i2c.c
@@ -414,7 +414,6 @@
 		i2c_adapter->dev.parent    = &dev->pci->dev;
 		i2c_adapter->algo	   = &saa7146_algo;
 		i2c_adapter->algo_data     = NULL;
-		i2c_adapter->id		   = I2C_HW_SAA7146;
 		i2c_adapter->timeout = SAA7146_I2C_TIMEOUT;
 		i2c_adapter->retries = SAA7146_I2C_RETRIES;
 	}
diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c
index 8224c30..2d4533a 100644
--- a/drivers/media/common/saa7146_vbi.c
+++ b/drivers/media/common/saa7146_vbi.c
@@ -412,7 +412,7 @@
 			    V4L2_BUF_TYPE_VBI_CAPTURE,
 			    V4L2_FIELD_SEQ_TB, // FIXME: does this really work?
 			    sizeof(struct saa7146_buf),
-			    file);
+			    file, NULL);
 
 	init_timer(&fh->vbi_read_timeout);
 	fh->vbi_read_timeout.function = vbi_read_timeout;
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
index a212a91..741c573 100644
--- a/drivers/media/common/saa7146_video.c
+++ b/drivers/media/common/saa7146_video.c
@@ -1386,7 +1386,7 @@
 			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
 			    V4L2_FIELD_INTERLACED,
 			    sizeof(struct saa7146_buf),
-			    file);
+			    file, NULL);
 
 	return 0;
 }
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig
index b3ed5da..2385e6c 100644
--- a/drivers/media/common/tuners/Kconfig
+++ b/drivers/media/common/tuners/Kconfig
@@ -179,4 +179,11 @@
 	help
 	  A driver for the silicon tuner MAX2165 from Maxim.
 
+config MEDIA_TUNER_TDA18218
+	tristate "NXP TDA18218 silicon tuner"
+	depends on VIDEO_MEDIA && I2C
+	default m if MEDIA_TUNER_CUSTOMISE
+	help
+	  NXP TDA18218 silicon tuner driver.
+
 endif # MEDIA_TUNER_CUSTOMISE
diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile
index a543852..96da03d 100644
--- a/drivers/media/common/tuners/Makefile
+++ b/drivers/media/common/tuners/Makefile
@@ -24,6 +24,7 @@
 obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o
 obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o
 obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
+obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
 
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/common/tuners/tda18218.c b/drivers/media/common/tuners/tda18218.c
new file mode 100644
index 0000000..8da1fde
--- /dev/null
+++ b/drivers/media/common/tuners/tda18218.c
@@ -0,0 +1,334 @@
+/*
+ * NXP TDA18218HN silicon tuner driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "tda18218.h"
+#include "tda18218_priv.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+/* write multiple registers */
+static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
+{
+	int ret;
+	u8 buf[1+len], quotient, remainder, i, msg_len, msg_len_max;
+	struct i2c_msg msg[1] = {
+		{
+			.addr = priv->cfg->i2c_address,
+			.flags = 0,
+			.buf = buf,
+		}
+	};
+
+	msg_len_max = priv->cfg->i2c_wr_max - 1;
+	quotient = len / msg_len_max;
+	remainder = len % msg_len_max;
+	msg_len = msg_len_max;
+	for (i = 0; (i <= quotient && remainder); i++) {
+		if (i == quotient)  /* set len of the last msg */
+			msg_len = remainder;
+
+		msg[0].len = msg_len + 1;
+		buf[0] = reg + i * msg_len_max;
+		memcpy(&buf[1], &val[i * msg_len_max], msg_len);
+
+		ret = i2c_transfer(priv->i2c, msg, 1);
+		if (ret != 1)
+			break;
+	}
+
+	if (ret == 1) {
+		ret = 0;
+	} else {
+		warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len);
+		ret = -EREMOTEIO;
+	}
+
+	return ret;
+}
+
+/* read multiple registers */
+static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
+{
+	int ret;
+	u8 buf[reg+len]; /* we must start read always from reg 0x00 */
+	struct i2c_msg msg[2] = {
+		{
+			.addr = priv->cfg->i2c_address,
+			.flags = 0,
+			.len = 1,
+			.buf = "\x00",
+		}, {
+			.addr = priv->cfg->i2c_address,
+			.flags = I2C_M_RD,
+			.len = sizeof(buf),
+			.buf = buf,
+		}
+	};
+
+	ret = i2c_transfer(priv->i2c, msg, 2);
+	if (ret == 2) {
+		memcpy(val, &buf[reg], len);
+		ret = 0;
+	} else {
+		warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len);
+		ret = -EREMOTEIO;
+	}
+
+	return ret;
+}
+
+/* write single register */
+static int tda18218_wr_reg(struct tda18218_priv *priv, u8 reg, u8 val)
+{
+	return tda18218_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+
+static int tda18218_rd_reg(struct tda18218_priv *priv, u8 reg, u8 *val)
+{
+	return tda18218_rd_regs(priv, reg, val, 1);
+}
+
+static int tda18218_set_params(struct dvb_frontend *fe,
+	struct dvb_frontend_parameters *params)
+{
+	struct tda18218_priv *priv = fe->tuner_priv;
+	int ret;
+	u8 buf[3], i, BP_Filter, LP_Fc;
+	u32 LO_Frac;
+	/* TODO: find out correct AGC algorithm */
+	u8 agc[][2] = {
+		{ R20_AGC11, 0x60 },
+		{ R23_AGC21, 0x02 },
+		{ R20_AGC11, 0xa0 },
+		{ R23_AGC21, 0x09 },
+		{ R20_AGC11, 0xe0 },
+		{ R23_AGC21, 0x0c },
+		{ R20_AGC11, 0x40 },
+		{ R23_AGC21, 0x01 },
+		{ R20_AGC11, 0x80 },
+		{ R23_AGC21, 0x08 },
+		{ R20_AGC11, 0xc0 },
+		{ R23_AGC21, 0x0b },
+		{ R24_AGC22, 0x1c },
+		{ R24_AGC22, 0x0c },
+	};
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+	/* low-pass filter cut-off frequency */
+	switch (params->u.ofdm.bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		LP_Fc = 0;
+		LO_Frac = params->frequency + 4000000;
+		break;
+	case BANDWIDTH_7_MHZ:
+		LP_Fc = 1;
+		LO_Frac = params->frequency + 3500000;
+		break;
+	case BANDWIDTH_8_MHZ:
+	default:
+		LP_Fc = 2;
+		LO_Frac = params->frequency + 4000000;
+		break;
+	}
+
+	/* band-pass filter */
+	if (LO_Frac < 188000000)
+		BP_Filter = 3;
+	else if (LO_Frac < 253000000)
+		BP_Filter = 4;
+	else if (LO_Frac < 343000000)
+		BP_Filter = 5;
+	else
+		BP_Filter = 6;
+
+	buf[0] = (priv->regs[R1A_IF1] & ~7) | BP_Filter; /* BP_Filter */
+	buf[1] = (priv->regs[R1B_IF2] & ~3) | LP_Fc; /* LP_Fc */
+	buf[2] = priv->regs[R1C_AGC2B];
+	ret = tda18218_wr_regs(priv, R1A_IF1, buf, 3);
+	if (ret)
+		goto error;
+
+	buf[0] = (LO_Frac / 1000) >> 12; /* LO_Frac_0 */
+	buf[1] = (LO_Frac / 1000) >> 4; /* LO_Frac_1 */
+	buf[2] = (LO_Frac / 1000) << 4 |
+		(priv->regs[R0C_MD5] & 0x0f); /* LO_Frac_2 */
+	ret = tda18218_wr_regs(priv, R0A_MD3, buf, 3);
+	if (ret)
+		goto error;
+
+	buf[0] = priv->regs[R0F_MD8] | (1 << 6); /* Freq_prog_Start */
+	ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
+	if (ret)
+		goto error;
+
+	buf[0] = priv->regs[R0F_MD8] & ~(1 << 6); /* Freq_prog_Start */
+	ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
+	if (ret)
+		goto error;
+
+	/* trigger AGC */
+	for (i = 0; i < ARRAY_SIZE(agc); i++) {
+		ret = tda18218_wr_reg(priv, agc[i][0], agc[i][1]);
+		if (ret)
+			goto error;
+	}
+
+error:
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+	if (ret)
+		dbg("%s: failed ret:%d", __func__, ret);
+
+	return ret;
+}
+
+static int tda18218_sleep(struct dvb_frontend *fe)
+{
+	struct tda18218_priv *priv = fe->tuner_priv;
+	int ret;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+	/* standby */
+	ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+	if (ret)
+		dbg("%s: failed ret:%d", __func__, ret);
+
+	return ret;
+}
+
+static int tda18218_init(struct dvb_frontend *fe)
+{
+	struct tda18218_priv *priv = fe->tuner_priv;
+	int ret;
+
+	/* TODO: calibrations */
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+	ret = tda18218_wr_regs(priv, R00_ID, priv->regs, TDA18218_NUM_REGS);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+	if (ret)
+		dbg("%s: failed ret:%d", __func__, ret);
+
+	return ret;
+}
+
+static int tda18218_release(struct dvb_frontend *fe)
+{
+	kfree(fe->tuner_priv);
+	fe->tuner_priv = NULL;
+	return 0;
+}
+
+static const struct dvb_tuner_ops tda18218_tuner_ops = {
+	.info = {
+		.name           = "NXP TDA18218",
+
+		.frequency_min  = 174000000,
+		.frequency_max  = 864000000,
+		.frequency_step =      1000,
+	},
+
+	.release       = tda18218_release,
+	.init          = tda18218_init,
+	.sleep         = tda18218_sleep,
+
+	.set_params    = tda18218_set_params,
+};
+
+struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
+	struct i2c_adapter *i2c, struct tda18218_config *cfg)
+{
+	struct tda18218_priv *priv = NULL;
+	u8 val;
+	int ret;
+	/* chip default registers values */
+	static u8 def_regs[] = {
+		0xc0, 0x88, 0x00, 0x8e, 0x03, 0x00, 0x00, 0xd0, 0x00, 0x40,
+		0x00, 0x00, 0x07, 0xff, 0x84, 0x09, 0x00, 0x13, 0x00, 0x00,
+		0x01, 0x84, 0x09, 0xf0, 0x19, 0x0a, 0x8e, 0x69, 0x98, 0x01,
+		0x00, 0x58, 0x10, 0x40, 0x8c, 0x00, 0x0c, 0x48, 0x85, 0xc9,
+		0xa7, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x00, 0x39, 0x00,
+		0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf6
+	};
+
+	priv = kzalloc(sizeof(struct tda18218_priv), GFP_KERNEL);
+	if (priv == NULL)
+		return NULL;
+
+	priv->cfg = cfg;
+	priv->i2c = i2c;
+	fe->tuner_priv = priv;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+	/* check if the tuner is there */
+	ret = tda18218_rd_reg(priv, R00_ID, &val);
+	dbg("%s: ret:%d chip ID:%02x", __func__, ret, val);
+	if (ret || val != def_regs[R00_ID]) {
+		kfree(priv);
+		return NULL;
+	}
+
+	info("NXP TDA18218HN successfully identified.");
+
+	memcpy(&fe->ops.tuner_ops, &tda18218_tuner_ops,
+		sizeof(struct dvb_tuner_ops));
+	memcpy(priv->regs, def_regs, sizeof(def_regs));
+
+	/* loop-through enabled chip default register values */
+	if (priv->cfg->loop_through) {
+		priv->regs[R17_PD1] = 0xb0;
+		priv->regs[R18_PD2] = 0x59;
+	}
+
+	/* standby */
+	ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
+	if (ret)
+		dbg("%s: failed ret:%d", __func__, ret);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+	return fe;
+}
+EXPORT_SYMBOL(tda18218_attach);
+
+MODULE_DESCRIPTION("NXP TDA18218HN silicon tuner driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tda18218.h b/drivers/media/common/tuners/tda18218.h
new file mode 100644
index 0000000..b4180d1
--- /dev/null
+++ b/drivers/media/common/tuners/tda18218.h
@@ -0,0 +1,45 @@
+/*
+ * NXP TDA18218HN silicon tuner driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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 TDA18218_H
+#define TDA18218_H
+
+#include "dvb_frontend.h"
+
+struct tda18218_config {
+	u8 i2c_address;
+	u8 i2c_wr_max;
+	u8 loop_through:1;
+};
+
+#if defined(CONFIG_MEDIA_TUNER_TDA18218) || \
+	(defined(CONFIG_MEDIA_TUNER_TDA18218_MODULE) && defined(MODULE))
+extern struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
+	struct i2c_adapter *i2c, struct tda18218_config *cfg);
+#else
+static inline struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
+	struct i2c_adapter *i2c, struct tda18218_config *cfg)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/common/tuners/tda18218_priv.h b/drivers/media/common/tuners/tda18218_priv.h
new file mode 100644
index 0000000..904e536
--- /dev/null
+++ b/drivers/media/common/tuners/tda18218_priv.h
@@ -0,0 +1,106 @@
+/*
+ * NXP TDA18218HN silicon tuner driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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 TDA18218_PRIV_H
+#define TDA18218_PRIV_H
+
+#define LOG_PREFIX "tda18218"
+
+#undef dbg
+#define dbg(f, arg...) \
+	if (debug) \
+		printk(KERN_DEBUG   LOG_PREFIX": " f "\n" , ## arg)
+#undef err
+#define err(f, arg...)  printk(KERN_ERR     LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO    LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+#define R00_ID         0x00	/* ID byte */
+#define R01_R1         0x01	/* Read byte 1 */
+#define R02_R2         0x02	/* Read byte 2 */
+#define R03_R3         0x03	/* Read byte 3 */
+#define R04_R4         0x04	/* Read byte 4 */
+#define R05_R5         0x05	/* Read byte 5 */
+#define R06_R6         0x06	/* Read byte 6 */
+#define R07_MD1        0x07	/* Main divider byte 1 */
+#define R08_PSM1       0x08	/* PSM byte 1 */
+#define R09_MD2        0x09	/* Main divider byte 2 */
+#define R0A_MD3        0x0a	/* Main divider byte 1 */
+#define R0B_MD4        0x0b	/* Main divider byte 4 */
+#define R0C_MD5        0x0c	/* Main divider byte 5 */
+#define R0D_MD6        0x0d	/* Main divider byte 6 */
+#define R0E_MD7        0x0e	/* Main divider byte 7 */
+#define R0F_MD8        0x0f	/* Main divider byte 8 */
+#define R10_CD1        0x10	/* Call divider byte 1 */
+#define R11_CD2        0x11	/* Call divider byte 2 */
+#define R12_CD3        0x12	/* Call divider byte 3 */
+#define R13_CD4        0x13	/* Call divider byte 4 */
+#define R14_CD5        0x14	/* Call divider byte 5 */
+#define R15_CD6        0x15	/* Call divider byte 6 */
+#define R16_CD7        0x16	/* Call divider byte 7 */
+#define R17_PD1        0x17	/* Power-down byte 1 */
+#define R18_PD2        0x18	/* Power-down byte 2 */
+#define R19_XTOUT      0x19	/* XTOUT byte */
+#define R1A_IF1        0x1a	/* IF byte 1 */
+#define R1B_IF2        0x1b	/* IF byte 2 */
+#define R1C_AGC2B      0x1c	/* AGC2b byte */
+#define R1D_PSM2       0x1d	/* PSM byte 2 */
+#define R1E_PSM3       0x1e	/* PSM byte 3 */
+#define R1F_PSM4       0x1f	/* PSM byte 4 */
+#define R20_AGC11      0x20	/* AGC1 byte 1 */
+#define R21_AGC12      0x21	/* AGC1 byte 2 */
+#define R22_AGC13      0x22	/* AGC1 byte 3 */
+#define R23_AGC21      0x23	/* AGC2 byte 1 */
+#define R24_AGC22      0x24	/* AGC2 byte 2 */
+#define R25_AAGC       0x25	/* Analog AGC byte */
+#define R26_RC         0x26	/* RC byte */
+#define R27_RSSI       0x27	/* RSSI byte */
+#define R28_IRCAL1     0x28	/* IR CAL byte 1 */
+#define R29_IRCAL2     0x29	/* IR CAL byte 2 */
+#define R2A_IRCAL3     0x2a	/* IR CAL byte 3 */
+#define R2B_IRCAL4     0x2b	/* IR CAL byte 4 */
+#define R2C_RFCAL1     0x2c	/* RF CAL byte 1 */
+#define R2D_RFCAL2     0x2d	/* RF CAL byte 2 */
+#define R2E_RFCAL3     0x2e	/* RF CAL byte 3 */
+#define R2F_RFCAL4     0x2f	/* RF CAL byte 4 */
+#define R30_RFCAL5     0x30	/* RF CAL byte 5 */
+#define R31_RFCAL6     0x31	/* RF CAL byte 6 */
+#define R32_RFCAL7     0x32	/* RF CAL byte 7 */
+#define R33_RFCAL8     0x33	/* RF CAL byte 8 */
+#define R34_RFCAL9     0x34	/* RF CAL byte 9 */
+#define R35_RFCAL10    0x35	/* RF CAL byte 10 */
+#define R36_RFCALRAM1  0x36	/* RF CAL RAM byte 1 */
+#define R37_RFCALRAM2  0x37	/* RF CAL RAM byte 2 */
+#define R38_MARGIN     0x38	/* Margin byte */
+#define R39_FMAX1      0x39	/* Fmax byte 1 */
+#define R3A_FMAX2      0x3a	/* Fmax byte 2 */
+
+#define TDA18218_NUM_REGS 59
+
+struct tda18218_priv {
+	struct tda18218_config *cfg;
+	struct i2c_adapter *i2c;
+
+	u8 regs[TDA18218_NUM_REGS];
+};
+
+#endif
diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c
index e1f6782..5466d47 100644
--- a/drivers/media/common/tuners/tda18271-common.c
+++ b/drivers/media/common/tuners/tda18271-common.c
@@ -193,25 +193,51 @@
 	unsigned char *regs = priv->tda18271_regs;
 	unsigned char buf[TDA18271_NUM_REGS + 1];
 	struct i2c_msg msg = { .addr = priv->i2c_props.addr, .flags = 0,
-			       .buf = buf, .len = len + 1 };
-	int i, ret;
+			       .buf = buf };
+	int i, ret = 1, max;
 
 	BUG_ON((len == 0) || (idx + len > sizeof(buf)));
 
-	buf[0] = idx;
-	for (i = 1; i <= len; i++)
-		buf[i] = regs[idx - 1 + i];
+
+	switch (priv->small_i2c) {
+	case TDA18271_03_BYTE_CHUNK_INIT:
+		max = 3;
+		break;
+	case TDA18271_08_BYTE_CHUNK_INIT:
+		max = 8;
+		break;
+	case TDA18271_16_BYTE_CHUNK_INIT:
+		max = 16;
+		break;
+	case TDA18271_39_BYTE_CHUNK_INIT:
+	default:
+		max = 39;
+	}
 
 	tda18271_i2c_gate_ctrl(fe, 1);
+	while (len) {
+		if (max > len)
+			max = len;
 
-	/* write registers */
-	ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
+		buf[0] = idx;
+		for (i = 1; i <= max; i++)
+			buf[i] = regs[idx - 1 + i];
 
+		msg.len = max + 1;
+
+		/* write registers */
+		ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
+		if (ret != 1)
+			break;
+
+		idx += max;
+		len -= max;
+	}
 	tda18271_i2c_gate_ctrl(fe, 0);
 
 	if (ret != 1)
 		tda_err("ERROR: idx = 0x%x, len = %d, "
-			"i2c_transfer returned: %d\n", idx, len, ret);
+			"i2c_transfer returned: %d\n", idx, max, ret);
 
 	return (ret == 1 ? 0 : ret);
 }
@@ -326,24 +352,7 @@
 	regs[R_EB22] = 0x48;
 	regs[R_EB23] = 0xb0;
 
-	switch (priv->small_i2c) {
-	case TDA18271_08_BYTE_CHUNK_INIT:
-		tda18271_write_regs(fe, 0x00, 0x08);
-		tda18271_write_regs(fe, 0x08, 0x08);
-		tda18271_write_regs(fe, 0x10, 0x08);
-		tda18271_write_regs(fe, 0x18, 0x08);
-		tda18271_write_regs(fe, 0x20, 0x07);
-		break;
-	case TDA18271_16_BYTE_CHUNK_INIT:
-		tda18271_write_regs(fe, 0x00, 0x10);
-		tda18271_write_regs(fe, 0x10, 0x10);
-		tda18271_write_regs(fe, 0x20, 0x07);
-		break;
-	case TDA18271_39_BYTE_CHUNK_INIT:
-	default:
-		tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS);
-		break;
-	}
+	tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS);
 
 	/* setup agc1 gain */
 	regs[R_EB17] = 0x00;
diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c
index 7955e49..9ad4454 100644
--- a/drivers/media/common/tuners/tda18271-fe.c
+++ b/drivers/media/common/tuners/tda18271-fe.c
@@ -1156,7 +1156,6 @@
 	struct tda18271_priv *priv = fe->tuner_priv;
 	unsigned char *regs = priv->tda18271_regs;
 	char *name;
-	int ret = 0;
 
 	mutex_lock(&priv->lock);
 	tda18271_read_regs(fe);
@@ -1172,17 +1171,16 @@
 		priv->id = TDA18271HDC2;
 		break;
 	default:
-		name = "Unknown device";
-		ret = -EINVAL;
-		break;
+		tda_info("Unknown device (%i) detected @ %d-%04x, device not supported.\n",
+			 regs[R_ID], i2c_adapter_id(priv->i2c_props.adap),
+			 priv->i2c_props.addr);
+		return -EINVAL;
 	}
 
-	tda_info("%s detected @ %d-%04x%s\n", name,
-		 i2c_adapter_id(priv->i2c_props.adap),
-		 priv->i2c_props.addr,
-		 (0 == ret) ? "" : ", device not supported.");
+	tda_info("%s detected @ %d-%04x\n", name,
+		 i2c_adapter_id(priv->i2c_props.adap), priv->i2c_props.addr);
 
-	return ret;
+	return 0;
 }
 
 static int tda18271_setup_configuration(struct dvb_frontend *fe,
diff --git a/drivers/media/common/tuners/tda18271.h b/drivers/media/common/tuners/tda18271.h
index d7fcc36..3abb221 100644
--- a/drivers/media/common/tuners/tda18271.h
+++ b/drivers/media/common/tuners/tda18271.h
@@ -80,8 +80,9 @@
 
 enum tda18271_small_i2c {
 	TDA18271_39_BYTE_CHUNK_INIT = 0,
-	TDA18271_16_BYTE_CHUNK_INIT = 1,
-	TDA18271_08_BYTE_CHUNK_INIT = 2,
+	TDA18271_16_BYTE_CHUNK_INIT = 16,
+	TDA18271_08_BYTE_CHUNK_INIT = 8,
+	TDA18271_03_BYTE_CHUNK_INIT = 3,
 };
 
 struct tda18271_config {
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
index d2b2c12..76ac5cd 100644
--- a/drivers/media/common/tuners/xc5000.c
+++ b/drivers/media/common/tuners/xc5000.c
@@ -1042,7 +1042,7 @@
 
 struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
 				   struct i2c_adapter *i2c,
-				   struct xc5000_config *cfg)
+				   const struct xc5000_config *cfg)
 {
 	struct xc5000_priv *priv = NULL;
 	int instance;
diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h
index e6d7236..3756e73 100644
--- a/drivers/media/common/tuners/xc5000.h
+++ b/drivers/media/common/tuners/xc5000.h
@@ -53,11 +53,11 @@
     (defined(CONFIG_MEDIA_TUNER_XC5000_MODULE) && defined(MODULE))
 extern struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
 					  struct i2c_adapter *i2c,
-					  struct xc5000_config *cfg);
+					  const struct xc5000_config *cfg);
 #else
 static inline struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
 						 struct i2c_adapter *i2c,
-						 struct xc5000_config *cfg)
+						 const struct xc5000_config *cfg)
 {
 	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
 	return NULL;
diff --git a/drivers/media/dvb/b2c2/flexcop-i2c.c b/drivers/media/dvb/b2c2/flexcop-i2c.c
index fd1df23..965d5eb 100644
--- a/drivers/media/dvb/b2c2/flexcop-i2c.c
+++ b/drivers/media/dvb/b2c2/flexcop-i2c.c
@@ -245,9 +245,6 @@
 	i2c_set_adapdata(&fc->fc_i2c_adap[1].i2c_adap, &fc->fc_i2c_adap[1]);
 	i2c_set_adapdata(&fc->fc_i2c_adap[2].i2c_adap, &fc->fc_i2c_adap[2]);
 
-	fc->fc_i2c_adap[0].i2c_adap.class =
-		fc->fc_i2c_adap[1].i2c_adap.class =
-		fc->fc_i2c_adap[2].i2c_adap.class = I2C_CLASS_TV_DIGITAL;
 	fc->fc_i2c_adap[0].i2c_adap.algo =
 		fc->fc_i2c_adap[1].i2c_adap.algo =
 		fc->fc_i2c_adap[2].i2c_adap.algo = &flexcop_algo;
diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c
index bca07c0..5d404f1 100644
--- a/drivers/media/dvb/dm1105/dm1105.c
+++ b/drivers/media/dvb/dm1105/dm1105.c
@@ -862,7 +862,6 @@
 	i2c_set_adapdata(&dev->i2c_adap, dev);
 	strcpy(dev->i2c_adap.name, DRIVER_NAME);
 	dev->i2c_adap.owner = THIS_MODULE;
-	dev->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
 	dev->i2c_adap.dev.parent = &pdev->dev;
 	dev->i2c_adap.algo = &dm1105_algo;
 	dev->i2c_adap.algo_data = dev;
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index 970c9b8..1589d5a 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -702,7 +702,7 @@
 
 	kthread_stop(fepriv->thread);
 
-	init_MUTEX (&fepriv->sem);
+	sema_init(&fepriv->sem, 1);
 	fepriv->state = FESTATE_IDLE;
 
 	/* paranoia check in case a signal arrived */
@@ -2062,7 +2062,7 @@
 	}
 	fepriv = fe->frontend_priv;
 
-	init_MUTEX (&fepriv->sem);
+	sema_init(&fepriv->sem, 1);
 	init_waitqueue_head (&fepriv->wait_queue);
 	init_waitqueue_head (&fepriv->events.wait_queue);
 	mutex_init(&fepriv->events.mtx);
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
index bf0e6be..f9f19be 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -260,7 +260,7 @@
 	int (*init)(struct dvb_frontend* fe);
 	int (*sleep)(struct dvb_frontend* fe);
 
-	int (*write)(struct dvb_frontend* fe, u8* buf, int len);
+	int (*write)(struct dvb_frontend* fe, const u8 buf[], int len);
 
 	/* if this is set, it overrides the default swzigzag */
 	int (*tune)(struct dvb_frontend* fe,
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index fdc19bb..2525d3b 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -314,6 +314,8 @@
 	select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE
+	select MEDIA_TUNER_TDA18218 if !MEDIA_TUNER_CUSTOMISE
+	select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE
 	help
 	  Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver
 
@@ -346,3 +348,13 @@
 	select DVB_STB6100 if !DVB_FE_CUSTOMISE
 	help
 	  Say Y here to support the AZ6027 device
+
+config DVB_USB_LME2510
+	tristate "LME DM04/QQBOX DVB-S USB2.0 support"
+	depends on DVB_USB
+	select DVB_TDA10086 if !DVB_FE_CUSTOMISE
+	select DVB_TDA826X if !DVB_FE_CUSTOMISE
+	select DVB_STV0288 if !DVB_FE_CUSTOMISE
+	select DVB_IX2505V if !DVB_FE_CUSTOMISE
+	help
+	  Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 .
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index 1a19245..5b1d12f 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -88,6 +88,9 @@
 dvb-usb-az6027-objs = az6027.o
 obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o
 
+dvb-usb-lmedm04-objs = lmedm04.o
+obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o
+
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
 # due to tuner-xc3028
 EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
index ea1ed3b..31c0a0e 100644
--- a/drivers/media/dvb/dvb-usb/af9015.c
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -31,6 +31,8 @@
 #include "tda18271.h"
 #include "mxl5005s.h"
 #include "mc44s803.h"
+#include "tda18218.h"
+#include "mxl5007t.h"
 
 static int dvb_usb_af9015_debug;
 module_param_named(debug, dvb_usb_af9015_debug, int, 0644);
@@ -205,10 +207,16 @@
 	return af9015_write_regs(d, addr, &val, 1);
 }
 
+static int af9015_read_regs(struct dvb_usb_device *d, u16 addr, u8 *val, u8 len)
+{
+	struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len,
+		val};
+	return af9015_ctrl_msg(d, &req);
+}
+
 static int af9015_read_reg(struct dvb_usb_device *d, u16 addr, u8 *val)
 {
-	struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, 1, val};
-	return af9015_ctrl_msg(d, &req);
+	return af9015_read_regs(d, addr, val, 1);
 }
 
 static int af9015_write_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
@@ -241,7 +249,7 @@
 	struct dvb_usb_device *d = i2c_get_adapdata(adap);
 	int ret = 0, i = 0;
 	u16 addr;
-	u8 mbox, addr_len;
+	u8 uninitialized_var(mbox), addr_len;
 	struct req_t req;
 
 /* TODO: implement bus lock
@@ -280,7 +288,7 @@
 		} else {
 			addr = msg[i].buf[0];
 			addr_len = 1;
-			mbox = 0;
+			/* mbox is don't care in that case */
 		}
 
 		if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
@@ -494,7 +502,8 @@
 	/* wait 2nd demodulator ready */
 	msleep(100);
 
-	ret = af9015_read_reg_i2c(d, 0x3a, 0x98be, &val);
+	ret = af9015_read_reg_i2c(d,
+		af9015_af9013_config[1].demod_address, 0x98be, &val);
 	if (ret)
 		goto error;
 	else
@@ -597,37 +606,6 @@
 	return ret;
 }
 
-static int af9015_download_ir_table(struct dvb_usb_device *d)
-{
-	int i, packets = 0, ret;
-	u16 addr = 0x9a56; /* ir-table start address */
-	struct req_t req = {WRITE_MEMORY, 0, 0, 0, 0, 1, NULL};
-	u8 *data = NULL;
-	deb_info("%s:\n", __func__);
-
-	data = af9015_config.ir_table;
-	packets = af9015_config.ir_table_size;
-
-	/* no remote */
-	if (!packets)
-		goto exit;
-
-	/* load remote ir-table */
-	for (i = 0; i < packets; i++) {
-		req.addr = addr + i;
-		req.data = &data[i];
-		ret = af9015_ctrl_msg(d, &req);
-		if (ret) {
-			err("ir-table download failed at packet %d with " \
-				"code %d", i, ret);
-			return ret;
-		}
-	}
-
-exit:
-	return 0;
-}
-
 static int af9015_init(struct dvb_usb_device *d)
 {
 	int ret;
@@ -637,10 +615,6 @@
 	if (ret)
 		goto error;
 
-	ret = af9015_download_ir_table(d);
-	if (ret)
-		goto error;
-
 error:
 	return ret;
 }
@@ -733,125 +707,102 @@
 	return ret;
 }
 
-struct af9015_setup {
+struct af9015_rc_setup {
 	unsigned int id;
-	struct ir_scancode *rc_key_map;
-	unsigned int rc_key_map_size;
-	u8 *ir_table;
-	unsigned int ir_table_size;
+	char *rc_codes;
 };
 
-static const struct af9015_setup *af9015_setup_match(unsigned int id,
-		const struct af9015_setup *table)
+static char *af9015_rc_setup_match(unsigned int id,
+	const struct af9015_rc_setup *table)
 {
-	for (; table->rc_key_map; table++)
+	for (; table->rc_codes; table++)
 		if (table->id == id)
-			return table;
+			return table->rc_codes;
 	return NULL;
 }
 
-static const struct af9015_setup af9015_setup_modparam[] = {
-	{ AF9015_REMOTE_A_LINK_DTU_M,
-		ir_codes_af9015_table_a_link, ARRAY_SIZE(ir_codes_af9015_table_a_link),
-		af9015_ir_table_a_link, ARRAY_SIZE(af9015_ir_table_a_link) },
-	{ AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
-		ir_codes_af9015_table_msi, ARRAY_SIZE(ir_codes_af9015_table_msi),
-		af9015_ir_table_msi, ARRAY_SIZE(af9015_ir_table_msi) },
-	{ AF9015_REMOTE_MYGICTV_U718,
-		ir_codes_af9015_table_mygictv, ARRAY_SIZE(ir_codes_af9015_table_mygictv),
-		af9015_ir_table_mygictv, ARRAY_SIZE(af9015_ir_table_mygictv) },
-	{ AF9015_REMOTE_DIGITTRADE_DVB_T,
-		ir_codes_af9015_table_digittrade, ARRAY_SIZE(ir_codes_af9015_table_digittrade),
-		af9015_ir_table_digittrade, ARRAY_SIZE(af9015_ir_table_digittrade) },
-	{ AF9015_REMOTE_AVERMEDIA_KS,
-		ir_codes_af9015_table_avermedia, ARRAY_SIZE(ir_codes_af9015_table_avermedia),
-		af9015_ir_table_avermedia_ks, ARRAY_SIZE(af9015_ir_table_avermedia_ks) },
+static const struct af9015_rc_setup af9015_rc_setup_modparam[] = {
+	{ AF9015_REMOTE_A_LINK_DTU_M, RC_MAP_ALINK_DTU_M },
+	{ AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, RC_MAP_MSI_DIGIVOX_II },
+	{ AF9015_REMOTE_MYGICTV_U718, RC_MAP_TOTAL_MEDIA_IN_HAND },
+	{ AF9015_REMOTE_DIGITTRADE_DVB_T, RC_MAP_DIGITTRADE },
+	{ AF9015_REMOTE_AVERMEDIA_KS, RC_MAP_AVERMEDIA_RM_KS },
 	{ }
 };
 
-/* don't add new entries here anymore, use hashes instead */
-static const struct af9015_setup af9015_setup_usbids[] = {
-	{ USB_VID_LEADTEK,
-		ir_codes_af9015_table_leadtek, ARRAY_SIZE(ir_codes_af9015_table_leadtek),
-		af9015_ir_table_leadtek, ARRAY_SIZE(af9015_ir_table_leadtek) },
-	{ USB_VID_VISIONPLUS,
-		ir_codes_af9015_table_twinhan, ARRAY_SIZE(ir_codes_af9015_table_twinhan),
-		af9015_ir_table_twinhan, ARRAY_SIZE(af9015_ir_table_twinhan) },
-	{ USB_VID_KWORLD_2, /* TODO: use correct rc keys */
-		ir_codes_af9015_table_twinhan, ARRAY_SIZE(ir_codes_af9015_table_twinhan),
-		af9015_ir_table_kworld, ARRAY_SIZE(af9015_ir_table_kworld) },
-	{ USB_VID_AVERMEDIA,
-		ir_codes_af9015_table_avermedia, ARRAY_SIZE(ir_codes_af9015_table_avermedia),
-		af9015_ir_table_avermedia, ARRAY_SIZE(af9015_ir_table_avermedia) },
-	{ USB_VID_MSI_2,
-		ir_codes_af9015_table_msi_digivox_iii, ARRAY_SIZE(ir_codes_af9015_table_msi_digivox_iii),
-		af9015_ir_table_msi_digivox_iii, ARRAY_SIZE(af9015_ir_table_msi_digivox_iii) },
+static const struct af9015_rc_setup af9015_rc_setup_hashes[] = {
+	{ 0xb8feb708, RC_MAP_MSI_DIGIVOX_II },
+	{ 0xa3703d00, RC_MAP_ALINK_DTU_M },
+	{ 0x9b7dc64e, RC_MAP_TOTAL_MEDIA_IN_HAND }, /* MYGICTV U718 */
 	{ }
 };
 
-static const struct af9015_setup af9015_setup_hashes[] = {
-	{ 0xb8feb708,
-		ir_codes_af9015_table_msi, ARRAY_SIZE(ir_codes_af9015_table_msi),
-		af9015_ir_table_msi, ARRAY_SIZE(af9015_ir_table_msi) },
-	{ 0xa3703d00,
-		ir_codes_af9015_table_a_link, ARRAY_SIZE(ir_codes_af9015_table_a_link),
-		af9015_ir_table_a_link, ARRAY_SIZE(af9015_ir_table_a_link) },
-	{ 0x9b7dc64e,
-		ir_codes_af9015_table_mygictv, ARRAY_SIZE(ir_codes_af9015_table_mygictv),
-		af9015_ir_table_mygictv, ARRAY_SIZE(af9015_ir_table_mygictv) },
+static const struct af9015_rc_setup af9015_rc_setup_usbids[] = {
+	{ (USB_VID_TERRATEC << 16) + USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC,
+		RC_MAP_TERRATEC_SLIM },
+	{ (USB_VID_VISIONPLUS << 16) + USB_PID_AZUREWAVE_AD_TU700,
+		RC_MAP_AZUREWAVE_AD_TU700 },
+	{ (USB_VID_VISIONPLUS << 16) + USB_PID_TINYTWIN,
+		RC_MAP_AZUREWAVE_AD_TU700 },
+	{ (USB_VID_MSI_2 << 16) + USB_PID_MSI_DIGI_VOX_MINI_III,
+		RC_MAP_MSI_DIGIVOX_III },
+	{ (USB_VID_LEADTEK << 16) + USB_PID_WINFAST_DTV_DONGLE_GOLD,
+		RC_MAP_LEADTEK_Y04G0051 },
+	{ (USB_VID_AVERMEDIA << 16) + USB_PID_AVERMEDIA_VOLAR_X,
+		RC_MAP_AVERMEDIA_M135A },
+	{ (USB_VID_AFATECH << 16) + USB_PID_TREKSTOR_DVBT,
+		RC_MAP_TREKSTOR },
+	{ (USB_VID_KWORLD_2 << 16) + USB_PID_TINYTWIN_2,
+		RC_MAP_DIGITALNOW_TINYTWIN },
+	{ (USB_VID_GTEK << 16) + USB_PID_TINYTWIN_3,
+		RC_MAP_DIGITALNOW_TINYTWIN },
 	{ }
 };
 
 static void af9015_set_remote_config(struct usb_device *udev,
 		struct dvb_usb_device_properties *props)
 {
-	const struct af9015_setup *table = NULL;
+	u16 vid = le16_to_cpu(udev->descriptor.idVendor);
+	u16 pid = le16_to_cpu(udev->descriptor.idProduct);
 
-	if (dvb_usb_af9015_remote) {
-		/* load remote defined as module param */
-		table = af9015_setup_match(dvb_usb_af9015_remote,
-				af9015_setup_modparam);
-	} else {
-		u16 vendor = le16_to_cpu(udev->descriptor.idVendor);
+	/* try to load remote based module param */
+	props->rc.core.rc_codes = af9015_rc_setup_match(
+		dvb_usb_af9015_remote, af9015_rc_setup_modparam);
 
-		table = af9015_setup_match(af9015_config.eeprom_sum,
-				af9015_setup_hashes);
+	/* try to load remote based eeprom hash */
+	if (!props->rc.core.rc_codes)
+		props->rc.core.rc_codes = af9015_rc_setup_match(
+			af9015_config.eeprom_sum, af9015_rc_setup_hashes);
 
-		if (!table && vendor == USB_VID_AFATECH) {
-			/* Check USB manufacturer and product strings and try
-			   to determine correct remote in case of chip vendor
-			   reference IDs are used.
-			   DO NOT ADD ANYTHING NEW HERE. Use hashes instead.
-			 */
-			char manufacturer[10];
-			memset(manufacturer, 0, sizeof(manufacturer));
-			usb_string(udev, udev->descriptor.iManufacturer,
-				manufacturer, sizeof(manufacturer));
-			if (!strcmp("MSI", manufacturer)) {
-				/* iManufacturer 1 MSI
-				   iProduct      2 MSI K-VOX */
-				table = af9015_setup_match(
-					AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
-					af9015_setup_modparam);
-			} else if (udev->descriptor.idProduct ==
-				cpu_to_le16(USB_PID_TREKSTOR_DVBT)) {
-				table = &(const struct af9015_setup){ 0,
-					ir_codes_af9015_table_trekstor,
-					ARRAY_SIZE(ir_codes_af9015_table_trekstor),
-					af9015_ir_table_trekstor,
-					ARRAY_SIZE(af9015_ir_table_trekstor)
-				};
-			}
-		} else if (!table)
-			table = af9015_setup_match(vendor, af9015_setup_usbids);
+	/* try to load remote based USB ID */
+	if (!props->rc.core.rc_codes)
+		props->rc.core.rc_codes = af9015_rc_setup_match(
+			(vid << 16) + pid, af9015_rc_setup_usbids);
+
+	/* try to load remote based USB iManufacturer string */
+	if (!props->rc.core.rc_codes && vid == USB_VID_AFATECH) {
+		/* Check USB manufacturer and product strings and try
+		   to determine correct remote in case of chip vendor
+		   reference IDs are used.
+		   DO NOT ADD ANYTHING NEW HERE. Use hashes instead. */
+		char manufacturer[10];
+		memset(manufacturer, 0, sizeof(manufacturer));
+		usb_string(udev, udev->descriptor.iManufacturer,
+			manufacturer, sizeof(manufacturer));
+		if (!strcmp("MSI", manufacturer)) {
+			/* iManufacturer 1 MSI
+			   iProduct      2 MSI K-VOX */
+			props->rc.core.rc_codes = af9015_rc_setup_match(
+				AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
+				af9015_rc_setup_modparam);
+		}
 	}
 
-	if (table) {
-		props->rc.legacy.rc_key_map = table->rc_key_map;
-		props->rc.legacy.rc_key_map_size = table->rc_key_map_size;
-		af9015_config.ir_table = table->ir_table;
-		af9015_config.ir_table_size = table->ir_table_size;
-	}
+	/* finally load "empty" just for leaving IR receiver enabled */
+	if (!props->rc.core.rc_codes)
+		props->rc.core.rc_codes = RC_MAP_EMPTY;
+
+	return;
 }
 
 static int af9015_read_config(struct usb_device *udev)
@@ -877,10 +828,9 @@
 
 	deb_info("%s: IR mode:%d\n", __func__, val);
 	for (i = 0; i < af9015_properties_count; i++) {
-		if (val == AF9015_IR_MODE_DISABLED) {
-			af9015_properties[i].rc.legacy.rc_key_map = NULL;
-			af9015_properties[i].rc.legacy.rc_key_map_size  = 0;
-		} else
+		if (val == AF9015_IR_MODE_DISABLED)
+			af9015_properties[i].rc.core.rc_codes = NULL;
+		else
 			af9015_set_remote_config(udev, &af9015_properties[i]);
 	}
 
@@ -992,20 +942,19 @@
 		case AF9013_TUNER_MT2060_2:
 		case AF9013_TUNER_TDA18271:
 		case AF9013_TUNER_QT1010A:
+		case AF9013_TUNER_TDA18218:
 			af9015_af9013_config[i].rf_spec_inv = 1;
 			break;
 		case AF9013_TUNER_MXL5003D:
 		case AF9013_TUNER_MXL5005D:
 		case AF9013_TUNER_MXL5005R:
+		case AF9013_TUNER_MXL5007T:
 			af9015_af9013_config[i].rf_spec_inv = 0;
 			break;
 		case AF9013_TUNER_MC44S803:
 			af9015_af9013_config[i].gpio[1] = AF9013_GPIO_LO;
 			af9015_af9013_config[i].rf_spec_inv = 1;
 			break;
-		case AF9013_TUNER_TDA18218:
-			warn("tuner NXP TDA18218 not supported yet");
-			return -ENODEV;
 		default:
 			warn("tuner id:%d not supported, please report!", val);
 			return -ENODEV;
@@ -1020,9 +969,13 @@
 		err("eeprom read failed:%d", ret);
 
 	/* AverMedia AVerTV Volar Black HD (A850) device have bad EEPROM
-	   content :-( Override some wrong values here. */
+	   content :-( Override some wrong values here. Ditto for the
+	   AVerTV Red HD+ (A850T) device. */
 	if (le16_to_cpu(udev->descriptor.idVendor) == USB_VID_AVERMEDIA &&
-	    le16_to_cpu(udev->descriptor.idProduct) == USB_PID_AVERMEDIA_A850) {
+		((le16_to_cpu(udev->descriptor.idProduct) ==
+			USB_PID_AVERMEDIA_A850) ||
+		(le16_to_cpu(udev->descriptor.idProduct) ==
+			USB_PID_AVERMEDIA_A850T))) {
 		deb_info("%s: AverMedia A850: overriding config\n", __func__);
 		/* disable dual mode */
 		af9015_config.dual_mode = 0;
@@ -1059,36 +1012,53 @@
 	return ret;
 }
 
-static int af9015_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
+static int af9015_rc_query(struct dvb_usb_device *d)
 {
-	u8 buf[8];
-	struct req_t req = {GET_IR_CODE, 0, 0, 0, 0, sizeof(buf), buf};
-	struct ir_scancode *keymap = d->props.rc.legacy.rc_key_map;
-	int i, ret;
+	struct af9015_state *priv = d->priv;
+	int ret;
+	u8 buf[16];
 
-	memset(buf, 0, sizeof(buf));
-
-	ret = af9015_ctrl_msg(d, &req);
+	/* read registers needed to detect remote controller code */
+	ret = af9015_read_regs(d, 0x98d9, buf, sizeof(buf));
 	if (ret)
-		return ret;
+		goto error;
 
-	*event = 0;
-	*state = REMOTE_NO_KEY_PRESSED;
+	if (buf[14] || buf[15]) {
+		deb_rc("%s: key pressed %02x %02x %02x %02x\n", __func__,
+			buf[12], buf[13], buf[14], buf[15]);
 
-	for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) {
-		if (!buf[1] && rc5_custom(&keymap[i]) == buf[0] &&
-		    rc5_data(&keymap[i]) == buf[2]) {
-			*event = keymap[i].keycode;
-			*state = REMOTE_KEY_PRESSED;
-			break;
+		/* clean IR code from mem */
+		ret = af9015_write_regs(d, 0x98e5, "\x00\x00\x00\x00", 4);
+		if (ret)
+			goto error;
+
+		if (buf[14] == (u8) ~buf[15]) {
+			if (buf[12] == (u8) ~buf[13]) {
+				/* NEC */
+				priv->rc_keycode = buf[12] << 8 | buf[14];
+			} else {
+				/* NEC extended*/
+				priv->rc_keycode = buf[12] << 16 |
+					buf[13] << 8 | buf[14];
+			}
+			ir_keydown(d->rc_input_dev, priv->rc_keycode, 0);
+		} else {
+			priv->rc_keycode = 0; /* clear just for sure */
 		}
+	} else if (priv->rc_repeat != buf[6] || buf[0]) {
+		deb_rc("%s: key repeated\n", __func__);
+		ir_keydown(d->rc_input_dev, priv->rc_keycode, 0);
+	} else {
+		deb_rc("%s: no key press\n", __func__);
 	}
-	if (!buf[1])
-		deb_rc("%s: %02x %02x %02x %02x %02x %02x %02x %02x\n",
-			__func__, buf[0], buf[1], buf[2], buf[3], buf[4],
-			buf[5], buf[6], buf[7]);
 
-	return 0;
+	priv->rc_repeat = buf[6];
+
+error:
+	if (ret)
+		err("%s: failed:%d", __func__, ret);
+
+	return ret;
 }
 
 /* init 2nd I2C adapter */
@@ -1100,11 +1070,6 @@
 
 	strncpy(state->i2c_adap.name, d->desc->name,
 		sizeof(state->i2c_adap.name));
-#ifdef I2C_ADAP_CLASS_TV_DIGITAL
-	state->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL,
-#else
-	state->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
-#endif
 	state->i2c_adap.algo      = d->props.i2c_algo;
 	state->i2c_adap.algo_data = NULL;
 	state->i2c_adap.dev.parent = &d->udev->dev;
@@ -1166,7 +1131,7 @@
 
 static struct tda18271_config af9015_tda18271_config = {
 	.gate = TDA18271_GATE_DIGITAL,
-	.small_i2c = 1,
+	.small_i2c = TDA18271_16_BYTE_CHUNK_INIT,
 };
 
 static struct mxl5005s_config af9015_mxl5003_config = {
@@ -1208,12 +1173,22 @@
 	.dig_out = 1,
 };
 
+static struct tda18218_config af9015_tda18218_config = {
+	.i2c_address = 0xc0,
+	.i2c_wr_max = 21, /* max wr bytes AF9015 I2C adap can handle at once */
+};
+
+static struct mxl5007t_config af9015_mxl5007t_config = {
+	.xtal_freq_hz = MxL_XTAL_24_MHZ,
+	.if_freq_hz = MxL_IF_4_57_MHZ,
+};
+
 static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
 {
 	struct af9015_state *state = adap->dev->priv;
 	struct i2c_adapter *i2c_adap;
 	int ret;
-	deb_info("%s: \n", __func__);
+	deb_info("%s:\n", __func__);
 
 	/* select I2C adapter */
 	if (adap->id == 0)
@@ -1238,6 +1213,10 @@
 		ret = dvb_attach(tda18271_attach, adap->fe, 0xc0, i2c_adap,
 			&af9015_tda18271_config) == NULL ? -ENODEV : 0;
 		break;
+	case AF9013_TUNER_TDA18218:
+		ret = dvb_attach(tda18218_attach, adap->fe, i2c_adap,
+			&af9015_tda18218_config) == NULL ? -ENODEV : 0;
+		break;
 	case AF9013_TUNER_MXL5003D:
 		ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
 			&af9015_mxl5003_config) == NULL ? -ENODEV : 0;
@@ -1255,6 +1234,10 @@
 		ret = dvb_attach(mc44s803_attach, adap->fe, i2c_adap,
 			&af9015_mc44s803_config) == NULL ? -ENODEV : 0;
 		break;
+	case AF9013_TUNER_MXL5007T:
+		ret = dvb_attach(mxl5007t_attach, adap->fe, i2c_adap,
+			0xc0, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0;
+		break;
 	case AF9013_TUNER_UNKNOWN:
 	default:
 		ret = -ENODEV;
@@ -1300,10 +1283,16 @@
 /* 30 */{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_UB383_T)},
 	{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_395U_4)},
 	{USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A815M)},
+	{USB_DEVICE(USB_VID_TERRATEC,  USB_PID_TERRATEC_CINERGY_T_STICK_RC)},
+	{USB_DEVICE(USB_VID_TERRATEC,
+		USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC)},
+/* 35 */{USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850T)},
+	{USB_DEVICE(USB_VID_GTEK,      USB_PID_TINYTWIN_3)},
 	{0},
 };
 MODULE_DEVICE_TABLE(usb, af9015_usb_table);
 
+#define AF9015_RC_INTERVAL 500
 static struct dvb_usb_device_properties af9015_properties[] = {
 	{
 		.caps = DVB_USB_IS_AN_I2C_ADAPTER,
@@ -1354,14 +1343,19 @@
 
 		.identify_state = af9015_identify_state,
 
-		.rc.legacy = {
+		.rc.core = {
+			.protocol         = IR_TYPE_NEC,
+			.module_name      = "af9015",
 			.rc_query         = af9015_rc_query,
-			.rc_interval      = 150,
+			.rc_interval      = AF9015_RC_INTERVAL,
+			.rc_props = {
+				.allowed_protos = IR_TYPE_NEC,
+			},
 		},
 
 		.i2c_algo = &af9015_i2c_algo,
 
-		.num_device_descs = 9, /* max 9 */
+		.num_device_descs = 12, /* check max from dvb-usb.h */
 		.devices = {
 			{
 				.name = "Afatech AF9015 DVB-T USB2.0 stick",
@@ -1389,7 +1383,8 @@
 			{
 				.name = "DigitalNow TinyTwin DVB-T Receiver",
 				.cold_ids = {&af9015_usb_table[5],
-					     &af9015_usb_table[28], NULL},
+					     &af9015_usb_table[28],
+					     &af9015_usb_table[36], NULL},
 				.warm_ids = {NULL},
 			},
 			{
@@ -1413,6 +1408,21 @@
 				.cold_ids = {&af9015_usb_table[9], NULL},
 				.warm_ids = {NULL},
 			},
+			{
+				.name = "TerraTec Cinergy T Stick RC",
+				.cold_ids = {&af9015_usb_table[33], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "TerraTec Cinergy T Stick Dual RC",
+				.cold_ids = {&af9015_usb_table[34], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "AverMedia AVerTV Red HD+ (A850T)",
+				.cold_ids = {&af9015_usb_table[35], NULL},
+				.warm_ids = {NULL},
+			},
 		}
 	}, {
 		.caps = DVB_USB_IS_AN_I2C_ADAPTER,
@@ -1463,14 +1473,19 @@
 
 		.identify_state = af9015_identify_state,
 
-		.rc.legacy = {
+		.rc.core = {
+			.protocol         = IR_TYPE_NEC,
+			.module_name      = "af9015",
 			.rc_query         = af9015_rc_query,
-			.rc_interval      = 150,
+			.rc_interval      = AF9015_RC_INTERVAL,
+			.rc_props = {
+				.allowed_protos = IR_TYPE_NEC,
+			},
 		},
 
 		.i2c_algo = &af9015_i2c_algo,
 
-		.num_device_descs = 9, /* max 9 */
+		.num_device_descs = 9, /* check max from dvb-usb.h */
 		.devices = {
 			{
 				.name = "Xtensions XD-380",
@@ -1572,14 +1587,19 @@
 
 		.identify_state = af9015_identify_state,
 
-		.rc.legacy = {
+		.rc.core = {
+			.protocol         = IR_TYPE_NEC,
+			.module_name      = "af9015",
 			.rc_query         = af9015_rc_query,
-			.rc_interval      = 150,
+			.rc_interval      = AF9015_RC_INTERVAL,
+			.rc_props = {
+				.allowed_protos = IR_TYPE_NEC,
+			},
 		},
 
 		.i2c_algo = &af9015_i2c_algo,
 
-		.num_device_descs = 9, /* max 9 */
+		.num_device_descs = 9, /* check max from dvb-usb.h */
 		.devices = {
 			{
 				.name = "AverMedia AVerTV Volar GPS 805 (A805)",
@@ -1672,7 +1692,7 @@
 static void af9015_i2c_exit(struct dvb_usb_device *d)
 {
 	struct af9015_state *state = d->priv;
-	deb_info("%s: \n", __func__);
+	deb_info("%s:\n", __func__);
 
 	/* remove 2nd I2C adapter */
 	if (d->state & DVB_USB_STATE_I2C)
@@ -1682,7 +1702,7 @@
 static void af9015_usb_device_exit(struct usb_interface *intf)
 {
 	struct dvb_usb_device *d = usb_get_intfdata(intf);
-	deb_info("%s: \n", __func__);
+	deb_info("%s:\n", __func__);
 
 	/* remove 2nd I2C adapter */
 	if (d != NULL && d->desc != NULL)
diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
index c8e9349..f20cfa6 100644
--- a/drivers/media/dvb/dvb-usb/af9015.h
+++ b/drivers/media/dvb/dvb-usb/af9015.h
@@ -100,6 +100,8 @@
 
 struct af9015_state {
 	struct i2c_adapter i2c_adap; /* I2C adapter for 2nd FE */
+	u8 rc_repeat;
+	u32 rc_keycode;
 };
 
 struct af9015_config {
@@ -108,8 +110,6 @@
 	u16 firmware_size;
 	u16 firmware_checksum;
 	u32 eeprom_sum;
-	u8  *ir_table;
-	u16 ir_table_size;
 };
 
 enum af9015_remote {
@@ -121,735 +121,4 @@
 /* 5 */	AF9015_REMOTE_AVERMEDIA_KS,
 };
 
-/* LeadTek - Y04G0051 */
-/* Leadtek WinFast DTV Dongle Gold */
-static struct ir_scancode ir_codes_af9015_table_leadtek[] = {
-	{ 0x001e, KEY_1 },
-	{ 0x001f, KEY_2 },
-	{ 0x0020, KEY_3 },
-	{ 0x0021, KEY_4 },
-	{ 0x0022, KEY_5 },
-	{ 0x0023, KEY_6 },
-	{ 0x0024, KEY_7 },
-	{ 0x0025, KEY_8 },
-	{ 0x0026, KEY_9 },
-	{ 0x0027, KEY_0 },
-	{ 0x0028, KEY_OK },
-	{ 0x004f, KEY_RIGHT },
-	{ 0x0050, KEY_LEFT },
-	{ 0x0051, KEY_DOWN },
-	{ 0x0052, KEY_UP },
-	{ 0x011a, KEY_POWER2 },
-	{ 0x04b4, KEY_TV },
-	{ 0x04b3, KEY_RED },
-	{ 0x04b2, KEY_GREEN },
-	{ 0x04b1, KEY_YELLOW },
-	{ 0x04b0, KEY_BLUE },
-	{ 0x003d, KEY_TEXT },
-	{ 0x0113, KEY_SLEEP },
-	{ 0x0010, KEY_MUTE },
-	{ 0x0105, KEY_ESC },
-	{ 0x0009, KEY_SCREEN },
-	{ 0x010f, KEY_MENU },
-	{ 0x003f, KEY_CHANNEL },
-	{ 0x0013, KEY_REWIND },
-	{ 0x0012, KEY_PLAY },
-	{ 0x0011, KEY_FASTFORWARD },
-	{ 0x0005, KEY_PREVIOUS },
-	{ 0x0029, KEY_STOP },
-	{ 0x002b, KEY_NEXT },
-	{ 0x0041, KEY_EPG },
-	{ 0x0019, KEY_VIDEO },
-	{ 0x0016, KEY_AUDIO },
-	{ 0x0037, KEY_DOT },
-	{ 0x002a, KEY_AGAIN },
-	{ 0x002c, KEY_CAMERA },
-	{ 0x003c, KEY_NEW },
-	{ 0x0115, KEY_RECORD },
-	{ 0x010b, KEY_TIME },
-	{ 0x0043, KEY_VOLUMEUP },
-	{ 0x0042, KEY_VOLUMEDOWN },
-	{ 0x004b, KEY_CHANNELUP },
-	{ 0x004e, KEY_CHANNELDOWN },
-};
-
-static u8 af9015_ir_table_leadtek[] = {
-	0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00, /* KEY_POWER2 */
-	0x03, 0xfc, 0x56, 0xa9, 0xb4, 0x04, 0x00, /* KEY_TV */
-	0x03, 0xfc, 0x4b, 0xb4, 0xb3, 0x04, 0x00, /* KEY_RED */
-	0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00, /* KEY_GREEN */
-	0x03, 0xfc, 0x4d, 0xb2, 0xb1, 0x04, 0x00, /* KEY_YELLOW */
-	0x03, 0xfc, 0x4e, 0xb1, 0xb0, 0x04, 0x00, /* KEY_BLUE */
-	0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00, /* KEY_TEXT */
-	0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00, /* KEY_SLEEP */
-	0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00, /* KEY_MUTE */
-	0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00, /* KEY_ESC */
-	0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00, /* KEY_STOP (1)*/
-	0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00, /* KEY_UP */
-	0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00, /* KEY_SCREEN */
-	0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00, /* KEY_LEFT */
-	0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00, /* KEY_OK (1) */
-	0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00, /* KEY_RIGHT */
-	0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00, /* KEY_MENU */
-	0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00, /* KEY_DOWN */
-	0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00, /* KEY_CHANNEL */
-	0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00, /* KEY_REWIND */
-	0x03, 0xfc, 0x43, 0xbc, 0x12, 0x00, 0x00, /* KEY_PLAY */
-	0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00, /* KEY_FASTFORWARD */
-	0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00, /* KEY_VIDEO (1) */
-	0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00, /* KEY_PREVIOUS */
-	0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00, /* KEY_STOP (2) */
-	0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00, /* KEY_NEXT */
-	0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00, /* KEY_EPG */
-	0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00, /* KEY_1 */
-	0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00, /* KEY_2 */
-	0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00, /* KEY_3 */
-	0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00, /* KEY_VIDEO (2) */
-	0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00, /* KEY_4 */
-	0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00, /* KEY_5 */
-	0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00, /* KEY_6 */
-	0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00, /* KEY_AUDIO */
-	0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00, /* KEY_7 */
-	0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00, /* KEY_8 */
-	0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00, /* KEY_9 */
-	0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, /* KEY_OK (2) */
-	0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00, /* KEY_DOT */
-	0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00, /* KEY_0 */
-	0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00, /* KEY_AGAIN */
-	0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00, /* KEY_CAMERA */
-	0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00, /* KEY_NEW */
-	0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00, /* KEY_RECORD */
-	0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00, /* KEY_TIME */
-	0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00, /* KEY_VOLUMEUP */
-	0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00, /* KEY_VOLUMEDOWN */
-	0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00, /* KEY_CHANNELUP */
-	0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00, /* KEY_CHANNELDOWN */
-};
-
-/* TwinHan AzureWave AD-TU700(704J) */
-static struct ir_scancode ir_codes_af9015_table_twinhan[] = {
-	{ 0x053f, KEY_POWER },
-	{ 0x0019, KEY_FAVORITES },    /* Favorite List */
-	{ 0x0004, KEY_TEXT },         /* Teletext */
-	{ 0x000e, KEY_POWER },
-	{ 0x000e, KEY_INFO },         /* Preview */
-	{ 0x0008, KEY_EPG },          /* Info/EPG */
-	{ 0x000f, KEY_LIST },         /* Record List */
-	{ 0x001e, KEY_1 },
-	{ 0x001f, KEY_2 },
-	{ 0x0020, KEY_3 },
-	{ 0x0021, KEY_4 },
-	{ 0x0022, KEY_5 },
-	{ 0x0023, KEY_6 },
-	{ 0x0024, KEY_7 },
-	{ 0x0025, KEY_8 },
-	{ 0x0026, KEY_9 },
-	{ 0x0027, KEY_0 },
-	{ 0x0029, KEY_CANCEL },       /* Cancel */
-	{ 0x004c, KEY_CLEAR },        /* Clear */
-	{ 0x002a, KEY_BACK },         /* Back */
-	{ 0x002b, KEY_TAB },          /* Tab */
-	{ 0x0052, KEY_UP },           /* up arrow */
-	{ 0x0051, KEY_DOWN },         /* down arrow */
-	{ 0x004f, KEY_RIGHT },        /* right arrow */
-	{ 0x0050, KEY_LEFT },         /* left arrow */
-	{ 0x0028, KEY_ENTER },        /* Enter / ok */
-	{ 0x0252, KEY_VOLUMEUP },
-	{ 0x0251, KEY_VOLUMEDOWN },
-	{ 0x004e, KEY_CHANNELDOWN },
-	{ 0x004b, KEY_CHANNELUP },
-	{ 0x004a, KEY_RECORD },
-	{ 0x0111, KEY_PLAY },
-	{ 0x0017, KEY_PAUSE },
-	{ 0x000c, KEY_REWIND },       /* FR << */
-	{ 0x0011, KEY_FASTFORWARD },  /* FF >> */
-	{ 0x0115, KEY_PREVIOUS },     /* Replay */
-	{ 0x010e, KEY_NEXT },         /* Skip */
-	{ 0x0013, KEY_CAMERA },       /* Capture */
-	{ 0x010f, KEY_LANGUAGE },     /* SAP */
-	{ 0x0113, KEY_TV2 },          /* PIP */
-	{ 0x001d, KEY_ZOOM },         /* Full Screen */
-	{ 0x0117, KEY_SUBTITLE },     /* Subtitle / CC */
-	{ 0x0010, KEY_MUTE },
-	{ 0x0119, KEY_AUDIO },        /* L/R */ /* TODO better event */
-	{ 0x0116, KEY_SLEEP },        /* Hibernate */
-	{ 0x0116, KEY_SWITCHVIDEOMODE },
-					  /* A/V */ /* TODO does not work */
-	{ 0x0006, KEY_AGAIN },        /* Recall */
-	{ 0x0116, KEY_KPPLUS },       /* Zoom+ */ /* TODO does not work */
-	{ 0x0116, KEY_KPMINUS },      /* Zoom- */ /* TODO does not work */
-	{ 0x0215, KEY_RED },
-	{ 0x020a, KEY_GREEN },
-	{ 0x021c, KEY_YELLOW },
-	{ 0x0205, KEY_BLUE },
-};
-
-static u8 af9015_ir_table_twinhan[] = {
-	0x00, 0xff, 0x16, 0xe9, 0x3f, 0x05, 0x00,
-	0x00, 0xff, 0x07, 0xf8, 0x16, 0x01, 0x00,
-	0x00, 0xff, 0x14, 0xeb, 0x11, 0x01, 0x00,
-	0x00, 0xff, 0x1a, 0xe5, 0x4d, 0x00, 0x00,
-	0x00, 0xff, 0x4c, 0xb3, 0x17, 0x00, 0x00,
-	0x00, 0xff, 0x12, 0xed, 0x11, 0x00, 0x00,
-	0x00, 0xff, 0x40, 0xbf, 0x0c, 0x00, 0x00,
-	0x00, 0xff, 0x11, 0xee, 0x4a, 0x00, 0x00,
-	0x00, 0xff, 0x54, 0xab, 0x13, 0x00, 0x00,
-	0x00, 0xff, 0x41, 0xbe, 0x15, 0x01, 0x00,
-	0x00, 0xff, 0x42, 0xbd, 0x0e, 0x01, 0x00,
-	0x00, 0xff, 0x43, 0xbc, 0x17, 0x01, 0x00,
-	0x00, 0xff, 0x50, 0xaf, 0x0f, 0x01, 0x00,
-	0x00, 0xff, 0x4d, 0xb2, 0x1d, 0x00, 0x00,
-	0x00, 0xff, 0x47, 0xb8, 0x13, 0x01, 0x00,
-	0x00, 0xff, 0x05, 0xfa, 0x4b, 0x00, 0x00,
-	0x00, 0xff, 0x02, 0xfd, 0x4e, 0x00, 0x00,
-	0x00, 0xff, 0x0e, 0xf1, 0x06, 0x00, 0x00,
-	0x00, 0xff, 0x1e, 0xe1, 0x52, 0x02, 0x00,
-	0x00, 0xff, 0x0a, 0xf5, 0x51, 0x02, 0x00,
-	0x00, 0xff, 0x10, 0xef, 0x10, 0x00, 0x00,
-	0x00, 0xff, 0x49, 0xb6, 0x19, 0x01, 0x00,
-	0x00, 0xff, 0x15, 0xea, 0x27, 0x00, 0x00,
-	0x00, 0xff, 0x03, 0xfc, 0x1e, 0x00, 0x00,
-	0x00, 0xff, 0x01, 0xfe, 0x1f, 0x00, 0x00,
-	0x00, 0xff, 0x06, 0xf9, 0x20, 0x00, 0x00,
-	0x00, 0xff, 0x09, 0xf6, 0x21, 0x00, 0x00,
-	0x00, 0xff, 0x1d, 0xe2, 0x22, 0x00, 0x00,
-	0x00, 0xff, 0x1f, 0xe0, 0x23, 0x00, 0x00,
-	0x00, 0xff, 0x0d, 0xf2, 0x24, 0x00, 0x00,
-	0x00, 0xff, 0x19, 0xe6, 0x25, 0x00, 0x00,
-	0x00, 0xff, 0x1b, 0xe4, 0x26, 0x00, 0x00,
-	0x00, 0xff, 0x00, 0xff, 0x2b, 0x00, 0x00,
-	0x00, 0xff, 0x4a, 0xb5, 0x4c, 0x00, 0x00,
-	0x00, 0xff, 0x4b, 0xb4, 0x52, 0x00, 0x00,
-	0x00, 0xff, 0x51, 0xae, 0x51, 0x00, 0x00,
-	0x00, 0xff, 0x52, 0xad, 0x4f, 0x00, 0x00,
-	0x00, 0xff, 0x4e, 0xb1, 0x50, 0x00, 0x00,
-	0x00, 0xff, 0x0c, 0xf3, 0x29, 0x00, 0x00,
-	0x00, 0xff, 0x4f, 0xb0, 0x28, 0x00, 0x00,
-	0x00, 0xff, 0x13, 0xec, 0x2a, 0x00, 0x00,
-	0x00, 0xff, 0x17, 0xe8, 0x19, 0x00, 0x00,
-	0x00, 0xff, 0x04, 0xfb, 0x0f, 0x00, 0x00,
-	0x00, 0xff, 0x48, 0xb7, 0x0e, 0x00, 0x00,
-	0x00, 0xff, 0x0f, 0xf0, 0x04, 0x00, 0x00,
-	0x00, 0xff, 0x1c, 0xe3, 0x08, 0x00, 0x00,
-	0x00, 0xff, 0x18, 0xe7, 0x15, 0x02, 0x00,
-	0x00, 0xff, 0x53, 0xac, 0x0a, 0x02, 0x00,
-	0x00, 0xff, 0x5e, 0xa1, 0x1c, 0x02, 0x00,
-	0x00, 0xff, 0x5f, 0xa0, 0x05, 0x02, 0x00,
-};
-
-/* A-Link DTU(m) */
-static struct ir_scancode ir_codes_af9015_table_a_link[] = {
-	{ 0x001e, KEY_1 },
-	{ 0x001f, KEY_2 },
-	{ 0x0020, KEY_3 },
-	{ 0x0021, KEY_4 },
-	{ 0x0022, KEY_5 },
-	{ 0x0023, KEY_6 },
-	{ 0x0024, KEY_7 },
-	{ 0x0025, KEY_8 },
-	{ 0x0026, KEY_9 },
-	{ 0x0027, KEY_0 },
-	{ 0x002e, KEY_CHANNELUP },
-	{ 0x002d, KEY_CHANNELDOWN },
-	{ 0x0428, KEY_ZOOM },
-	{ 0x0041, KEY_MUTE },
-	{ 0x0042, KEY_VOLUMEDOWN },
-	{ 0x0043, KEY_VOLUMEUP },
-	{ 0x0044, KEY_GOTO },         /* jump */
-	{ 0x0545, KEY_POWER },
-};
-
-static u8 af9015_ir_table_a_link[] = {
-	0x08, 0xf7, 0x12, 0xed, 0x45, 0x05, 0x00, /* power */
-	0x08, 0xf7, 0x1a, 0xe5, 0x41, 0x00, 0x00, /* mute */
-	0x08, 0xf7, 0x01, 0xfe, 0x1e, 0x00, 0x00, /* 1 */
-	0x08, 0xf7, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */
-	0x08, 0xf7, 0x03, 0xfc, 0x24, 0x00, 0x00, /* 7 */
-	0x08, 0xf7, 0x05, 0xfa, 0x28, 0x04, 0x00, /* zoom */
-	0x08, 0xf7, 0x00, 0xff, 0x43, 0x00, 0x00, /* volume up */
-	0x08, 0xf7, 0x16, 0xe9, 0x42, 0x00, 0x00, /* volume down */
-	0x08, 0xf7, 0x0f, 0xf0, 0x1f, 0x00, 0x00, /* 2 */
-	0x08, 0xf7, 0x0d, 0xf2, 0x22, 0x00, 0x00, /* 5 */
-	0x08, 0xf7, 0x1b, 0xe4, 0x25, 0x00, 0x00, /* 8 */
-	0x08, 0xf7, 0x06, 0xf9, 0x27, 0x00, 0x00, /* 0 */
-	0x08, 0xf7, 0x14, 0xeb, 0x2e, 0x00, 0x00, /* channel up */
-	0x08, 0xf7, 0x1d, 0xe2, 0x2d, 0x00, 0x00, /* channel down */
-	0x08, 0xf7, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */
-	0x08, 0xf7, 0x18, 0xe7, 0x23, 0x00, 0x00, /* 6 */
-	0x08, 0xf7, 0x04, 0xfb, 0x26, 0x00, 0x00, /* 9 */
-	0x08, 0xf7, 0x07, 0xf8, 0x44, 0x00, 0x00, /* jump */
-};
-
-/* MSI DIGIVOX mini II V3.0 */
-static struct ir_scancode ir_codes_af9015_table_msi[] = {
-	{ 0x001e, KEY_1 },
-	{ 0x001f, KEY_2 },
-	{ 0x0020, KEY_3 },
-	{ 0x0021, KEY_4 },
-	{ 0x0022, KEY_5 },
-	{ 0x0023, KEY_6 },
-	{ 0x0024, KEY_7 },
-	{ 0x0025, KEY_8 },
-	{ 0x0026, KEY_9 },
-	{ 0x0027, KEY_0 },
-	{ 0x030f, KEY_CHANNELUP },
-	{ 0x030e, KEY_CHANNELDOWN },
-	{ 0x0042, KEY_VOLUMEDOWN },
-	{ 0x0043, KEY_VOLUMEUP },
-	{ 0x0545, KEY_POWER },
-	{ 0x0052, KEY_UP },           /* up */
-	{ 0x0051, KEY_DOWN },         /* down */
-	{ 0x0028, KEY_ENTER },
-};
-
-static u8 af9015_ir_table_msi[] = {
-	0x03, 0xfc, 0x17, 0xe8, 0x45, 0x05, 0x00, /* power */
-	0x03, 0xfc, 0x0d, 0xf2, 0x51, 0x00, 0x00, /* down */
-	0x03, 0xfc, 0x03, 0xfc, 0x52, 0x00, 0x00, /* up */
-	0x03, 0xfc, 0x1a, 0xe5, 0x1e, 0x00, 0x00, /* 1 */
-	0x03, 0xfc, 0x02, 0xfd, 0x1f, 0x00, 0x00, /* 2 */
-	0x03, 0xfc, 0x04, 0xfb, 0x20, 0x00, 0x00, /* 3 */
-	0x03, 0xfc, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */
-	0x03, 0xfc, 0x08, 0xf7, 0x22, 0x00, 0x00, /* 5 */
-	0x03, 0xfc, 0x1d, 0xe2, 0x23, 0x00, 0x00, /* 6 */
-	0x03, 0xfc, 0x11, 0xee, 0x24, 0x00, 0x00, /* 7 */
-	0x03, 0xfc, 0x0b, 0xf4, 0x25, 0x00, 0x00, /* 8 */
-	0x03, 0xfc, 0x10, 0xef, 0x26, 0x00, 0x00, /* 9 */
-	0x03, 0xfc, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */
-	0x03, 0xfc, 0x14, 0xeb, 0x43, 0x00, 0x00, /* volume up */
-	0x03, 0xfc, 0x1f, 0xe0, 0x42, 0x00, 0x00, /* volume down */
-	0x03, 0xfc, 0x15, 0xea, 0x0f, 0x03, 0x00, /* channel up */
-	0x03, 0xfc, 0x05, 0xfa, 0x0e, 0x03, 0x00, /* channel down */
-	0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, /* enter */
-};
-
-/* MYGICTV U718 */
-static struct ir_scancode ir_codes_af9015_table_mygictv[] = {
-	{ 0x003d, KEY_SWITCHVIDEOMODE },
-					  /* TV / AV */
-	{ 0x0545, KEY_POWER },
-	{ 0x001e, KEY_1 },
-	{ 0x001f, KEY_2 },
-	{ 0x0020, KEY_3 },
-	{ 0x0021, KEY_4 },
-	{ 0x0022, KEY_5 },
-	{ 0x0023, KEY_6 },
-	{ 0x0024, KEY_7 },
-	{ 0x0025, KEY_8 },
-	{ 0x0026, KEY_9 },
-	{ 0x0027, KEY_0 },
-	{ 0x0041, KEY_MUTE },
-	{ 0x002a, KEY_ESC },          /* Esc */
-	{ 0x002e, KEY_CHANNELUP },
-	{ 0x002d, KEY_CHANNELDOWN },
-	{ 0x0042, KEY_VOLUMEDOWN },
-	{ 0x0043, KEY_VOLUMEUP },
-	{ 0x0052, KEY_UP },           /* up arrow */
-	{ 0x0051, KEY_DOWN },         /* down arrow */
-	{ 0x004f, KEY_RIGHT },        /* right arrow */
-	{ 0x0050, KEY_LEFT },         /* left arrow */
-	{ 0x0028, KEY_ENTER },        /* ok */
-	{ 0x0115, KEY_RECORD },
-	{ 0x0313, KEY_PLAY },
-	{ 0x0113, KEY_PAUSE },
-	{ 0x0116, KEY_STOP },
-	{ 0x0307, KEY_REWIND },       /* FR << */
-	{ 0x0309, KEY_FASTFORWARD },  /* FF >> */
-	{ 0x003b, KEY_TIME },         /* TimeShift */
-	{ 0x003e, KEY_CAMERA },       /* Snapshot */
-	{ 0x0316, KEY_CYCLEWINDOWS }, /* yellow, min / max */
-	{ 0x0000, KEY_ZOOM },         /* 'select' (?) */
-	{ 0x0316, KEY_SHUFFLE },      /* Shuffle */
-	{ 0x0345, KEY_POWER },
-};
-
-static u8 af9015_ir_table_mygictv[] = {
-	0x02, 0xbd, 0x0c, 0xf3, 0x3d, 0x00, 0x00, /* TV / AV */
-	0x02, 0xbd, 0x14, 0xeb, 0x45, 0x05, 0x00, /* power */
-	0x02, 0xbd, 0x00, 0xff, 0x1e, 0x00, 0x00, /* 1 */
-	0x02, 0xbd, 0x01, 0xfe, 0x1f, 0x00, 0x00, /* 2 */
-	0x02, 0xbd, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */
-	0x02, 0xbd, 0x03, 0xfc, 0x21, 0x00, 0x00, /* 4 */
-	0x02, 0xbd, 0x04, 0xfb, 0x22, 0x00, 0x00, /* 5 */
-	0x02, 0xbd, 0x05, 0xfa, 0x23, 0x00, 0x00, /* 6 */
-	0x02, 0xbd, 0x06, 0xf9, 0x24, 0x00, 0x00, /* 7 */
-	0x02, 0xbd, 0x07, 0xf8, 0x25, 0x00, 0x00, /* 8 */
-	0x02, 0xbd, 0x08, 0xf7, 0x26, 0x00, 0x00, /* 9 */
-	0x02, 0xbd, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */
-	0x02, 0xbd, 0x0a, 0xf5, 0x41, 0x00, 0x00, /* mute */
-	0x02, 0xbd, 0x1c, 0xe3, 0x2a, 0x00, 0x00, /* esc */
-	0x02, 0xbd, 0x1f, 0xe0, 0x43, 0x00, 0x00, /* volume up */
-	0x02, 0xbd, 0x12, 0xed, 0x52, 0x00, 0x00, /* up arrow */
-	0x02, 0xbd, 0x11, 0xee, 0x50, 0x00, 0x00, /* left arrow */
-	0x02, 0xbd, 0x15, 0xea, 0x28, 0x00, 0x00, /* ok */
-	0x02, 0xbd, 0x10, 0xef, 0x4f, 0x00, 0x00, /* right arrow */
-	0x02, 0xbd, 0x13, 0xec, 0x51, 0x00, 0x00, /* down arrow */
-	0x02, 0xbd, 0x0e, 0xf1, 0x42, 0x00, 0x00, /* volume down */
-	0x02, 0xbd, 0x19, 0xe6, 0x15, 0x01, 0x00, /* record */
-	0x02, 0xbd, 0x1e, 0xe1, 0x13, 0x03, 0x00, /* play */
-	0x02, 0xbd, 0x16, 0xe9, 0x16, 0x01, 0x00, /* stop */
-	0x02, 0xbd, 0x0b, 0xf4, 0x28, 0x04, 0x00, /* yellow, min / max */
-	0x02, 0xbd, 0x0f, 0xf0, 0x3b, 0x00, 0x00, /* time shift */
-	0x02, 0xbd, 0x18, 0xe7, 0x2e, 0x00, 0x00, /* channel up */
-	0x02, 0xbd, 0x1a, 0xe5, 0x2d, 0x00, 0x00, /* channel down */
-	0x02, 0xbd, 0x17, 0xe8, 0x3e, 0x00, 0x00, /* snapshot */
-	0x02, 0xbd, 0x40, 0xbf, 0x13, 0x01, 0x00, /* pause */
-	0x02, 0xbd, 0x41, 0xbe, 0x09, 0x03, 0x00, /* FF >> */
-	0x02, 0xbd, 0x42, 0xbd, 0x07, 0x03, 0x00, /* FR << */
-	0x02, 0xbd, 0x43, 0xbc, 0x00, 0x00, 0x00, /* 'select' (?) */
-	0x02, 0xbd, 0x44, 0xbb, 0x16, 0x03, 0x00, /* shuffle */
-	0x02, 0xbd, 0x45, 0xba, 0x45, 0x03, 0x00, /* power */
-};
-
-/* KWorld PlusTV Dual DVB-T Stick (DVB-T 399U) */
-static u8 af9015_ir_table_kworld[] = {
-	0x86, 0x6b, 0x0c, 0xf3, 0x2e, 0x07, 0x00,
-	0x86, 0x6b, 0x16, 0xe9, 0x2d, 0x07, 0x00,
-	0x86, 0x6b, 0x1d, 0xe2, 0x37, 0x07, 0x00,
-	0x86, 0x6b, 0x00, 0xff, 0x1e, 0x07, 0x00,
-	0x86, 0x6b, 0x01, 0xfe, 0x1f, 0x07, 0x00,
-	0x86, 0x6b, 0x02, 0xfd, 0x20, 0x07, 0x00,
-	0x86, 0x6b, 0x03, 0xfc, 0x21, 0x07, 0x00,
-	0x86, 0x6b, 0x04, 0xfb, 0x22, 0x07, 0x00,
-	0x86, 0x6b, 0x05, 0xfa, 0x23, 0x07, 0x00,
-	0x86, 0x6b, 0x06, 0xf9, 0x24, 0x07, 0x00,
-	0x86, 0x6b, 0x07, 0xf8, 0x25, 0x07, 0x00,
-	0x86, 0x6b, 0x08, 0xf7, 0x26, 0x07, 0x00,
-	0x86, 0x6b, 0x09, 0xf6, 0x4d, 0x07, 0x00,
-	0x86, 0x6b, 0x0a, 0xf5, 0x4e, 0x07, 0x00,
-	0x86, 0x6b, 0x14, 0xeb, 0x4f, 0x07, 0x00,
-	0x86, 0x6b, 0x1e, 0xe1, 0x50, 0x07, 0x00,
-	0x86, 0x6b, 0x17, 0xe8, 0x52, 0x07, 0x00,
-	0x86, 0x6b, 0x1f, 0xe0, 0x51, 0x07, 0x00,
-	0x86, 0x6b, 0x0e, 0xf1, 0x0b, 0x07, 0x00,
-	0x86, 0x6b, 0x20, 0xdf, 0x0c, 0x07, 0x00,
-	0x86, 0x6b, 0x42, 0xbd, 0x0d, 0x07, 0x00,
-	0x86, 0x6b, 0x0b, 0xf4, 0x0e, 0x07, 0x00,
-	0x86, 0x6b, 0x43, 0xbc, 0x0f, 0x07, 0x00,
-	0x86, 0x6b, 0x10, 0xef, 0x10, 0x07, 0x00,
-	0x86, 0x6b, 0x21, 0xde, 0x11, 0x07, 0x00,
-	0x86, 0x6b, 0x13, 0xec, 0x12, 0x07, 0x00,
-	0x86, 0x6b, 0x11, 0xee, 0x13, 0x07, 0x00,
-	0x86, 0x6b, 0x12, 0xed, 0x14, 0x07, 0x00,
-	0x86, 0x6b, 0x19, 0xe6, 0x15, 0x07, 0x00,
-	0x86, 0x6b, 0x1a, 0xe5, 0x16, 0x07, 0x00,
-	0x86, 0x6b, 0x1b, 0xe4, 0x17, 0x07, 0x00,
-	0x86, 0x6b, 0x4b, 0xb4, 0x18, 0x07, 0x00,
-	0x86, 0x6b, 0x40, 0xbf, 0x19, 0x07, 0x00,
-	0x86, 0x6b, 0x44, 0xbb, 0x1a, 0x07, 0x00,
-	0x86, 0x6b, 0x41, 0xbe, 0x1b, 0x07, 0x00,
-	0x86, 0x6b, 0x22, 0xdd, 0x1c, 0x07, 0x00,
-	0x86, 0x6b, 0x15, 0xea, 0x1d, 0x07, 0x00,
-	0x86, 0x6b, 0x0f, 0xf0, 0x3f, 0x07, 0x00,
-	0x86, 0x6b, 0x1c, 0xe3, 0x40, 0x07, 0x00,
-	0x86, 0x6b, 0x4a, 0xb5, 0x41, 0x07, 0x00,
-	0x86, 0x6b, 0x48, 0xb7, 0x42, 0x07, 0x00,
-	0x86, 0x6b, 0x49, 0xb6, 0x43, 0x07, 0x00,
-	0x86, 0x6b, 0x18, 0xe7, 0x44, 0x07, 0x00,
-	0x86, 0x6b, 0x23, 0xdc, 0x45, 0x07, 0x00,
-};
-
-/* AverMedia Volar X */
-static struct ir_scancode ir_codes_af9015_table_avermedia[] = {
-	{ 0x053d, KEY_PROG1 },       /* SOURCE */
-	{ 0x0512, KEY_POWER },       /* POWER */
-	{ 0x051e, KEY_1 },           /* 1 */
-	{ 0x051f, KEY_2 },           /* 2 */
-	{ 0x0520, KEY_3 },           /* 3 */
-	{ 0x0521, KEY_4 },           /* 4 */
-	{ 0x0522, KEY_5 },           /* 5 */
-	{ 0x0523, KEY_6 },           /* 6 */
-	{ 0x0524, KEY_7 },           /* 7 */
-	{ 0x0525, KEY_8 },           /* 8 */
-	{ 0x0526, KEY_9 },           /* 9 */
-	{ 0x053f, KEY_LEFT },        /* L / DISPLAY */
-	{ 0x0527, KEY_0 },           /* 0 */
-	{ 0x050f, KEY_RIGHT },       /* R / CH RTN */
-	{ 0x0518, KEY_PROG2 },       /* SNAP SHOT */
-	{ 0x051c, KEY_PROG3 },       /* 16-CH PREV */
-	{ 0x052d, KEY_VOLUMEDOWN },  /* VOL DOWN */
-	{ 0x053e, KEY_ZOOM },        /* FULL SCREEN */
-	{ 0x052e, KEY_VOLUMEUP },    /* VOL UP */
-	{ 0x0510, KEY_MUTE },        /* MUTE */
-	{ 0x0504, KEY_AUDIO },       /* AUDIO */
-	{ 0x0515, KEY_RECORD },      /* RECORD */
-	{ 0x0511, KEY_PLAY },        /* PLAY */
-	{ 0x0516, KEY_STOP },        /* STOP */
-	{ 0x050c, KEY_PLAYPAUSE },   /* TIMESHIFT / PAUSE */
-	{ 0x0505, KEY_BACK },        /* << / RED */
-	{ 0x0509, KEY_FORWARD },     /* >> / YELLOW */
-	{ 0x0517, KEY_TEXT },        /* TELETEXT */
-	{ 0x050a, KEY_EPG },         /* EPG */
-	{ 0x0513, KEY_MENU },        /* MENU */
-
-	{ 0x050e, KEY_CHANNELUP },   /* CH UP */
-	{ 0x050d, KEY_CHANNELDOWN }, /* CH DOWN */
-	{ 0x0519, KEY_FIRST },       /* |<< / GREEN */
-	{ 0x0508, KEY_LAST },        /* >>| / BLUE */
-};
-
-static u8 af9015_ir_table_avermedia[] = {
-	0x02, 0xfd, 0x00, 0xff, 0x12, 0x05, 0x00,
-	0x02, 0xfd, 0x01, 0xfe, 0x3d, 0x05, 0x00,
-	0x02, 0xfd, 0x03, 0xfc, 0x17, 0x05, 0x00,
-	0x02, 0xfd, 0x04, 0xfb, 0x0a, 0x05, 0x00,
-	0x02, 0xfd, 0x05, 0xfa, 0x1e, 0x05, 0x00,
-	0x02, 0xfd, 0x06, 0xf9, 0x1f, 0x05, 0x00,
-	0x02, 0xfd, 0x07, 0xf8, 0x20, 0x05, 0x00,
-	0x02, 0xfd, 0x09, 0xf6, 0x21, 0x05, 0x00,
-	0x02, 0xfd, 0x0a, 0xf5, 0x22, 0x05, 0x00,
-	0x02, 0xfd, 0x0b, 0xf4, 0x23, 0x05, 0x00,
-	0x02, 0xfd, 0x0d, 0xf2, 0x24, 0x05, 0x00,
-	0x02, 0xfd, 0x0e, 0xf1, 0x25, 0x05, 0x00,
-	0x02, 0xfd, 0x0f, 0xf0, 0x26, 0x05, 0x00,
-	0x02, 0xfd, 0x11, 0xee, 0x27, 0x05, 0x00,
-	0x02, 0xfd, 0x08, 0xf7, 0x04, 0x05, 0x00,
-	0x02, 0xfd, 0x0c, 0xf3, 0x3e, 0x05, 0x00,
-	0x02, 0xfd, 0x10, 0xef, 0x1c, 0x05, 0x00,
-	0x02, 0xfd, 0x12, 0xed, 0x3f, 0x05, 0x00,
-	0x02, 0xfd, 0x13, 0xec, 0x0f, 0x05, 0x00,
-	0x02, 0xfd, 0x14, 0xeb, 0x10, 0x05, 0x00,
-	0x02, 0xfd, 0x15, 0xea, 0x13, 0x05, 0x00,
-	0x02, 0xfd, 0x17, 0xe8, 0x18, 0x05, 0x00,
-	0x02, 0xfd, 0x18, 0xe7, 0x11, 0x05, 0x00,
-	0x02, 0xfd, 0x19, 0xe6, 0x15, 0x05, 0x00,
-	0x02, 0xfd, 0x1a, 0xe5, 0x0c, 0x05, 0x00,
-	0x02, 0xfd, 0x1b, 0xe4, 0x16, 0x05, 0x00,
-	0x02, 0xfd, 0x1c, 0xe3, 0x09, 0x05, 0x00,
-	0x02, 0xfd, 0x1d, 0xe2, 0x05, 0x05, 0x00,
-	0x02, 0xfd, 0x1e, 0xe1, 0x2d, 0x05, 0x00,
-	0x02, 0xfd, 0x1f, 0xe0, 0x2e, 0x05, 0x00,
-	0x03, 0xfc, 0x00, 0xff, 0x08, 0x05, 0x00,
-	0x03, 0xfc, 0x01, 0xfe, 0x19, 0x05, 0x00,
-	0x03, 0xfc, 0x02, 0xfd, 0x0d, 0x05, 0x00,
-	0x03, 0xfc, 0x03, 0xfc, 0x0e, 0x05, 0x00,
-};
-
-static u8 af9015_ir_table_avermedia_ks[] = {
-	0x05, 0xfa, 0x01, 0xfe, 0x12, 0x05, 0x00,
-	0x05, 0xfa, 0x02, 0xfd, 0x0e, 0x05, 0x00,
-	0x05, 0xfa, 0x03, 0xfc, 0x0d, 0x05, 0x00,
-	0x05, 0xfa, 0x04, 0xfb, 0x2e, 0x05, 0x00,
-	0x05, 0xfa, 0x05, 0xfa, 0x2d, 0x05, 0x00,
-	0x05, 0xfa, 0x06, 0xf9, 0x10, 0x05, 0x00,
-	0x05, 0xfa, 0x07, 0xf8, 0x0f, 0x05, 0x00,
-	0x05, 0xfa, 0x08, 0xf7, 0x3d, 0x05, 0x00,
-	0x05, 0xfa, 0x09, 0xf6, 0x1e, 0x05, 0x00,
-	0x05, 0xfa, 0x0a, 0xf5, 0x1f, 0x05, 0x00,
-	0x05, 0xfa, 0x0b, 0xf4, 0x20, 0x05, 0x00,
-	0x05, 0xfa, 0x0c, 0xf3, 0x21, 0x05, 0x00,
-	0x05, 0xfa, 0x0d, 0xf2, 0x22, 0x05, 0x00,
-	0x05, 0xfa, 0x0e, 0xf1, 0x23, 0x05, 0x00,
-	0x05, 0xfa, 0x0f, 0xf0, 0x24, 0x05, 0x00,
-	0x05, 0xfa, 0x10, 0xef, 0x25, 0x05, 0x00,
-	0x05, 0xfa, 0x11, 0xee, 0x26, 0x05, 0x00,
-	0x05, 0xfa, 0x12, 0xed, 0x27, 0x05, 0x00,
-	0x05, 0xfa, 0x13, 0xec, 0x04, 0x05, 0x00,
-	0x05, 0xfa, 0x15, 0xea, 0x0a, 0x05, 0x00,
-	0x05, 0xfa, 0x16, 0xe9, 0x11, 0x05, 0x00,
-	0x05, 0xfa, 0x17, 0xe8, 0x15, 0x05, 0x00,
-	0x05, 0xfa, 0x18, 0xe7, 0x16, 0x05, 0x00,
-	0x05, 0xfa, 0x1c, 0xe3, 0x05, 0x05, 0x00,
-	0x05, 0xfa, 0x1d, 0xe2, 0x09, 0x05, 0x00,
-	0x05, 0xfa, 0x4d, 0xb2, 0x3f, 0x05, 0x00,
-	0x05, 0xfa, 0x56, 0xa9, 0x3e, 0x05, 0x00
-};
-
-/* Digittrade DVB-T USB Stick */
-static struct ir_scancode ir_codes_af9015_table_digittrade[] = {
-	{ 0x010f, KEY_LAST },	/* RETURN */
-	{ 0x0517, KEY_TEXT },	/* TELETEXT */
-	{ 0x0108, KEY_EPG },	/* EPG */
-	{ 0x0513, KEY_POWER },	/* POWER */
-	{ 0x0109, KEY_ZOOM },	/* FULLSCREEN */
-	{ 0x0040, KEY_AUDIO },	/* DUAL SOUND */
-	{ 0x002c, KEY_PRINT },	/* SNAPSHOT */
-	{ 0x0516, KEY_SUBTITLE },	/* SUBTITLE */
-	{ 0x0052, KEY_CHANNELUP },	/* CH Up */
-	{ 0x0051, KEY_CHANNELDOWN },/* Ch Dn */
-	{ 0x0057, KEY_VOLUMEUP },	/* Vol Up */
-	{ 0x0056, KEY_VOLUMEDOWN },	/* Vol Dn */
-	{ 0x0110, KEY_MUTE },	/* MUTE */
-	{ 0x0027, KEY_0 },
-	{ 0x001e, KEY_1 },
-	{ 0x001f, KEY_2 },
-	{ 0x0020, KEY_3 },
-	{ 0x0021, KEY_4 },
-	{ 0x0022, KEY_5 },
-	{ 0x0023, KEY_6 },
-	{ 0x0024, KEY_7 },
-	{ 0x0025, KEY_8 },
-	{ 0x0026, KEY_9 },
-	{ 0x0117, KEY_PLAYPAUSE },	/* TIMESHIFT */
-	{ 0x0115, KEY_RECORD },	/* RECORD */
-	{ 0x0313, KEY_PLAY },	/* PLAY */
-	{ 0x0116, KEY_STOP },	/* STOP */
-	{ 0x0113, KEY_PAUSE },	/* PAUSE */
-};
-
-static u8 af9015_ir_table_digittrade[] = {
-	0x00, 0xff, 0x06, 0xf9, 0x13, 0x05, 0x00,
-	0x00, 0xff, 0x4d, 0xb2, 0x17, 0x01, 0x00,
-	0x00, 0xff, 0x1f, 0xe0, 0x2c, 0x00, 0x00,
-	0x00, 0xff, 0x0a, 0xf5, 0x15, 0x01, 0x00,
-	0x00, 0xff, 0x0e, 0xf1, 0x16, 0x01, 0x00,
-	0x00, 0xff, 0x09, 0xf6, 0x09, 0x01, 0x00,
-	0x00, 0xff, 0x01, 0xfe, 0x08, 0x01, 0x00,
-	0x00, 0xff, 0x05, 0xfa, 0x10, 0x01, 0x00,
-	0x00, 0xff, 0x02, 0xfd, 0x56, 0x00, 0x00,
-	0x00, 0xff, 0x40, 0xbf, 0x57, 0x00, 0x00,
-	0x00, 0xff, 0x19, 0xe6, 0x52, 0x00, 0x00,
-	0x00, 0xff, 0x17, 0xe8, 0x51, 0x00, 0x00,
-	0x00, 0xff, 0x10, 0xef, 0x0f, 0x01, 0x00,
-	0x00, 0xff, 0x54, 0xab, 0x27, 0x00, 0x00,
-	0x00, 0xff, 0x1b, 0xe4, 0x1e, 0x00, 0x00,
-	0x00, 0xff, 0x11, 0xee, 0x1f, 0x00, 0x00,
-	0x00, 0xff, 0x15, 0xea, 0x20, 0x00, 0x00,
-	0x00, 0xff, 0x12, 0xed, 0x21, 0x00, 0x00,
-	0x00, 0xff, 0x16, 0xe9, 0x22, 0x00, 0x00,
-	0x00, 0xff, 0x4c, 0xb3, 0x23, 0x00, 0x00,
-	0x00, 0xff, 0x48, 0xb7, 0x24, 0x00, 0x00,
-	0x00, 0xff, 0x04, 0xfb, 0x25, 0x00, 0x00,
-	0x00, 0xff, 0x00, 0xff, 0x26, 0x00, 0x00,
-	0x00, 0xff, 0x1e, 0xe1, 0x13, 0x03, 0x00,
-	0x00, 0xff, 0x1a, 0xe5, 0x13, 0x01, 0x00,
-	0x00, 0xff, 0x03, 0xfc, 0x17, 0x05, 0x00,
-	0x00, 0xff, 0x0d, 0xf2, 0x16, 0x05, 0x00,
-	0x00, 0xff, 0x1d, 0xe2, 0x40, 0x00, 0x00,
-};
-
-/* TREKSTOR DVB-T USB Stick */
-static struct ir_scancode ir_codes_af9015_table_trekstor[] = {
-	{ 0x0704, KEY_AGAIN },		/* Home */
-	{ 0x0705, KEY_MUTE },		/* Mute */
-	{ 0x0706, KEY_UP },			/* Up */
-	{ 0x0707, KEY_DOWN },		/* Down */
-	{ 0x0709, KEY_RIGHT },		/* Right */
-	{ 0x070a, KEY_ENTER },		/* OK */
-	{ 0x070b, KEY_FASTFORWARD },	/* Fast forward */
-	{ 0x070c, KEY_REWIND },		/* Rewind */
-	{ 0x070d, KEY_PLAY },		/* Play/Pause */
-	{ 0x070e, KEY_VOLUMEUP },		/* Volume + */
-	{ 0x070f, KEY_VOLUMEDOWN },		/* Volume - */
-	{ 0x0710, KEY_RECORD },		/* Record */
-	{ 0x0711, KEY_STOP },		/* Stop */
-	{ 0x0712, KEY_ZOOM },		/* TV */
-	{ 0x0713, KEY_EPG },		/* Info/EPG */
-	{ 0x0714, KEY_CHANNELDOWN },	/* Channel - */
-	{ 0x0715, KEY_CHANNELUP },		/* Channel + */
-	{ 0x071e, KEY_1 },
-	{ 0x071f, KEY_2 },
-	{ 0x0720, KEY_3 },
-	{ 0x0721, KEY_4 },
-	{ 0x0722, KEY_5 },
-	{ 0x0723, KEY_6 },
-	{ 0x0724, KEY_7 },
-	{ 0x0725, KEY_8 },
-	{ 0x0726, KEY_9 },
-	{ 0x0708, KEY_LEFT },		/* LEFT */
-	{ 0x0727, KEY_0 },
-};
-
-static u8 af9015_ir_table_trekstor[] = {
-	0x00, 0xff, 0x86, 0x79, 0x04, 0x07, 0x00,
-	0x00, 0xff, 0x85, 0x7a, 0x05, 0x07, 0x00,
-	0x00, 0xff, 0x87, 0x78, 0x06, 0x07, 0x00,
-	0x00, 0xff, 0x8c, 0x73, 0x07, 0x07, 0x00,
-	0x00, 0xff, 0x89, 0x76, 0x09, 0x07, 0x00,
-	0x00, 0xff, 0x88, 0x77, 0x0a, 0x07, 0x00,
-	0x00, 0xff, 0x8a, 0x75, 0x0b, 0x07, 0x00,
-	0x00, 0xff, 0x9e, 0x61, 0x0c, 0x07, 0x00,
-	0x00, 0xff, 0x8d, 0x72, 0x0d, 0x07, 0x00,
-	0x00, 0xff, 0x8b, 0x74, 0x0e, 0x07, 0x00,
-	0x00, 0xff, 0x9b, 0x64, 0x0f, 0x07, 0x00,
-	0x00, 0xff, 0x9d, 0x62, 0x10, 0x07, 0x00,
-	0x00, 0xff, 0x8e, 0x71, 0x11, 0x07, 0x00,
-	0x00, 0xff, 0x9c, 0x63, 0x12, 0x07, 0x00,
-	0x00, 0xff, 0x8f, 0x70, 0x13, 0x07, 0x00,
-	0x00, 0xff, 0x93, 0x6c, 0x14, 0x07, 0x00,
-	0x00, 0xff, 0x97, 0x68, 0x15, 0x07, 0x00,
-	0x00, 0xff, 0x92, 0x6d, 0x1e, 0x07, 0x00,
-	0x00, 0xff, 0x96, 0x69, 0x1f, 0x07, 0x00,
-	0x00, 0xff, 0x9a, 0x65, 0x20, 0x07, 0x00,
-	0x00, 0xff, 0x91, 0x6e, 0x21, 0x07, 0x00,
-	0x00, 0xff, 0x95, 0x6a, 0x22, 0x07, 0x00,
-	0x00, 0xff, 0x99, 0x66, 0x23, 0x07, 0x00,
-	0x00, 0xff, 0x90, 0x6f, 0x24, 0x07, 0x00,
-	0x00, 0xff, 0x94, 0x6b, 0x25, 0x07, 0x00,
-	0x00, 0xff, 0x98, 0x67, 0x26, 0x07, 0x00,
-	0x00, 0xff, 0x9f, 0x60, 0x08, 0x07, 0x00,
-	0x00, 0xff, 0x84, 0x7b, 0x27, 0x07, 0x00,
-};
-
-/* MSI DIGIVOX mini III */
-static struct ir_scancode ir_codes_af9015_table_msi_digivox_iii[] = {
-	{ 0x0713, KEY_POWER },       /* [red power button] */
-	{ 0x073b, KEY_VIDEO },       /* Source */
-	{ 0x073e, KEY_ZOOM },        /* Zoom */
-	{ 0x070b, KEY_POWER2 },      /* ShutDown */
-	{ 0x071e, KEY_1 },
-	{ 0x071f, KEY_2 },
-	{ 0x0720, KEY_3 },
-	{ 0x0721, KEY_4 },
-	{ 0x0722, KEY_5 },
-	{ 0x0723, KEY_6 },
-	{ 0x0724, KEY_7 },
-	{ 0x0725, KEY_8 },
-	{ 0x0726, KEY_9 },
-	{ 0x0727, KEY_0 },
-	{ 0x0752, KEY_CHANNELUP },   /* CH+ */
-	{ 0x0751, KEY_CHANNELDOWN }, /* CH- */
-	{ 0x0750, KEY_VOLUMEUP },    /* Vol+ */
-	{ 0x074f, KEY_VOLUMEDOWN },  /* Vol- */
-	{ 0x0705, KEY_ESC },         /* [back up arrow] */
-	{ 0x0708, KEY_OK },          /* [enter arrow] */
-	{ 0x073f, KEY_RECORD },      /* Rec */
-	{ 0x0716, KEY_STOP },        /* Stop */
-	{ 0x072a, KEY_PLAY },        /* Play */
-	{ 0x073c, KEY_MUTE },        /* Mute */
-	{ 0x0718, KEY_UP },
-	{ 0x0707, KEY_DOWN },
-	{ 0x070f, KEY_LEFT },
-	{ 0x0715, KEY_RIGHT },
-	{ 0x0736, KEY_RED },
-	{ 0x0737, KEY_GREEN },
-	{ 0x072d, KEY_YELLOW },
-	{ 0x072e, KEY_BLUE },
-};
-
-static u8 af9015_ir_table_msi_digivox_iii[] = {
-	0x61, 0xd6, 0x43, 0xbc, 0x13, 0x07, 0x00, /* KEY_POWER */
-	0x61, 0xd6, 0x01, 0xfe, 0x3b, 0x07, 0x00, /* KEY_VIDEO */
-	0x61, 0xd6, 0x0b, 0xf4, 0x3e, 0x07, 0x00, /* KEY_ZOOM */
-	0x61, 0xd6, 0x03, 0xfc, 0x0b, 0x07, 0x00, /* KEY_POWER2 */
-	0x61, 0xd6, 0x04, 0xfb, 0x1e, 0x07, 0x00, /* KEY_1 */
-	0x61, 0xd6, 0x08, 0xf7, 0x1f, 0x07, 0x00, /* KEY_2 */
-	0x61, 0xd6, 0x02, 0xfd, 0x20, 0x07, 0x00, /* KEY_3 */
-	0x61, 0xd6, 0x0f, 0xf0, 0x21, 0x07, 0x00, /* KEY_4 */
-	0x61, 0xd6, 0x05, 0xfa, 0x22, 0x07, 0x00, /* KEY_5 */
-	0x61, 0xd6, 0x06, 0xf9, 0x23, 0x07, 0x00, /* KEY_6 */
-	0x61, 0xd6, 0x0c, 0xf3, 0x24, 0x07, 0x00, /* KEY_7 */
-	0x61, 0xd6, 0x0d, 0xf2, 0x25, 0x07, 0x00, /* KEY_8 */
-	0x61, 0xd6, 0x0a, 0xf5, 0x26, 0x07, 0x00, /* KEY_9 */
-	0x61, 0xd6, 0x11, 0xee, 0x27, 0x07, 0x00, /* KEY_0 */
-	0x61, 0xd6, 0x09, 0xf6, 0x52, 0x07, 0x00, /* KEY_CHANNELUP */
-	0x61, 0xd6, 0x07, 0xf8, 0x51, 0x07, 0x00, /* KEY_CHANNELDOWN */
-	0x61, 0xd6, 0x0e, 0xf1, 0x50, 0x07, 0x00, /* KEY_VOLUMEUP */
-	0x61, 0xd6, 0x13, 0xec, 0x4f, 0x07, 0x00, /* KEY_VOLUMEDOWN */
-	0x61, 0xd6, 0x10, 0xef, 0x05, 0x07, 0x00, /* KEY_ESC */
-	0x61, 0xd6, 0x12, 0xed, 0x08, 0x07, 0x00, /* KEY_OK */
-	0x61, 0xd6, 0x14, 0xeb, 0x3f, 0x07, 0x00, /* KEY_RECORD */
-	0x61, 0xd6, 0x15, 0xea, 0x16, 0x07, 0x00, /* KEY_STOP */
-	0x61, 0xd6, 0x16, 0xe9, 0x2a, 0x07, 0x00, /* KEY_PLAY */
-	0x61, 0xd6, 0x17, 0xe8, 0x3c, 0x07, 0x00, /* KEY_MUTE */
-	0x61, 0xd6, 0x18, 0xe7, 0x18, 0x07, 0x00, /* KEY_UP */
-	0x61, 0xd6, 0x19, 0xe6, 0x07, 0x07, 0x00, /* KEY_DOWN */
-	0x61, 0xd6, 0x1a, 0xe5, 0x0f, 0x07, 0x00, /* KEY_LEFT */
-	0x61, 0xd6, 0x1b, 0xe4, 0x15, 0x07, 0x00, /* KEY_RIGHT */
-	0x61, 0xd6, 0x1c, 0xe3, 0x36, 0x07, 0x00, /* KEY_RED */
-	0x61, 0xd6, 0x1d, 0xe2, 0x37, 0x07, 0x00, /* KEY_GREEN */
-	0x61, 0xd6, 0x1e, 0xe1, 0x2d, 0x07, 0x00, /* KEY_YELLOW */
-	0x61, 0xd6, 0x1f, 0xe0, 0x2e, 0x07, 0x00, /* KEY_BLUE */
-};
-
 #endif
diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c
index 4685259..1759d26 100644
--- a/drivers/media/dvb/dvb-usb/anysee.c
+++ b/drivers/media/dvb/dvb-usb/anysee.c
@@ -354,7 +354,7 @@
 static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
 {
 	struct anysee_state *state = adap->dev->priv;
-	deb_info("%s: \n", __func__);
+	deb_info("%s:\n", __func__);
 
 	switch (state->tuner) {
 	case DVB_PLL_THOMSON_DTT7579:
@@ -374,78 +374,32 @@
 	return 0;
 }
 
-static int anysee_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
+static int anysee_rc_query(struct dvb_usb_device *d)
 {
 	u8 buf[] = {CMD_GET_IR_CODE};
-	struct ir_scancode *keymap = d->props.rc.legacy.rc_key_map;
 	u8 ircode[2];
-	int i, ret;
+	int ret;
 
-	ret = anysee_ctrl_msg(d, buf, sizeof(buf), &ircode[0], 2);
+	/* Remote controller is basic NEC using address byte 0x08.
+	   Anysee device RC query returns only two bytes, status and code,
+	   address byte is dropped. Also it does not return any value for
+	   NEC RCs having address byte other than 0x08. Due to that, we
+	   cannot use that device as standard NEC receiver.
+	   It could be possible make hack which reads whole code directly
+	   from device memory... */
+
+	ret = anysee_ctrl_msg(d, buf, sizeof(buf), ircode, sizeof(ircode));
 	if (ret)
 		return ret;
 
-	*event = 0;
-	*state = REMOTE_NO_KEY_PRESSED;
-
-	for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) {
-		if (rc5_custom(&keymap[i]) == ircode[0] &&
-		    rc5_data(&keymap[i]) == ircode[1]) {
-			*event = keymap[i].keycode;
-			*state = REMOTE_KEY_PRESSED;
-			return 0;
-		}
+	if (ircode[0]) {
+		deb_rc("%s: key pressed %02x\n", __func__, ircode[1]);
+		ir_keydown(d->rc_input_dev, 0x08 << 8 | ircode[1], 0);
 	}
+
 	return 0;
 }
 
-static struct ir_scancode ir_codes_anysee_table[] = {
-	{ 0x0100, KEY_0 },
-	{ 0x0101, KEY_1 },
-	{ 0x0102, KEY_2 },
-	{ 0x0103, KEY_3 },
-	{ 0x0104, KEY_4 },
-	{ 0x0105, KEY_5 },
-	{ 0x0106, KEY_6 },
-	{ 0x0107, KEY_7 },
-	{ 0x0108, KEY_8 },
-	{ 0x0109, KEY_9 },
-	{ 0x010a, KEY_POWER },
-	{ 0x010b, KEY_DOCUMENTS },    /* * */
-	{ 0x0119, KEY_FAVORITES },
-	{ 0x0120, KEY_SLEEP },
-	{ 0x0121, KEY_MODE },         /* 4:3 / 16:9 select */
-	{ 0x0122, KEY_ZOOM },
-	{ 0x0147, KEY_TEXT },
-	{ 0x0116, KEY_TV },           /* TV / radio select */
-	{ 0x011e, KEY_LANGUAGE },     /* Second Audio Program */
-	{ 0x011a, KEY_SUBTITLE },
-	{ 0x011b, KEY_CAMERA },       /* screenshot */
-	{ 0x0142, KEY_MUTE },
-	{ 0x010e, KEY_MENU },
-	{ 0x010f, KEY_EPG },
-	{ 0x0117, KEY_INFO },
-	{ 0x0110, KEY_EXIT },
-	{ 0x0113, KEY_VOLUMEUP },
-	{ 0x0112, KEY_VOLUMEDOWN },
-	{ 0x0111, KEY_CHANNELUP },
-	{ 0x0114, KEY_CHANNELDOWN },
-	{ 0x0115, KEY_OK },
-	{ 0x011d, KEY_RED },
-	{ 0x011f, KEY_GREEN },
-	{ 0x011c, KEY_YELLOW },
-	{ 0x0144, KEY_BLUE },
-	{ 0x010c, KEY_SHUFFLE },      /* snapshot */
-	{ 0x0148, KEY_STOP },
-	{ 0x0150, KEY_PLAY },
-	{ 0x0151, KEY_PAUSE },
-	{ 0x0149, KEY_RECORD },
-	{ 0x0118, KEY_PREVIOUS },     /* |<< */
-	{ 0x010d, KEY_NEXT },         /* >>| */
-	{ 0x0124, KEY_PROG1 },        /* F1 */
-	{ 0x0125, KEY_PROG2 },        /* F2 */
-};
-
 /* DVB USB Driver stuff */
 static struct dvb_usb_device_properties anysee_properties;
 
@@ -520,11 +474,12 @@
 		}
 	},
 
-	.rc.legacy = {
-		.rc_key_map       = ir_codes_anysee_table,
-		.rc_key_map_size  = ARRAY_SIZE(ir_codes_anysee_table),
+	.rc.core = {
+		.rc_codes         = RC_MAP_ANYSEE,
+		.protocol         = IR_TYPE_OTHER,
+		.module_name      = "anysee",
 		.rc_query         = anysee_rc_query,
-		.rc_interval      = 200,  /* windows driver uses 500ms */
+		.rc_interval      = 250,  /* windows driver uses 500ms */
 	},
 
 	.i2c_algo         = &anysee_i2c_algo,
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c b/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c
index cead089b..88e4a62 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c
@@ -20,7 +20,6 @@
 	}
 
 	strlcpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name));
-	d->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
 	d->i2c_adap.algo      = d->props.i2c_algo;
 	d->i2c_adap.algo_data = NULL;
 	d->i2c_adap.dev.parent = &d->udev->dev;
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 1a774d5..192a40c 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -32,6 +32,7 @@
 #define USB_VID_EMPIA				0xeb1a
 #define USB_VID_GENPIX				0x09c0
 #define USB_VID_GRANDTEC			0x5032
+#define USB_VID_GTEK				0x1f4d
 #define USB_VID_HANFTEK				0x15f4
 #define USB_VID_HAUPPAUGE			0x2040
 #define USB_VID_HYPER_PALTEK			0x1025
@@ -133,6 +134,8 @@
 #define USB_PID_KWORLD_VSTREAM_WARM			0x17df
 #define USB_PID_TERRATEC_CINERGY_T_USB_XE		0x0055
 #define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2		0x0069
+#define USB_PID_TERRATEC_CINERGY_T_STICK_RC		0x0097
+#define USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC	0x0099
 #define USB_PID_TWINHAN_VP7041_COLD			0x3201
 #define USB_PID_TWINHAN_VP7041_WARM			0x3202
 #define USB_PID_TWINHAN_VP7020_COLD			0x3203
@@ -143,6 +146,7 @@
 #define USB_PID_TWINHAN_VP7021_WARM			0x3208
 #define USB_PID_TINYTWIN				0x3226
 #define USB_PID_TINYTWIN_2				0xe402
+#define USB_PID_TINYTWIN_3				0x9016
 #define USB_PID_DNTV_TINYUSB2_COLD			0x3223
 #define USB_PID_DNTV_TINYUSB2_WARM			0x3224
 #define USB_PID_ULTIMA_TVBOX_COLD			0x8105
@@ -196,6 +200,7 @@
 #define USB_PID_AVERMEDIA_A309				0xa309
 #define USB_PID_AVERMEDIA_A310				0xa310
 #define USB_PID_AVERMEDIA_A850				0x850a
+#define USB_PID_AVERMEDIA_A850T				0x850b
 #define USB_PID_AVERMEDIA_A805				0xa805
 #define USB_PID_AVERMEDIA_A815M				0x815a
 #define USB_PID_TECHNOTREND_CONNECT_S2400               0x3006
@@ -268,6 +273,7 @@
 #define USB_PID_GENPIX_8PSK_REV_2			0x0202
 #define USB_PID_GENPIX_SKYWALKER_1			0x0203
 #define USB_PID_GENPIX_SKYWALKER_CW3K			0x0204
+#define USB_PID_GENPIX_SKYWALKER_2			0x0206
 #define USB_PID_SIGMATEK_DVB_110			0x6610
 #define USB_PID_MSI_DIGI_VOX_MINI_II			0x1513
 #define USB_PID_MSI_DIGIVOX_DUO				0x8801
diff --git a/drivers/media/dvb/dvb-usb/friio-fe.c b/drivers/media/dvb/dvb-usb/friio-fe.c
index 93c21dd..015b4e8 100644
--- a/drivers/media/dvb/dvb-usb/friio-fe.c
+++ b/drivers/media/dvb/dvb-usb/friio-fe.c
@@ -75,7 +75,7 @@
 	return 0;
 }
 
-static int _jdvbt90502_write(struct dvb_frontend *fe, u8 *buf, int len)
+static int _jdvbt90502_write(struct dvb_frontend *fe, const u8 buf[], int len)
 {
 	struct jdvbt90502_state *state = fe->demodulator_priv;
 	int err, i;
diff --git a/drivers/media/dvb/dvb-usb/gp8psk-fe.c b/drivers/media/dvb/dvb-usb/gp8psk-fe.c
index dbdb534..60d11e5 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk-fe.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk-fe.c
@@ -109,7 +109,7 @@
 
 static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
 {
-	tune->min_delay_ms = 200;
+	tune->min_delay_ms = 800;
 	return 0;
 }
 
@@ -334,7 +334,7 @@
 
 static struct dvb_frontend_ops gp8psk_fe_ops = {
 	.info = {
-		.name			= "Genpix 8psk-to-USB2 DVB-S",
+		.name			= "Genpix DVB-S",
 		.type			= FE_QPSK,
 		.frequency_min		= 800000,
 		.frequency_max		= 2250000,
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c
index 45106ac..c821293 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk.c
@@ -227,6 +227,7 @@
 	    { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_REV_1_WARM) },
 	    { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_REV_2) },
 	    { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_SKYWALKER_1) },
+	    { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_SKYWALKER_2) },
 /*	    { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_SKYWALKER_CW3K) }, */
 	    { 0 },
 };
@@ -258,7 +259,7 @@
 
 	.generic_bulk_ctrl_endpoint = 0x01,
 
-	.num_device_descs = 3,
+	.num_device_descs = 4,
 	.devices = {
 		{ .name = "Genpix 8PSK-to-USB2 Rev.1 DVB-S receiver",
 		  .cold_ids = { &gp8psk_usb_table[0], NULL },
@@ -272,6 +273,10 @@
 		  .cold_ids = { NULL },
 		  .warm_ids = { &gp8psk_usb_table[3], NULL },
 		},
+		{ .name = "Genpix SkyWalker-2 DVB-S receiver",
+		  .cold_ids = { NULL },
+		  .warm_ids = { &gp8psk_usb_table[4], NULL },
+		},
 		{ NULL },
 	}
 };
@@ -306,6 +311,6 @@
 module_exit(gp8psk_usb_module_exit);
 
 MODULE_AUTHOR("Alan Nisota <alannisota@gamil.com>");
-MODULE_DESCRIPTION("Driver for Genpix 8psk-to-USB2 DVB-S");
+MODULE_DESCRIPTION("Driver for Genpix DVB-S");
 MODULE_VERSION("1.1");
 MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c
new file mode 100644
index 0000000..d939fbb
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/lmedm04.c
@@ -0,0 +1,1088 @@
+/* DVB USB compliant linux driver for
+ *
+ * DM04/QQBOX DVB-S USB BOX	LME2510C + SHARP:BS2F7HZ7395
+ *				LME2510C + LG TDQY-P001F
+ *				LME2510 + LG TDQY-P001F
+ *
+ * MVB7395 (LME2510C+SHARP:BS2F7HZ7395)
+ * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V)
+ *
+ * MV001F (LME2510+LGTDQY-P001F)
+ * LG TDQY - P001F =(TDA8263 + TDA10086H)
+ *
+ * MVB0001F (LME2510C+LGTDQT-P001F)
+ *
+ * For firmware see Documentation/dvb/lmedm04.txt
+ *
+ * I2C addresses:
+ * 0xd0 - STV0288	- Demodulator
+ * 0xc0 - Sharp IX2505V	- Tuner
+ * --or--
+ * 0x1c - TDA10086   - Demodulator
+ * 0xc0 - TDA8263    - Tuner
+ *
+ * ***Please Note***
+ *		There are other variants of the DM04
+ *		***NOT SUPPORTED***
+ *		MV0194 (LME2510+SHARP0194)
+ *		MVB0194 (LME2510C+SHARP0194)
+ *
+ *
+ * VID = 3344  PID LME2510=1122 LME2510C=1120
+ *
+ * Copyright (C) 2010 Malcolm Priestley (tvboxspy@gmail.com)
+ * LME2510(C)(C) Leaguerme (Shenzhen) MicroElectronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ *
+ * Known Issues :
+ *	LME2510: Non Intel USB chipsets fail to maintain High Speed on
+ * Boot or Hot Plug.
+ *
+ * QQbox suffers from noise on LNB voltage.
+ *
+ *	PID functions have been removed from this driver version due to
+ * problems with different firmware and application versions.
+ */
+#define DVB_USB_LOG_PREFIX "LME2510(C)"
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <media/ir-core.h>
+
+#include "dvb-usb.h"
+#include "lmedm04.h"
+#include "tda826x.h"
+#include "tda10086.h"
+#include "stv0288.h"
+#include "ix2505v.h"
+
+
+
+/* debug */
+static int dvb_usb_lme2510_debug;
+#define l_dprintk(var, level, args...) do { \
+	if ((var >= level)) \
+		printk(KERN_DEBUG DVB_USB_LOG_PREFIX ": " args); \
+} while (0)
+
+#define deb_info(level, args...) l_dprintk(dvb_usb_lme2510_debug, level, args)
+#define debug_data_snipet(level, name, p) \
+	 deb_info(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \
+		*p, *(p+1), *(p+2), *(p+3), *(p+4), \
+			*(p+5), *(p+6), *(p+7));
+
+
+module_param_named(debug, dvb_usb_lme2510_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."
+			DVB_USB_DEBUG_STATUS);
+
+static int dvb_usb_lme2510_firmware;
+module_param_named(firmware, dvb_usb_lme2510_firmware, int, 0644);
+MODULE_PARM_DESC(firmware, "set default firmware 0=Sharp7395 1=LG");
+
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+#define TUNER_LG	0x1
+#define TUNER_S7395	0x2
+
+struct lme2510_state {
+	u8 id;
+	u8 tuner_config;
+	u8 signal_lock;
+	u8 signal_level;
+	u8 signal_sn;
+	u8 time_key;
+	u8 i2c_talk_onoff;
+	u8 i2c_gate;
+	u8 i2c_tuner_gate_w;
+	u8 i2c_tuner_gate_r;
+	u8 i2c_tuner_addr;
+	u8 stream_on;
+	u8 one_tune;
+	void *buffer;
+	struct urb *lme_urb;
+	void *usb_buffer;
+
+};
+
+static int lme2510_bulk_write(struct usb_device *dev,
+				u8 *snd, int len, u8 pipe)
+{
+	int ret, actual_l;
+
+	ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe),
+				snd, len , &actual_l, 500);
+	return ret;
+}
+
+static int lme2510_bulk_read(struct usb_device *dev,
+				u8 *rev, int len, u8 pipe)
+{
+	int ret, actual_l;
+
+	ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe),
+				 rev, len , &actual_l, 500);
+	return ret;
+}
+
+static int lme2510_usb_talk(struct dvb_usb_device *d,
+		u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+{
+	struct lme2510_state *st = d->priv;
+	u8 *buff;
+	int ret = 0;
+
+	if (st->usb_buffer == NULL) {
+		st->usb_buffer = kmalloc(512, GFP_KERNEL);
+		if (st->usb_buffer == NULL) {
+			info("MEM Error no memory");
+			return -ENOMEM;
+		}
+	}
+	buff = st->usb_buffer;
+
+	/* the read/write capped at 512 */
+	memcpy(buff, wbuf, (wlen > 512) ? 512 : wlen);
+
+	ret = mutex_lock_interruptible(&d->usb_mutex);
+
+	if (ret < 0)
+		return -EAGAIN;
+
+	ret |= usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, 0x01));
+
+	ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x01);
+
+	msleep(12);
+
+	ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x01));
+
+	ret |= lme2510_bulk_read(d->udev, buff, (rlen > 512) ?
+			512 : rlen , 0x01);
+
+	if (rlen > 0)
+		memcpy(rbuf, buff, rlen);
+
+	mutex_unlock(&d->usb_mutex);
+
+	return (ret < 0) ? -ENODEV : 0;
+}
+
+static int lme2510_usb_talk_restart(struct dvb_usb_device *d,
+		u8 *wbuf, int wlen, u8 *rbuf, int rlen) {
+	static u8 stream_on[] = LME_ST_ON_W;
+	int ret;
+	u8 rbuff[10];
+	/*Send Normal Command*/
+	ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
+	/*Restart Stream Command*/
+	ret |= lme2510_usb_talk(d, stream_on, sizeof(stream_on),
+			rbuff, sizeof(rbuff));
+	return ret;
+}
+static int lme2510_remote_keypress(struct dvb_usb_adapter *adap, u16 keypress)
+{
+	struct dvb_usb_device *d = adap->dev;
+
+	deb_info(1, "INT Key Keypress =%04x", keypress);
+
+	if (keypress > 0)
+		ir_keydown(d->rc_input_dev, keypress, 0);
+
+	return 0;
+}
+
+static void lme2510_int_response(struct urb *lme_urb)
+{
+	struct dvb_usb_adapter *adap = lme_urb->context;
+	struct lme2510_state *st = adap->dev->priv;
+	static u8 *ibuf, *rbuf;
+	int i = 0, offset;
+
+	switch (lme_urb->status) {
+	case 0:
+	case -ETIMEDOUT:
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+	default:
+		info("Error %x", lme_urb->status);
+		break;
+	}
+
+	rbuf = (u8 *) lme_urb->transfer_buffer;
+
+	offset = ((lme_urb->actual_length/8) > 4)
+			? 4 : (lme_urb->actual_length/8) ;
+
+	for (i = 0; i < offset; ++i) {
+		ibuf = (u8 *)&rbuf[i*8];
+		deb_info(5, "INT O/S C =%02x C/O=%02x Type =%02x%02x",
+		offset, i, ibuf[0], ibuf[1]);
+
+		switch (ibuf[0]) {
+		case 0xaa:
+			debug_data_snipet(1, "INT Remote data snipet in", ibuf);
+			lme2510_remote_keypress(adap,
+				(u16)(ibuf[4]<<8)+ibuf[5]);
+			break;
+		case 0xbb:
+			switch (st->tuner_config) {
+			case TUNER_LG:
+				if (ibuf[2] > 0)
+					st->signal_lock = ibuf[2];
+				st->signal_level = ibuf[4];
+				st->signal_sn = ibuf[3];
+				st->time_key = ibuf[7];
+				break;
+			case TUNER_S7395:
+				/* Tweak for earlier firmware*/
+				if (ibuf[1] == 0x03) {
+					st->signal_level = ibuf[3];
+					st->signal_sn = ibuf[4];
+				} else {
+					st->signal_level = ibuf[4];
+					st->signal_sn = ibuf[5];
+				}
+				break;
+			default:
+				break;
+			}
+			debug_data_snipet(5, "INT Remote data snipet in", ibuf);
+		break;
+		case 0xcc:
+			debug_data_snipet(1, "INT Control data snipet", ibuf);
+			break;
+		default:
+			debug_data_snipet(1, "INT Unknown data snipet", ibuf);
+		break;
+		}
+	}
+	usb_submit_urb(lme_urb, GFP_ATOMIC);
+}
+
+static int lme2510_int_read(struct dvb_usb_adapter *adap)
+{
+	struct lme2510_state *lme_int = adap->dev->priv;
+
+	lme_int->lme_urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+	if (lme_int->lme_urb == NULL)
+			return -ENOMEM;
+
+	lme_int->buffer = usb_alloc_coherent(adap->dev->udev, 5000, GFP_ATOMIC,
+					&lme_int->lme_urb->transfer_dma);
+
+	if (lme_int->buffer == NULL)
+			return -ENOMEM;
+
+	usb_fill_int_urb(lme_int->lme_urb,
+				adap->dev->udev,
+				usb_rcvintpipe(adap->dev->udev, 0xa),
+				lme_int->buffer,
+				4096,
+				lme2510_int_response,
+				adap,
+				11);
+
+	lme_int->lme_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	usb_submit_urb(lme_int->lme_urb, GFP_ATOMIC);
+	info("INT Interupt Service Started");
+
+	return 0;
+}
+
+static int lme2510_return_status(struct usb_device *dev)
+{
+	int ret = 0;
+	u8 data[10] = {0};
+
+	ret |= usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			0x06, 0x80, 0x0302, 0x00, data, 0x0006, 200);
+	info("Firmware Status: %x (%x)", ret , data[2]);
+
+	return (ret < 0) ? -ENODEV : data[2];
+}
+
+static int lme2510_msg(struct dvb_usb_device *d,
+		u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+{
+	int ret = 0;
+	struct lme2510_state *st = d->priv;
+
+	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+			return -EAGAIN;
+
+	if (st->i2c_talk_onoff == 1) {
+
+		ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
+
+		switch (st->tuner_config) {
+		case TUNER_LG:
+			if (wbuf[2] == 0x1c) {
+				if (wbuf[3] == 0x0e) {
+					st->signal_lock = rbuf[1];
+					if ((st->stream_on & 1) &&
+						(st->signal_lock & 0x10)) {
+						lme2510_usb_talk_restart(d,
+							wbuf, wlen, rbuf, rlen);
+						st->i2c_talk_onoff = 0;
+					}
+				msleep(80);
+				}
+			}
+			break;
+		case TUNER_S7395:
+			if (wbuf[2] == 0xd0) {
+				if (wbuf[3] == 0x24) {
+					st->signal_lock = rbuf[1];
+					if ((st->stream_on & 1) &&
+						(st->signal_lock & 0x8)) {
+						lme2510_usb_talk_restart(d,
+							wbuf, wlen, rbuf, rlen);
+						st->i2c_talk_onoff = 0;
+					}
+				}
+				if ((wbuf[3] != 0x6) & (wbuf[3] != 0x5))
+					msleep(5);
+
+
+			}
+			break;
+		default:
+			break;
+		}
+	} else {
+		switch (st->tuner_config) {
+		case TUNER_LG:
+			switch (wbuf[3]) {
+			case 0x0e:
+				rbuf[0] = 0x55;
+				rbuf[1] = st->signal_lock;
+				break;
+			case 0x43:
+				rbuf[0] = 0x55;
+				rbuf[1] = st->signal_level;
+				break;
+			case 0x1c:
+				rbuf[0] = 0x55;
+				rbuf[1] = st->signal_sn;
+				break;
+			/*DiSEqC functions as per TDA10086*/
+			case 0x36:
+			case 0x48:
+			case 0x49:
+			case 0x4a:
+			case 0x4b:
+			case 0x4c:
+			case 0x4d:
+			if (wbuf[2] == 0x1c)
+					lme2510_usb_talk_restart(d,
+						wbuf, wlen, rbuf, rlen);
+			default:
+				break;
+			}
+			break;
+		case TUNER_S7395:
+			switch (wbuf[3]) {
+			case 0x10:
+				rbuf[0] = 0x55;
+				rbuf[1] = (st->signal_level & 0x80)
+						? 0 : (st->signal_level * 2);
+				break;
+			case 0x2d:
+				rbuf[0] = 0x55;
+				rbuf[1] = st->signal_sn;
+				break;
+			case 0x24:
+				rbuf[0] = 0x55;
+				rbuf[1] = (st->signal_level & 0x80)
+						? 0 : st->signal_lock;
+				break;
+			case 0x6:
+				if (wbuf[2] == 0xd0)
+					lme2510_usb_talk(d,
+						wbuf, wlen, rbuf, rlen);
+				break;
+			case 0x1:
+				if (st->one_tune > 0)
+					break;
+				st->one_tune++;
+				st->i2c_talk_onoff = 1;
+			/*DiSEqC functions as per STV0288*/
+			case 0x5:
+			case 0x7:
+			case 0x8:
+			case 0x9:
+			case 0xa:
+			case 0xb:
+				if (wbuf[2] == 0xd0)
+					lme2510_usb_talk_restart(d,
+						wbuf, wlen, rbuf, rlen);
+				break;
+			default:
+				rbuf[0] = 0x55;
+				rbuf[1] = 0x00;
+				break;
+			}
+			break;
+		default:
+			break;
+
+		}
+
+		deb_info(4, "I2C From Interupt Message out(%02x) in(%02x)",
+				wbuf[3], rbuf[1]);
+
+	}
+
+	mutex_unlock(&d->i2c_mutex);
+
+	return ret;
+}
+
+
+static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+				 int num)
+{
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	struct lme2510_state *st = d->priv;
+	static u8 obuf[64], ibuf[512];
+	int i, read, read_o;
+	u16 len;
+	u8 gate = st->i2c_gate;
+
+	if (gate == 0)
+		gate = 5;
+
+	if (num > 2)
+		warn("more than 2 i2c messages"
+			"at a time is not handled yet.	TODO.");
+
+	for (i = 0; i < num; i++) {
+		read_o = 1 & (msg[i].flags & I2C_M_RD);
+		read = i+1 < num && (msg[i+1].flags & I2C_M_RD);
+		read |= read_o;
+		gate = (msg[i].addr == st->i2c_tuner_addr)
+			? (read)	? st->i2c_tuner_gate_r
+					: st->i2c_tuner_gate_w
+			: st->i2c_gate;
+		obuf[0] = gate | (read << 7);
+
+		if (gate == 5)
+			obuf[1] = (read) ? 2 : msg[i].len + 1;
+		else
+			obuf[1] = msg[i].len + read + 1;
+
+		obuf[2] = msg[i].addr;
+		if (read) {
+			if (read_o)
+				len = 3;
+			else {
+				memcpy(&obuf[3], msg[i].buf, msg[i].len);
+				obuf[msg[i].len+3] = msg[i+1].len;
+				len = msg[i].len+4;
+			}
+		} else {
+			memcpy(&obuf[3], msg[i].buf, msg[i].len);
+			len = msg[i].len+3;
+		}
+
+		if (lme2510_msg(d, obuf, len, ibuf, 512) < 0) {
+			deb_info(1, "i2c transfer failed.");
+			return -EAGAIN;
+		}
+
+		if (read) {
+			if (read_o)
+				memcpy(msg[i].buf, &ibuf[1], msg[i].len);
+			else {
+				memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len);
+				i++;
+			}
+		}
+	}
+	return i;
+}
+
+static u32 lme2510_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm lme2510_i2c_algo = {
+	.master_xfer   = lme2510_i2c_xfer,
+	.functionality = lme2510_i2c_func,
+};
+
+/* Callbacks for DVB USB */
+static int lme2510_identify_state(struct usb_device *udev,
+		struct dvb_usb_device_properties *props,
+		struct dvb_usb_device_description **desc,
+		int *cold)
+{
+	if (lme2510_return_status(udev) == 0x44)
+		*cold = 1;
+	else
+		*cold = 0;
+	return 0;
+}
+
+static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+	struct lme2510_state *st = adap->dev->priv;
+	static u8 stream_on[] = LME_ST_ON_W;
+	static u8 clear_reg_3[] =  LME_CLEAR_PID;
+	static u8 rbuf[1];
+	static u8 timeout;
+	int ret = 0, len = 2, rlen = sizeof(rbuf);
+
+	deb_info(1, "STM  (%02x)", onoff);
+
+	if (onoff == 1)	{
+		st->i2c_talk_onoff = 0;
+		timeout = 0;
+		/* wait for i2C to be free */
+		while (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) {
+			timeout++;
+			if (timeout > 5)
+				return -ENODEV;
+		}
+		msleep(100);
+		ret |= lme2510_usb_talk(adap->dev,
+				 stream_on,  len, rbuf, rlen);
+		st->stream_on = 1;
+		st->one_tune = 0;
+		mutex_unlock(&adap->dev->i2c_mutex);
+	} else {
+		deb_info(1, "STM Steam Off");
+		ret |= lme2510_usb_talk(adap->dev, clear_reg_3,
+				sizeof(clear_reg_3), rbuf, rlen);
+		st->stream_on = 0;
+		st->i2c_talk_onoff = 1;
+	}
+
+	return (ret < 0) ? -ENODEV : 0;
+}
+
+static int lme2510_int_service(struct dvb_usb_adapter *adap)
+{
+	struct dvb_usb_device *d = adap->dev;
+	struct input_dev *input_dev;
+	char *ir_codes = RC_MAP_LME2510;
+	int ret = 0;
+
+	info("STA Configuring Remote");
+
+	usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys));
+
+	strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys));
+
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		return -ENOMEM;
+
+	input_dev->name = "LME2510 Remote Control";
+	input_dev->phys = d->rc_phys;
+
+	usb_to_input_id(d->udev, &input_dev->id);
+
+	ret |= ir_input_register(input_dev, ir_codes, NULL, "LME 2510");
+
+	if (ret) {
+		input_free_device(input_dev);
+		return ret;
+	}
+
+	d->rc_input_dev = input_dev;
+	/* Start the Interupt */
+	ret = lme2510_int_read(adap);
+
+	if (ret < 0) {
+		ir_input_unregister(input_dev);
+		input_free_device(input_dev);
+	}
+	return (ret < 0) ? -ENODEV : 0;
+}
+
+static u8 check_sum(u8 *p, u8 len)
+{
+	u8 sum = 0;
+	while (len--)
+		sum += *p++;
+	return sum;
+}
+
+static int lme2510_download_firmware(struct usb_device *dev,
+					const struct firmware *fw)
+{
+	int ret = 0;
+	u8 data[512] = {0};
+	u16 j, wlen, len_in, start, end;
+	u8 packet_size, dlen, i;
+	u8 *fw_data;
+
+	packet_size = 0x31;
+	len_in = 1;
+
+
+	info("FRM Starting Firmware Download");
+
+	for (i = 1; i < 3; i++) {
+		start = (i == 1) ? 0 : 512;
+		end = (i == 1) ? 512 : fw->size;
+		for (j = start; j < end; j += (packet_size+1)) {
+			fw_data = (u8 *)(fw->data + j);
+			if ((end - j) > packet_size) {
+				data[0] = i;
+				dlen = packet_size;
+			} else {
+				data[0] = i | 0x80;
+				dlen = (u8)(end - j)-1;
+			}
+		data[1] = dlen;
+		memcpy(&data[2], fw_data, dlen+1);
+		wlen = (u8) dlen + 4;
+		data[wlen-1] = check_sum(fw_data, dlen+1);
+		deb_info(1, "Data S=%02x:E=%02x CS= %02x", data[3],
+				data[dlen+2], data[dlen+3]);
+		ret |= lme2510_bulk_write(dev, data,  wlen, 1);
+		ret |= lme2510_bulk_read(dev, data, len_in , 1);
+		ret |= (data[0] == 0x88) ? 0 : -1;
+		}
+	}
+	usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			0x06, 0x80, 0x0200, 0x00, data, 0x0109, 1000);
+
+
+	data[0] = 0x8a;
+	len_in = 1;
+	msleep(2000);
+	ret |= lme2510_bulk_write(dev, data , len_in, 1); /*Resetting*/
+	ret |= lme2510_bulk_read(dev, data, len_in, 1);
+	msleep(400);
+
+	if (ret < 0)
+		info("FRM Firmware Download Failed (%04x)" , ret);
+	else
+		info("FRM Firmware Download Completed - Resetting Device");
+
+
+	return (ret < 0) ? -ENODEV : 0;
+}
+
+/* Default firmware for LME2510C */
+const char lme_firmware[50] = "dvb-usb-lme2510c-s7395.fw";
+
+static void lme_coldreset(struct usb_device *dev)
+{
+	int ret = 0, len_in;
+	u8 data[512] = {0};
+
+	data[0] = 0x0a;
+	len_in = 1;
+	info("FRM Firmware Cold Reset");
+	ret |= lme2510_bulk_write(dev, data , len_in, 1); /*Cold Resetting*/
+	ret |= lme2510_bulk_read(dev, data, len_in, 1);
+	return;
+}
+
+static void lme_firmware_switch(struct usb_device *udev, int cold)
+{
+	const struct firmware *fw = NULL;
+	char lme2510c_s7395[] = "dvb-usb-lme2510c-s7395.fw";
+	char lme2510c_lg[] = "dvb-usb-lme2510c-lg.fw";
+	char *firm_msg[] = {"Loading", "Switching to"};
+	int ret;
+
+	if (udev->descriptor.idProduct == 0x1122)
+		return;
+
+	switch (dvb_usb_lme2510_firmware) {
+	case 0:
+	default:
+		memcpy(&lme_firmware, lme2510c_s7395, sizeof(lme2510c_s7395));
+		ret = request_firmware(&fw, lme_firmware, &udev->dev);
+		if (ret == 0) {
+			info("FRM %s S7395 Firmware", firm_msg[cold]);
+			break;
+		}
+		if (cold == 0)
+			dvb_usb_lme2510_firmware = 1;
+		else
+			cold = 0;
+	case 1:
+		memcpy(&lme_firmware, lme2510c_lg, sizeof(lme2510c_lg));
+		ret = request_firmware(&fw, lme_firmware, &udev->dev);
+		if (ret == 0) {
+			info("FRM %s LG Firmware", firm_msg[cold]);
+			break;
+		}
+		info("FRM No Firmware Found - please install");
+		dvb_usb_lme2510_firmware = 0;
+		cold = 0;
+		break;
+	}
+	release_firmware(fw);
+	if (cold)
+		lme_coldreset(udev);
+	return;
+}
+
+static int lme2510_kill_urb(struct usb_data_stream *stream)
+{
+	int i;
+	for (i = 0; i < stream->urbs_submitted; i++) {
+		deb_info(3, "killing URB no. %d.", i);
+
+		/* stop the URB */
+		usb_kill_urb(stream->urb_list[i]);
+	}
+	stream->urbs_submitted = 0;
+	return 0;
+}
+
+static struct tda10086_config tda10086_config = {
+	.demod_address = 0x1c,
+	.invert = 0,
+	.diseqc_tone = 1,
+	.xtal_freq = TDA10086_XTAL_16M,
+};
+
+static struct stv0288_config lme_config = {
+	.demod_address = 0xd0,
+	.min_delay_ms = 15,
+	.inittab = s7395_inittab,
+};
+
+static struct ix2505v_config lme_tuner = {
+	.tuner_address = 0xc0,
+	.min_delay_ms = 100,
+	.tuner_gain = 0x0,
+	.tuner_chargepump = 0x3,
+};
+
+static int dm04_lme2510_set_voltage(struct dvb_frontend *fe,
+					fe_sec_voltage_t voltage)
+{
+	struct dvb_usb_adapter *adap = fe->dvb->priv;
+	struct lme2510_state *st = adap->dev->priv;
+	static u8 voltage_low[]	= LME_VOLTAGE_L;
+	static u8 voltage_high[] = LME_VOLTAGE_H;
+	static u8 lnb_on[] = LNB_ON;
+	static u8 lnb_off[] = LNB_OFF;
+	static u8 rbuf[1];
+	int ret = 0, len = 3, rlen = 1;
+
+	if (st->stream_on == 1)
+		return 0;
+
+	ret |= lme2510_usb_talk(adap->dev, lnb_on, len, rbuf, rlen);
+
+	switch (voltage) {
+	case SEC_VOLTAGE_18:
+		ret |= lme2510_usb_talk(adap->dev,
+			voltage_high, len, rbuf, rlen);
+		break;
+
+	case SEC_VOLTAGE_OFF:
+		ret |= lme2510_usb_talk(adap->dev,
+					lnb_off, len, rbuf, rlen);
+	case SEC_VOLTAGE_13:
+	default:
+		ret |= lme2510_usb_talk(adap->dev,
+				voltage_low, len, rbuf, rlen);
+		break;
+
+
+	};
+	st->i2c_talk_onoff = 1;
+	return (ret < 0) ? -ENODEV : 0;
+}
+
+static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	int ret = 0;
+	struct lme2510_state *st = adap->dev->priv;
+
+	/* Interupt Start  */
+	ret = lme2510_int_service(adap);
+	if (ret < 0) {
+		info("INT Unable to start Interupt Service");
+		return -ENODEV;
+	}
+
+	st->i2c_talk_onoff = 1;
+	st->i2c_gate = 4;
+
+	adap->fe = dvb_attach(tda10086_attach, &tda10086_config,
+		&adap->dev->i2c_adap);
+
+	if (adap->fe) {
+		info("TUN Found Frontend TDA10086");
+		memcpy(&adap->fe->ops.info.name,
+				&"DM04_LG_TDQY-P001F DVB-S", 24);
+		adap->fe->ops.set_voltage = dm04_lme2510_set_voltage;
+		st->i2c_tuner_gate_w = 4;
+		st->i2c_tuner_gate_r = 4;
+		st->i2c_tuner_addr = 0xc0;
+		if (dvb_attach(tda826x_attach, adap->fe, 0xc0,
+			&adap->dev->i2c_adap, 1)) {
+			info("TUN TDA8263 Found");
+			st->tuner_config = TUNER_LG;
+			if (dvb_usb_lme2510_firmware != 1) {
+				dvb_usb_lme2510_firmware = 1;
+				lme_firmware_switch(adap->dev->udev, 1);
+			}
+			return 0;
+		}
+		kfree(adap->fe);
+		adap->fe = NULL;
+	}
+	st->i2c_gate = 5;
+	adap->fe = dvb_attach(stv0288_attach, &lme_config,
+			&adap->dev->i2c_adap);
+
+	if (adap->fe) {
+		info("FE Found Stv0288");
+		memcpy(&adap->fe->ops.info.name,
+				&"DM04_SHARP:BS2F7HZ7395", 22);
+		adap->fe->ops.set_voltage = dm04_lme2510_set_voltage;
+		st->i2c_tuner_gate_w = 4;
+		st->i2c_tuner_gate_r = 5;
+		st->i2c_tuner_addr = 0xc0;
+		if (dvb_attach(ix2505v_attach , adap->fe, &lme_tuner,
+					&adap->dev->i2c_adap)) {
+			st->tuner_config = TUNER_S7395;
+			info("TUN Sharp IX2505V silicon tuner");
+			if (dvb_usb_lme2510_firmware != 0) {
+				dvb_usb_lme2510_firmware = 0;
+				lme_firmware_switch(adap->dev->udev, 1);
+			}
+			return 0;
+		}
+		kfree(adap->fe);
+		adap->fe = NULL;
+	}
+
+	info("DM04 Not Supported");
+	return -ENODEV;
+}
+
+static int lme2510_powerup(struct dvb_usb_device *d, int onoff)
+{
+	struct lme2510_state *st = d->priv;
+	st->i2c_talk_onoff = 1;
+	return 0;
+}
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties lme2510_properties;
+static struct dvb_usb_device_properties lme2510c_properties;
+
+static int lme2510_probe(struct usb_interface *intf,
+		const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	int ret = 0;
+
+	usb_reset_configuration(udev);
+
+	usb_set_interface(udev, intf->cur_altsetting->desc.bInterfaceNumber, 1);
+
+	if (udev->speed != USB_SPEED_HIGH) {
+		ret = usb_reset_device(udev);
+		info("DEV Failed to connect in HIGH SPEED mode");
+		return -ENODEV;
+	}
+
+	lme_firmware_switch(udev, 0);
+
+	if (0 == dvb_usb_device_init(intf, &lme2510_properties,
+				     THIS_MODULE, NULL, adapter_nr)) {
+		info("DEV registering device driver");
+		return 0;
+	}
+	if (0 == dvb_usb_device_init(intf, &lme2510c_properties,
+				     THIS_MODULE, NULL, adapter_nr)) {
+		info("DEV registering device driver");
+		return 0;
+	}
+
+	info("DEV lme2510 Error");
+	return -ENODEV;
+
+}
+
+static struct usb_device_id lme2510_table[] = {
+	{ USB_DEVICE(0x3344, 0x1122) },  /* LME2510 */
+	{ USB_DEVICE(0x3344, 0x1120) },  /* LME2510C */
+	{}		/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, lme2510_table);
+
+static struct dvb_usb_device_properties lme2510_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+	.usb_ctrl = DEVICE_SPECIFIC,
+	.download_firmware = lme2510_download_firmware,
+	.firmware = "dvb-usb-lme2510-lg.fw",
+
+	.size_of_priv = sizeof(struct lme2510_state),
+	.num_adapters = 1,
+	.adapter = {
+		{
+			.streaming_ctrl   = lme2510_streaming_ctrl,
+			.frontend_attach  = dm04_lme2510_frontend_attach,
+			/* parameter for the MPEG2-data transfer */
+			.stream = {
+				.type = USB_BULK,
+				.count = 10,
+				.endpoint = 0x06,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+
+					}
+				}
+			}
+		}
+	},
+	.power_ctrl       = lme2510_powerup,
+	.identify_state   = lme2510_identify_state,
+	.i2c_algo         = &lme2510_i2c_algo,
+	.generic_bulk_ctrl_endpoint = 0,
+	.num_device_descs = 1,
+	.devices = {
+		{   "DM04 LME2510 DVB-S USB 2.0",
+			{ &lme2510_table[0], NULL },
+			},
+
+	}
+};
+
+static struct dvb_usb_device_properties lme2510c_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+	.usb_ctrl = DEVICE_SPECIFIC,
+	.download_firmware = lme2510_download_firmware,
+	.firmware = lme_firmware,
+	.size_of_priv = sizeof(struct lme2510_state),
+	.num_adapters = 1,
+	.adapter = {
+		{
+			.streaming_ctrl   = lme2510_streaming_ctrl,
+			.frontend_attach  = dm04_lme2510_frontend_attach,
+			/* parameter for the MPEG2-data transfer */
+			.stream = {
+				.type = USB_BULK,
+				.count = 10,
+				.endpoint = 0x8,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+
+					}
+				}
+			}
+		}
+	},
+	.power_ctrl       = lme2510_powerup,
+	.identify_state   = lme2510_identify_state,
+	.i2c_algo         = &lme2510_i2c_algo,
+	.generic_bulk_ctrl_endpoint = 0,
+	.num_device_descs = 1,
+	.devices = {
+		{   "DM04 LME2510C USB2.0",
+			{ &lme2510_table[1], NULL },
+			},
+	}
+};
+
+void *lme2510_exit_int(struct dvb_usb_device *d)
+{
+	struct lme2510_state *st = d->priv;
+	struct dvb_usb_adapter *adap = &d->adapter[0];
+	void *buffer = NULL;
+
+	if (adap != NULL) {
+		lme2510_kill_urb(&adap->stream);
+		adap->feedcount = 0;
+	}
+
+	if (st->lme_urb != NULL) {
+		st->i2c_talk_onoff = 1;
+		st->signal_lock = 0;
+		st->signal_level = 0;
+		st->signal_sn = 0;
+		buffer = st->usb_buffer;
+		usb_kill_urb(st->lme_urb);
+		usb_free_coherent(d->udev, 5000, st->buffer,
+				  st->lme_urb->transfer_dma);
+		info("Interupt Service Stopped");
+		ir_input_unregister(d->rc_input_dev);
+		info("Remote Stopped");
+	}
+	return buffer;
+}
+
+void lme2510_exit(struct usb_interface *intf)
+{
+	struct dvb_usb_device *d = usb_get_intfdata(intf);
+	void *usb_buffer;
+
+	if (d != NULL) {
+		usb_buffer = lme2510_exit_int(d);
+		dvb_usb_device_exit(intf);
+		kfree(usb_buffer);
+	}
+}
+
+static struct usb_driver lme2510_driver = {
+	.name		= "LME2510C_DVBS",
+	.probe		= lme2510_probe,
+	.disconnect	= lme2510_exit,
+	.id_table	= lme2510_table,
+};
+
+/* module stuff */
+static int __init lme2510_module_init(void)
+{
+	int result = usb_register(&lme2510_driver);
+	if (result) {
+		err("usb_register failed. Error number %d", result);
+		return result;
+	}
+
+	return 0;
+}
+
+static void __exit lme2510_module_exit(void)
+{
+	/* deregister this driver from the USB subsystem */
+	usb_deregister(&lme2510_driver);
+}
+
+module_init(lme2510_module_init);
+module_exit(lme2510_module_exit);
+
+MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
+MODULE_DESCRIPTION("LM2510(C) DVB-S USB2.0");
+MODULE_VERSION("1.60");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/lmedm04.h b/drivers/media/dvb/dvb-usb/lmedm04.h
new file mode 100644
index 0000000..e6af16c
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/lmedm04.h
@@ -0,0 +1,173 @@
+/* DVB USB compliant linux driver for
+ *
+ * DM04/QQBOX DVB-S USB BOX	LME2510C + SHARP:BS2F7HZ7395
+ *				LME2510C + LG TDQY-P001F
+ *				LME2510 + LG TDQY-P001F
+ *
+ * MVB7395 (LME2510C+SHARP:BS2F7HZ7395)
+ * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V)
+ *
+ * MVB001F (LME2510+LGTDQT-P001F)
+ * LG TDQY - P001F =(TDA8263 + TDA10086H)
+ *
+ * MVB0001F (LME2510C+LGTDQT-P001F)
+ *
+ * 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,  version 2.
+ * *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
+#ifndef _DVB_USB_LME2510_H_
+#define _DVB_USB_LME2510_H_
+
+/* Streamer &  PID
+ *
+ * Note:	These commands do not actually stop the streaming
+ *		but form some kind of packet filtering/stream count
+ *		or tuning related functions.
+ *  06 XX
+ *  offset 1 = 00 Enable Streaming
+ *
+ *
+ *  PID
+ *  03 XX XX  ----> reg number ---> setting....20 XX
+ *  offset 1 = length
+ *  offset 2 = start of data
+ *  end byte -1 = 20
+ *  end byte = clear pid always a0, other wise 9c, 9a ??
+ *
+*/
+#define LME_ST_ON_W	{0x06, 0x00}
+#define LME_CLEAR_PID   {0x03, 0x02, 0x20, 0xa0}
+
+/*  LNB Voltage
+ *  07 XX XX
+ *  offset 1 = 01
+ *  offset 2 = 00=Voltage low 01=Voltage high
+ *
+ *  LNB Power
+ *  03 01 XX
+ *  offset 2 = 00=ON 01=OFF
+ */
+
+#define LME_VOLTAGE_L	{0x07, 0x01, 0x00}
+#define LME_VOLTAGE_H	{0x07, 0x01, 0x01}
+#define LNB_ON		{0x3a, 0x01, 0x00}
+#define LNB_OFF		{0x3a, 0x01, 0x01}
+
+/* Initial stv0288 settings for 7395 Frontend */
+static u8 s7395_inittab[] = {
+	0x01, 0x15,
+	0x02, 0x20,
+	0x03, 0xa0,
+	0x04, 0xa0,
+	0x05, 0x12,
+	0x06, 0x00,
+	0x09, 0x00,
+	0x0a, 0x04,
+	0x0b, 0x00,
+	0x0c, 0x00,
+	0x0d, 0x00,
+	0x0e, 0xc1,
+	0x0f, 0x54,
+	0x11, 0x7a,
+	0x12, 0x03,
+	0x13, 0x48,
+	0x14, 0x84,
+	0x15, 0xc5,
+	0x16, 0xb8,
+	0x17, 0x9c,
+	0x18, 0x00,
+	0x19, 0xa6,
+	0x1a, 0x88,
+	0x1b, 0x8f,
+	0x1c, 0xf0,
+	0x20, 0x0b,
+	0x21, 0x54,
+	0x22, 0xff,
+	0x23, 0x01,
+	0x28, 0x46,
+	0x29, 0x66,
+	0x2a, 0x90,
+	0x2b, 0xfa,
+	0x2c, 0xd9,
+	0x30, 0x0,
+	0x31, 0x1e,
+	0x32, 0x14,
+	0x33, 0x0f,
+	0x34, 0x09,
+	0x35, 0x0c,
+	0x36, 0x05,
+	0x37, 0x2f,
+	0x38, 0x16,
+	0x39, 0xbd,
+	0x3a, 0x0,
+	0x3b, 0x13,
+	0x3c, 0x11,
+	0x3d, 0x30,
+	0x40, 0x63,
+	0x41, 0x04,
+	0x42, 0x60,
+	0x43, 0x00,
+	0x44, 0x00,
+	0x45, 0x00,
+	0x46, 0x00,
+	0x47, 0x00,
+	0x4a, 0x00,
+	0x50, 0x12,
+	0x51, 0x36,
+	0x52, 0x21,
+	0x53, 0x94,
+	0x54, 0xb2,
+	0x55, 0x29,
+	0x56, 0x64,
+	0x57, 0x2b,
+	0x58, 0x54,
+	0x59, 0x86,
+	0x5a, 0x00,
+	0x5b, 0x9b,
+	0x5c, 0x08,
+	0x5d, 0x7f,
+	0x5e, 0xff,
+	0x5f, 0x8d,
+	0x70, 0x0,
+	0x71, 0x0,
+	0x72, 0x0,
+	0x74, 0x0,
+	0x75, 0x0,
+	0x76, 0x0,
+	0x81, 0x0,
+	0x82, 0x3f,
+	0x83, 0x3f,
+	0x84, 0x0,
+	0x85, 0x0,
+	0x88, 0x0,
+	0x89, 0x0,
+	0x8a, 0x0,
+	0x8b, 0x0,
+	0x8c, 0x0,
+	0x90, 0x0,
+	0x91, 0x0,
+	0x92, 0x0,
+	0x93, 0x0,
+	0x94, 0x1c,
+	0x97, 0x0,
+	0xa0, 0x48,
+	0xa1, 0x0,
+	0xb0, 0xb8,
+	0xb1, 0x3a,
+	0xb2, 0x10,
+	0xb3, 0x82,
+	0xb4, 0x80,
+	0xb5, 0x82,
+	0xb6, 0x82,
+	0xb7, 0x82,
+	0xb8, 0x20,
+	0xb9, 0x0,
+	0xf0, 0x0,
+	0xf1, 0x0,
+	0xf2, 0xc0,
+	0xff, 0xff,
+};
+#endif
diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c
index 28294af..f0f1842 100644
--- a/drivers/media/dvb/firewire/firedtv-avc.c
+++ b/drivers/media/dvb/firewire/firedtv-avc.c
@@ -24,6 +24,8 @@
 #include <linux/wait.h>
 #include <linux/workqueue.h>
 
+#include <dvb_frontend.h>
+
 #include "firedtv.h"
 
 #define FCP_COMMAND_REGISTER		0xfffff0000b00ULL
@@ -130,6 +132,20 @@
 	", FCP payloads = "		__stringify(AVC_DEBUG_FCP_PAYLOADS)
 	", or a combination, or all = -1)");
 
+/*
+ * This is a workaround since there is no vendor specific command to retrieve
+ * ca_info using AVC. If this parameter is not used, ca_system_id will be
+ * filled with application_manufacturer from ca_app_info.
+ * Digital Everywhere have said that adding ca_info is on their TODO list.
+ */
+static unsigned int num_fake_ca_system_ids;
+static int fake_ca_system_ids[4] = { -1, -1, -1, -1 };
+module_param_array(fake_ca_system_ids, int, &num_fake_ca_system_ids, 0644);
+MODULE_PARM_DESC(fake_ca_system_ids, "If your CAM application manufacturer "
+		 "does not have the same ca_system_id as your CAS, you can "
+		 "override what ca_system_ids are presented to the "
+		 "application by setting this field to an array of ids.");
+
 static const char *debug_fcp_ctype(unsigned int ctype)
 {
 	static const char *ctypes[] = {
@@ -368,10 +384,30 @@
 		c->operand[12] = 0;
 
 	if (fdtv->type == FIREDTV_DVB_S2) {
-		c->operand[13] = 0x1;
-		c->operand[14] = 0xff;
-		c->operand[15] = 0xff;
-
+		if (fdtv->fe.dtv_property_cache.delivery_system == SYS_DVBS2) {
+			switch (fdtv->fe.dtv_property_cache.modulation) {
+			case QAM_16:		c->operand[13] = 0x1; break;
+			case QPSK:		c->operand[13] = 0x2; break;
+			case PSK_8:		c->operand[13] = 0x3; break;
+			default:		c->operand[13] = 0x2; break;
+			}
+			switch (fdtv->fe.dtv_property_cache.rolloff) {
+			case ROLLOFF_AUTO:	c->operand[14] = 0x2; break;
+			case ROLLOFF_35:	c->operand[14] = 0x2; break;
+			case ROLLOFF_20:	c->operand[14] = 0x0; break;
+			case ROLLOFF_25:	c->operand[14] = 0x1; break;
+			/* case ROLLOFF_NONE:	c->operand[14] = 0xff; break; */
+			}
+			switch (fdtv->fe.dtv_property_cache.pilot) {
+			case PILOT_AUTO:	c->operand[15] = 0x0; break;
+			case PILOT_OFF:		c->operand[15] = 0x0; break;
+			case PILOT_ON:		c->operand[15] = 0x1; break;
+			}
+		} else {
+			c->operand[13] = 0x1;  /* auto modulation */
+			c->operand[14] = 0xff; /* disable rolloff */
+			c->operand[15] = 0xff; /* disable pilot */
+		}
 		return 16;
 	} else {
 		return 13;
@@ -977,7 +1013,7 @@
 {
 	struct avc_command_frame *c = (void *)fdtv->avc_data;
 	struct avc_response_frame *r = (void *)fdtv->avc_data;
-	int pos, ret;
+	int i, pos, ret;
 
 	mutex_lock(&fdtv->avc_mutex);
 
@@ -1004,9 +1040,18 @@
 	app_info[0] = (EN50221_TAG_CA_INFO >> 16) & 0xff;
 	app_info[1] = (EN50221_TAG_CA_INFO >>  8) & 0xff;
 	app_info[2] = (EN50221_TAG_CA_INFO >>  0) & 0xff;
-	app_info[3] = 2;
-	app_info[4] = r->operand[pos + 0];
-	app_info[5] = r->operand[pos + 1];
+	if (num_fake_ca_system_ids == 0) {
+		app_info[3] = 2;
+		app_info[4] = r->operand[pos + 0];
+		app_info[5] = r->operand[pos + 1];
+	} else {
+		app_info[3] = num_fake_ca_system_ids * 2;
+		for (i = 0; i < num_fake_ca_system_ids; i++) {
+			app_info[4 + i * 2] =
+				(fake_ca_system_ids[i] >> 8) & 0xff;
+			app_info[5 + i * 2] = fake_ca_system_ids[i] & 0xff;
+		}
+	}
 	*len = app_info[3] + 4;
 out:
 	mutex_unlock(&fdtv->avc_mutex);
diff --git a/drivers/media/dvb/firewire/firedtv-fe.c b/drivers/media/dvb/firewire/firedtv-fe.c
index e49cdc8..d10920e 100644
--- a/drivers/media/dvb/firewire/firedtv-fe.c
+++ b/drivers/media/dvb/firewire/firedtv-fe.c
@@ -155,6 +155,16 @@
 	return -EOPNOTSUPP;
 }
 
+static int fdtv_get_property(struct dvb_frontend *fe, struct dtv_property *tvp)
+{
+	return 0;
+}
+
+static int fdtv_set_property(struct dvb_frontend *fe, struct dtv_property *tvp)
+{
+	return 0;
+}
+
 void fdtv_frontend_init(struct firedtv *fdtv)
 {
 	struct dvb_frontend_ops *ops = &fdtv->fe.ops;
@@ -166,6 +176,9 @@
 	ops->set_frontend		= fdtv_set_frontend;
 	ops->get_frontend		= fdtv_get_frontend;
 
+	ops->get_property		= fdtv_get_property;
+	ops->set_property		= fdtv_set_property;
+
 	ops->read_status		= fdtv_read_status;
 	ops->read_ber			= fdtv_read_ber;
 	ops->read_signal_strength	= fdtv_read_signal_strength;
@@ -179,6 +192,24 @@
 
 	switch (fdtv->type) {
 	case FIREDTV_DVB_S:
+		fi->type		= FE_QPSK;
+
+		fi->frequency_min	= 950000;
+		fi->frequency_max	= 2150000;
+		fi->frequency_stepsize	= 125;
+		fi->symbol_rate_min	= 1000000;
+		fi->symbol_rate_max	= 40000000;
+
+		fi->caps		= FE_CAN_INVERSION_AUTO |
+					  FE_CAN_FEC_1_2	|
+					  FE_CAN_FEC_2_3	|
+					  FE_CAN_FEC_3_4	|
+					  FE_CAN_FEC_5_6	|
+					  FE_CAN_FEC_7_8	|
+					  FE_CAN_FEC_AUTO	|
+					  FE_CAN_QPSK;
+		break;
+
 	case FIREDTV_DVB_S2:
 		fi->type		= FE_QPSK;
 
@@ -188,14 +219,15 @@
 		fi->symbol_rate_min	= 1000000;
 		fi->symbol_rate_max	= 40000000;
 
-		fi->caps 		= FE_CAN_INVERSION_AUTO	|
-					  FE_CAN_FEC_1_2	|
-					  FE_CAN_FEC_2_3	|
-					  FE_CAN_FEC_3_4	|
-					  FE_CAN_FEC_5_6	|
-					  FE_CAN_FEC_7_8	|
-					  FE_CAN_FEC_AUTO	|
-					  FE_CAN_QPSK;
+		fi->caps		= FE_CAN_INVERSION_AUTO |
+					  FE_CAN_FEC_1_2        |
+					  FE_CAN_FEC_2_3        |
+					  FE_CAN_FEC_3_4        |
+					  FE_CAN_FEC_5_6        |
+					  FE_CAN_FEC_7_8        |
+					  FE_CAN_FEC_AUTO       |
+					  FE_CAN_QPSK           |
+					  FE_CAN_2G_MODULATION;
 		break;
 
 	case FIREDTV_DVB_C:
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index b5f6a04..e9062b0 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -257,6 +257,13 @@
 	help
 	  A DVB-T tuner module. Say Y when you want to support this frontend.
 
+config DVB_S5H1432
+	tristate "Samsung s5h1432 demodulator (OFDM)"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-T tuner module. Say Y when you want to support this frontend.
+
 config DVB_DRX397XD
 	tristate "Micronas DRX3975D/DRX3977D based"
 	depends on DVB_CORE && I2C
@@ -455,16 +462,8 @@
 	  An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
 	  to support this frontend.
 
-config DVB_LGDT3304
-	tristate "LG Electronics LGDT3304"
-	depends on DVB_CORE && I2C
-	default m if DVB_FE_CUSTOMISE
-	help
-	  An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
-	  to support this frontend.
-
 config DVB_LGDT3305
-	tristate "LG Electronics LGDT3305 based"
+	tristate "LG Electronics LGDT3304 and LGDT3305 based"
 	depends on DVB_CORE && I2C
 	default m if DVB_FE_CUSTOMISE
 	help
@@ -607,6 +606,13 @@
 	  Currently supported tuners:
 	  * Panasonic ENV57H12D5 (ET-50DT)
 
+config DVB_IX2505V
+	tristate "Sharp IX2505V silicon tuner"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-S tuner module. Say Y when you want to support this frontend.
+
 comment "Tools to develop new frontends"
 
 config DVB_DUMMY_FE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 874e8ad..9a31985 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -16,6 +16,7 @@
 obj-$(CONFIG_DVB_STB6100) += stb6100.o
 obj-$(CONFIG_DVB_SP8870) += sp8870.o
 obj-$(CONFIG_DVB_CX22700) += cx22700.o
+obj-$(CONFIG_DVB_S5H1432) += s5h1432.o
 obj-$(CONFIG_DVB_CX24110) += cx24110.o
 obj-$(CONFIG_DVB_TDA8083) += tda8083.o
 obj-$(CONFIG_DVB_L64781) += l64781.o
@@ -45,7 +46,6 @@
 obj-$(CONFIG_DVB_BCM3510) += bcm3510.o
 obj-$(CONFIG_DVB_S5H1420) += s5h1420.o
 obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o
-obj-$(CONFIG_DVB_LGDT3304) += lgdt3304.o
 obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o
 obj-$(CONFIG_DVB_CX24123) += cx24123.o
 obj-$(CONFIG_DVB_LNBP21) += lnbp21.o
@@ -82,3 +82,4 @@
 obj-$(CONFIG_DVB_EC100) += ec100.o
 obj-$(CONFIG_DVB_DS3000) += ds3000.o
 obj-$(CONFIG_DVB_MB86A16) += mb86a16.o
+obj-$(CONFIG_DVB_IX2505V) += ix2505v.o
diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c
index dac917f..e2a95c0 100644
--- a/drivers/media/dvb/frontends/af9013.c
+++ b/drivers/media/dvb/frontends/af9013.c
@@ -42,6 +42,8 @@
 
 	struct af9013_config config;
 
+	/* tuner/demod RF and IF AGC limits used for signal strength calc */
+	u8 signal_strength_en, rf_50, rf_80, if_50, if_80;
 	u16 signal_strength;
 	u32 ber;
 	u32 ucblocks;
@@ -220,184 +222,37 @@
 
 static int af9013_set_coeff(struct af9013_state *state, fe_bandwidth_t bw)
 {
-	int ret = 0;
-	u8 i = 0;
-	u8 buf[24];
-	u32 uninitialized_var(ns_coeff1_2048nu);
-	u32 uninitialized_var(ns_coeff1_8191nu);
-	u32 uninitialized_var(ns_coeff1_8192nu);
-	u32 uninitialized_var(ns_coeff1_8193nu);
-	u32 uninitialized_var(ns_coeff2_2k);
-	u32 uninitialized_var(ns_coeff2_8k);
-
+	int ret, i, j, found;
 	deb_info("%s: adc_clock:%d bw:%d\n", __func__,
 		state->config.adc_clock, bw);
 
-	switch (state->config.adc_clock) {
-	case 28800: /* 28.800 MHz */
-		switch (bw) {
-		case BANDWIDTH_6_MHZ:
-			ns_coeff1_2048nu = 0x01e79e7a;
-			ns_coeff1_8191nu = 0x0079eb6e;
-			ns_coeff1_8192nu = 0x0079e79e;
-			ns_coeff1_8193nu = 0x0079e3cf;
-			ns_coeff2_2k     = 0x00f3cf3d;
-			ns_coeff2_8k     = 0x003cf3cf;
+	/* lookup coeff from table */
+	for (i = 0, found = 0; i < ARRAY_SIZE(coeff_table); i++) {
+		if (coeff_table[i].adc_clock == state->config.adc_clock &&
+			coeff_table[i].bw == bw) {
+			found = 1;
 			break;
-		case BANDWIDTH_7_MHZ:
-			ns_coeff1_2048nu = 0x0238e38e;
-			ns_coeff1_8191nu = 0x008e3d55;
-			ns_coeff1_8192nu = 0x008e38e4;
-			ns_coeff1_8193nu = 0x008e3472;
-			ns_coeff2_2k     = 0x011c71c7;
-			ns_coeff2_8k     = 0x00471c72;
-			break;
-		case BANDWIDTH_8_MHZ:
-			ns_coeff1_2048nu = 0x028a28a3;
-			ns_coeff1_8191nu = 0x00a28f3d;
-			ns_coeff1_8192nu = 0x00a28a29;
-			ns_coeff1_8193nu = 0x00a28514;
-			ns_coeff2_2k     = 0x01451451;
-			ns_coeff2_8k     = 0x00514514;
-			break;
-		default:
-			ret = -EINVAL;
 		}
-		break;
-	case 20480: /* 20.480 MHz */
-		switch (bw) {
-		case BANDWIDTH_6_MHZ:
-			ns_coeff1_2048nu = 0x02adb6dc;
-			ns_coeff1_8191nu = 0x00ab7313;
-			ns_coeff1_8192nu = 0x00ab6db7;
-			ns_coeff1_8193nu = 0x00ab685c;
-			ns_coeff2_2k     = 0x0156db6e;
-			ns_coeff2_8k     = 0x0055b6dc;
-			break;
-		case BANDWIDTH_7_MHZ:
-			ns_coeff1_2048nu = 0x03200001;
-			ns_coeff1_8191nu = 0x00c80640;
-			ns_coeff1_8192nu = 0x00c80000;
-			ns_coeff1_8193nu = 0x00c7f9c0;
-			ns_coeff2_2k     = 0x01900000;
-			ns_coeff2_8k     = 0x00640000;
-			break;
-		case BANDWIDTH_8_MHZ:
-			ns_coeff1_2048nu = 0x03924926;
-			ns_coeff1_8191nu = 0x00e4996e;
-			ns_coeff1_8192nu = 0x00e49249;
-			ns_coeff1_8193nu = 0x00e48b25;
-			ns_coeff2_2k     = 0x01c92493;
-			ns_coeff2_8k     = 0x00724925;
-			break;
-		default:
-			ret = -EINVAL;
-		}
-		break;
-	case 28000: /* 28.000 MHz */
-		switch (bw) {
-		case BANDWIDTH_6_MHZ:
-			ns_coeff1_2048nu = 0x01f58d10;
-			ns_coeff1_8191nu = 0x007d672f;
-			ns_coeff1_8192nu = 0x007d6344;
-			ns_coeff1_8193nu = 0x007d5f59;
-			ns_coeff2_2k     = 0x00fac688;
-			ns_coeff2_8k     = 0x003eb1a2;
-			break;
-		case BANDWIDTH_7_MHZ:
-			ns_coeff1_2048nu = 0x02492492;
-			ns_coeff1_8191nu = 0x00924db7;
-			ns_coeff1_8192nu = 0x00924925;
-			ns_coeff1_8193nu = 0x00924492;
-			ns_coeff2_2k     = 0x01249249;
-			ns_coeff2_8k     = 0x00492492;
-			break;
-		case BANDWIDTH_8_MHZ:
-			ns_coeff1_2048nu = 0x029cbc15;
-			ns_coeff1_8191nu = 0x00a7343f;
-			ns_coeff1_8192nu = 0x00a72f05;
-			ns_coeff1_8193nu = 0x00a729cc;
-			ns_coeff2_2k     = 0x014e5e0a;
-			ns_coeff2_8k     = 0x00539783;
-			break;
-		default:
-			ret = -EINVAL;
-		}
-		break;
-	case 25000: /* 25.000 MHz */
-		switch (bw) {
-		case BANDWIDTH_6_MHZ:
-			ns_coeff1_2048nu = 0x0231bcb5;
-			ns_coeff1_8191nu = 0x008c7391;
-			ns_coeff1_8192nu = 0x008c6f2d;
-			ns_coeff1_8193nu = 0x008c6aca;
-			ns_coeff2_2k     = 0x0118de5b;
-			ns_coeff2_8k     = 0x00463797;
-			break;
-		case BANDWIDTH_7_MHZ:
-			ns_coeff1_2048nu = 0x028f5c29;
-			ns_coeff1_8191nu = 0x00a3dc29;
-			ns_coeff1_8192nu = 0x00a3d70a;
-			ns_coeff1_8193nu = 0x00a3d1ec;
-			ns_coeff2_2k     = 0x0147ae14;
-			ns_coeff2_8k     = 0x0051eb85;
-			break;
-		case BANDWIDTH_8_MHZ:
-			ns_coeff1_2048nu = 0x02ecfb9d;
-			ns_coeff1_8191nu = 0x00bb44c1;
-			ns_coeff1_8192nu = 0x00bb3ee7;
-			ns_coeff1_8193nu = 0x00bb390d;
-			ns_coeff2_2k     = 0x01767dce;
-			ns_coeff2_8k     = 0x005d9f74;
-			break;
-		default:
-			ret = -EINVAL;
-		}
-		break;
-	default:
-		err("invalid xtal");
-		return -EINVAL;
-	}
-	if (ret) {
-		err("invalid bandwidth");
-		return ret;
 	}
 
-	buf[i++] = (u8) ((ns_coeff1_2048nu & 0x03000000) >> 24);
-	buf[i++] = (u8) ((ns_coeff1_2048nu & 0x00ff0000) >> 16);
-	buf[i++] = (u8) ((ns_coeff1_2048nu & 0x0000ff00) >> 8);
-	buf[i++] = (u8) ((ns_coeff1_2048nu & 0x000000ff));
-	buf[i++] = (u8) ((ns_coeff2_2k     & 0x01c00000) >> 22);
-	buf[i++] = (u8) ((ns_coeff2_2k     & 0x003fc000) >> 14);
-	buf[i++] = (u8) ((ns_coeff2_2k     & 0x00003fc0) >> 6);
-	buf[i++] = (u8) ((ns_coeff2_2k     & 0x0000003f));
-	buf[i++] = (u8) ((ns_coeff1_8191nu & 0x03000000) >> 24);
-	buf[i++] = (u8) ((ns_coeff1_8191nu & 0x00ffc000) >> 16);
-	buf[i++] = (u8) ((ns_coeff1_8191nu & 0x0000ff00) >> 8);
-	buf[i++] = (u8) ((ns_coeff1_8191nu & 0x000000ff));
-	buf[i++] = (u8) ((ns_coeff1_8192nu & 0x03000000) >> 24);
-	buf[i++] = (u8) ((ns_coeff1_8192nu & 0x00ffc000) >> 16);
-	buf[i++] = (u8) ((ns_coeff1_8192nu & 0x0000ff00) >> 8);
-	buf[i++] = (u8) ((ns_coeff1_8192nu & 0x000000ff));
-	buf[i++] = (u8) ((ns_coeff1_8193nu & 0x03000000) >> 24);
-	buf[i++] = (u8) ((ns_coeff1_8193nu & 0x00ffc000) >> 16);
-	buf[i++] = (u8) ((ns_coeff1_8193nu & 0x0000ff00) >> 8);
-	buf[i++] = (u8) ((ns_coeff1_8193nu & 0x000000ff));
-	buf[i++] = (u8) ((ns_coeff2_8k     & 0x01c00000) >> 22);
-	buf[i++] = (u8) ((ns_coeff2_8k     & 0x003fc000) >> 14);
-	buf[i++] = (u8) ((ns_coeff2_8k     & 0x00003fc0) >> 6);
-	buf[i++] = (u8) ((ns_coeff2_8k     & 0x0000003f));
+	if (!found) {
+		err("invalid bw or clock");
+		ret = -EINVAL;
+		goto error;
+	}
 
-	deb_info("%s: coeff:", __func__);
-	debug_dump(buf, sizeof(buf), deb_info);
+	deb_info("%s: coeff: ", __func__);
+	debug_dump(coeff_table[i].val, sizeof(coeff_table[i].val), deb_info);
 
 	/* program */
-	for (i = 0; i < sizeof(buf); i++) {
-		ret = af9013_write_reg(state, 0xae00 + i, buf[i]);
+	for (j = 0; j < sizeof(coeff_table[i].val); j++) {
+		ret = af9013_write_reg(state, 0xae00 + j,
+			coeff_table[i].val[j]);
 		if (ret)
 			break;
 	}
 
+error:
 	return ret;
 }
 
@@ -486,6 +341,19 @@
 				if_sample_freq = 4300000; /* 4.3 MHz */
 				break;
 			}
+		} else if (state->config.tuner == AF9013_TUNER_TDA18218) {
+			switch (bw) {
+			case BANDWIDTH_6_MHZ:
+				if_sample_freq = 3000000; /* 3 MHz */
+				break;
+			case BANDWIDTH_7_MHZ:
+				if_sample_freq = 3500000; /* 3.5 MHz */
+				break;
+			case BANDWIDTH_8_MHZ:
+			default:
+				if_sample_freq = 4000000; /* 4 MHz */
+				break;
+			}
 		}
 
 		while (if_sample_freq > (adc_freq / 2))
@@ -1097,45 +965,31 @@
 {
 	struct af9013_state *state = fe->demodulator_priv;
 	int ret;
-	u8 tmp0;
-	u8 rf_gain, rf_50, rf_80, if_gain, if_50, if_80;
+	u8 rf_gain, if_gain;
 	int signal_strength;
 
 	deb_info("%s\n", __func__);
 
-	state->signal_strength = 0;
-
-	ret = af9013_read_reg_bits(state, 0x9bee, 0, 1, &tmp0);
-	if (ret)
-		goto error;
-	if (tmp0) {
-		ret = af9013_read_reg(state, 0x9bbd, &rf_50);
-		if (ret)
-			goto error;
-		ret = af9013_read_reg(state, 0x9bd0, &rf_80);
-		if (ret)
-			goto error;
-		ret = af9013_read_reg(state, 0x9be2, &if_50);
-		if (ret)
-			goto error;
-		ret = af9013_read_reg(state, 0x9be4, &if_80);
-		if (ret)
-			goto error;
+	if (state->signal_strength_en) {
 		ret = af9013_read_reg(state, 0xd07c, &rf_gain);
 		if (ret)
 			goto error;
 		ret = af9013_read_reg(state, 0xd07d, &if_gain);
 		if (ret)
 			goto error;
-		signal_strength = (0xffff / (9 * (rf_50 + if_50) - \
-			11 * (rf_80 + if_80))) * (10 * (rf_gain + if_gain) - \
-			11 * (rf_80 + if_80));
+		signal_strength = (0xffff / \
+			(9 * (state->rf_50 + state->if_50) - \
+			11 * (state->rf_80 + state->if_80))) * \
+			(10 * (rf_gain + if_gain) - \
+			11 * (state->rf_80 + state->if_80));
 		if (signal_strength < 0)
 			signal_strength = 0;
 		else if (signal_strength > 0xffff)
 			signal_strength = 0xffff;
 
 		state->signal_strength = signal_strength;
+	} else {
+		state->signal_strength = 0;
 	}
 
 error:
@@ -1368,6 +1222,7 @@
 		break;
 	case AF9013_TUNER_MXL5005D:
 	case AF9013_TUNER_MXL5005R:
+	case AF9013_TUNER_MXL5007T:
 		len = ARRAY_SIZE(tuner_init_mxl5005);
 		init = tuner_init_mxl5005;
 		break;
@@ -1393,6 +1248,7 @@
 		init = tuner_init_mt2060_2;
 		break;
 	case AF9013_TUNER_TDA18271:
+	case AF9013_TUNER_TDA18218:
 		len = ARRAY_SIZE(tuner_init_tda18271);
 		init = tuner_init_tda18271;
 		break;
@@ -1438,6 +1294,27 @@
 	if (ret)
 		goto error;
 
+	/* read values needed for signal strength calculation */
+	ret = af9013_read_reg_bits(state, 0x9bee, 0, 1,
+		&state->signal_strength_en);
+	if (ret)
+		goto error;
+
+	if (state->signal_strength_en) {
+		ret = af9013_read_reg(state, 0x9bbd, &state->rf_50);
+		if (ret)
+			goto error;
+		ret = af9013_read_reg(state, 0x9bd0, &state->rf_80);
+		if (ret)
+			goto error;
+		ret = af9013_read_reg(state, 0x9be2, &state->if_50);
+		if (ret)
+			goto error;
+		ret = af9013_read_reg(state, 0x9be4, &state->if_80);
+		if (ret)
+			goto error;
+	}
+
 error:
 	return ret;
 }
diff --git a/drivers/media/dvb/frontends/af9013.h b/drivers/media/dvb/frontends/af9013.h
index 72c71bb..e53d873 100644
--- a/drivers/media/dvb/frontends/af9013.h
+++ b/drivers/media/dvb/frontends/af9013.h
@@ -44,6 +44,7 @@
 	AF9013_TUNER_MT2060_2   = 147, /* Microtune */
 	AF9013_TUNER_TDA18271   = 156, /* NXP */
 	AF9013_TUNER_QT1010A    = 162, /* Quantek */
+	AF9013_TUNER_MXL5007T   = 177, /* MaxLinear */
 	AF9013_TUNER_TDA18218   = 179, /* NXP */
 };
 
diff --git a/drivers/media/dvb/frontends/af9013_priv.h b/drivers/media/dvb/frontends/af9013_priv.h
index 0fd42b7..e00b2a4 100644
--- a/drivers/media/dvb/frontends/af9013_priv.h
+++ b/drivers/media/dvb/frontends/af9013_priv.h
@@ -60,6 +60,56 @@
 	u8 snr;
 };
 
+struct coeff {
+	u32 adc_clock;
+	fe_bandwidth_t bw;
+	u8 val[24];
+};
+
+/* pre-calculated coeff lookup table */
+static struct coeff coeff_table[] = {
+	/* 28.800 MHz */
+	{ 28800, BANDWIDTH_8_MHZ, { 0x02, 0x8a, 0x28, 0xa3, 0x05, 0x14,
+		0x51, 0x11, 0x00, 0xa2, 0x8f, 0x3d, 0x00, 0xa2, 0x8a,
+		0x29, 0x00, 0xa2, 0x85, 0x14, 0x01, 0x45, 0x14, 0x14 } },
+	{ 28800, BANDWIDTH_7_MHZ, { 0x02, 0x38, 0xe3, 0x8e, 0x04, 0x71,
+		0xc7, 0x07, 0x00, 0x8e, 0x3d, 0x55, 0x00, 0x8e, 0x38,
+		0xe4, 0x00, 0x8e, 0x34, 0x72, 0x01, 0x1c, 0x71, 0x32 } },
+	{ 28800, BANDWIDTH_6_MHZ, { 0x01, 0xe7, 0x9e, 0x7a, 0x03, 0xcf,
+		0x3c, 0x3d, 0x00, 0x79, 0xeb, 0x6e, 0x00, 0x79, 0xe7,
+		0x9e, 0x00, 0x79, 0xe3, 0xcf, 0x00, 0xf3, 0xcf, 0x0f } },
+	/* 20.480 MHz */
+	{ 20480, BANDWIDTH_8_MHZ, { 0x03, 0x92, 0x49, 0x26, 0x07, 0x24,
+		0x92, 0x13, 0x00, 0xe4, 0x99, 0x6e, 0x00, 0xe4, 0x92,
+		0x49, 0x00, 0xe4, 0x8b, 0x25, 0x01, 0xc9, 0x24, 0x25 } },
+	{ 20480, BANDWIDTH_7_MHZ, { 0x03, 0x20, 0x00, 0x01, 0x06, 0x40,
+		0x00, 0x00, 0x00, 0xc8, 0x06, 0x40, 0x00, 0xc8, 0x00,
+		0x00, 0x00, 0xc7, 0xf9, 0xc0, 0x01, 0x90, 0x00, 0x00 } },
+	{ 20480, BANDWIDTH_6_MHZ, { 0x02, 0xad, 0xb6, 0xdc, 0x05, 0x5b,
+		0x6d, 0x2e, 0x00, 0xab, 0x73, 0x13, 0x00, 0xab, 0x6d,
+		0xb7, 0x00, 0xab, 0x68, 0x5c, 0x01, 0x56, 0xdb, 0x1c } },
+	/* 28.000 MHz */
+	{ 28000, BANDWIDTH_8_MHZ, { 0x02, 0x9c, 0xbc, 0x15, 0x05, 0x39,
+		0x78, 0x0a, 0x00, 0xa7, 0x34, 0x3f, 0x00, 0xa7, 0x2f,
+		0x05, 0x00, 0xa7, 0x29, 0xcc, 0x01, 0x4e, 0x5e, 0x03 } },
+	{ 28000, BANDWIDTH_7_MHZ, { 0x02, 0x49, 0x24, 0x92, 0x04, 0x92,
+		0x49, 0x09, 0x00, 0x92, 0x4d, 0xb7, 0x00, 0x92, 0x49,
+		0x25, 0x00, 0x92, 0x44, 0x92, 0x01, 0x24, 0x92, 0x12 } },
+	{ 28000, BANDWIDTH_6_MHZ, { 0x01, 0xf5, 0x8d, 0x10, 0x03, 0xeb,
+		0x1a, 0x08, 0x00, 0x7d, 0x67, 0x2f, 0x00, 0x7d, 0x63,
+		0x44, 0x00, 0x7d, 0x5f, 0x59, 0x00, 0xfa, 0xc6, 0x22 } },
+	/* 25.000 MHz */
+	{ 25000, BANDWIDTH_8_MHZ, { 0x02, 0xec, 0xfb, 0x9d, 0x05, 0xd9,
+		0xf7, 0x0e, 0x00, 0xbb, 0x44, 0xc1, 0x00, 0xbb, 0x3e,
+		0xe7, 0x00, 0xbb, 0x39, 0x0d, 0x01, 0x76, 0x7d, 0x34 } },
+	{ 25000, BANDWIDTH_7_MHZ, { 0x02, 0x8f, 0x5c, 0x29, 0x05, 0x1e,
+		0xb8, 0x14, 0x00, 0xa3, 0xdc, 0x29, 0x00, 0xa3, 0xd7,
+		0x0a, 0x00, 0xa3, 0xd1, 0xec, 0x01, 0x47, 0xae, 0x05 } },
+	{ 25000, BANDWIDTH_6_MHZ, { 0x02, 0x31, 0xbc, 0xb5, 0x04, 0x63,
+		0x79, 0x1b, 0x00, 0x8c, 0x73, 0x91, 0x00, 0x8c, 0x6f,
+		0x2d, 0x00, 0x8c, 0x6a, 0xca, 0x01, 0x18, 0xde, 0x17 } },
+};
+
 /* QPSK SNR lookup table */
 static struct snr_table qpsk_snr_table[] = {
 	{ 0x0b4771,  0 },
@@ -480,9 +530,10 @@
 	{ 0x9bd9, 0, 8, 0x08 },
 };
 
-/* MaxLinear MXL5005 tuner init
+/* MaxLinear MXL5005S & MXL5007T tuner init
    AF9013_TUNER_MXL5005D   =  13
-   AF9013_TUNER_MXL5005R   =  30 */
+   AF9013_TUNER_MXL5005R   =  30
+   AF9013_TUNER_MXL5007T   = 177 */
 static struct regdesc tuner_init_mxl5005[] = {
 	{ 0x9bd5, 0, 8, 0x01 },
 	{ 0x9bd6, 0, 8, 0x07 },
@@ -791,8 +842,9 @@
 	{ 0x9bd9, 0, 8, 0x08 },
 };
 
-/* NXP TDA18271 tuner init
-   AF9013_TUNER_TDA18271   = 156 */
+/* NXP TDA18271 & TDA18218 tuner init
+   AF9013_TUNER_TDA18271   = 156
+   AF9013_TUNER_TDA18218   = 179 */
 static struct regdesc tuner_init_tda18271[] = {
 	{ 0x9bd5, 0, 8, 0x01 },
 	{ 0x9bd6, 0, 8, 0x04 },
diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c
index 29cdbfe..6d9c594 100644
--- a/drivers/media/dvb/frontends/au8522_decoder.c
+++ b/drivers/media/dvb/frontends/au8522_decoder.c
@@ -36,7 +36,6 @@
 #include <linux/delay.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 #include <media/v4l2-device.h>
 #include "au8522.h"
 #include "au8522_priv.h"
@@ -831,9 +830,25 @@
 
 MODULE_DEVICE_TABLE(i2c, au8522_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "au8522",
-	.probe = au8522_probe,
-	.remove = au8522_remove,
-	.id_table = au8522_id,
+static struct i2c_driver au8522_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "au8522",
+	},
+	.probe		= au8522_probe,
+	.remove		= au8522_remove,
+	.id_table	= au8522_id,
 };
+
+static __init int init_au8522(void)
+{
+	return i2c_add_driver(&au8522_driver);
+}
+
+static __exit void exit_au8522(void)
+{
+	i2c_del_driver(&au8522_driver);
+}
+
+module_init(init_au8522);
+module_exit(exit_au8522);
diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c
index 00b5c7e..ff6c498 100644
--- a/drivers/media/dvb/frontends/cx22702.c
+++ b/drivers/media/dvb/frontends/cx22702.c
@@ -54,7 +54,7 @@
 #define dprintk	if (debug) printk
 
 /* Register values to initialise the demod */
-static u8 init_tab[] = {
+static const u8 init_tab[] = {
 	0x00, 0x00, /* Stop aquisition */
 	0x0B, 0x06,
 	0x09, 0x01,
@@ -92,52 +92,56 @@
 
 	ret = i2c_transfer(state->i2c, &msg, 1);
 
-	if (ret != 1)
+	if (unlikely(ret != 1)) {
 		printk(KERN_ERR
 			"%s: error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
 			__func__, reg, data, ret);
+		return -1;
+	}
 
-	return (ret != 1) ? -1 : 0;
+	return 0;
 }
 
 static u8 cx22702_readreg(struct cx22702_state *state, u8 reg)
 {
 	int ret;
-	u8 b0[] = { reg };
-	u8 b1[] = { 0 };
+	u8 data;
 
 	struct i2c_msg msg[] = {
 		{ .addr = state->config->demod_address, .flags = 0,
-			.buf = b0, .len = 1 },
+			.buf = &reg, .len = 1 },
 		{ .addr = state->config->demod_address, .flags = I2C_M_RD,
-			.buf = b1, .len = 1 } };
+			.buf = &data, .len = 1 } };
 
 	ret = i2c_transfer(state->i2c, msg, 2);
 
-	if (ret != 2)
-		printk(KERN_ERR "%s: readreg error (ret == %i)\n",
-			__func__, ret);
+	if (unlikely(ret != 2)) {
+		printk(KERN_ERR "%s: error (reg == 0x%02x, ret == %i)\n",
+			__func__, reg, ret);
+		return 0;
+	}
 
-	return b1[0];
+	return data;
 }
 
 static int cx22702_set_inversion(struct cx22702_state *state, int inversion)
 {
 	u8 val;
 
+	val = cx22702_readreg(state, 0x0C);
 	switch (inversion) {
 	case INVERSION_AUTO:
 		return -EOPNOTSUPP;
 	case INVERSION_ON:
-		val = cx22702_readreg(state, 0x0C);
-		return cx22702_writereg(state, 0x0C, val | 0x01);
+		val |= 0x01;
+		break;
 	case INVERSION_OFF:
-		val = cx22702_readreg(state, 0x0C);
-		return cx22702_writereg(state, 0x0C, val & 0xfe);
+		val &= 0xfe;
+		break;
 	default:
 		return -EINVAL;
 	}
-
+	return cx22702_writereg(state, 0x0C, val);
 }
 
 /* Retrieve the demod settings */
@@ -244,13 +248,15 @@
 static int cx22702_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
 {
 	struct cx22702_state *state = fe->demodulator_priv;
+	u8 val;
+
 	dprintk("%s(%d)\n", __func__, enable);
+	val = cx22702_readreg(state, 0x0D);
 	if (enable)
-		return cx22702_writereg(state, 0x0D,
-			cx22702_readreg(state, 0x0D) & 0xfe);
+		val &= 0xfe;
 	else
-		return cx22702_writereg(state, 0x0D,
-			cx22702_readreg(state, 0x0D) | 1);
+		val |= 0x01;
+	return cx22702_writereg(state, 0x0D, val);
 }
 
 /* Talk to the demod, set the FEC, GUARD, QAM settings etc */
@@ -270,23 +276,21 @@
 	cx22702_set_inversion(state, p->inversion);
 
 	/* set bandwidth */
+	val = cx22702_readreg(state, 0x0C) & 0xcf;
 	switch (p->u.ofdm.bandwidth) {
 	case BANDWIDTH_6_MHZ:
-		cx22702_writereg(state, 0x0C,
-			(cx22702_readreg(state, 0x0C) & 0xcf) | 0x20);
+		val |= 0x20;
 		break;
 	case BANDWIDTH_7_MHZ:
-		cx22702_writereg(state, 0x0C,
-			(cx22702_readreg(state, 0x0C) & 0xcf) | 0x10);
+		val |= 0x10;
 		break;
 	case BANDWIDTH_8_MHZ:
-		cx22702_writereg(state, 0x0C,
-			cx22702_readreg(state, 0x0C) & 0xcf);
 		break;
 	default:
 		dprintk("%s: invalid bandwidth\n", __func__);
 		return -EINVAL;
 	}
+	cx22702_writereg(state, 0x0C, val);
 
 	p->u.ofdm.code_rate_LP = FEC_AUTO; /* temp hack as manual not working */
 
@@ -312,33 +316,31 @@
 	}
 
 	/* manually programmed values */
-	val = 0;
-	switch (p->u.ofdm.constellation) {
+	switch (p->u.ofdm.constellation) {		/* mask 0x18 */
 	case QPSK:
-		val = (val & 0xe7);
+		val = 0x00;
 		break;
 	case QAM_16:
-		val = (val & 0xe7) | 0x08;
+		val = 0x08;
 		break;
 	case QAM_64:
-		val = (val & 0xe7) | 0x10;
+		val = 0x10;
 		break;
 	default:
 		dprintk("%s: invalid constellation\n", __func__);
 		return -EINVAL;
 	}
-	switch (p->u.ofdm.hierarchy_information) {
+	switch (p->u.ofdm.hierarchy_information) {	/* mask 0x07 */
 	case HIERARCHY_NONE:
-		val = (val & 0xf8);
 		break;
 	case HIERARCHY_1:
-		val = (val & 0xf8) | 1;
+		val |= 0x01;
 		break;
 	case HIERARCHY_2:
-		val = (val & 0xf8) | 2;
+		val |= 0x02;
 		break;
 	case HIERARCHY_4:
-		val = (val & 0xf8) | 3;
+		val |= 0x03;
 		break;
 	default:
 		dprintk("%s: invalid hierarchy\n", __func__);
@@ -346,44 +348,42 @@
 	}
 	cx22702_writereg(state, 0x06, val);
 
-	val = 0;
-	switch (p->u.ofdm.code_rate_HP) {
+	switch (p->u.ofdm.code_rate_HP) {		/* mask 0x38 */
 	case FEC_NONE:
 	case FEC_1_2:
-		val = (val & 0xc7);
+		val = 0x00;
 		break;
 	case FEC_2_3:
-		val = (val & 0xc7) | 0x08;
+		val = 0x08;
 		break;
 	case FEC_3_4:
-		val = (val & 0xc7) | 0x10;
+		val = 0x10;
 		break;
 	case FEC_5_6:
-		val = (val & 0xc7) | 0x18;
+		val = 0x18;
 		break;
 	case FEC_7_8:
-		val = (val & 0xc7) | 0x20;
+		val = 0x20;
 		break;
 	default:
 		dprintk("%s: invalid code_rate_HP\n", __func__);
 		return -EINVAL;
 	}
-	switch (p->u.ofdm.code_rate_LP) {
+	switch (p->u.ofdm.code_rate_LP) {		/* mask 0x07 */
 	case FEC_NONE:
 	case FEC_1_2:
-		val = (val & 0xf8);
 		break;
 	case FEC_2_3:
-		val = (val & 0xf8) | 1;
+		val |= 0x01;
 		break;
 	case FEC_3_4:
-		val = (val & 0xf8) | 2;
+		val |= 0x02;
 		break;
 	case FEC_5_6:
-		val = (val & 0xf8) | 3;
+		val |= 0x03;
 		break;
 	case FEC_7_8:
-		val = (val & 0xf8) | 4;
+		val |= 0x04;
 		break;
 	default:
 		dprintk("%s: invalid code_rate_LP\n", __func__);
@@ -391,30 +391,28 @@
 	}
 	cx22702_writereg(state, 0x07, val);
 
-	val = 0;
-	switch (p->u.ofdm.guard_interval) {
+	switch (p->u.ofdm.guard_interval) {		/* mask 0x0c */
 	case GUARD_INTERVAL_1_32:
-		val = (val & 0xf3);
+		val = 0x00;
 		break;
 	case GUARD_INTERVAL_1_16:
-		val = (val & 0xf3) | 0x04;
+		val = 0x04;
 		break;
 	case GUARD_INTERVAL_1_8:
-		val = (val & 0xf3) | 0x08;
+		val = 0x08;
 		break;
 	case GUARD_INTERVAL_1_4:
-		val = (val & 0xf3) | 0x0c;
+		val = 0x0c;
 		break;
 	default:
 		dprintk("%s: invalid guard_interval\n", __func__);
 		return -EINVAL;
 	}
-	switch (p->u.ofdm.transmission_mode) {
+	switch (p->u.ofdm.transmission_mode) {		/* mask 0x03 */
 	case TRANSMISSION_MODE_2K:
-		val = (val & 0xfc);
 		break;
 	case TRANSMISSION_MODE_8K:
-		val = (val & 0xfc) | 1;
+		val |= 0x1;
 		break;
 	default:
 		dprintk("%s: invalid transmission_mode\n", __func__);
@@ -505,7 +503,7 @@
 {
 	struct cx22702_state *state = fe->demodulator_priv;
 
-	u16 rs_ber = 0;
+	u16 rs_ber;
 	rs_ber = cx22702_readreg(state, 0x23);
 	*signal_strength = (rs_ber << 8) | rs_ber;
 
@@ -516,7 +514,7 @@
 {
 	struct cx22702_state *state = fe->demodulator_priv;
 
-	u16 rs_ber = 0;
+	u16 rs_ber;
 	if (cx22702_readreg(state, 0xE4) & 0x02) {
 		/* Realtime statistics */
 		rs_ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7
@@ -572,7 +570,7 @@
 	kfree(state);
 }
 
-static struct dvb_frontend_ops cx22702_ops;
+static const struct dvb_frontend_ops cx22702_ops;
 
 struct dvb_frontend *cx22702_attach(const struct cx22702_config *config,
 	struct i2c_adapter *i2c)
@@ -587,7 +585,6 @@
 	/* setup the state */
 	state->config = config;
 	state->i2c = i2c;
-	state->prevUCBlocks = 0;
 
 	/* check if the demod is there */
 	if (cx22702_readreg(state, 0x1f) != 0x3)
@@ -605,7 +602,7 @@
 }
 EXPORT_SYMBOL(cx22702_attach);
 
-static struct dvb_frontend_ops cx22702_ops = {
+static const struct dvb_frontend_ops cx22702_ops = {
 
 	.info = {
 		.name			= "Conexant CX22702 DVB-T",
diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c
index 00a4e8f..7a1a5bc 100644
--- a/drivers/media/dvb/frontends/cx24110.c
+++ b/drivers/media/dvb/frontends/cx24110.c
@@ -310,7 +310,7 @@
 
 }
 
-static int _cx24110_pll_write (struct dvb_frontend* fe, u8 *buf, int len)
+static int _cx24110_pll_write (struct dvb_frontend* fe, const u8 buf[], int len)
 {
 	struct cx24110_state *state = fe->demodulator_priv;
 
diff --git a/drivers/media/dvb/frontends/cx24123.c b/drivers/media/dvb/frontends/cx24123.c
index d8f921b..fad6a99 100644
--- a/drivers/media/dvb/frontends/cx24123.c
+++ b/drivers/media/dvb/frontends/cx24123.c
@@ -1108,7 +1108,6 @@
 
 	strlcpy(state->tuner_i2c_adapter.name, "CX24123 tuner I2C bus",
 		sizeof(state->tuner_i2c_adapter.name));
-	state->tuner_i2c_adapter.class     = I2C_CLASS_TV_DIGITAL,
 	state->tuner_i2c_adapter.algo      = &cx24123_tuner_i2c_algo;
 	state->tuner_i2c_adapter.algo_data = NULL;
 	i2c_set_adapdata(&state->tuner_i2c_adapter, state);
diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c
index 980e02f..a499102 100644
--- a/drivers/media/dvb/frontends/dibx000_common.c
+++ b/drivers/media/dvb/frontends/dibx000_common.c
@@ -130,7 +130,6 @@
 			    struct dibx000_i2c_master *mst)
 {
 	strncpy(i2c_adap->name, name, sizeof(i2c_adap->name));
-	i2c_adap->class = I2C_CLASS_TV_DIGITAL, i2c_adap->algo = algo;
 	i2c_adap->algo_data = NULL;
 	i2c_set_adapdata(i2c_adap, mst);
 	if (i2c_add_adapter(i2c_adap) < 0)
diff --git a/drivers/media/dvb/frontends/drx397xD.c b/drivers/media/dvb/frontends/drx397xD.c
index f74cca6..a05007c 100644
--- a/drivers/media/dvb/frontends/drx397xD.c
+++ b/drivers/media/dvb/frontends/drx397xD.c
@@ -232,7 +232,7 @@
 exit_rc:
 	read_unlock(&fw[s->chip_rev].lock);
 
-	return 0;
+	return rc;
 }
 
 /* Function is not endian safe, use the RD16 wrapper below */
diff --git a/drivers/media/dvb/frontends/ix2505v.c b/drivers/media/dvb/frontends/ix2505v.c
new file mode 100644
index 0000000..55f2eba
--- /dev/null
+++ b/drivers/media/dvb/frontends/ix2505v.c
@@ -0,0 +1,323 @@
+/**
+ * Driver for Sharp IX2505V (marked B0017) DVB-S silicon tuner
+ *
+ * Copyright (C) 2010 Malcolm Priestley
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/dvb/frontend.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "ix2505v.h"
+
+static int ix2505v_debug;
+#define dprintk(level, args...) do { \
+	if (ix2505v_debug & level) \
+		printk(KERN_DEBUG "ix2505v: " args); \
+} while (0)
+
+#define deb_info(args...)  dprintk(0x01, args)
+#define deb_i2c(args...)  dprintk(0x02, args)
+
+struct ix2505v_state {
+	struct i2c_adapter *i2c;
+	const struct ix2505v_config *config;
+	u32 frequency;
+};
+
+/**
+ *  Data read format of the Sharp IX2505V B0017
+ *
+ *  byte1:   1   |   1   |   0   |   0   |   0   |  MA1  |  MA0  |  1
+ *  byte2:  POR  |   FL  |  RD2  |  RD1  |  RD0  |   X   |   X   |  X
+ *
+ *  byte1 = address
+ *  byte2;
+ *	POR = Power on Reset (VCC H=<2.2v L=>2.2v)
+ *	FL  = Phase Lock (H=lock L=unlock)
+ *	RD0-2 = Reserved internal operations
+ *
+ * Only POR can be used to check the tuner is present
+ *
+ * Caution: after byte2 the I2C reverts to write mode continuing to read
+ *          may corrupt tuning data.
+ *
+ */
+
+static int ix2505v_read_status_reg(struct ix2505v_state *state)
+{
+	u8 addr = state->config->tuner_address;
+	u8 b2[] = {0};
+	int ret;
+
+	struct i2c_msg msg[1] = {
+		{ .addr = addr, .flags = I2C_M_RD, .buf = b2, .len = 1 }
+	};
+
+	ret = i2c_transfer(state->i2c, msg, 1);
+	deb_i2c("Read %s ", __func__);
+
+	return (ret = 1) ? (int) b2[0] : -1;
+}
+
+static int ix2505v_write(struct ix2505v_state *state, u8 buf[], u8 count)
+{
+	struct i2c_msg msg[1] = {
+		{ .addr = state->config->tuner_address, .flags = 0,
+		  .buf = buf, .len = count },
+	};
+
+	int ret;
+
+	ret = i2c_transfer(state->i2c, msg, 1);
+
+	if (ret != 1) {
+		deb_i2c("%s: i2c error, ret=%d\n", __func__, ret);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int ix2505v_release(struct dvb_frontend *fe)
+{
+	struct ix2505v_state *state = fe->tuner_priv;
+
+	fe->tuner_priv = NULL;
+	kfree(state);
+
+	return 0;
+}
+
+/**
+ *  Data write format of the Sharp IX2505V B0017
+ *
+ *  byte1:   1   |   1   |   0   |   0   |   0   | 0(MA1)| 0(MA0)|  0
+ *  byte2:   0   |  BG1  |  BG2  |   N8  |   N7  |   N6  |  N5   |  N4
+ *  byte3:   N3  |   N2  |   N1  |   A5  |   A4  |   A3  |   A2  |  A1
+ *  byte4:   1   | 1(C1) | 1(C0) |  PD5  |  PD4  |   TM  | 0(RTS)| 1(REF)
+ *  byte5:   BA2 |   BA1 |  BA0  |  PSC  |  PD3  |PD2/TS2|DIV/TS1|PD0/TS0
+ *
+ *  byte1 = address
+ *
+ *  Write order
+ *  1) byte1 -> byte2 -> byte3 -> byte4 -> byte5
+ *  2) byte1 -> byte4 -> byte5 -> byte2 -> byte3
+ *  3) byte1 -> byte2 -> byte3 -> byte4
+ *  4) byte1 -> byte4 -> byte5 -> byte2
+ *  5) byte1 -> byte2 -> byte3
+ *  6) byte1 -> byte4 -> byte5
+ *  7) byte1 -> byte2
+ *  8) byte1 -> byte4
+ *
+ *  Recommended Setup
+ *  1 -> 8 -> 6
+ */
+
+static int ix2505v_set_params(struct dvb_frontend *fe,
+		struct dvb_frontend_parameters *params)
+{
+	struct ix2505v_state *state = fe->tuner_priv;
+	u32 frequency = params->frequency;
+	u32 b_w  = (params->u.qpsk.symbol_rate * 27) / 32000;
+	u32 div_factor, N , A, x;
+	int ret = 0, len;
+	u8 gain, cc, ref, psc, local_osc, lpf;
+	u8 data[4] = {0};
+
+	if ((frequency < fe->ops.info.frequency_min)
+	||  (frequency > fe->ops.info.frequency_max))
+		return -EINVAL;
+
+	if (state->config->tuner_gain)
+		gain = (state->config->tuner_gain < 4)
+			? state->config->tuner_gain : 0;
+	else
+		gain = 0x0;
+
+	if (state->config->tuner_chargepump)
+		cc = state->config->tuner_chargepump;
+	else
+		cc = 0x3;
+
+	ref = 8; /* REF =1 */
+	psc = 32; /* PSC = 0 */
+
+	div_factor = (frequency * ref) / 40; /* local osc = 4Mhz */
+	x = div_factor / psc;
+	N = x/100;
+	A = ((x - (N * 100)) * psc) / 100;
+
+	data[0] = ((gain & 0x3) << 5) | (N >> 3);
+	data[1] = (N << 5) | (A & 0x1f);
+	data[2] = 0x81 | ((cc & 0x3) << 5) ; /*PD5,PD4 & TM = 0|C1,C0|REF=1*/
+
+	deb_info("Frq=%d x=%d N=%d A=%d\n", frequency, x, N, A);
+
+	if (frequency <= 1065000)
+		local_osc = (6 << 5) | 2;
+	else if (frequency <= 1170000)
+		local_osc = (7 << 5) | 2;
+	else if (frequency <= 1300000)
+		local_osc = (1 << 5);
+	else if (frequency <= 1445000)
+		local_osc = (2 << 5);
+	else if (frequency <= 1607000)
+		local_osc = (3 << 5);
+	else if (frequency <= 1778000)
+		local_osc = (4 << 5);
+	else if (frequency <= 1942000)
+		local_osc = (5 << 5);
+	else		/*frequency up to 2150000*/
+		local_osc = (6 << 5);
+
+	data[3] = local_osc; /* all other bits set 0 */
+
+	if (b_w <= 10000)
+		lpf = 0xc;
+	else if (b_w <= 12000)
+		lpf = 0x2;
+	else if (b_w <= 14000)
+		lpf = 0xa;
+	else if (b_w <= 16000)
+		lpf = 0x6;
+	else if (b_w <= 18000)
+		lpf = 0xe;
+	else if (b_w <= 20000)
+		lpf = 0x1;
+	else if (b_w <= 22000)
+		lpf = 0x9;
+	else if (b_w <= 24000)
+		lpf = 0x5;
+	else if (b_w <= 26000)
+		lpf = 0xd;
+	else if (b_w <= 28000)
+		lpf = 0x3;
+		else
+		lpf = 0xb;
+
+	deb_info("Osc=%x b_w=%x lpf=%x\n", local_osc, b_w, lpf);
+	deb_info("Data 0=[%x%x%x%x]\n", data[0], data[1], data[2], data[3]);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	len = sizeof(data);
+
+	ret |= ix2505v_write(state, data, len);
+
+	data[2] |= 0x4; /* set TM = 1 other bits same */
+
+	len = 1;
+	ret |= ix2505v_write(state, &data[2], len); /* write byte 4 only */
+
+	msleep(10);
+
+	data[2] |= ((lpf >> 2) & 0x3) << 3; /* lpf */
+	data[3] |= (lpf & 0x3) << 2;
+
+	deb_info("Data 2=[%x%x]\n", data[2], data[3]);
+
+	len = 2;
+	ret |= ix2505v_write(state, &data[2], len); /* write byte 4 & 5 */
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	if (state->config->min_delay_ms)
+		msleep(state->config->min_delay_ms);
+
+	state->frequency = frequency;
+
+	return ret;
+}
+
+static int ix2505v_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+	struct ix2505v_state *state = fe->tuner_priv;
+
+	*frequency = state->frequency;
+
+	return 0;
+}
+
+static struct dvb_tuner_ops ix2505v_tuner_ops = {
+	.info = {
+		.name = "Sharp IX2505V (B0017)",
+		.frequency_min = 950000,
+		.frequency_max = 2175000
+	},
+	.release = ix2505v_release,
+	.set_params = ix2505v_set_params,
+	.get_frequency = ix2505v_get_frequency,
+};
+
+struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe,
+				    const struct ix2505v_config *config,
+				    struct i2c_adapter *i2c)
+{
+	struct ix2505v_state *state = NULL;
+	int ret;
+
+	if (NULL == config) {
+		deb_i2c("%s: no config ", __func__);
+		goto error;
+	}
+
+	state = kzalloc(sizeof(struct ix2505v_state), GFP_KERNEL);
+	if (NULL == state)
+		return NULL;
+
+	state->config = config;
+	state->i2c = i2c;
+
+	if (state->config->tuner_write_only) {
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+
+		ret = ix2505v_read_status_reg(state);
+
+		if (ret & 0x80) {
+			deb_i2c("%s: No IX2505V found\n", __func__);
+			goto error;
+		}
+
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 0);
+	}
+
+	fe->tuner_priv = state;
+
+	memcpy(&fe->ops.tuner_ops, &ix2505v_tuner_ops,
+		sizeof(struct dvb_tuner_ops));
+	deb_i2c("%s: initialization (%s addr=0x%02x) ok\n",
+		__func__, fe->ops.tuner_ops.info.name, config->tuner_address);
+
+	return fe;
+
+error:
+	ix2505v_release(fe);
+	return NULL;
+}
+EXPORT_SYMBOL(ix2505v_attach);
+
+module_param_named(debug, ix2505v_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+MODULE_DESCRIPTION("DVB IX2505V tuner driver");
+MODULE_AUTHOR("Malcolm Priestley");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/ix2505v.h b/drivers/media/dvb/frontends/ix2505v.h
new file mode 100644
index 0000000..67e89d6
--- /dev/null
+++ b/drivers/media/dvb/frontends/ix2505v.h
@@ -0,0 +1,64 @@
+/**
+ * Driver for Sharp IX2505V (marked B0017) DVB-S silicon tuner
+ *
+ * Copyright (C) 2010 Malcolm Priestley
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * 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 DVB_IX2505V_H
+#define DVB_IX2505V_H
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+/**
+ * Attach a ix2505v tuner to the supplied frontend structure.
+ *
+ * @param fe Frontend to attach to.
+ * @param config ix2505v_config structure
+ * @return FE pointer on success, NULL on failure.
+ */
+
+struct ix2505v_config {
+	u8 tuner_address;
+
+	/*Baseband AMP gain control 0/1=0dB(default) 2=-2bB 3=-4dB */
+	u8 tuner_gain;
+
+	/*Charge pump output +/- 0=120 1=260 2=555 3=1200(default) */
+	u8 tuner_chargepump;
+
+	/* delay after tune */
+	int min_delay_ms;
+
+	/* disables reads*/
+	u8 tuner_write_only;
+
+};
+
+#if defined(CONFIG_DVB_IX2505V) || \
+	(defined(CONFIG_DVB_IX2505V_MODULE) && defined(MODULE))
+extern struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe,
+	const struct ix2505v_config *config, struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe,
+	const struct ix2505v_config *config, struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif
+
+#endif /* DVB_IX2505V_H */
diff --git a/drivers/media/dvb/frontends/lgdt3304.c b/drivers/media/dvb/frontends/lgdt3304.c
deleted file mode 100644
index 45a529b..0000000
--- a/drivers/media/dvb/frontends/lgdt3304.c
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Driver for LG ATSC lgdt3304 driver
- *
- * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de>
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include "dvb_frontend.h"
-#include "lgdt3304.h"
-
-static  unsigned int debug = 0;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug,"lgdt3304 debugging (default off)");
-
-#define dprintk(fmt, args...) if (debug) do {\
-			printk("lgdt3304 debug: " fmt, ##args); } while (0)
-
-struct lgdt3304_state
-{
-	struct dvb_frontend frontend;
-	fe_modulation_t current_modulation;
-	__u32 snr;
-	__u32 current_frequency;
-	__u8 addr;
-	struct i2c_adapter *i2c;
-};
-
-static int i2c_write_demod_bytes (struct dvb_frontend *fe, __u8 *buf, int len)
-{
-	struct lgdt3304_state *state = fe->demodulator_priv;
-	struct i2c_msg i2cmsgs = {
-		.addr = state->addr,
-		.flags = 0,
-		.len = 3,
-		.buf = buf
-	};
-	int i;
-	int err;
-
-	for (i=0; i<len-1; i+=3){
-		if((err = i2c_transfer(state->i2c, &i2cmsgs, 1))<0) {
-			printk("%s i2c_transfer error %d\n", __func__, err);
-			if (err < 0)
-				return err;
-			else
-				return -EREMOTEIO;
-		}
-		i2cmsgs.buf += 3;
-	}
-	return 0;
-}
-
-static int lgdt3304_i2c_read_reg(struct dvb_frontend *fe, unsigned int reg)
-{
-	struct lgdt3304_state *state = fe->demodulator_priv;
-	struct i2c_msg i2cmsgs[2];
-	int ret;
-	__u8 buf;
-
-	__u8 regbuf[2] = { reg>>8, reg&0xff };
-
-	i2cmsgs[0].addr = state->addr;
-	i2cmsgs[0].flags = 0;
-	i2cmsgs[0].len = 2;
-	i2cmsgs[0].buf = regbuf;
-
-	i2cmsgs[1].addr = state->addr;
-	i2cmsgs[1].flags = I2C_M_RD;
-	i2cmsgs[1].len = 1;
-	i2cmsgs[1].buf = &buf;
-
-	if((ret = i2c_transfer(state->i2c, i2cmsgs, 2))<0) {
-		printk("%s i2c_transfer error %d\n", __func__, ret);
-		return ret;
-	}
-
-	return buf;
-}
-
-static int lgdt3304_i2c_write_reg(struct dvb_frontend *fe, int reg, int val)
-{
-	struct lgdt3304_state *state = fe->demodulator_priv;
-	char buffer[3] = { reg>>8, reg&0xff, val };
-	int ret;
-
-	struct i2c_msg i2cmsgs = {
-		.addr = state->addr,
-		.flags = 0,
-		.len = 3,
-		.buf=buffer
-	};
-	ret = i2c_transfer(state->i2c, &i2cmsgs, 1);
-	if (ret != 1) {
-		printk("%s i2c_transfer error %d\n", __func__, ret);
-		return ret;
-	}
-
-	return 0;
-}
-
-
-static int lgdt3304_soft_Reset(struct dvb_frontend *fe)
-{
-	lgdt3304_i2c_write_reg(fe, 0x0002, 0x9a);
-	lgdt3304_i2c_write_reg(fe, 0x0002, 0x9b);
-	mdelay(200);
-	return 0;
-}
-
-static int lgdt3304_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) {
-	int err = 0;
-
-	static __u8 lgdt3304_vsb8_data[] = {
-		/* 16bit  , 8bit */
-		/* regs   , val  */
-		0x00, 0x00, 0x02,
-		0x00, 0x00, 0x13,
-		0x00, 0x0d, 0x02,
-		0x00, 0x0e, 0x02,
-		0x00, 0x12, 0x32,
-		0x00, 0x13, 0xc4,
-		0x01, 0x12, 0x17,
-		0x01, 0x13, 0x15,
-		0x01, 0x14, 0x18,
-		0x01, 0x15, 0xff,
-		0x01, 0x16, 0x2c,
-		0x02, 0x14, 0x67,
-		0x02, 0x24, 0x8d,
-		0x04, 0x27, 0x12,
-		0x04, 0x28, 0x4f,
-		0x03, 0x08, 0x80,
-		0x03, 0x09, 0x00,
-		0x03, 0x0d, 0x00,
-		0x03, 0x0e, 0x1c,
-		0x03, 0x14, 0xe1,
-		0x05, 0x0e, 0x5b,
-	};
-
-	/* not yet tested .. */
-	static __u8 lgdt3304_qam64_data[] = {
-		/* 16bit  , 8bit */
-		/* regs   , val  */
-		0x00, 0x00, 0x18,
-		0x00, 0x0d, 0x02,
-		//0x00, 0x0e, 0x02,
-		0x00, 0x12, 0x2a,
-		0x00, 0x13, 0x00,
-		0x03, 0x14, 0xe3,
-		0x03, 0x0e, 0x1c,
-		0x03, 0x08, 0x66,
-		0x03, 0x09, 0x66,
-		0x03, 0x0a, 0x08,
-		0x03, 0x0b, 0x9b,
-		0x05, 0x0e, 0x5b,
-	};
-
-
-	/* tested with KWorld a340 */
-	static __u8 lgdt3304_qam256_data[] = {
-		/* 16bit  , 8bit */
-		/* regs   , val  */
-		0x00, 0x00, 0x01,  //0x19,
-		0x00, 0x12, 0x2a,
-		0x00, 0x13, 0x80,
-		0x00, 0x0d, 0x02,
-		0x03, 0x14, 0xe3,
-
-		0x03, 0x0e, 0x1c,
-		0x03, 0x08, 0x66,
-		0x03, 0x09, 0x66,
-		0x03, 0x0a, 0x08,
-		0x03, 0x0b, 0x9b,
-
-		0x03, 0x0d, 0x14,
-		//0x05, 0x0e, 0x5b,
-		0x01, 0x06, 0x4a,
-		0x01, 0x07, 0x3d,
-		0x01, 0x08, 0x70,
-		0x01, 0x09, 0xa3,
-
-		0x05, 0x04, 0xfd,
-
-		0x00, 0x0d, 0x82,
-
-		0x05, 0x0e, 0x5b,
-
-		0x05, 0x0e, 0x5b,
-
-		0x00, 0x02, 0x9a,
-
-		0x00, 0x02, 0x9b,
-
-		0x00, 0x00, 0x01,
-		0x00, 0x12, 0x2a,
-		0x00, 0x13, 0x80,
-		0x00, 0x0d, 0x02,
-		0x03, 0x14, 0xe3,
-
-		0x03, 0x0e, 0x1c,
-		0x03, 0x08, 0x66,
-		0x03, 0x09, 0x66,
-		0x03, 0x0a, 0x08,
-		0x03, 0x0b, 0x9b,
-
-		0x03, 0x0d, 0x14,
-		0x01, 0x06, 0x4a,
-		0x01, 0x07, 0x3d,
-		0x01, 0x08, 0x70,
-		0x01, 0x09, 0xa3,
-
-		0x05, 0x04, 0xfd,
-
-		0x00, 0x0d, 0x82,
-
-		0x05, 0x0e, 0x5b,
-	};
-
-	struct lgdt3304_state *state = fe->demodulator_priv;
-	if (state->current_modulation != param->u.vsb.modulation) {
-		switch(param->u.vsb.modulation) {
-		case VSB_8:
-			err = i2c_write_demod_bytes(fe, lgdt3304_vsb8_data,
-					sizeof(lgdt3304_vsb8_data));
-			break;
-		case QAM_64:
-			err = i2c_write_demod_bytes(fe, lgdt3304_qam64_data,
-					sizeof(lgdt3304_qam64_data));
-			break;
-		case QAM_256:
-			err = i2c_write_demod_bytes(fe, lgdt3304_qam256_data,
-					sizeof(lgdt3304_qam256_data));
-			break;
-		default:
-			break;
-		}
-
-		if (err) {
-			printk("%s error setting modulation\n", __func__);
-		} else {
-			state->current_modulation = param->u.vsb.modulation;
-		}
-	}
-	state->current_frequency = param->frequency;
-
-	lgdt3304_soft_Reset(fe);
-
-
-	if (fe->ops.tuner_ops.set_params)
-		fe->ops.tuner_ops.set_params(fe, param);
-
-	return 0;
-}
-
-static int lgdt3304_init(struct dvb_frontend *fe) {
-	return 0;
-}
-
-static int lgdt3304_sleep(struct dvb_frontend *fe) {
-	return 0;
-}
-
-
-static int lgdt3304_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
-	struct lgdt3304_state *state = fe->demodulator_priv;
-	int r011d;
-	int qam_lck;
-
-	*status = 0;
-	dprintk("lgdt read status\n");
-
-	r011d = lgdt3304_i2c_read_reg(fe, 0x011d);
-
-	dprintk("%02x\n", r011d);
-
-	switch(state->current_modulation) {
-	case VSB_8:
-		if (r011d & 0x80) {
-			dprintk("VSB Locked\n");
-			*status |= FE_HAS_CARRIER;
-			*status |= FE_HAS_LOCK;
-			*status |= FE_HAS_SYNC;
-			*status |= FE_HAS_SIGNAL;
-		}
-		break;
-	case QAM_64:
-	case QAM_256:
-		qam_lck = r011d & 0x7;
-		switch(qam_lck) {
-			case 0x0: dprintk("Unlock\n");
-				  break;
-			case 0x4: dprintk("1st Lock in acquisition state\n");
-				  break;
-			case 0x6: dprintk("2nd Lock in acquisition state\n");
-				  break;
-			case 0x7: dprintk("Final Lock in good reception state\n");
-				  *status |= FE_HAS_CARRIER;
-				  *status |= FE_HAS_LOCK;
-				  *status |= FE_HAS_SYNC;
-				  *status |= FE_HAS_SIGNAL;
-				  break;
-		}
-		break;
-	default:
-		printk("%s unhandled modulation\n", __func__);
-	}
-
-
-	return 0;
-}
-
-static int lgdt3304_read_ber(struct dvb_frontend *fe, __u32 *ber)
-{
-	dprintk("read ber\n");
-	return 0;
-}
-
-static int lgdt3304_read_snr(struct dvb_frontend *fe, __u16 *snr)
-{
-	dprintk("read snr\n");
-	return 0;
-}
-
-static int lgdt3304_read_ucblocks(struct dvb_frontend *fe, __u32 *ucblocks)
-{
-	dprintk("read ucblocks\n");
-	return 0;
-}
-
-static void lgdt3304_release(struct dvb_frontend *fe)
-{
-	struct lgdt3304_state *state = (struct lgdt3304_state *)fe->demodulator_priv;
-	kfree(state);
-}
-
-static struct dvb_frontend_ops demod_lgdt3304={
-	.info = {
-		.name = "LG 3304",
-		.type = FE_ATSC,
-		.frequency_min = 54000000,
-		.frequency_max = 858000000,
-		.frequency_stepsize = 62500,
-		.symbol_rate_min = 5056941,
-		.symbol_rate_max = 10762000,
-		.caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
-	},
-	.init = lgdt3304_init,
-	.sleep = lgdt3304_sleep,
-	.set_frontend = lgdt3304_set_parameters,
-	.read_snr = lgdt3304_read_snr,
-	.read_ber = lgdt3304_read_ber,
-	.read_status = lgdt3304_read_status,
-	.read_ucblocks = lgdt3304_read_ucblocks,
-	.release = lgdt3304_release,
-};
-
-struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config,
-					   struct i2c_adapter *i2c)
-{
-
-	struct lgdt3304_state *state;
-	state = kzalloc(sizeof(struct lgdt3304_state), GFP_KERNEL);
-	if (state == NULL)
-		return NULL;
-	state->addr = config->i2c_address;
-	state->i2c = i2c;
-
-	memcpy(&state->frontend.ops, &demod_lgdt3304, sizeof(struct dvb_frontend_ops));
-	state->frontend.demodulator_priv = state;
-	return &state->frontend;
-}
-
-EXPORT_SYMBOL_GPL(lgdt3304_attach);
-MODULE_AUTHOR("Markus Rechberger <mrechberger@empiatech.com>");
-MODULE_DESCRIPTION("LGE LGDT3304 DVB-T demodulator driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/lgdt3304.h b/drivers/media/dvb/frontends/lgdt3304.h
deleted file mode 100644
index fc409fe..0000000
--- a/drivers/media/dvb/frontends/lgdt3304.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- *  Driver for DVB-T lgdt3304 demodulator
- *
- *  Copyright (C) 2008 Markus Rechberger <mrechberger@gmail.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
- */
-
-#ifndef LGDT3304_H
-#define LGDT3304_H
-
-#include <linux/dvb/frontend.h>
-
-struct lgdt3304_config
-{
-	/* demodulator's I2C address */
-	u8 i2c_address;
-};
-
-#if defined(CONFIG_DVB_LGDT3304) || (defined(CONFIG_DVB_LGDT3304_MODULE) && defined(MODULE))
-extern struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config,
-					   struct i2c_adapter *i2c);
-#else
-static inline struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config,
-					   struct i2c_adapter *i2c)
-{
-	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
-	return NULL;
-}
-#endif /* CONFIG_DVB_LGDT */
-
-#endif /* LGDT3304_H */
diff --git a/drivers/media/dvb/frontends/lgs8gxx.c b/drivers/media/dvb/frontends/lgs8gxx.c
index 5ea28ae..0fcddc4 100644
--- a/drivers/media/dvb/frontends/lgs8gxx.c
+++ b/drivers/media/dvb/frontends/lgs8gxx.c
@@ -662,7 +662,7 @@
 }
 
 
-static int lgs8gxx_write(struct dvb_frontend *fe, u8 *buf, int len)
+static int lgs8gxx_write(struct dvb_frontend *fe, const u8 buf[], int len)
 {
 	struct lgs8gxx_state *priv = fe->demodulator_priv;
 
diff --git a/drivers/media/dvb/frontends/mt352.c b/drivers/media/dvb/frontends/mt352.c
index beba5aa..319672f 100644
--- a/drivers/media/dvb/frontends/mt352.c
+++ b/drivers/media/dvb/frontends/mt352.c
@@ -69,7 +69,7 @@
 	return 0;
 }
 
-static int _mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen)
+static int _mt352_write(struct dvb_frontend* fe, const u8 ibuf[], int ilen)
 {
 	int err,i;
 	for (i=0; i < ilen-1; i++)
diff --git a/drivers/media/dvb/frontends/mt352.h b/drivers/media/dvb/frontends/mt352.h
index 595092f..ca2562d 100644
--- a/drivers/media/dvb/frontends/mt352.h
+++ b/drivers/media/dvb/frontends/mt352.h
@@ -63,7 +63,7 @@
 }
 #endif // CONFIG_DVB_MT352
 
-static inline int mt352_write(struct dvb_frontend *fe, u8 *buf, int len) {
+static inline int mt352_write(struct dvb_frontend *fe, const u8 buf[], int len) {
 	int r = 0;
 	if (fe->ops.write)
 		r = fe->ops.write(fe, buf, len);
diff --git a/drivers/media/dvb/frontends/s5h1420.c b/drivers/media/dvb/frontends/s5h1420.c
index 2e9fd28..e87b747 100644
--- a/drivers/media/dvb/frontends/s5h1420.c
+++ b/drivers/media/dvb/frontends/s5h1420.c
@@ -920,7 +920,6 @@
 	/* create tuner i2c adapter */
 	strlcpy(state->tuner_i2c_adapter.name, "S5H1420-PN1010 tuner I2C bus",
 		sizeof(state->tuner_i2c_adapter.name));
-	state->tuner_i2c_adapter.class     = I2C_CLASS_TV_DIGITAL,
 	state->tuner_i2c_adapter.algo      = &s5h1420_tuner_i2c_algo;
 	state->tuner_i2c_adapter.algo_data = NULL;
 	i2c_set_adapdata(&state->tuner_i2c_adapter, state);
diff --git a/drivers/media/dvb/frontends/s5h1432.c b/drivers/media/dvb/frontends/s5h1432.c
new file mode 100644
index 0000000..0c6dcb9
--- /dev/null
+++ b/drivers/media/dvb/frontends/s5h1432.c
@@ -0,0 +1,415 @@
+/*
+ *  Samsung s5h1432 DVB-T demodulator driver
+ *
+ *  Copyright (C) 2009 Bill Liu <Bill.Liu@Conexant.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "dvb_frontend.h"
+#include "s5h1432.h"
+
+struct s5h1432_state {
+
+	struct i2c_adapter *i2c;
+
+	/* configuration settings */
+	const struct s5h1432_config *config;
+
+	struct dvb_frontend frontend;
+
+	fe_modulation_t current_modulation;
+	unsigned int first_tune:1;
+
+	u32 current_frequency;
+	int if_freq;
+
+	u8 inversion;
+};
+
+static int debug;
+
+#define dprintk(arg...) do {	\
+	if (debug)		\
+		printk(arg);	\
+	} while (0)
+
+static int s5h1432_writereg(struct s5h1432_state *state,
+			    u8 addr, u8 reg, u8 data)
+{
+	int ret;
+	u8 buf[] = { reg, data };
+
+	struct i2c_msg msg = {.addr = addr, .flags = 0, .buf = buf, .len = 2 };
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+
+	if (ret != 1)
+		printk(KERN_ERR "%s: writereg error 0x%02x 0x%02x 0x%04x, "
+		       "ret == %i)\n", __func__, addr, reg, data, ret);
+
+	return (ret != 1) ? -1 : 0;
+}
+
+static u8 s5h1432_readreg(struct s5h1432_state *state, u8 addr, u8 reg)
+{
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+
+	struct i2c_msg msg[] = {
+		{.addr = addr, .flags = 0, .buf = b0, .len = 1},
+		{.addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 1}
+	};
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2)
+		printk(KERN_ERR "%s: readreg error (ret == %i)\n",
+		       __func__, ret);
+	return b1[0];
+}
+
+static int s5h1432_sleep(struct dvb_frontend *fe)
+{
+	return 0;
+}
+
+static int s5h1432_set_channel_bandwidth(struct dvb_frontend *fe,
+					 u32 bandwidth)
+{
+	struct s5h1432_state *state = fe->demodulator_priv;
+
+	u8 reg = 0;
+
+	/* Register [0x2E] bit 3:2 : 8MHz = 0; 7MHz = 1; 6MHz = 2 */
+	reg = s5h1432_readreg(state, S5H1432_I2C_TOP_ADDR, 0x2E);
+	reg &= ~(0x0C);
+	switch (bandwidth) {
+	case 6:
+		reg |= 0x08;
+		break;
+	case 7:
+		reg |= 0x04;
+		break;
+	case 8:
+		reg |= 0x00;
+		break;
+	default:
+		return 0;
+	}
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x2E, reg);
+	return 1;
+}
+
+static int s5h1432_set_IF(struct dvb_frontend *fe, u32 ifFreqHz)
+{
+	struct s5h1432_state *state = fe->demodulator_priv;
+
+	switch (ifFreqHz) {
+	case TAIWAN_HI_IF_FREQ_44_MHZ:
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x55);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x55);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0x15);
+		break;
+	case EUROPE_HI_IF_FREQ_36_MHZ:
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x00);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x00);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0x40);
+		break;
+	case IF_FREQ_6_MHZ:
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x00);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x00);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xe0);
+		break;
+	case IF_FREQ_3point3_MHZ:
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x66);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x66);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEE);
+		break;
+	case IF_FREQ_3point5_MHZ:
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x55);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x55);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xED);
+		break;
+	case IF_FREQ_4_MHZ:
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0xAA);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0xAA);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEA);
+		break;
+	default:
+		{
+			u32 value = 0;
+			value = (u32) (((48000 - (ifFreqHz / 1000)) * 512 *
+					(u32) 32768) / (48 * 1000));
+			printk(KERN_INFO
+			       "Default IFFreq %d :reg value = 0x%x\n",
+			       ifFreqHz, value);
+			s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4,
+					 (u8) value & 0xFF);
+			s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5,
+					 (u8) (value >> 8) & 0xFF);
+			s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7,
+					 (u8) (value >> 16) & 0xFF);
+			break;
+		}
+
+	}
+
+	return 1;
+}
+
+/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
+static int s5h1432_set_frontend(struct dvb_frontend *fe,
+				struct dvb_frontend_parameters *p)
+{
+	u32 dvb_bandwidth = 8;
+	struct s5h1432_state *state = fe->demodulator_priv;
+
+	if (p->frequency == state->current_frequency) {
+		/*current_frequency = p->frequency; */
+		/*state->current_frequency = p->frequency; */
+	} else {
+		fe->ops.tuner_ops.set_params(fe, p);
+		msleep(300);
+		s5h1432_set_channel_bandwidth(fe, dvb_bandwidth);
+		switch (p->u.ofdm.bandwidth) {
+		case BANDWIDTH_6_MHZ:
+			dvb_bandwidth = 6;
+			s5h1432_set_IF(fe, IF_FREQ_4_MHZ);
+			break;
+		case BANDWIDTH_7_MHZ:
+			dvb_bandwidth = 7;
+			s5h1432_set_IF(fe, IF_FREQ_4_MHZ);
+			break;
+		case BANDWIDTH_8_MHZ:
+			dvb_bandwidth = 8;
+			s5h1432_set_IF(fe, IF_FREQ_4_MHZ);
+			break;
+		default:
+			return 0;
+		}
+		/*fe->ops.tuner_ops.set_params(fe, p); */
+/*Soft Reset chip*/
+		msleep(30);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a);
+		msleep(30);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b);
+
+		s5h1432_set_channel_bandwidth(fe, dvb_bandwidth);
+		switch (p->u.ofdm.bandwidth) {
+		case BANDWIDTH_6_MHZ:
+			dvb_bandwidth = 6;
+			s5h1432_set_IF(fe, IF_FREQ_4_MHZ);
+			break;
+		case BANDWIDTH_7_MHZ:
+			dvb_bandwidth = 7;
+			s5h1432_set_IF(fe, IF_FREQ_4_MHZ);
+			break;
+		case BANDWIDTH_8_MHZ:
+			dvb_bandwidth = 8;
+			s5h1432_set_IF(fe, IF_FREQ_4_MHZ);
+			break;
+		default:
+			return 0;
+		}
+		/*fe->ops.tuner_ops.set_params(fe,p); */
+		/*Soft Reset chip*/
+		msleep(30);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a);
+		msleep(30);
+		s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b);
+
+	}
+
+	state->current_frequency = p->frequency;
+
+	return 0;
+}
+
+static int s5h1432_init(struct dvb_frontend *fe)
+{
+	struct s5h1432_state *state = fe->demodulator_priv;
+
+	u8 reg = 0;
+	state->current_frequency = 0;
+	printk(KERN_INFO " s5h1432_init().\n");
+
+	/*Set VSB mode as default, this also does a soft reset */
+	/*Initialize registers */
+
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x04, 0xa8);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x05, 0x01);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x07, 0x70);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x19, 0x80);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1b, 0x9D);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1c, 0x30);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1d, 0x20);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1e, 0x1B);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x2e, 0x40);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x42, 0x84);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x50, 0x5a);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x5a, 0xd3);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x68, 0x50);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xb8, 0x3c);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xc4, 0x10);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xcc, 0x9c);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xDA, 0x00);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe1, 0x94);
+	/* s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xf4, 0xa1); */
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xf9, 0x00);
+
+	/*For NXP tuner*/
+
+	/*Set 3.3MHz as default IF frequency */
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x66);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x66);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEE);
+	/* Set reg 0x1E to get the full dynamic range */
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1e, 0x31);
+
+	/* Mode setting in demod */
+	reg = s5h1432_readreg(state, S5H1432_I2C_TOP_ADDR, 0x42);
+	reg |= 0x80;
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x42, reg);
+	/* Serial mode */
+
+	/* Soft Reset chip */
+
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a);
+	msleep(30);
+	s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b);
+
+
+	return 0;
+}
+
+static int s5h1432_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	return 0;
+}
+
+static int s5h1432_read_signal_strength(struct dvb_frontend *fe,
+					u16 *signal_strength)
+{
+	return 0;
+}
+
+static int s5h1432_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	return 0;
+}
+
+static int s5h1432_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+
+	return 0;
+}
+
+static int s5h1432_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	return 0;
+}
+
+static int s5h1432_get_frontend(struct dvb_frontend *fe,
+				struct dvb_frontend_parameters *p)
+{
+	return 0;
+}
+
+static int s5h1432_get_tune_settings(struct dvb_frontend *fe,
+				     struct dvb_frontend_tune_settings *tune)
+{
+	return 0;
+}
+
+static void s5h1432_release(struct dvb_frontend *fe)
+{
+	struct s5h1432_state *state = fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops s5h1432_ops;
+
+struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config,
+				    struct i2c_adapter *i2c)
+{
+	struct s5h1432_state *state = NULL;
+
+	printk(KERN_INFO " Enter s5h1432_attach(). attach success!\n");
+	/* allocate memory for the internal state */
+	state = kmalloc(sizeof(struct s5h1432_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	state->current_modulation = QAM_16;
+	state->inversion = state->config->inversion;
+
+	/* create dvb_frontend */
+	memcpy(&state->frontend.ops, &s5h1432_ops,
+	       sizeof(struct dvb_frontend_ops));
+
+	state->frontend.demodulator_priv = state;
+
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+EXPORT_SYMBOL(s5h1432_attach);
+
+static struct dvb_frontend_ops s5h1432_ops = {
+
+	.info = {
+		 .name = "Samsung s5h1432 DVB-T Frontend",
+		 .type = FE_OFDM,
+		 .frequency_min = 177000000,
+		 .frequency_max = 858000000,
+		 .frequency_stepsize = 166666,
+		 .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+		 FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+		 FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+		 FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+		 FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER},
+
+	.init = s5h1432_init,
+	.sleep = s5h1432_sleep,
+	.set_frontend = s5h1432_set_frontend,
+	.get_frontend = s5h1432_get_frontend,
+	.get_tune_settings = s5h1432_get_tune_settings,
+	.read_status = s5h1432_read_status,
+	.read_ber = s5h1432_read_ber,
+	.read_signal_strength = s5h1432_read_signal_strength,
+	.read_snr = s5h1432_read_snr,
+	.read_ucblocks = s5h1432_read_ucblocks,
+	.release = s5h1432_release,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable verbose debug messages");
+
+MODULE_DESCRIPTION("Samsung s5h1432 DVB-T Demodulator driver");
+MODULE_AUTHOR("Bill Liu");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/s5h1432.h b/drivers/media/dvb/frontends/s5h1432.h
new file mode 100644
index 0000000..b57438c
--- /dev/null
+++ b/drivers/media/dvb/frontends/s5h1432.h
@@ -0,0 +1,91 @@
+/*
+ *  Samsung s5h1432 VSB/QAM demodulator driver
+ *
+ *  Copyright (C) 2009 Bill Liu <Bill.Liu@Conexant.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __S5H1432_H__
+#define __S5H1432_H__
+
+#include <linux/dvb/frontend.h>
+
+#define S5H1432_I2C_TOP_ADDR (0x02 >> 1)
+
+#define TAIWAN_HI_IF_FREQ_44_MHZ 44000000
+#define EUROPE_HI_IF_FREQ_36_MHZ 36000000
+#define IF_FREQ_6_MHZ             6000000
+#define IF_FREQ_3point3_MHZ       3300000
+#define IF_FREQ_3point5_MHZ       3500000
+#define IF_FREQ_4_MHZ             4000000
+
+struct s5h1432_config {
+
+	/* serial/parallel output */
+#define S5H1432_PARALLEL_OUTPUT 0
+#define S5H1432_SERIAL_OUTPUT   1
+	u8 output_mode;
+
+	/* GPIO Setting */
+#define S5H1432_GPIO_OFF 0
+#define S5H1432_GPIO_ON  1
+	u8 gpio;
+
+	/* MPEG signal timing */
+#define S5H1432_MPEGTIMING_CONTINOUS_INVERTING_CLOCK       0
+#define S5H1432_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK    1
+#define S5H1432_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK    2
+#define S5H1432_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3
+	u16 mpeg_timing;
+
+	/* IF Freq for QAM and VSB in KHz */
+#define S5H1432_IF_3250  3250
+#define S5H1432_IF_3500  3500
+#define S5H1432_IF_4000  4000
+#define S5H1432_IF_5380  5380
+#define S5H1432_IF_44000 44000
+#define S5H1432_VSB_IF_DEFAULT s5h1432_IF_44000
+#define S5H1432_QAM_IF_DEFAULT s5h1432_IF_44000
+	u16 qam_if;
+	u16 vsb_if;
+
+	/* Spectral Inversion */
+#define S5H1432_INVERSION_OFF 0
+#define S5H1432_INVERSION_ON  1
+	u8 inversion;
+
+	/* Return lock status based on tuner lock, or demod lock */
+#define S5H1432_TUNERLOCKING 0
+#define S5H1432_DEMODLOCKING 1
+	u8 status_mode;
+};
+
+#if defined(CONFIG_DVB_S5H1432) || \
+	(defined(CONFIG_DVB_S5H1432_MODULE) && defined(MODULE))
+extern struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config,
+					   struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *s5h1432_attach(const struct s5h1432_config
+						  *config,
+						  struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_s5h1432 */
+
+#endif /* __s5h1432_H__ */
diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb/frontends/si21xx.c
index d21a327..4b0c99a 100644
--- a/drivers/media/dvb/frontends/si21xx.c
+++ b/drivers/media/dvb/frontends/si21xx.c
@@ -268,7 +268,7 @@
 	return (ret != 1) ? -EREMOTEIO : 0;
 }
 
-static int si21_write(struct dvb_frontend *fe, u8 *buf, int len)
+static int si21_write(struct dvb_frontend *fe, const u8 buf[], int len)
 {
 	struct si21xx_state *state = fe->demodulator_priv;
 
diff --git a/drivers/media/dvb/frontends/stb6100.c b/drivers/media/dvb/frontends/stb6100.c
index f73c133..80a9e4c 100644
--- a/drivers/media/dvb/frontends/stb6100.c
+++ b/drivers/media/dvb/frontends/stb6100.c
@@ -506,7 +506,7 @@
 };
 
 struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe,
-				    struct stb6100_config *config,
+				    const struct stb6100_config *config,
 				    struct i2c_adapter *i2c)
 {
 	struct stb6100_state *state = NULL;
diff --git a/drivers/media/dvb/frontends/stb6100.h b/drivers/media/dvb/frontends/stb6100.h
index 395d056..2ab0966 100644
--- a/drivers/media/dvb/frontends/stb6100.h
+++ b/drivers/media/dvb/frontends/stb6100.h
@@ -97,13 +97,13 @@
 #if defined(CONFIG_DVB_STB6100) || (defined(CONFIG_DVB_STB6100_MODULE) && defined(MODULE))
 
 extern struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe,
-					   struct stb6100_config *config,
+					   const struct stb6100_config *config,
 					   struct i2c_adapter *i2c);
 
 #else
 
 static inline struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe,
-						  struct stb6100_config *config,
+						  const struct stb6100_config *config,
 						  struct i2c_adapter *i2c)
 {
 	printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__);
diff --git a/drivers/media/dvb/frontends/stv0288.c b/drivers/media/dvb/frontends/stv0288.c
index 2930a5d..63db8fd 100644
--- a/drivers/media/dvb/frontends/stv0288.c
+++ b/drivers/media/dvb/frontends/stv0288.c
@@ -6,6 +6,8 @@
 	Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by>
 		Removed stb6000 specific tuner code and revised some
 		procedures.
+	2010-09-01 Josef Pavlik <josef@pavlik.it>
+		Fixed diseqc_msg, diseqc_burst and set_tone problems
 
 	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
@@ -78,7 +80,7 @@
 	return (ret != 1) ? -EREMOTEIO : 0;
 }
 
-static int stv0288_write(struct dvb_frontend *fe, u8 *buf, int len)
+static int stv0288_write(struct dvb_frontend *fe, const u8 buf[], int len)
 {
 	struct stv0288_state *state = fe->demodulator_priv;
 
@@ -156,14 +158,13 @@
 
 	stv0288_writeregI(state, 0x09, 0);
 	msleep(30);
-	stv0288_writeregI(state, 0x05, 0x16);
+	stv0288_writeregI(state, 0x05, 0x12);/* modulated mode, single shot */
 
 	for (i = 0; i < m->msg_len; i++) {
 		if (stv0288_writeregI(state, 0x06, m->msg[i]))
 			return -EREMOTEIO;
-		msleep(12);
 	}
-
+	msleep(m->msg_len*12);
 	return 0;
 }
 
@@ -174,13 +175,14 @@
 
 	dprintk("%s\n", __func__);
 
-	if (stv0288_writeregI(state, 0x05, 0x16))/* burst mode */
+	if (stv0288_writeregI(state, 0x05, 0x03))/* burst mode, single shot */
 		return -EREMOTEIO;
 
 	if (stv0288_writeregI(state, 0x06, burst == SEC_MINI_A ? 0x00 : 0xff))
 		return -EREMOTEIO;
 
-	if (stv0288_writeregI(state, 0x06, 0x12))
+	msleep(15);
+	if (stv0288_writeregI(state, 0x05, 0x12))
 		return -EREMOTEIO;
 
 	return 0;
@@ -192,18 +194,19 @@
 
 	switch (tone) {
 	case SEC_TONE_ON:
-		if (stv0288_writeregI(state, 0x05, 0x10))/* burst mode */
+		if (stv0288_writeregI(state, 0x05, 0x10))/* cont carrier */
 			return -EREMOTEIO;
-		return stv0288_writeregI(state, 0x06, 0xff);
+	break;
 
 	case SEC_TONE_OFF:
-		if (stv0288_writeregI(state, 0x05, 0x13))/* burst mode */
+		if (stv0288_writeregI(state, 0x05, 0x12))/* burst mode off*/
 			return -EREMOTEIO;
-		return stv0288_writeregI(state, 0x06, 0x00);
+	break;
 
 	default:
 		return -EINVAL;
 	}
+	return 0;
 }
 
 static u8 stv0288_inittab[] = {
@@ -486,7 +489,7 @@
 	tda[2] = 0x0; /* CFRL */
 	for (tm = -6; tm < 7;) {
 		/* Viterbi status */
-		if (stv0288_readreg(state, 0x24) & 0x80)
+		if (stv0288_readreg(state, 0x24) & 0x8)
 			break;
 
 		tda[2] += 40;
diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c
index 9688744..4e3db3a 100644
--- a/drivers/media/dvb/frontends/stv0299.c
+++ b/drivers/media/dvb/frontends/stv0299.c
@@ -92,7 +92,7 @@
 	return (ret != 1) ? -EREMOTEIO : 0;
 }
 
-static int stv0299_write(struct dvb_frontend* fe, u8 *buf, int len)
+static int stv0299_write(struct dvb_frontend* fe, const u8 buf[], int len)
 {
 	struct stv0299_state* state = fe->demodulator_priv;
 
diff --git a/drivers/media/dvb/frontends/stv0299.h b/drivers/media/dvb/frontends/stv0299.h
index 0fd96e2..ba219b7 100644
--- a/drivers/media/dvb/frontends/stv0299.h
+++ b/drivers/media/dvb/frontends/stv0299.h
@@ -65,7 +65,7 @@
 	 * First of each pair is the register, second is the value.
 	 * List should be terminated with an 0xff, 0xff pair.
 	 */
-	u8* inittab;
+	const u8* inittab;
 
 	/* master clock to use */
 	u32 mclk;
diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
index f2a8abe..ea485d9 100644
--- a/drivers/media/dvb/frontends/tda1004x.c
+++ b/drivers/media/dvb/frontends/tda1004x.c
@@ -598,7 +598,7 @@
 	return -1;
 }
 
-static int tda1004x_write(struct dvb_frontend* fe, u8 *buf, int len)
+static int tda1004x_write(struct dvb_frontend* fe, const u8 buf[], int len)
 {
 	struct tda1004x_state* state = fe->demodulator_priv;
 
diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c
index 8c61271..adbbf6d 100644
--- a/drivers/media/dvb/frontends/zl10353.c
+++ b/drivers/media/dvb/frontends/zl10353.c
@@ -64,7 +64,7 @@
 	return 0;
 }
 
-static int zl10353_write(struct dvb_frontend *fe, u8 *ibuf, int ilen)
+static int zl10353_write(struct dvb_frontend *fe, const u8 ibuf[], int ilen)
 {
 	int err, i;
 	for (i = 0; i < ilen - 1; i++)
diff --git a/drivers/media/dvb/mantis/mantis_core.c b/drivers/media/dvb/mantis/mantis_core.c
index 8113b23..22524a8 100644
--- a/drivers/media/dvb/mantis/mantis_core.c
+++ b/drivers/media/dvb/mantis/mantis_core.c
@@ -91,10 +91,7 @@
 		return err;
 	}
 	dprintk(verbose, MANTIS_ERROR, 0,
-		"    MAC Address=[%02x:%02x:%02x:%02x:%02x:%02x]\n",
-		mantis->mac_address[0], mantis->mac_address[1],
-		mantis->mac_address[2],	mantis->mac_address[3],
-		mantis->mac_address[4], mantis->mac_address[5]);
+		"    MAC Address=[%pM]\n", mantis->mac_address);
 
 	return 0;
 }
diff --git a/drivers/media/dvb/mantis/mantis_i2c.c b/drivers/media/dvb/mantis/mantis_i2c.c
index 7870bcf..e779451 100644
--- a/drivers/media/dvb/mantis/mantis_i2c.c
+++ b/drivers/media/dvb/mantis/mantis_i2c.c
@@ -229,7 +229,6 @@
 	i2c_set_adapdata(i2c_adapter, mantis);
 
 	i2c_adapter->owner	= THIS_MODULE;
-	i2c_adapter->class	= I2C_CLASS_TV_DIGITAL;
 	i2c_adapter->algo	= &mantis_algo;
 	i2c_adapter->algo_data	= NULL;
 	i2c_adapter->timeout	= 500;
diff --git a/drivers/media/dvb/mantis/mantis_ioc.c b/drivers/media/dvb/mantis/mantis_ioc.c
index de148de..fe31cfb 100644
--- a/drivers/media/dvb/mantis/mantis_ioc.c
+++ b/drivers/media/dvb/mantis/mantis_ioc.c
@@ -68,14 +68,7 @@
 		return err;
 	}
 
-	dprintk(MANTIS_ERROR, 0,
-		"    MAC Address=[%02x:%02x:%02x:%02x:%02x:%02x]\n",
-		mac_addr[0],
-		mac_addr[1],
-		mac_addr[2],
-		mac_addr[3],
-		mac_addr[4],
-		mac_addr[5]);
+	dprintk(MANTIS_ERROR, 0, "    MAC Address=[%pM]\n", mac_addr);
 
 	return 0;
 }
diff --git a/drivers/media/dvb/ngene/ngene-i2c.c b/drivers/media/dvb/ngene/ngene-i2c.c
index 477fe0a..c3ae956 100644
--- a/drivers/media/dvb/ngene/ngene-i2c.c
+++ b/drivers/media/dvb/ngene/ngene-i2c.c
@@ -165,7 +165,6 @@
 	struct i2c_adapter *adap = &(dev->channel[dev_nr].i2c_adapter);
 
 	i2c_set_adapdata(adap, &(dev->channel[dev_nr]));
-	adap->class = I2C_CLASS_TV_DIGITAL | I2C_CLASS_TV_ANALOG;
 
 	strcpy(adap->name, "nGene");
 
diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c
index 1c79821..6ca6713d 100644
--- a/drivers/media/dvb/pluto2/pluto2.c
+++ b/drivers/media/dvb/pluto2/pluto2.c
@@ -647,7 +647,6 @@
 	i2c_set_adapdata(&pluto->i2c_adap, pluto);
 	strcpy(pluto->i2c_adap.name, DRIVER_NAME);
 	pluto->i2c_adap.owner = THIS_MODULE;
-	pluto->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
 	pluto->i2c_adap.dev.parent = &pdev->dev;
 	pluto->i2c_adap.algo_data = &pluto->i2c_bit;
 	pluto->i2c_bit.data = pluto;
diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c
index 69ad949..0486919 100644
--- a/drivers/media/dvb/pt1/pt1.c
+++ b/drivers/media/dvb/pt1/pt1.c
@@ -1087,7 +1087,6 @@
 	pt1_update_power(pt1);
 
 	i2c_adap = &pt1->i2c_adap;
-	i2c_adap->class = I2C_CLASS_TV_DIGITAL;
 	i2c_adap->algo = &pt1_i2c_algo;
 	i2c_adap->algo_data = NULL;
 	i2c_adap->dev.parent = &pdev->dev;
diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c
index ff3b0fa..135e45b 100644
--- a/drivers/media/dvb/siano/smscoreapi.c
+++ b/drivers/media/dvb/siano/smscoreapi.c
@@ -1504,8 +1504,7 @@
 		u32 msgData[3]; /* keep it 3 ! */
 	} *pMsg;
 
-	if ((NewLevel > 1) || (PinNum > MAX_GPIO_PIN_NUMBER) ||
-			(PinNum > MAX_GPIO_PIN_NUMBER))
+	if ((NewLevel > 1) || (PinNum > MAX_GPIO_PIN_NUMBER))
 		return -EINVAL;
 
 	totalLen = sizeof(struct SmsMsgHdr_ST) +
diff --git a/drivers/media/dvb/siano/smsir.c b/drivers/media/dvb/siano/smsir.c
index d0e4639..a27c44a 100644
--- a/drivers/media/dvb/siano/smsir.c
+++ b/drivers/media/dvb/siano/smsir.c
@@ -40,7 +40,7 @@
 	const s32 *samples = (const void *)buf;
 
 	for (i = 0; i < len >> 2; i++) {
-		struct ir_raw_event ev;
+		DEFINE_IR_RAW_EVENT(ev);
 
 		ev.duration = abs(samples[i]) * 1000; /* Convert to ns */
 		ev.pulse = (samples[i] > 0) ? false : true;
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index a12b88f..fc0a60f 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -2472,7 +2472,6 @@
 	   get recognized before the main driver is fully loaded */
 	saa7146_write(dev, GPIO_CTRL, 0x500000);
 
-	av7110->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
 	strlcpy(av7110->i2c_adap.name, pci_ext->ext_priv, sizeof(av7110->i2c_adap.name));
 
 	saa7146_i2c_adapter_prepare(dev, &av7110->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); /* 275 kHz */
@@ -2886,7 +2885,7 @@
 
 
 static struct saa7146_extension av7110_extension_driver = {
-	.name		= "dvb",
+	.name		= "av7110",
 	.flags		= SAA7146_USE_I2C_IRQ,
 
 	.module		= THIS_MODULE,
diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c
index 244d5d5..952b33d 100644
--- a/drivers/media/dvb/ttpci/av7110_av.c
+++ b/drivers/media/dvb/ttpci/av7110_av.c
@@ -245,8 +245,11 @@
 		return -1;
 	}
 	while (1) {
-		if ((len = dvb_ringbuffer_avail(buf)) < 6)
+		len = dvb_ringbuffer_avail(buf);
+		if (len < 6) {
+			wake_up(&buf->queue);
 			return -1;
+		}
 		sync =  DVB_RINGBUFFER_PEEK(buf, 0) << 24;
 		sync |= DVB_RINGBUFFER_PEEK(buf, 1) << 16;
 		sync |= DVB_RINGBUFFER_PEEK(buf, 2) << 8;
diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c
index 0546613..37666d4 100644
--- a/drivers/media/dvb/ttpci/budget-core.c
+++ b/drivers/media/dvb/ttpci/budget-core.c
@@ -495,8 +495,6 @@
 	if (bi->type != BUDGET_FS_ACTIVY)
 		saa7146_write(dev, GPIO_CTRL, 0x500000);	/* GPIO 3 = 1 */
 
-	budget->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
-
 	strlcpy(budget->i2c_adap.name, budget->card->name, sizeof(budget->i2c_adap.name));
 
 	saa7146_i2c_adapter_prepare(dev, &budget->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120);
diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
index 4a3f2b8..40625b2 100644
--- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
+++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
@@ -1694,7 +1694,6 @@
 
 	i2c_set_adapdata(&ttusb->i2c_adap, ttusb);
 
-	ttusb->i2c_adap.class		  = I2C_CLASS_TV_DIGITAL;
 	ttusb->i2c_adap.algo              = &ttusb_dec_algo;
 	ttusb->i2c_adap.algo_data         = NULL;
 	ttusb->i2c_adap.dev.parent	  = &udev->dev;
diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
index 482d0f3b..b701ea6 100644
--- a/drivers/media/radio/radio-cadet.c
+++ b/drivers/media/radio/radio-cadet.c
@@ -374,7 +374,8 @@
 	switch (v->index) {
 	case 0:
 		strlcpy(v->name, "FM", sizeof(v->name));
-		v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS;
+		v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
+			V4L2_TUNER_CAP_RDS_BLOCK_IO;
 		v->rangelow = 1400;     /* 87.5 MHz */
 		v->rangehigh = 1728;    /* 108.0 MHz */
 		v->rxsubchans = cadet_getstereo(dev);
diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c
index 353b828..b540e80 100644
--- a/drivers/media/radio/radio-mr800.c
+++ b/drivers/media/radio/radio-mr800.c
@@ -176,8 +176,6 @@
 	int retval;
 	int size;
 
-	BUG_ON(!mutex_is_locked(&radio->lock));
-
 	radio->buffer[0] = 0x00;
 	radio->buffer[1] = 0x55;
 	radio->buffer[2] = 0xaa;
@@ -207,8 +205,6 @@
 	int size;
 	unsigned short freq_send = 0x10 + (freq >> 3) / 25;
 
-	BUG_ON(!mutex_is_locked(&radio->lock));
-
 	radio->buffer[0] = 0x00;
 	radio->buffer[1] = 0x55;
 	radio->buffer[2] = 0xaa;
@@ -253,8 +249,6 @@
 	int retval;
 	int size;
 
-	BUG_ON(!mutex_is_locked(&radio->lock));
-
 	radio->buffer[0] = 0x00;
 	radio->buffer[1] = 0x55;
 	radio->buffer[2] = 0xaa;
@@ -290,11 +284,13 @@
 	struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf));
 
 	mutex_lock(&radio->lock);
-	radio->usbdev = NULL;
-	mutex_unlock(&radio->lock);
-
+	/* increase the device node's refcount */
+	get_device(&radio->videodev.dev);
 	v4l2_device_disconnect(&radio->v4l2_dev);
 	video_unregister_device(&radio->videodev);
+	mutex_unlock(&radio->lock);
+	/* decrease the device node's refcount, allowing it to be released */
+	put_device(&radio->videodev.dev);
 }
 
 /* vidioc_querycap - query device capabilities */
@@ -503,28 +499,18 @@
 static int usb_amradio_open(struct file *file)
 {
 	struct amradio_device *radio = video_drvdata(file);
-	int retval = 0;
-
-	mutex_lock(&radio->lock);
-
-	if (!radio->usbdev) {
-		retval = -EIO;
-		goto unlock;
-	}
+	int retval;
 
 	file->private_data = radio;
 	retval = usb_autopm_get_interface(radio->intf);
 	if (retval)
-		goto unlock;
+		return retval;
 
 	if (unlikely(!radio->initialized)) {
 		retval = usb_amradio_init(radio);
 		if (retval)
 			usb_autopm_put_interface(radio->intf);
 	}
-
-unlock:
-	mutex_unlock(&radio->lock);
 	return retval;
 }
 
@@ -532,37 +518,10 @@
 static int usb_amradio_close(struct file *file)
 {
 	struct amradio_device *radio = file->private_data;
-	int retval = 0;
 
-	mutex_lock(&radio->lock);
-
-	if (!radio->usbdev)
-		retval = -EIO;
-	else
+	if (video_is_registered(&radio->videodev))
 		usb_autopm_put_interface(radio->intf);
-
-	mutex_unlock(&radio->lock);
-	return retval;
-}
-
-static long usb_amradio_ioctl(struct file *file, unsigned int cmd,
-				unsigned long arg)
-{
-	struct amradio_device *radio = file->private_data;
-	long retval = 0;
-
-	mutex_lock(&radio->lock);
-
-	if (!radio->usbdev) {
-		retval = -EIO;
-		goto unlock;
-	}
-
-	retval = video_ioctl2(file, cmd, arg);
-
-unlock:
-	mutex_unlock(&radio->lock);
-	return retval;
+	return 0;
 }
 
 /* Suspend device - stop device. Need to be checked and fixed */
@@ -571,15 +530,13 @@
 	struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf));
 
 	mutex_lock(&radio->lock);
-
 	if (!radio->muted && radio->initialized) {
 		amradio_set_mute(radio, AMRADIO_STOP);
 		radio->muted = 0;
 	}
+	mutex_unlock(&radio->lock);
 
 	dev_info(&intf->dev, "going into suspend..\n");
-
-	mutex_unlock(&radio->lock);
 	return 0;
 }
 
@@ -589,7 +546,6 @@
 	struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf));
 
 	mutex_lock(&radio->lock);
-
 	if (unlikely(!radio->initialized))
 		goto unlock;
 
@@ -604,9 +560,9 @@
 		amradio_set_mute(radio, AMRADIO_START);
 
 unlock:
-	dev_info(&intf->dev, "coming out of suspend..\n");
-
 	mutex_unlock(&radio->lock);
+
+	dev_info(&intf->dev, "coming out of suspend..\n");
 	return 0;
 }
 
@@ -615,7 +571,7 @@
 	.owner		= THIS_MODULE,
 	.open		= usb_amradio_open,
 	.release	= usb_amradio_close,
-	.ioctl		= usb_amradio_ioctl,
+	.unlocked_ioctl	= video_ioctl2,
 };
 
 static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
@@ -671,19 +627,20 @@
 		goto err_v4l2;
 	}
 
+	mutex_init(&radio->lock);
+
 	strlcpy(radio->videodev.name, radio->v4l2_dev.name,
 		sizeof(radio->videodev.name));
 	radio->videodev.v4l2_dev = &radio->v4l2_dev;
 	radio->videodev.fops = &usb_amradio_fops;
 	radio->videodev.ioctl_ops = &usb_amradio_ioctl_ops;
 	radio->videodev.release = usb_amradio_video_device_release;
+	radio->videodev.lock = &radio->lock;
 
 	radio->usbdev = interface_to_usbdev(intf);
 	radio->intf = intf;
 	radio->curfreq = 95.16 * FREQ_MUL;
 
-	mutex_init(&radio->lock);
-
 	video_set_drvdata(&radio->videodev, radio);
 
 	retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO,
diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c
index 13554ab..6a43578 100644
--- a/drivers/media/radio/radio-si4713.c
+++ b/drivers/media/radio/radio-si4713.c
@@ -291,19 +291,19 @@
 		goto unregister_v4l2_dev;
 	}
 
-	sd = v4l2_i2c_new_subdev_board(&rsdev->v4l2_dev, adapter, "si4713_i2c",
+	sd = v4l2_i2c_new_subdev_board(&rsdev->v4l2_dev, adapter, NULL,
 					pdata->subdev_board_info, NULL);
 	if (!sd) {
 		dev_err(&pdev->dev, "Cannot get v4l2 subdevice\n");
 		rval = -ENODEV;
-		goto unregister_v4l2_dev;
+		goto put_adapter;
 	}
 
 	rsdev->radio_dev = video_device_alloc();
 	if (!rsdev->radio_dev) {
 		dev_err(&pdev->dev, "Failed to alloc video device.\n");
 		rval = -ENOMEM;
-		goto unregister_v4l2_dev;
+		goto put_adapter;
 	}
 
 	memcpy(rsdev->radio_dev, &radio_si4713_vdev_template,
@@ -320,6 +320,8 @@
 
 free_vdev:
 	video_device_release(rsdev->radio_dev);
+put_adapter:
+	i2c_put_adapter(adapter);
 unregister_v4l2_dev:
 	v4l2_device_unregister(&rsdev->v4l2_dev);
 free_rsdev:
@@ -335,8 +337,12 @@
 	struct radio_si4713_device *rsdev = container_of(v4l2_dev,
 						struct radio_si4713_device,
 						v4l2_dev);
+	struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next,
+					    struct v4l2_subdev, list);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	video_unregister_device(rsdev->radio_dev);
+	i2c_put_adapter(client->adapter);
 	v4l2_device_unregister(&rsdev->v4l2_dev);
 	kfree(rsdev);
 
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c
index 9927a59..ac76dfe 100644
--- a/drivers/media/radio/si470x/radio-si470x-common.c
+++ b/drivers/media/radio/si470x/radio-si470x-common.c
@@ -408,17 +408,15 @@
 /*
  * si470x_rds_on - switch on rds reception
  */
-int si470x_rds_on(struct si470x_device *radio)
+static int si470x_rds_on(struct si470x_device *radio)
 {
 	int retval;
 
 	/* sysconfig 1 */
-	mutex_lock(&radio->lock);
 	radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
 	retval = si470x_set_register(radio, SYSCONFIG1);
 	if (retval < 0)
 		radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
-	mutex_unlock(&radio->lock);
 
 	return retval;
 }
@@ -440,6 +438,7 @@
 	unsigned int block_count = 0;
 
 	/* switch on rds reception */
+	mutex_lock(&radio->lock);
 	if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
 		si470x_rds_on(radio);
 
@@ -480,9 +479,9 @@
 		buf += 3;
 		retval += 3;
 	}
-	mutex_unlock(&radio->lock);
 
 done:
+	mutex_unlock(&radio->lock);
 	return retval;
 }
 
@@ -497,8 +496,11 @@
 	int retval = 0;
 
 	/* switch on rds reception */
+
+	mutex_lock(&radio->lock);
 	if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
 		si470x_rds_on(radio);
+	mutex_unlock(&radio->lock);
 
 	poll_wait(file, &radio->read_queue, pts);
 
@@ -516,7 +518,7 @@
 	.owner			= THIS_MODULE,
 	.read			= si470x_fops_read,
 	.poll			= si470x_fops_poll,
-	.ioctl			= video_ioctl2,
+	.unlocked_ioctl		= video_ioctl2,
 	.open			= si470x_fops_open,
 	.release		= si470x_fops_release,
 };
@@ -572,6 +574,7 @@
 	struct si470x_device *radio = video_drvdata(file);
 	int retval = 0;
 
+	mutex_lock(&radio->lock);
 	/* safety checks */
 	retval = si470x_disconnect_check(radio);
 	if (retval)
@@ -594,6 +597,8 @@
 	if (retval < 0)
 		dev_warn(&radio->videodev->dev,
 			"get control failed with %d\n", retval);
+
+	mutex_unlock(&radio->lock);
 	return retval;
 }
 
@@ -607,6 +612,7 @@
 	struct si470x_device *radio = video_drvdata(file);
 	int retval = 0;
 
+	mutex_lock(&radio->lock);
 	/* safety checks */
 	retval = si470x_disconnect_check(radio);
 	if (retval)
@@ -633,6 +639,7 @@
 	if (retval < 0)
 		dev_warn(&radio->videodev->dev,
 			"set control failed with %d\n", retval);
+	mutex_unlock(&radio->lock);
 	return retval;
 }
 
@@ -662,6 +669,7 @@
 	struct si470x_device *radio = video_drvdata(file);
 	int retval = 0;
 
+	mutex_lock(&radio->lock);
 	/* safety checks */
 	retval = si470x_disconnect_check(radio);
 	if (retval)
@@ -681,7 +689,7 @@
 	tuner->type = V4L2_TUNER_RADIO;
 #if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
 	tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
-			    V4L2_TUNER_CAP_RDS;
+			    V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO;
 #else
 	tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
 #endif
@@ -737,6 +745,7 @@
 	if (retval < 0)
 		dev_warn(&radio->videodev->dev,
 			"get tuner failed with %d\n", retval);
+	mutex_unlock(&radio->lock);
 	return retval;
 }
 
@@ -750,6 +759,7 @@
 	struct si470x_device *radio = video_drvdata(file);
 	int retval = 0;
 
+	mutex_lock(&radio->lock);
 	/* safety checks */
 	retval = si470x_disconnect_check(radio);
 	if (retval)
@@ -776,6 +786,7 @@
 	if (retval < 0)
 		dev_warn(&radio->videodev->dev,
 			"set tuner failed with %d\n", retval);
+	mutex_unlock(&radio->lock);
 	return retval;
 }
 
@@ -790,6 +801,7 @@
 	int retval = 0;
 
 	/* safety checks */
+	mutex_lock(&radio->lock);
 	retval = si470x_disconnect_check(radio);
 	if (retval)
 		goto done;
@@ -806,6 +818,7 @@
 	if (retval < 0)
 		dev_warn(&radio->videodev->dev,
 			"get frequency failed with %d\n", retval);
+	mutex_unlock(&radio->lock);
 	return retval;
 }
 
@@ -819,6 +832,7 @@
 	struct si470x_device *radio = video_drvdata(file);
 	int retval = 0;
 
+	mutex_lock(&radio->lock);
 	/* safety checks */
 	retval = si470x_disconnect_check(radio);
 	if (retval)
@@ -835,6 +849,7 @@
 	if (retval < 0)
 		dev_warn(&radio->videodev->dev,
 			"set frequency failed with %d\n", retval);
+	mutex_unlock(&radio->lock);
 	return retval;
 }
 
@@ -848,6 +863,7 @@
 	struct si470x_device *radio = video_drvdata(file);
 	int retval = 0;
 
+	mutex_lock(&radio->lock);
 	/* safety checks */
 	retval = si470x_disconnect_check(radio);
 	if (retval)
@@ -864,6 +880,7 @@
 	if (retval < 0)
 		dev_warn(&radio->videodev->dev,
 			"set hardware frequency seek failed with %d\n", retval);
+	mutex_unlock(&radio->lock);
 	return retval;
 }
 
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index 5ec13e5..392e84f 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -517,7 +517,7 @@
 	struct si470x_device *radio = video_drvdata(file);
 	int retval;
 
-	lock_kernel();
+	mutex_lock(&radio->lock);
 	radio->users++;
 
 	retval = usb_autopm_get_interface(radio->intf);
@@ -558,7 +558,7 @@
 	}
 
 done:
-	unlock_kernel();
+	mutex_unlock(&radio->lock);
 	return retval;
 }
 
@@ -577,7 +577,7 @@
 		goto done;
 	}
 
-	mutex_lock(&radio->disconnect_lock);
+	mutex_lock(&radio->lock);
 	radio->users--;
 	if (radio->users == 0) {
 		/* shutdown interrupt handler */
@@ -591,7 +591,7 @@
 			video_unregister_device(radio->videodev);
 			kfree(radio->int_in_buffer);
 			kfree(radio->buffer);
-			mutex_unlock(&radio->disconnect_lock);
+			mutex_unlock(&radio->lock);
 			kfree(radio);
 			goto done;
 		}
@@ -603,7 +603,7 @@
 		retval = si470x_stop(radio);
 		usb_autopm_put_interface(radio->intf);
 	}
-	mutex_unlock(&radio->disconnect_lock);
+	mutex_unlock(&radio->lock);
 done:
 	return retval;
 }
@@ -661,7 +661,6 @@
 	radio->disconnected = 0;
 	radio->usbdev = interface_to_usbdev(intf);
 	radio->intf = intf;
-	mutex_init(&radio->disconnect_lock);
 	mutex_init(&radio->lock);
 
 	iface_desc = intf->cur_altsetting;
@@ -830,7 +829,7 @@
 {
 	struct si470x_device *radio = usb_get_intfdata(intf);
 
-	mutex_lock(&radio->disconnect_lock);
+	mutex_lock(&radio->lock);
 	radio->disconnected = 1;
 	usb_set_intfdata(intf, NULL);
 	if (radio->users == 0) {
@@ -843,10 +842,10 @@
 		kfree(radio->int_in_buffer);
 		video_unregister_device(radio->videodev);
 		kfree(radio->buffer);
-		mutex_unlock(&radio->disconnect_lock);
+		mutex_unlock(&radio->lock);
 		kfree(radio);
 	} else {
-		mutex_unlock(&radio->disconnect_lock);
+		mutex_unlock(&radio->lock);
 	}
 }
 
diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h
index 3cd0a29..ea12782 100644
--- a/drivers/media/radio/si470x/radio-si470x.h
+++ b/drivers/media/radio/si470x/radio-si470x.h
@@ -177,7 +177,6 @@
 
 	/* driver management */
 	unsigned char disconnected;
-	struct mutex disconnect_lock;
 #endif
 
 #if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE)
@@ -221,7 +220,6 @@
 int si470x_set_freq(struct si470x_device *radio, unsigned int freq);
 int si470x_start(struct si470x_device *radio);
 int si470x_stop(struct si470x_device *radio);
-int si470x_rds_on(struct si470x_device *radio);
 int si470x_fops_open(struct file *file);
 int si470x_fops_release(struct file *file);
 int si470x_vidioc_querycap(struct file *file, void *priv,
diff --git a/drivers/media/radio/si4713-i2c.c b/drivers/media/radio/si4713-i2c.c
index fc7f4b7..a6e6f19 100644
--- a/drivers/media/radio/si4713-i2c.c
+++ b/drivers/media/radio/si4713-i2c.c
@@ -1804,7 +1804,7 @@
 
 	strncpy(vm->name, "FM Modulator", 32);
 	vm->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW |
-						V4L2_TUNER_CAP_RDS;
+		V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_CONTROLS;
 
 	/* Report current frequency range limits */
 	vm->rangelow = si4713_to_v4l2(FREQ_RANGE_LOW);
diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c
index 90cae90..7c0d777 100644
--- a/drivers/media/radio/tef6862.c
+++ b/drivers/media/radio/tef6862.c
@@ -22,7 +22,6 @@
 #include <linux/kernel.h>
 #include <linux/interrupt.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/slab.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-device.h>
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index d000522..ac16e81 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -539,7 +539,7 @@
 config VIDEO_VIVI
 	tristate "Virtual Video Driver"
 	depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64
-	depends on (FRAMEBUFFER_CONSOLE || STI_CONSOLE) && FONTS
+	depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
 	select FONT_8x16
 	select VIDEOBUF_VMALLOC
 	default n
@@ -599,68 +599,8 @@
 	  Check out <file:Documentation/video4linux/w9966.txt> for more
 	  information.
 
-config VIDEO_CPIA
-	tristate "CPiA Video For Linux (DEPRECATED)"
-	depends on VIDEO_V4L1
-	default n
-	---help---
-	  This driver is DEPRECATED please use the gspca cpia1 module
-	  instead. Note that you need atleast version 0.6.4 of libv4l for
-	  the cpia1 gspca module.
-
-	  This is the video4linux driver for cameras based on Vision's CPiA
-	  (Colour Processor Interface ASIC), such as the Creative Labs Video
-	  Blaster Webcam II. If you have one of these cameras, say Y here
-	  and select parallel port and/or USB lowlevel support below,
-	  otherwise say N. This will not work with the Creative Webcam III.
-
-	  Please read <file:Documentation/video4linux/README.cpia> for more
-	  information.
-
-	  This driver is also available as a module (cpia).
-
-config VIDEO_CPIA_PP
-	tristate "CPiA Parallel Port Lowlevel Support"
-	depends on PARPORT_1284 && VIDEO_CPIA && PARPORT
-	help
-	  This is the lowlevel parallel port support for cameras based on
-	  Vision's CPiA (Colour Processor Interface ASIC), such as the
-	  Creative Webcam II. If you have the parallel port version of one
-	  of these cameras, say Y here, otherwise say N. It is also available
-	  as a module (cpia_pp).
-
-config VIDEO_CPIA_USB
-	tristate "CPiA USB Lowlevel Support"
-	depends on VIDEO_CPIA && USB
-	help
-	  This is the lowlevel USB support for cameras based on Vision's CPiA
-	  (Colour Processor Interface ASIC), such as the Creative Webcam II.
-	  If you have the USB version of one of these cameras, say Y here,
-	  otherwise say N. This will not work with the Creative Webcam III.
-	  It is also available as a module (cpia_usb).
-
 source "drivers/media/video/cpia2/Kconfig"
 
-config VIDEO_SAA5246A
-	tristate "SAA5246A, SAA5281 Teletext processor"
-	depends on I2C && VIDEO_V4L2
-	help
-	  Support for I2C bus based teletext using the SAA5246A or SAA5281
-	  chip. Useful only if you live in Europe.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called saa5246a.
-
-config VIDEO_SAA5249
-	tristate "SAA5249 Teletext processor"
-	depends on I2C && VIDEO_V4L2
-	help
-	  Support for I2C bus based teletext using the SAA5249 chip. At the
-	  moment this is only useful on some European WinTV cards.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called saa5249.
-
 config VIDEO_VINO
 	tristate "SGI Vino Video For Linux (EXPERIMENTAL)"
 	depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L2
@@ -669,14 +609,6 @@
 	  Say Y here to build in support for the Vino video input system found
 	  on SGI Indy machines.
 
-config VIDEO_STRADIS
-	tristate "Stradis 4:2:2 MPEG-2 video driver  (EXPERIMENTAL)"
-	depends on EXPERIMENTAL && PCI && VIDEO_V4L1 && VIRT_TO_BUS
-	help
-	  Say Y here to enable support for the Stradis 4:2:2 MPEG-2 video
-	  driver for PCI.  There is a product page at
-	  <http://www.stradis.com/>.
-
 source "drivers/media/video/zoran/Kconfig"
 
 config VIDEO_MEYE
@@ -774,6 +706,22 @@
 	  CMOS camera controller.  This is the controller found on first-
 	  generation OLPC systems.
 
+config VIDEO_SR030PC30
+	tristate "SR030PC30 VGA camera sensor support"
+	depends on I2C && VIDEO_V4L2
+	---help---
+	  This driver supports SR030PC30 VGA camera from Siliconfile
+
+config VIDEO_VIA_CAMERA
+	tristate "VIAFB camera controller support"
+	depends on FB_VIA
+	select VIDEOBUF_DMA_SG
+	select VIDEO_OV7670
+	help
+	   Driver support for the integrated camera controller in VIA
+	   Chrome9 chipsets.  Currently only tested on OLPC xo-1.5 systems
+	   with ov7670 sensors.
+
 config SOC_CAMERA
 	tristate "SoC camera support"
 	depends on VIDEO_V4L2 && HAS_DMA && I2C
@@ -783,6 +731,12 @@
 	  over a bus like PCI or USB. For example some i2c camera connected
 	  directly to the data bus of an SoC.
 
+config SOC_CAMERA_IMX074
+	tristate "imx074 support"
+	depends on SOC_CAMERA && I2C
+	help
+	  This driver supports IMX074 cameras from Sony
+
 config SOC_CAMERA_MT9M001
 	tristate "mt9m001 support"
 	depends on SOC_CAMERA && I2C
@@ -835,6 +789,12 @@
 	help
 	  This is a generic SoC camera platform driver, useful for testing
 
+config SOC_CAMERA_OV6650
+	tristate "ov6650 sensor support"
+	depends on SOC_CAMERA && I2C
+	---help---
+	  This is a V4L2 SoC camera driver for the OmniVision OV6650 sensor
+
 config SOC_CAMERA_OV772X
 	tristate "ov772x camera support"
 	depends on SOC_CAMERA && I2C
@@ -890,6 +850,14 @@
 	---help---
 	  This is a v4l2 driver for the SuperH Mobile CEU Interface
 
+config VIDEO_OMAP1
+	tristate "OMAP1 Camera Interface driver"
+	depends on VIDEO_DEV && ARCH_OMAP1 && SOC_CAMERA
+	select VIDEOBUF_DMA_CONTIG
+	select VIDEOBUF_DMA_SG
+	---help---
+	  This is a v4l2 driver for the TI OMAP1 camera interface
+
 config VIDEO_OMAP2
 	tristate "OMAP2 Camera Capture Interface driver"
 	depends on VIDEO_DEV && ARCH_OMAP2
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 40f98fb..af79d47 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -33,8 +33,6 @@
 obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
 obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o
 obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
-obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o
-obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o
 obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o
 obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o
 obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o
@@ -73,12 +71,15 @@
 obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
 obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
 obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
+obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
 
+obj-$(CONFIG_SOC_CAMERA_IMX074)		+= imx074.o
 obj-$(CONFIG_SOC_CAMERA_MT9M001)	+= mt9m001.o
 obj-$(CONFIG_SOC_CAMERA_MT9M111)	+= mt9m111.o
 obj-$(CONFIG_SOC_CAMERA_MT9T031)	+= mt9t031.o
 obj-$(CONFIG_SOC_CAMERA_MT9T112)	+= mt9t112.o
 obj-$(CONFIG_SOC_CAMERA_MT9V022)	+= mt9v022.o
+obj-$(CONFIG_SOC_CAMERA_OV6650)		+= ov6650.o
 obj-$(CONFIG_SOC_CAMERA_OV772X)		+= ov772x.o
 obj-$(CONFIG_SOC_CAMERA_OV9640)		+= ov9640.o
 obj-$(CONFIG_SOC_CAMERA_RJ54N1)		+= rj54n1cb0c.o
@@ -93,10 +94,6 @@
 obj-$(CONFIG_VIDEO_W9966) += w9966.o
 obj-$(CONFIG_VIDEO_PMS) += pms.o
 obj-$(CONFIG_VIDEO_VINO) += vino.o
-obj-$(CONFIG_VIDEO_STRADIS) += stradis.o
-obj-$(CONFIG_VIDEO_CPIA) += cpia.o
-obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o
-obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o
 obj-$(CONFIG_VIDEO_MEYE) += meye.o
 obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
 obj-$(CONFIG_VIDEO_CX88) += cx88/
@@ -125,6 +122,8 @@
 
 obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o
 
+obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
+
 obj-$(CONFIG_USB_DABUSB)        += dabusb.o
 obj-$(CONFIG_USB_SE401)         += se401.o
 obj-$(CONFIG_USB_ZR364XX)       += zr364xx.o
@@ -163,6 +162,7 @@
 obj-$(CONFIG_VIDEO_PXA27x)		+= pxa_camera.o
 obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2)	+= sh_mobile_csi2.o
 obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
+obj-$(CONFIG_VIDEO_OMAP1)		+= omap1_camera.o
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) 	+= s5p-fimc/
 
 obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
diff --git a/drivers/media/video/adv7170.c b/drivers/media/video/adv7170.c
index 48e89fb..23ba5c3 100644
--- a/drivers/media/video/adv7170.c
+++ b/drivers/media/video/adv7170.c
@@ -34,11 +34,9 @@
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver");
 MODULE_AUTHOR("Maxim Yevtyushkin");
@@ -337,9 +335,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, adv7170_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "adv7170",
-	.probe = adv7170_probe,
-	.remove = adv7170_remove,
-	.id_table = adv7170_id,
+static struct i2c_driver adv7170_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "adv7170",
+	},
+	.probe		= adv7170_probe,
+	.remove		= adv7170_remove,
+	.id_table	= adv7170_id,
 };
+
+static __init int init_adv7170(void)
+{
+	return i2c_add_driver(&adv7170_driver);
+}
+
+static __exit void exit_adv7170(void)
+{
+	i2c_del_driver(&adv7170_driver);
+}
+
+module_init(init_adv7170);
+module_exit(exit_adv7170);
diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c
index f1ba0d7..f318b51 100644
--- a/drivers/media/video/adv7175.c
+++ b/drivers/media/video/adv7175.c
@@ -30,11 +30,9 @@
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver");
 MODULE_AUTHOR("Dave Perks");
@@ -376,9 +374,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, adv7175_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "adv7175",
-	.probe = adv7175_probe,
-	.remove = adv7175_remove,
-	.id_table = adv7175_id,
+static struct i2c_driver adv7175_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "adv7175",
+	},
+	.probe		= adv7175_probe,
+	.remove		= adv7175_remove,
+	.id_table	= adv7175_id,
 };
+
+static __init int init_adv7175(void)
+{
+	return i2c_add_driver(&adv7175_driver);
+}
+
+static __exit void exit_adv7175(void)
+{
+	i2c_del_driver(&adv7175_driver);
+}
+
+module_init(init_adv7175);
+module_exit(exit_adv7175);
diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c
index 23e610f..d2138d0 100644
--- a/drivers/media/video/adv7180.c
+++ b/drivers/media/video/adv7180.c
@@ -22,7 +22,6 @@
 #include <linux/kernel.h>
 #include <linux/interrupt.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/slab.h>
 #include <media/v4l2-ioctl.h>
 #include <linux/videodev2.h>
diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c
index 57dd919..0453816 100644
--- a/drivers/media/video/au0828/au0828-cards.c
+++ b/drivers/media/video/au0828/au0828-cards.c
@@ -212,7 +212,7 @@
 		   be abstracted out if we ever need to support a different
 		   demod) */
 		sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
-				"au8522", "au8522", 0x8e >> 1, NULL);
+				NULL, "au8522", 0x8e >> 1, NULL);
 		if (sd == NULL)
 			printk(KERN_ERR "analog subdev registration failed\n");
 	}
@@ -221,7 +221,7 @@
 	if (dev->board.tuner_type != TUNER_ABSENT) {
 		/* Load the tuner module, which does the attach */
 		sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
-				"tuner", "tuner", dev->board.tuner_addr, NULL);
+				NULL, "tuner", dev->board.tuner_addr, NULL);
 		if (sd == NULL)
 			printk(KERN_ERR "tuner subdev registration fail\n");
 
diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c
index 7989a7b..162fd5f 100644
--- a/drivers/media/video/au0828/au0828-video.c
+++ b/drivers/media/video/au0828/au0828-video.c
@@ -965,7 +965,7 @@
 				    NULL, &dev->slock,
 				    V4L2_BUF_TYPE_VIDEO_CAPTURE,
 				    V4L2_FIELD_INTERLACED,
-				    sizeof(struct au0828_buffer), fh);
+				    sizeof(struct au0828_buffer), fh, NULL);
 
 	/* VBI Setup */
 	dev->vbi_width = 720;
@@ -974,7 +974,7 @@
 				    NULL, &dev->slock,
 				    V4L2_BUF_TYPE_VBI_CAPTURE,
 				    V4L2_FIELD_SEQ_TB,
-				    sizeof(struct au0828_buffer), fh);
+				    sizeof(struct au0828_buffer), fh, NULL);
 
 
 	return ret;
diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c
index 770cb9a..c38300f 100644
--- a/drivers/media/video/bt819.c
+++ b/drivers/media/video/bt819.c
@@ -33,12 +33,10 @@
 #include <linux/ioctl.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/videodev2.h>
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 #include <media/bt819.h>
 
 MODULE_DESCRIPTION("Brooktree-819 video decoder driver");
@@ -537,9 +535,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, bt819_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "bt819",
-	.probe = bt819_probe,
-	.remove = bt819_remove,
-	.id_table = bt819_id,
+static struct i2c_driver bt819_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "bt819",
+	},
+	.probe		= bt819_probe,
+	.remove		= bt819_remove,
+	.id_table	= bt819_id,
 };
+
+static __init int init_bt819(void)
+{
+	return i2c_add_driver(&bt819_driver);
+}
+
+static __exit void exit_bt819(void)
+{
+	i2c_del_driver(&bt819_driver);
+}
+
+module_init(init_bt819);
+module_exit(exit_bt819);
diff --git a/drivers/media/video/bt856.c b/drivers/media/video/bt856.c
index ae33373..a43059d 100644
--- a/drivers/media/video/bt856.c
+++ b/drivers/media/video/bt856.c
@@ -34,11 +34,9 @@
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("Brooktree-856A video encoder driver");
 MODULE_AUTHOR("Mike Bernson & Dave Perks");
@@ -262,9 +260,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, bt856_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "bt856",
-	.probe = bt856_probe,
-	.remove = bt856_remove,
-	.id_table = bt856_id,
+static struct i2c_driver bt856_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "bt856",
+	},
+	.probe		= bt856_probe,
+	.remove		= bt856_remove,
+	.id_table	= bt856_id,
 };
+
+static __init int init_bt856(void)
+{
+	return i2c_add_driver(&bt856_driver);
+}
+
+static __exit void exit_bt856(void)
+{
+	i2c_del_driver(&bt856_driver);
+}
+
+module_init(init_bt856);
+module_exit(exit_bt856);
diff --git a/drivers/media/video/bt866.c b/drivers/media/video/bt866.c
index 62ac422..4e5dcea 100644
--- a/drivers/media/video/bt866.c
+++ b/drivers/media/video/bt866.c
@@ -34,11 +34,9 @@
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("Brooktree-866 video encoder driver");
 MODULE_AUTHOR("Mike Bernson & Dave Perks");
@@ -232,9 +230,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, bt866_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "bt866",
-	.probe = bt866_probe,
-	.remove = bt866_remove,
-	.id_table = bt866_id,
+static struct i2c_driver bt866_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "bt866",
+	},
+	.probe		= bt866_probe,
+	.remove		= bt866_remove,
+	.id_table	= bt866_id,
 };
+
+static __init int init_bt866(void)
+{
+	return i2c_add_driver(&bt866_driver);
+}
+
+static __exit void exit_bt866(void)
+{
+	i2c_del_driver(&bt866_driver);
+}
+
+module_init(init_bt866);
+module_exit(exit_bt866);
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index 7af56cd..87d8b00 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -3529,7 +3529,7 @@
 		struct v4l2_subdev *sd;
 
 		sd = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
-			&btv->c.i2c_adap, "saa6588", "saa6588", 0, addrs);
+			&btv->c.i2c_adap, NULL, "saa6588", 0, addrs);
 		btv->has_saa6588 = (sd != NULL);
 	}
 
@@ -3554,7 +3554,7 @@
 		};
 
 		btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
-			&btv->c.i2c_adap, "msp3400", "msp3400", 0, addrs);
+			&btv->c.i2c_adap, NULL, "msp3400", 0, addrs);
 		if (btv->sd_msp34xx)
 			return;
 		goto no_audio;
@@ -3568,7 +3568,7 @@
 		};
 
 		if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
-				&btv->c.i2c_adap, "tda7432", "tda7432", 0, addrs))
+				&btv->c.i2c_adap, NULL, "tda7432", 0, addrs))
 			return;
 		goto no_audio;
 	}
@@ -3576,7 +3576,7 @@
 	case 3: {
 		/* The user specified that we should probe for tvaudio */
 		btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
-			&btv->c.i2c_adap, "tvaudio", "tvaudio", 0, tvaudio_addrs());
+			&btv->c.i2c_adap, NULL, "tvaudio", 0, tvaudio_addrs());
 		if (btv->sd_tvaudio)
 			return;
 		goto no_audio;
@@ -3596,11 +3596,11 @@
 	   found is really something else (e.g. a tea6300). */
 	if (!bttv_tvcards[btv->c.type].no_msp34xx) {
 		btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
-			&btv->c.i2c_adap, "msp3400", "msp3400",
+			&btv->c.i2c_adap, NULL, "msp3400",
 			0, I2C_ADDRS(I2C_ADDR_MSP3400 >> 1));
 	} else if (bttv_tvcards[btv->c.type].msp34xx_alt) {
 		btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
-			&btv->c.i2c_adap, "msp3400", "msp3400",
+			&btv->c.i2c_adap, NULL, "msp3400",
 			0, I2C_ADDRS(I2C_ADDR_MSP3400_ALT >> 1));
 	}
 
@@ -3616,13 +3616,13 @@
 		};
 
 		if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
-				&btv->c.i2c_adap, "tda7432", "tda7432", 0, addrs))
+				&btv->c.i2c_adap, NULL, "tda7432", 0, addrs))
 			return;
 	}
 
 	/* Now see if we can find one of the tvaudio devices. */
 	btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
-		&btv->c.i2c_adap, "tvaudio", "tvaudio", 0, tvaudio_addrs());
+		&btv->c.i2c_adap, NULL, "tvaudio", 0, tvaudio_addrs());
 	if (btv->sd_tvaudio)
 		return;
 
@@ -3646,13 +3646,13 @@
 		/* Load tuner module before issuing tuner config call! */
 		if (bttv_tvcards[btv->c.type].has_radio)
 			v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
-				&btv->c.i2c_adap, "tuner", "tuner",
+				&btv->c.i2c_adap, NULL, "tuner",
 				0, v4l2_i2c_tuner_addrs(ADDRS_RADIO));
 		v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
-				&btv->c.i2c_adap, "tuner", "tuner",
+				&btv->c.i2c_adap, NULL, "tuner",
 				0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
 		v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
-				&btv->c.i2c_adap, "tuner", "tuner",
+				&btv->c.i2c_adap, NULL, "tuner",
 				0, v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD));
 
 		tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 38c7f78..3da6e80 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -842,7 +842,7 @@
 			 RESOURCE_OVERLAY)
 
 static
-int check_alloc_btres(struct bttv *btv, struct bttv_fh *fh, int bit)
+int check_alloc_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bit)
 {
 	int xbits; /* mutual exclusive resources */
 
@@ -935,7 +935,7 @@
 }
 
 static
-void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits)
+void free_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bits)
 {
 	if ((fh->resources & bits) != bits) {
 		/* trying to free ressources not allocated by us ... */
@@ -1682,7 +1682,7 @@
 		kfree(old);
 	}
 	if (NULL == new)
-		free_btres(btv,fh,RESOURCE_OVERLAY);
+		free_btres_lock(btv,fh,RESOURCE_OVERLAY);
 	dprintk("switch_overlay: done\n");
 	return retval;
 }
@@ -1859,21 +1859,25 @@
 	unsigned int i;
 	int err;
 
+	mutex_lock(&btv->lock);
 	err = v4l2_prio_check(&btv->prio, fh->prio);
-	if (0 != err)
-		return err;
+	if (err)
+		goto err;
 
 	for (i = 0; i < BTTV_TVNORMS; i++)
 		if (*id & bttv_tvnorms[i].v4l2_id)
 			break;
-	if (i == BTTV_TVNORMS)
-		return -EINVAL;
+	if (i == BTTV_TVNORMS) {
+		err = -EINVAL;
+		goto err;
+	}
 
-	mutex_lock(&btv->lock);
 	set_tvnorm(btv, i);
+
+err:
 	mutex_unlock(&btv->lock);
 
-	return 0;
+	return err;
 }
 
 static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id)
@@ -1893,10 +1897,13 @@
 {
 	struct bttv_fh *fh = priv;
 	struct bttv *btv = fh->btv;
-	int n;
+	int rc = 0;
 
-	if (i->index >= bttv_tvcards[btv->c.type].video_inputs)
-		return -EINVAL;
+	mutex_lock(&btv->lock);
+	if (i->index >= bttv_tvcards[btv->c.type].video_inputs) {
+		rc = -EINVAL;
+		goto err;
+	}
 
 	i->type     = V4L2_INPUT_TYPE_CAMERA;
 	i->audioset = 1;
@@ -1919,10 +1926,12 @@
 			i->status |= V4L2_IN_ST_NO_H_LOCK;
 	}
 
-	for (n = 0; n < BTTV_TVNORMS; n++)
-		i->std |= bttv_tvnorms[n].v4l2_id;
+	i->std = BTTV_NORMS;
 
-	return 0;
+err:
+	mutex_unlock(&btv->lock);
+
+	return rc;
 }
 
 static int bttv_g_input(struct file *file, void *priv, unsigned int *i)
@@ -1930,7 +1939,10 @@
 	struct bttv_fh *fh = priv;
 	struct bttv *btv = fh->btv;
 
+	mutex_lock(&btv->lock);
 	*i = btv->input;
+	mutex_unlock(&btv->lock);
+
 	return 0;
 }
 
@@ -1941,15 +1953,19 @@
 
 	int err;
 
-	err = v4l2_prio_check(&btv->prio, fh->prio);
-	if (0 != err)
-		return err;
-
-	if (i > bttv_tvcards[btv->c.type].video_inputs)
-		return -EINVAL;
-
 	mutex_lock(&btv->lock);
+	err = v4l2_prio_check(&btv->prio, fh->prio);
+	if (unlikely(err))
+		goto err;
+
+	if (i > bttv_tvcards[btv->c.type].video_inputs) {
+		err = -EINVAL;
+		goto err;
+	}
+
 	set_input(btv, i, btv->tvnorm);
+
+err:
 	mutex_unlock(&btv->lock);
 	return 0;
 }
@@ -1961,22 +1977,25 @@
 	struct bttv *btv = fh->btv;
 	int err;
 
-	err = v4l2_prio_check(&btv->prio, fh->prio);
-	if (0 != err)
-		return err;
-
-	if (btv->tuner_type == TUNER_ABSENT)
-		return -EINVAL;
-
-	if (0 != t->index)
+	if (unlikely(0 != t->index))
 		return -EINVAL;
 
 	mutex_lock(&btv->lock);
+	if (unlikely(btv->tuner_type == TUNER_ABSENT)) {
+		err = -EINVAL;
+		goto err;
+	}
+
+	err = v4l2_prio_check(&btv->prio, fh->prio);
+	if (unlikely(err))
+		goto err;
+
 	bttv_call_all(btv, tuner, s_tuner, t);
 
 	if (btv->audio_mode_gpio)
 		btv->audio_mode_gpio(btv, t, 1);
 
+err:
 	mutex_unlock(&btv->lock);
 
 	return 0;
@@ -1988,8 +2007,10 @@
 	struct bttv_fh *fh  = priv;
 	struct bttv *btv = fh->btv;
 
+	mutex_lock(&btv->lock);
 	f->type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
 	f->frequency = btv->freq;
+	mutex_unlock(&btv->lock);
 
 	return 0;
 }
@@ -2001,21 +2022,26 @@
 	struct bttv *btv = fh->btv;
 	int err;
 
-	err = v4l2_prio_check(&btv->prio, fh->prio);
-	if (0 != err)
-		return err;
-
 	if (unlikely(f->tuner != 0))
 		return -EINVAL;
-	if (unlikely(f->type != (btv->radio_user
-		? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV)))
-		return -EINVAL;
+
 	mutex_lock(&btv->lock);
+	err = v4l2_prio_check(&btv->prio, fh->prio);
+	if (unlikely(err))
+		goto err;
+
+	if (unlikely(f->type != (btv->radio_user
+		? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV))) {
+		err = -EINVAL;
+		goto err;
+	}
 	btv->freq = f->frequency;
 	bttv_call_all(btv, tuner, s_frequency, f);
 	if (btv->has_matchbox && btv->radio_user)
 		tea5757_set_freq(btv, btv->freq);
+err:
 	mutex_unlock(&btv->lock);
+
 	return 0;
 }
 
@@ -2124,7 +2150,7 @@
    also adjust the current cropping parameters to get closer to the
    desired image size. */
 static int
-limit_scaled_size       (struct bttv_fh *               fh,
+limit_scaled_size_lock       (struct bttv_fh *               fh,
 			 __s32 *                        width,
 			 __s32 *                        height,
 			 enum v4l2_field                field,
@@ -2238,7 +2264,7 @@
    may also adjust the current cropping parameters to get closer
    to the desired window size. */
 static int
-verify_window		(struct bttv_fh *               fh,
+verify_window_lock		(struct bttv_fh *               fh,
 			 struct v4l2_window *           win,
 			 int                            adjust_size,
 			 int                            adjust_crop)
@@ -2257,7 +2283,9 @@
 	if (V4L2_FIELD_ANY == field) {
 		__s32 height2;
 
+		mutex_lock(&fh->btv->lock);
 		height2 = fh->btv->crop[!!fh->do_crop].rect.height >> 1;
+		mutex_unlock(&fh->btv->lock);
 		field = (win->w.height > height2)
 			? V4L2_FIELD_INTERLACED
 			: V4L2_FIELD_TOP;
@@ -2292,7 +2320,7 @@
 	win->w.width -= win->w.left & ~width_mask;
 	win->w.left = (win->w.left - width_mask - 1) & width_mask;
 
-	rc = limit_scaled_size(fh, &win->w.width, &win->w.height,
+	rc = limit_scaled_size_lock(fh, &win->w.width, &win->w.height,
 			       field, width_mask,
 			       /* width_bias: round down */ 0,
 			       adjust_size, adjust_crop);
@@ -2303,7 +2331,7 @@
 	return 0;
 }
 
-static int setup_window(struct bttv_fh *fh, struct bttv *btv,
+static int setup_window_lock(struct bttv_fh *fh, struct bttv *btv,
 			struct v4l2_window *win, int fixup)
 {
 	struct v4l2_clip *clips = NULL;
@@ -2313,7 +2341,7 @@
 		return -EINVAL;
 	if (!(fh->ovfmt->flags & FORMAT_FLAGS_PACKED))
 		return -EINVAL;
-	retval = verify_window(fh, win,
+	retval = verify_window_lock(fh, win,
 			       /* adjust_size */ fixup,
 			       /* adjust_crop */ fixup);
 	if (0 != retval)
@@ -2332,6 +2360,8 @@
 			return -EFAULT;
 		}
 	}
+
+	mutex_lock(&fh->cap.vb_lock);
 	/* clip against screen */
 	if (NULL != btv->fbuf.base)
 		n = btcx_screen_clips(btv->fbuf.fmt.width, btv->fbuf.fmt.height,
@@ -2354,7 +2384,6 @@
 		BUG();
 	}
 
-	mutex_lock(&fh->cap.vb_lock);
 	kfree(fh->ov.clips);
 	fh->ov.clips    = clips;
 	fh->ov.nclips   = n;
@@ -2362,6 +2391,14 @@
 	fh->ov.w        = win->w;
 	fh->ov.field    = win->field;
 	fh->ov.setup_ok = 1;
+
+	/*
+	 * FIXME: btv is protected by btv->lock mutex, while btv->init
+	 *	  is protected by fh->cap.vb_lock. This seems to open the
+	 *	  possibility for some race situations. Maybe the better would
+	 *	  be to unify those locks or to use another way to store the
+	 *	  init values that will be consumed by videobuf callbacks
+	 */
 	btv->init.ov.w.width   = win->w.width;
 	btv->init.ov.w.height  = win->w.height;
 	btv->init.ov.field     = win->field;
@@ -2490,7 +2527,9 @@
 	if (V4L2_FIELD_ANY == field) {
 		__s32 height2;
 
+		mutex_lock(&btv->lock);
 		height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
+		mutex_unlock(&btv->lock);
 		field = (f->fmt.pix.height > height2)
 			? V4L2_FIELD_INTERLACED
 			: V4L2_FIELD_BOTTOM;
@@ -2516,7 +2555,7 @@
 	width = f->fmt.pix.width;
 	height = f->fmt.pix.height;
 
-	rc = limit_scaled_size(fh, &width, &height, field,
+	rc = limit_scaled_size_lock(fh, &width, &height, field,
 			       /* width_mask: 4 pixels */ ~3,
 			       /* width_bias: nearest */ 2,
 			       /* adjust_size */ 1,
@@ -2536,7 +2575,7 @@
 {
 	struct bttv_fh *fh = priv;
 
-	return verify_window(fh, &f->fmt.win,
+	return verify_window_lock(fh, &f->fmt.win,
 			/* adjust_size */ 1,
 			/* adjust_crop */ 0);
 }
@@ -2563,7 +2602,7 @@
 	height = f->fmt.pix.height;
 	field = f->fmt.pix.field;
 
-	retval = limit_scaled_size(fh, &width, &height, f->fmt.pix.field,
+	retval = limit_scaled_size_lock(fh, &width, &height, f->fmt.pix.field,
 			       /* width_mask: 4 pixels */ ~3,
 			       /* width_bias: nearest */ 2,
 			       /* adjust_size */ 1,
@@ -2601,7 +2640,7 @@
 		return -EINVAL;
 	}
 
-	return setup_window(fh, btv, &f->fmt.win, 1);
+	return setup_window_lock(fh, btv, &f->fmt.win, 1);
 }
 
 #ifdef CONFIG_VIDEO_V4L1_COMPAT
@@ -2651,11 +2690,15 @@
 		V4L2_CAP_VBI_CAPTURE |
 		V4L2_CAP_READWRITE |
 		V4L2_CAP_STREAMING;
-	if (btv->has_saa6588)
-		cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
 	if (no_overlay <= 0)
 		cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
 
+	/*
+	 * No need to lock here: those vars are initialized during board
+	 * probe and remains untouched during the rest of the driver lifecycle
+	 */
+	if (btv->has_saa6588)
+		cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
 	if (btv->tuner_type != TUNER_ABSENT)
 		cap->capabilities |= V4L2_CAP_TUNER;
 	return 0;
@@ -2730,19 +2773,25 @@
 	struct bttv_fh *fh = f;
 	struct bttv *btv = fh->btv;
 	struct bttv_buffer *new;
-	int retval;
+	int retval = 0;
 
 	if (on) {
+		mutex_lock(&fh->cap.vb_lock);
 		/* verify args */
-		if (NULL == btv->fbuf.base)
-			return -EINVAL;
-		if (!fh->ov.setup_ok) {
-			dprintk("bttv%d: overlay: !setup_ok\n", btv->c.nr);
+		if (unlikely(!btv->fbuf.base)) {
+			mutex_unlock(&fh->cap.vb_lock);
 			return -EINVAL;
 		}
+		if (unlikely(!fh->ov.setup_ok)) {
+			dprintk("bttv%d: overlay: !setup_ok\n", btv->c.nr);
+			retval = -EINVAL;
+		}
+		if (retval)
+			return retval;
+		mutex_unlock(&fh->cap.vb_lock);
 	}
 
-	if (!check_alloc_btres(btv, fh, RESOURCE_OVERLAY))
+	if (!check_alloc_btres_lock(btv, fh, RESOURCE_OVERLAY))
 		return -EBUSY;
 
 	mutex_lock(&fh->cap.vb_lock);
@@ -2785,7 +2834,7 @@
 		__s32 width = fb->fmt.width;
 		__s32 height = fb->fmt.height;
 
-		retval = limit_scaled_size(fh, &width, &height,
+		retval = limit_scaled_size_lock(fh, &width, &height,
 					   V4L2_FIELD_INTERLACED,
 					   /* width_mask */ ~3,
 					   /* width_bias */ 2,
@@ -2852,7 +2901,7 @@
 	struct bttv *btv = fh->btv;
 	int res = bttv_resource(fh);
 
-	if (!check_alloc_btres(btv, fh, res))
+	if (!check_alloc_btres_lock(btv, fh, res))
 		return -EBUSY;
 
 	return videobuf_qbuf(bttv_queue(fh), b);
@@ -2872,7 +2921,7 @@
 	struct bttv *btv = fh->btv;
 	int res = bttv_resource(fh);
 
-	if (!check_alloc_btres(btv, fh, res))
+	if (!check_alloc_btres_lock(btv, fh, res))
 		return -EBUSY;
 	return videobuf_streamon(bttv_queue(fh));
 }
@@ -2890,7 +2939,7 @@
 	retval = videobuf_streamoff(bttv_queue(fh));
 	if (retval < 0)
 		return retval;
-	free_btres(btv, fh, res);
+	free_btres_lock(btv, fh, res);
 	return 0;
 }
 
@@ -2907,6 +2956,7 @@
 	     c->id >= V4L2_CID_PRIVATE_LASTP1))
 		return -EINVAL;
 
+	mutex_lock(&btv->lock);
 	if (!btv->volume_gpio && (c->id == V4L2_CID_AUDIO_VOLUME))
 		*c = no_ctl;
 	else {
@@ -2914,6 +2964,7 @@
 
 		*c = (NULL != ctrl) ? *ctrl : no_ctl;
 	}
+	mutex_unlock(&btv->lock);
 
 	return 0;
 }
@@ -2924,8 +2975,11 @@
 	struct bttv_fh *fh = f;
 	struct bttv *btv = fh->btv;
 
+	mutex_lock(&btv->lock);
 	v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id,
 				    &parm->parm.capture.timeperframe);
+	mutex_unlock(&btv->lock);
+
 	return 0;
 }
 
@@ -2961,7 +3015,9 @@
 	struct bttv_fh *fh = f;
 	struct bttv *btv = fh->btv;
 
+	mutex_lock(&btv->lock);
 	*p = v4l2_prio_max(&btv->prio);
+	mutex_unlock(&btv->lock);
 
 	return 0;
 }
@@ -2971,8 +3027,13 @@
 {
 	struct bttv_fh *fh = f;
 	struct bttv *btv = fh->btv;
+	int	rc;
 
-	return v4l2_prio_change(&btv->prio, &fh->prio, prio);
+	mutex_lock(&btv->lock);
+	rc = v4l2_prio_change(&btv->prio, &fh->prio, prio);
+	mutex_unlock(&btv->lock);
+
+	return rc;
 }
 
 static int bttv_cropcap(struct file *file, void *priv,
@@ -2985,7 +3046,9 @@
 	    cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
 		return -EINVAL;
 
+	mutex_lock(&btv->lock);
 	*cap = bttv_tvnorms[btv->tvnorm].cropcap;
+	mutex_unlock(&btv->lock);
 
 	return 0;
 }
@@ -3003,7 +3066,9 @@
 	   inconsistent with fh->width or fh->height and apps
 	   do not expect a change here. */
 
+	mutex_lock(&btv->lock);
 	crop->c = btv->crop[!!fh->do_crop].rect;
+	mutex_unlock(&btv->lock);
 
 	return 0;
 }
@@ -3024,14 +3089,15 @@
 	    crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
 		return -EINVAL;
 
-	retval = v4l2_prio_check(&btv->prio, fh->prio);
-	if (0 != retval)
-		return retval;
-
 	/* Make sure tvnorm, vbi_end and the current cropping
 	   parameters remain consistent until we're done. Note
-	   read() may change vbi_end in check_alloc_btres(). */
+	   read() may change vbi_end in check_alloc_btres_lock(). */
 	mutex_lock(&btv->lock);
+	retval = v4l2_prio_check(&btv->prio, fh->prio);
+	if (0 != retval) {
+		mutex_unlock(&btv->lock);
+		return retval;
+	}
 
 	retval = -EBUSY;
 
@@ -3128,17 +3194,17 @@
 
 	switch (fh->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		if (!check_alloc_btres(fh->btv, fh, RESOURCE_VIDEO_READ)) {
+		if (!check_alloc_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ)) {
 			/* VIDEO_READ in use by another fh,
 			   or VIDEO_STREAM by any fh. */
 			return -EBUSY;
 		}
 		retval = videobuf_read_one(&fh->cap, data, count, ppos,
 					   file->f_flags & O_NONBLOCK);
-		free_btres(fh->btv, fh, RESOURCE_VIDEO_READ);
+		free_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ);
 		break;
 	case V4L2_BUF_TYPE_VBI_CAPTURE:
-		if (!check_alloc_btres(fh->btv,fh,RESOURCE_VBI))
+		if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
 			return -EBUSY;
 		retval = videobuf_read_stream(&fh->vbi, data, count, ppos, 1,
 					      file->f_flags & O_NONBLOCK);
@@ -3157,20 +3223,19 @@
 	unsigned int rc = POLLERR;
 
 	if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
-		if (!check_alloc_btres(fh->btv,fh,RESOURCE_VBI))
+		if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
 			return POLLERR;
 		return videobuf_poll_stream(file, &fh->vbi, wait);
 	}
 
+	mutex_lock(&fh->cap.vb_lock);
 	if (check_btres(fh,RESOURCE_VIDEO_STREAM)) {
-		mutex_lock(&fh->cap.vb_lock);
 		/* streaming capture */
 		if (list_empty(&fh->cap.stream))
 			goto err;
 		buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream);
 	} else {
 		/* read() capture */
-		mutex_lock(&fh->cap.vb_lock);
 		if (NULL == fh->cap.read_buf) {
 			/* need to capture a new frame */
 			if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM))
@@ -3188,7 +3253,6 @@
 			fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
 			fh->cap.read_off = 0;
 		}
-		mutex_unlock(&fh->cap.vb_lock);
 		buf = (struct bttv_buffer*)fh->cap.read_buf;
 	}
 
@@ -3221,21 +3285,32 @@
 		return -ENODEV;
 	}
 
-	lock_kernel();
-
 	dprintk(KERN_DEBUG "bttv%d: open called (type=%s)\n",
 		btv->c.nr,v4l2_type_names[type]);
 
 	/* allocate per filehandle data */
-	fh = kmalloc(sizeof(*fh),GFP_KERNEL);
-	if (NULL == fh) {
-		unlock_kernel();
+	fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+	if (unlikely(!fh))
 		return -ENOMEM;
-	}
 	file->private_data = fh;
+
+	/*
+	 * btv is protected by btv->lock mutex, while btv->init and other
+	 * streaming vars are protected by fh->cap.vb_lock. We need to take
+	 * care of both locks to avoid troubles. However, vb_lock is used also
+	 * inside videobuf, without calling buf->lock. So, it is a very bad
+	 * idea to hold both locks at the same time.
+	 * Let's first copy btv->init at fh, holding cap.vb_lock, and then work
+	 * with the rest of init, holding btv->lock.
+	 */
+	mutex_lock(&fh->cap.vb_lock);
 	*fh = btv->init;
+	mutex_unlock(&fh->cap.vb_lock);
+
 	fh->type = type;
 	fh->ov.setup_ok = 0;
+
+	mutex_lock(&btv->lock);
 	v4l2_prio_open(&btv->prio, &fh->prio);
 
 	videobuf_queue_sg_init(&fh->cap, &bttv_video_qops,
@@ -3243,13 +3318,13 @@
 			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
 			    V4L2_FIELD_INTERLACED,
 			    sizeof(struct bttv_buffer),
-			    fh);
+			    fh, NULL);
 	videobuf_queue_sg_init(&fh->vbi, &bttv_vbi_qops,
 			    &btv->c.pci->dev, &btv->s_lock,
 			    V4L2_BUF_TYPE_VBI_CAPTURE,
 			    V4L2_FIELD_SEQ_TB,
 			    sizeof(struct bttv_buffer),
-			    fh);
+			    fh, NULL);
 	set_tvnorm(btv,btv->tvnorm);
 	set_input(btv, btv->input, btv->tvnorm);
 
@@ -3272,7 +3347,7 @@
 	bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm);
 
 	bttv_field_count(btv);
-	unlock_kernel();
+	mutex_unlock(&btv->lock);
 	return 0;
 }
 
@@ -3281,6 +3356,7 @@
 	struct bttv_fh *fh = file->private_data;
 	struct bttv *btv = fh->btv;
 
+	mutex_lock(&btv->lock);
 	/* turn off overlay */
 	if (check_btres(fh, RESOURCE_OVERLAY))
 		bttv_switch_overlay(btv,fh,NULL);
@@ -3288,25 +3364,32 @@
 	/* stop video capture */
 	if (check_btres(fh, RESOURCE_VIDEO_STREAM)) {
 		videobuf_streamoff(&fh->cap);
-		free_btres(btv,fh,RESOURCE_VIDEO_STREAM);
+		free_btres_lock(btv,fh,RESOURCE_VIDEO_STREAM);
 	}
 	if (fh->cap.read_buf) {
 		buffer_release(&fh->cap,fh->cap.read_buf);
 		kfree(fh->cap.read_buf);
 	}
 	if (check_btres(fh, RESOURCE_VIDEO_READ)) {
-		free_btres(btv, fh, RESOURCE_VIDEO_READ);
+		free_btres_lock(btv, fh, RESOURCE_VIDEO_READ);
 	}
 
 	/* stop vbi capture */
 	if (check_btres(fh, RESOURCE_VBI)) {
 		videobuf_stop(&fh->vbi);
-		free_btres(btv,fh,RESOURCE_VBI);
+		free_btres_lock(btv,fh,RESOURCE_VBI);
 	}
 
 	/* free stuff */
+
+	/*
+	 * videobuf uses cap.vb_lock - we should avoid holding btv->lock,
+	 * otherwise we may have dead lock conditions
+	 */
+	mutex_unlock(&btv->lock);
 	videobuf_mmap_free(&fh->cap);
 	videobuf_mmap_free(&fh->vbi);
+	mutex_lock(&btv->lock);
 	v4l2_prio_close(&btv->prio, fh->prio);
 	file->private_data = NULL;
 	kfree(fh);
@@ -3316,6 +3399,7 @@
 
 	if (!btv->users)
 		audio_mute(btv, 1);
+	mutex_unlock(&btv->lock);
 
 	return 0;
 }
@@ -3333,13 +3417,13 @@
 
 static const struct v4l2_file_operations bttv_fops =
 {
-	.owner	  = THIS_MODULE,
-	.open	  = bttv_open,
-	.release  = bttv_release,
-	.ioctl	  = video_ioctl2,
-	.read	  = bttv_read,
-	.mmap	  = bttv_mmap,
-	.poll     = bttv_poll,
+	.owner		  = THIS_MODULE,
+	.open		  = bttv_open,
+	.release	  = bttv_release,
+	.unlocked_ioctl	  = video_ioctl2,
+	.read		  = bttv_read,
+	.mmap		  = bttv_mmap,
+	.poll		  = bttv_poll,
 };
 
 static const struct v4l2_ioctl_ops bttv_ioctl_ops = {
@@ -3412,21 +3496,19 @@
 
 	dprintk("bttv: open dev=%s\n", video_device_node_name(vdev));
 
-	lock_kernel();
-
 	dprintk("bttv%d: open called (radio)\n",btv->c.nr);
 
 	/* allocate per filehandle data */
 	fh = kmalloc(sizeof(*fh), GFP_KERNEL);
-	if (NULL == fh) {
-		unlock_kernel();
+	if (unlikely(!fh))
 		return -ENOMEM;
-	}
 	file->private_data = fh;
+	mutex_lock(&fh->cap.vb_lock);
 	*fh = btv->init;
-	v4l2_prio_open(&btv->prio, &fh->prio);
+	mutex_unlock(&fh->cap.vb_lock);
 
 	mutex_lock(&btv->lock);
+	v4l2_prio_open(&btv->prio, &fh->prio);
 
 	btv->radio_user++;
 
@@ -3434,7 +3516,6 @@
 	audio_input(btv,TVAUDIO_INPUT_RADIO);
 
 	mutex_unlock(&btv->lock);
-	unlock_kernel();
 	return 0;
 }
 
@@ -3444,6 +3525,7 @@
 	struct bttv *btv = fh->btv;
 	struct rds_command cmd;
 
+	mutex_lock(&btv->lock);
 	v4l2_prio_close(&btv->prio, fh->prio);
 	file->private_data = NULL;
 	kfree(fh);
@@ -3451,6 +3533,7 @@
 	btv->radio_user--;
 
 	bttv_call_all(btv, core, ioctl, RDS_CMD_CLOSE, &cmd);
+	mutex_unlock(&btv->lock);
 
 	return 0;
 }
diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c
index 685d659..d49b675 100644
--- a/drivers/media/video/bt8xx/bttv-i2c.c
+++ b/drivers/media/video/bt8xx/bttv-i2c.c
@@ -121,9 +121,8 @@
 
 	/* timeout */
 	if (wait_event_interruptible_timeout(btv->i2c_queue,
-		btv->i2c_done, msecs_to_jiffies(85)) == -ERESTARTSYS)
-
-	rc = -EIO;
+	    btv->i2c_done, msecs_to_jiffies(85)) == -ERESTARTSYS)
+		rc = -EIO;
 
 	if (btv->i2c_done & BT848_INT_RACK)
 		rc = 1;
@@ -390,41 +389,3 @@
 
 	return btv->i2c_rc;
 }
-
-/* Instantiate the I2C IR receiver device, if present */
-void __devinit init_bttv_i2c_ir(struct bttv *btv)
-{
-	if (0 == btv->i2c_rc) {
-		struct i2c_board_info info;
-		/* The external IR receiver is at i2c address 0x34 (0x35 for
-		   reads).  Future Hauppauge cards will have an internal
-		   receiver at 0x30 (0x31 for reads).  In theory, both can be
-		   fitted, and Hauppauge suggest an external overrides an
-		   internal.
-
-		   That's why we probe 0x1a (~0x34) first. CB
-		*/
-		const unsigned short addr_list[] = {
-			0x1a, 0x18, 0x4b, 0x64, 0x30, 0x71,
-			I2C_CLIENT_END
-		};
-
-		memset(&info, 0, sizeof(struct i2c_board_info));
-		strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
-		i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list, NULL);
-	}
-}
-
-int __devexit fini_bttv_i2c(struct bttv *btv)
-{
-	if (0 != btv->i2c_rc)
-		return 0;
-
-	return i2c_del_adapter(&btv->c.i2c_adap);
-}
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c
index f68717a..6bf05a7d 100644
--- a/drivers/media/video/bt8xx/bttv-input.c
+++ b/drivers/media/video/bt8xx/bttv-input.c
@@ -245,6 +245,83 @@
 	}
 }
 
+/*
+ * Get_key functions used by I2C remotes
+ */
+
+static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+	unsigned char b;
+
+	/* poll IR chip */
+	if (1 != i2c_master_recv(ir->c, &b, 1)) {
+		dprintk(KERN_INFO DEVNAME ": read error\n");
+		return -EIO;
+	}
+
+	/* ignore 0xaa */
+	if (b==0xaa)
+		return 0;
+	dprintk(KERN_INFO DEVNAME ": key %02x\n", b);
+
+	*ir_key = b;
+	*ir_raw = b;
+	return 1;
+}
+
+/* Instantiate the I2C IR receiver device, if present */
+void __devinit init_bttv_i2c_ir(struct bttv *btv)
+{
+	const unsigned short addr_list[] = {
+		0x1a, 0x18, 0x64, 0x30, 0x71,
+		I2C_CLIENT_END
+	};
+	struct i2c_board_info info;
+
+	if (0 != btv->i2c_rc)
+		return;
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	memset(&btv->init_data, 0, sizeof(btv->init_data));
+	strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+
+	switch (btv->c.type) {
+	case BTTV_BOARD_PV951:
+		btv->init_data.name = "PV951";
+		btv->init_data.get_key = get_key_pv951;
+		btv->init_data.ir_codes = RC_MAP_PV951;
+		btv->init_data.type = IR_TYPE_OTHER;
+		info.addr = 0x4b;
+		break;
+	default:
+		/*
+		 * The external IR receiver is at i2c address 0x34 (0x35 for
+                 * reads).  Future Hauppauge cards will have an internal
+                 * receiver at 0x30 (0x31 for reads).  In theory, both can be
+                 * fitted, and Hauppauge suggest an external overrides an
+                 * internal.
+		 * That's why we probe 0x1a (~0x34) first. CB
+		 */
+
+		i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list, NULL);
+		return;
+	}
+
+	if (btv->init_data.name)
+		info.platform_data = &btv->init_data;
+	i2c_new_device(&btv->c.i2c_adap, &info);
+
+	return;
+}
+
+int __devexit fini_bttv_i2c(struct bttv *btv)
+{
+	if (0 != btv->i2c_rc)
+		return 0;
+
+	return i2c_del_adapter(&btv->c.i2c_adap);
+}
+
 int bttv_input_init(struct bttv *btv)
 {
 	struct card_ir *ir;
@@ -420,10 +497,3 @@
 	kfree(btv->remote);
 	btv->remote = NULL;
 }
-
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/bt8xx/bttv-risc.c b/drivers/media/video/bt8xx/bttv-risc.c
index 0fa9f39..9b57d09 100644
--- a/drivers/media/video/bt8xx/bttv-risc.c
+++ b/drivers/media/video/bt8xx/bttv-risc.c
@@ -582,7 +582,7 @@
 	struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
 
 	BUG_ON(in_interrupt());
-	videobuf_waiton(&buf->vb,0,0);
+	videobuf_waiton(q, &buf->vb, 0, 0);
 	videobuf_dma_unmap(q->dev, dma);
 	videobuf_dma_free(dma);
 	btcx_riscmem_free(btv->c.pci,&buf->bottom);
diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h
index 3ec2402..6fd2a8e 100644
--- a/drivers/media/video/bt8xx/bttv.h
+++ b/drivers/media/video/bt8xx/bttv.h
@@ -18,7 +18,6 @@
 #include <linux/i2c.h>
 #include <media/v4l2-device.h>
 #include <media/ir-common.h>
-#include <media/ir-kbd-i2c.h>
 #include <media/i2c-addr.h>
 #include <media/tuner.h>
 
diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h
index 6cccc2a..d1e26a4 100644
--- a/drivers/media/video/bt8xx/bttvp.h
+++ b/drivers/media/video/bt8xx/bttvp.h
@@ -42,7 +42,7 @@
 #include <media/videobuf-dma-sg.h>
 #include <media/tveeprom.h>
 #include <media/ir-common.h>
-
+#include <media/ir-kbd-i2c.h>
 
 #include "bt848.h"
 #include "bttv.h"
@@ -271,6 +271,12 @@
 extern int no_overlay;
 
 /* ---------------------------------------------------------- */
+/* bttv-input.c                                               */
+
+extern void init_bttv_i2c_ir(struct bttv *btv);
+extern int fini_bttv_i2c(struct bttv *btv);
+
+/* ---------------------------------------------------------- */
 /* bttv-driver.c                                              */
 
 /* insmod options */
@@ -279,8 +285,6 @@
 extern unsigned int bttv_gpio;
 extern void bttv_gpio_tracking(struct bttv *btv, char *comment);
 extern int init_bttv_i2c(struct bttv *btv);
-extern void init_bttv_i2c_ir(struct bttv *btv);
-extern int fini_bttv_i2c(struct bttv *btv);
 
 #define bttv_printk if (bttv_verbose) printk
 #define dprintk  if (bttv_debug >= 1) printk
@@ -366,6 +370,9 @@
 	int has_remote;
 	struct card_ir *remote;
 
+	/* I2C remote data */
+	struct IR_i2c_init_data    init_data;
+
 	/* locking */
 	spinlock_t s_lock;
 	struct mutex lock;
diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
index 9536f1a..2934770 100644
--- a/drivers/media/video/cafe_ccic.c
+++ b/drivers/media/video/cafe_ccic.c
@@ -25,6 +25,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/fs.h>
+#include <linux/dmi.h>
 #include <linux/mm.h>
 #include <linux/pci.h>
 #include <linux/i2c.h>
@@ -46,6 +47,7 @@
 #include <asm/uaccess.h>
 #include <asm/io.h>
 
+#include "ov7670.h"
 #include "cafe_ccic-regs.h"
 
 #define CAFE_VERSION 0x000002
@@ -180,6 +182,7 @@
 	/* Current operating parameters */
 	u32 sensor_type;		/* Currently ov7670 only */
 	struct v4l2_pix_format pix_format;
+	enum v4l2_mbus_pixelcode mbus_code;
 
 	/* Locks */
 	struct mutex s_mutex; /* Access to this structure */
@@ -207,6 +210,49 @@
 	return container_of(dev, struct cafe_camera, v4l2_dev);
 }
 
+static struct cafe_format_struct {
+	__u8 *desc;
+	__u32 pixelformat;
+	int bpp;   /* Bytes per pixel */
+	enum v4l2_mbus_pixelcode mbus_code;
+} cafe_formats[] = {
+	{
+		.desc		= "YUYV 4:2:2",
+		.pixelformat	= V4L2_PIX_FMT_YUYV,
+		.mbus_code	= V4L2_MBUS_FMT_YUYV8_2X8,
+		.bpp		= 2,
+	},
+	{
+		.desc		= "RGB 444",
+		.pixelformat	= V4L2_PIX_FMT_RGB444,
+		.mbus_code	= V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
+		.bpp		= 2,
+	},
+	{
+		.desc		= "RGB 565",
+		.pixelformat	= V4L2_PIX_FMT_RGB565,
+		.mbus_code	= V4L2_MBUS_FMT_RGB565_2X8_LE,
+		.bpp		= 2,
+	},
+	{
+		.desc		= "Raw RGB Bayer",
+		.pixelformat	= V4L2_PIX_FMT_SBGGR8,
+		.mbus_code	= V4L2_MBUS_FMT_SBGGR8_1X8,
+		.bpp		= 1
+	},
+};
+#define N_CAFE_FMTS ARRAY_SIZE(cafe_formats)
+
+static struct cafe_format_struct *cafe_find_format(u32 pixelformat)
+{
+	unsigned i;
+
+	for (i = 0; i < N_CAFE_FMTS; i++)
+		if (cafe_formats[i].pixelformat == pixelformat)
+			return cafe_formats + i;
+	/* Not found? Then return the first format. */
+	return cafe_formats;
+}
 
 /*
  * Start over with DMA buffers - dev_lock needed.
@@ -319,7 +365,6 @@
 {
 	unsigned int rval;
 	unsigned long flags;
-	DEFINE_WAIT(the_wait);
 
 	spin_lock_irqsave(&cam->dev_lock, flags);
 	rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
@@ -334,28 +379,27 @@
 	cafe_reg_write(cam, REG_TWSIC1, rval);
 	spin_unlock_irqrestore(&cam->dev_lock, flags);
 
-	/*
-	 * Time to wait for the write to complete.  THIS IS A RACY
-	 * WAY TO DO IT, but the sad fact is that reading the TWSIC1
-	 * register too quickly after starting the operation sends
-	 * the device into a place that may be kinder and better, but
-	 * which is absolutely useless for controlling the sensor.  In
-	 * practice we have plenty of time to get into our sleep state
-	 * before the interrupt hits, and the worst case is that we
-	 * time out and then see that things completed, so this seems
-	 * the best way for now.
+	/* Unfortunately, reading TWSIC1 too soon after sending a command
+	 * causes the device to die.
+	 * Use a busy-wait because we often send a large quantity of small
+	 * commands at-once; using msleep() would cause a lot of context
+	 * switches which take longer than 2ms, resulting in a noticable
+	 * boot-time and capture-start delays.
 	 */
-	do {
-		prepare_to_wait(&cam->smbus_wait, &the_wait,
-				TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1); /* even 1 jiffy is too long */
-		finish_wait(&cam->smbus_wait, &the_wait);
-	} while (!cafe_smbus_write_done(cam));
+	mdelay(2);
 
-#ifdef IF_THE_CAFE_HARDWARE_WORKED_RIGHT
+	/*
+	 * Another sad fact is that sometimes, commands silently complete but
+	 * cafe_smbus_write_done() never becomes aware of this.
+	 * This happens at random and appears to possible occur with any
+	 * command.
+	 * We don't understand why this is. We work around this issue
+	 * with the timeout in the wait below, assuming that all commands
+	 * complete within the timeout.
+	 */
 	wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(cam),
 			CAFE_SMBUS_TIMEOUT);
-#endif
+
 	spin_lock_irqsave(&cam->dev_lock, flags);
 	rval = cafe_reg_read(cam, REG_TWSIC1);
 	spin_unlock_irqrestore(&cam->dev_lock, flags);
@@ -812,15 +856,15 @@
 
 static int cafe_cam_configure(struct cafe_camera *cam)
 {
-	struct v4l2_format fmt;
+	struct v4l2_mbus_framefmt mbus_fmt;
 	int ret;
 
 	if (cam->state != S_IDLE)
 		return -EINVAL;
-	fmt.fmt.pix = cam->pix_format;
+	v4l2_fill_mbus_format(&mbus_fmt, &cam->pix_format, cam->mbus_code);
 	ret = sensor_call(cam, core, init, 0);
 	if (ret == 0)
-		ret = sensor_call(cam, video, s_fmt, &fmt);
+		ret = sensor_call(cam, video, s_mbus_fmt, &mbus_fmt);
 	/*
 	 * OV7670 does weird things if flip is set *before* format...
 	 */
@@ -1481,7 +1525,7 @@
 /*
  * The default format we use until somebody says otherwise.
  */
-static struct v4l2_pix_format cafe_def_pix_format = {
+static const struct v4l2_pix_format cafe_def_pix_format = {
 	.width		= VGA_WIDTH,
 	.height		= VGA_HEIGHT,
 	.pixelformat	= V4L2_PIX_FMT_YUYV,
@@ -1490,28 +1534,38 @@
 	.sizeimage	= VGA_WIDTH*VGA_HEIGHT*2,
 };
 
+static const enum v4l2_mbus_pixelcode cafe_def_mbus_code =
+					V4L2_MBUS_FMT_YUYV8_2X8;
+
 static int cafe_vidioc_enum_fmt_vid_cap(struct file *filp,
 		void *priv, struct v4l2_fmtdesc *fmt)
 {
-	struct cafe_camera *cam = priv;
-	int ret;
-
-	mutex_lock(&cam->s_mutex);
-	ret = sensor_call(cam, video, enum_fmt, fmt);
-	mutex_unlock(&cam->s_mutex);
-	return ret;
+	if (fmt->index >= N_CAFE_FMTS)
+		return -EINVAL;
+	strlcpy(fmt->description, cafe_formats[fmt->index].desc,
+			sizeof(fmt->description));
+	fmt->pixelformat = cafe_formats[fmt->index].pixelformat;
+	return 0;
 }
 
-
 static int cafe_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
 		struct v4l2_format *fmt)
 {
 	struct cafe_camera *cam = priv;
+	struct cafe_format_struct *f;
+	struct v4l2_pix_format *pix = &fmt->fmt.pix;
+	struct v4l2_mbus_framefmt mbus_fmt;
 	int ret;
 
+	f = cafe_find_format(pix->pixelformat);
+	pix->pixelformat = f->pixelformat;
+	v4l2_fill_mbus_format(&mbus_fmt, pix, f->mbus_code);
 	mutex_lock(&cam->s_mutex);
-	ret = sensor_call(cam, video, try_fmt, fmt);
+	ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt);
 	mutex_unlock(&cam->s_mutex);
+	v4l2_fill_pix_format(pix, &mbus_fmt);
+	pix->bytesperline = pix->width * f->bpp;
+	pix->sizeimage = pix->height * pix->bytesperline;
 	return ret;
 }
 
@@ -1519,6 +1573,7 @@
 		struct v4l2_format *fmt)
 {
 	struct cafe_camera *cam = priv;
+	struct cafe_format_struct *f;
 	int ret;
 
 	/*
@@ -1527,6 +1582,9 @@
 	 */
 	if (cam->state != S_IDLE || cam->n_sbufs > 0)
 		return -EBUSY;
+
+	f = cafe_find_format(fmt->fmt.pix.pixelformat);
+
 	/*
 	 * See if the formatting works in principle.
 	 */
@@ -1539,6 +1597,8 @@
 	 */
 	mutex_lock(&cam->s_mutex);
 	cam->pix_format = fmt->fmt.pix;
+	cam->mbus_code = f->mbus_code;
+
 	/*
 	 * Make sure we have appropriate DMA buffers.
 	 */
@@ -1652,6 +1712,30 @@
 	return sensor_call(cam, core, g_chip_ident, chip);
 }
 
+static int cafe_vidioc_enum_framesizes(struct file *filp, void *priv,
+		struct v4l2_frmsizeenum *sizes)
+{
+	struct cafe_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = sensor_call(cam, video, enum_framesizes, sizes);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+static int cafe_vidioc_enum_frameintervals(struct file *filp, void *priv,
+		struct v4l2_frmivalenum *interval)
+{
+	struct cafe_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = sensor_call(cam, video, enum_frameintervals, interval);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 static int cafe_vidioc_g_register(struct file *file, void *priv,
 		struct v4l2_dbg_register *reg)
@@ -1715,6 +1799,8 @@
 	.vidioc_s_ctrl		= cafe_vidioc_s_ctrl,
 	.vidioc_g_parm		= cafe_vidioc_g_parm,
 	.vidioc_s_parm		= cafe_vidioc_s_parm,
+	.vidioc_enum_framesizes = cafe_vidioc_enum_framesizes,
+	.vidioc_enum_frameintervals = cafe_vidioc_enum_frameintervals,
 	.vidioc_g_chip_ident    = cafe_vidioc_g_chip_ident,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.vidioc_g_register 	= cafe_vidioc_g_register,
@@ -1890,11 +1976,33 @@
  * PCI interface stuff.
  */
 
+static const struct dmi_system_id olpc_xo1_dmi[] = {
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "OLPC"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "XO"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "1"),
+		},
+	},
+	{ }
+};
+
 static int cafe_pci_probe(struct pci_dev *pdev,
 		const struct pci_device_id *id)
 {
 	int ret;
 	struct cafe_camera *cam;
+	struct ov7670_config sensor_cfg = {
+		/* This controller only does SMBUS */
+		.use_smbus = true,
+
+		/*
+		 * Exclude QCIF mode, because it only captures a tiny portion
+		 * of the sensor FOV
+		 */
+		.min_width = 320,
+		.min_height = 240,
+	};
 
 	/*
 	 * Start putting together one of our big camera structures.
@@ -1915,6 +2023,7 @@
 	init_waitqueue_head(&cam->iowait);
 	cam->pdev = pdev;
 	cam->pix_format = cafe_def_pix_format;
+	cam->mbus_code = cafe_def_mbus_code;
 	INIT_LIST_HEAD(&cam->dev_list);
 	INIT_LIST_HEAD(&cam->sb_avail);
 	INIT_LIST_HEAD(&cam->sb_full);
@@ -1951,13 +2060,18 @@
 	if (ret)
 		goto out_freeirq;
 
+	/* Apply XO-1 clock speed */
+	if (dmi_check_system(olpc_xo1_dmi))
+		sensor_cfg.clock_speed = 45;
+
 	cam->sensor_addr = 0x42;
 	cam->sensor = v4l2_i2c_new_subdev(&cam->v4l2_dev, &cam->i2c_adapter,
-			"ov7670", "ov7670", cam->sensor_addr, NULL);
+			NULL, "ov7670", cam->sensor_addr, NULL);
 	if (cam->sensor == NULL) {
 		ret = -ENODEV;
 		goto out_smbus;
 	}
+
 	ret = cafe_cam_init(cam);
 	if (ret)
 		goto out_smbus;
diff --git a/drivers/media/video/cpia2/Kconfig b/drivers/media/video/cpia2/Kconfig
index e39a961..66e9283 100644
--- a/drivers/media/video/cpia2/Kconfig
+++ b/drivers/media/video/cpia2/Kconfig
@@ -1,6 +1,6 @@
 config VIDEO_CPIA2
 	tristate "CPiA2 Video For Linux"
-	depends on VIDEO_DEV && USB && VIDEO_V4L1
+	depends on VIDEO_DEV && USB && VIDEO_V4L2
 	---help---
 	  This is the video4linux driver for cameras based on Vision's CPiA2
 	  (Colour Processor Interface ASIC), such as the Digital Blue QX5
diff --git a/drivers/media/video/cpia2/cpia2.h b/drivers/media/video/cpia2/cpia2.h
index 8d2dfc1..916c13d 100644
--- a/drivers/media/video/cpia2/cpia2.h
+++ b/drivers/media/video/cpia2/cpia2.h
@@ -32,7 +32,7 @@
 #define __CPIA2_H__
 
 #include <linux/version.h>
-#include <linux/videodev.h>
+#include <linux/videodev2.h>
 #include <media/v4l2-common.h>
 #include <linux/usb.h>
 #include <linux/poll.h>
@@ -43,7 +43,7 @@
 /* define for verbose debug output */
 //#define _CPIA2_DEBUG_
 
-#define CPIA2_MAJ_VER	2
+#define CPIA2_MAJ_VER	3
 #define CPIA2_MIN_VER   0
 #define CPIA2_PATCH_VER	0
 
@@ -396,8 +396,8 @@
 	/* v4l */
 	int video_size;			/* VIDEO_SIZE_ */
 	struct video_device *vdev;	/* v4l videodev */
-	struct video_picture vp;	/* v4l camera settings */
-	struct video_window vw;		/* v4l capture area */
+	u32 width;
+	u32 height;			/* Its size */
 	__u32 pixelformat;       /* Format fourcc      */
 
 	/* USB */
diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c
index 1cc0df8..9606bc0 100644
--- a/drivers/media/video/cpia2/cpia2_core.c
+++ b/drivers/media/video/cpia2/cpia2_core.c
@@ -1058,44 +1058,44 @@
 		DBG("Setting size to VGA\n");
 		cam->params.roi.width = STV_IMAGE_VGA_COLS;
 		cam->params.roi.height = STV_IMAGE_VGA_ROWS;
-		cam->vw.width = STV_IMAGE_VGA_COLS;
-		cam->vw.height = STV_IMAGE_VGA_ROWS;
+		cam->width = STV_IMAGE_VGA_COLS;
+		cam->height = STV_IMAGE_VGA_ROWS;
 		break;
 	case VIDEOSIZE_CIF:
 		DBG("Setting size to CIF\n");
 		cam->params.roi.width = STV_IMAGE_CIF_COLS;
 		cam->params.roi.height = STV_IMAGE_CIF_ROWS;
-		cam->vw.width = STV_IMAGE_CIF_COLS;
-		cam->vw.height = STV_IMAGE_CIF_ROWS;
+		cam->width = STV_IMAGE_CIF_COLS;
+		cam->height = STV_IMAGE_CIF_ROWS;
 		break;
 	case VIDEOSIZE_QVGA:
 		DBG("Setting size to QVGA\n");
 		cam->params.roi.width = STV_IMAGE_QVGA_COLS;
 		cam->params.roi.height = STV_IMAGE_QVGA_ROWS;
-		cam->vw.width = STV_IMAGE_QVGA_COLS;
-		cam->vw.height = STV_IMAGE_QVGA_ROWS;
+		cam->width = STV_IMAGE_QVGA_COLS;
+		cam->height = STV_IMAGE_QVGA_ROWS;
 		break;
 	case VIDEOSIZE_288_216:
 		cam->params.roi.width = 288;
 		cam->params.roi.height = 216;
-		cam->vw.width = 288;
-		cam->vw.height = 216;
+		cam->width = 288;
+		cam->height = 216;
 		break;
 	case VIDEOSIZE_256_192:
-		cam->vw.width = 256;
-		cam->vw.height = 192;
+		cam->width = 256;
+		cam->height = 192;
 		cam->params.roi.width = 256;
 		cam->params.roi.height = 192;
 		break;
 	case VIDEOSIZE_224_168:
-		cam->vw.width = 224;
-		cam->vw.height = 168;
+		cam->width = 224;
+		cam->height = 168;
 		cam->params.roi.width = 224;
 		cam->params.roi.height = 168;
 		break;
 	case VIDEOSIZE_192_144:
-		cam->vw.width = 192;
-		cam->vw.height = 144;
+		cam->width = 192;
+		cam->height = 144;
 		cam->params.roi.width = 192;
 		cam->params.roi.height = 144;
 		break;
@@ -1103,8 +1103,8 @@
 		DBG("Setting size to QCIF\n");
 		cam->params.roi.width = STV_IMAGE_QCIF_COLS;
 		cam->params.roi.height = STV_IMAGE_QCIF_ROWS;
-		cam->vw.width = STV_IMAGE_QCIF_COLS;
-		cam->vw.height = STV_IMAGE_QCIF_ROWS;
+		cam->width = STV_IMAGE_QCIF_COLS;
+		cam->height = STV_IMAGE_QCIF_ROWS;
 		break;
 	default:
 		retval = -EINVAL;
@@ -2224,23 +2224,8 @@
 		cam->params.roi.height = STV_IMAGE_CIF_ROWS;
 	}
 
-	/***
-	 * Fill in the v4l structures.  video_cap is filled in inside the VIDIOCCAP
-	 * Ioctl.  Here, just do the window and picture stucts.
-	 ***/
-	cam->vp.palette = (u16) VIDEO_PALETTE_RGB24;	/* Is this right? */
-	cam->vp.brightness = (u16) cam->params.color_params.brightness * 256;
-	cam->vp.colour = (u16) cam->params.color_params.saturation * 256;
-	cam->vp.contrast = (u16) cam->params.color_params.contrast * 256;
-
-	cam->vw.x = 0;
-	cam->vw.y = 0;
-	cam->vw.width = cam->params.roi.width;
-	cam->vw.height = cam->params.roi.height;
-	cam->vw.flags = 0;
-	cam->vw.clipcount = 0;
-
-	return;
+	cam->width = cam->params.roi.width;
+	cam->height = cam->params.roi.height;
 }
 
 /******************************************************************************
diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c
index 5520789..46b433b 100644
--- a/drivers/media/video/cpia2/cpia2_v4l.c
+++ b/drivers/media/video/cpia2/cpia2_v4l.c
@@ -37,7 +37,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/init.h>
-#include <linux/videodev.h>
+#include <linux/videodev2.h>
 #include <linux/stringify.h>
 #include <media/v4l2-ioctl.h>
 
@@ -391,113 +391,6 @@
 }
 
 
-/******************************************************************************
- *
- *  ioctl_cap_query
- *
- *****************************************************************************/
-static int ioctl_cap_query(void *arg, struct camera_data *cam)
-{
-	struct video_capability *vc;
-	int retval = 0;
-	vc = arg;
-
-	if (cam->params.pnp_id.product == 0x151)
-		strcpy(vc->name, "QX5 Microscope");
-	else
-		strcpy(vc->name, "CPiA2 Camera");
-
-	vc->type = VID_TYPE_CAPTURE | VID_TYPE_MJPEG_ENCODER;
-	vc->channels = 1;
-	vc->audios = 0;
-	vc->minwidth = 176;	/* VIDEOSIZE_QCIF */
-	vc->minheight = 144;
-	switch (cam->params.version.sensor_flags) {
-	case CPIA2_VP_SENSOR_FLAGS_500:
-		vc->maxwidth = STV_IMAGE_VGA_COLS;
-		vc->maxheight = STV_IMAGE_VGA_ROWS;
-		break;
-	case CPIA2_VP_SENSOR_FLAGS_410:
-		vc->maxwidth = STV_IMAGE_CIF_COLS;
-		vc->maxheight = STV_IMAGE_CIF_ROWS;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return retval;
-}
-
-/******************************************************************************
- *
- *  ioctl_get_channel
- *
- *****************************************************************************/
-static int ioctl_get_channel(void *arg)
-{
-	int retval = 0;
-	struct video_channel *v;
-	v = arg;
-
-	if (v->channel != 0)
-		return -EINVAL;
-
-	v->channel = 0;
-	strcpy(v->name, "Camera");
-	v->tuners = 0;
-	v->flags = 0;
-	v->type = VIDEO_TYPE_CAMERA;
-	v->norm = 0;
-
-	return retval;
-}
-
-/******************************************************************************
- *
- *  ioctl_set_channel
- *
- *****************************************************************************/
-static int ioctl_set_channel(void *arg)
-{
-	struct video_channel *v;
-	int retval = 0;
-	v = arg;
-
-	if (retval == 0 && v->channel != 0)
-		retval = -EINVAL;
-
-	return retval;
-}
-
-/******************************************************************************
- *
- *  ioctl_set_image_prop
- *
- *****************************************************************************/
-static int ioctl_set_image_prop(void *arg, struct camera_data *cam)
-{
-	struct video_picture *vp;
-	int retval = 0;
-	vp = arg;
-
-	/* brightness, color, contrast need no check 0-65535 */
-	memcpy(&cam->vp, vp, sizeof(*vp));
-
-	/* update cam->params.colorParams */
-	cam->params.color_params.brightness = vp->brightness / 256;
-	cam->params.color_params.saturation = vp->colour / 256;
-	cam->params.color_params.contrast = vp->contrast / 256;
-
-	DBG("Requested params: bright 0x%X, sat 0x%X, contrast 0x%X\n",
-	    cam->params.color_params.brightness,
-	    cam->params.color_params.saturation,
-	    cam->params.color_params.contrast);
-
-	cpia2_set_color_params(cam);
-
-	return retval;
-}
-
 static int sync(struct camera_data *cam, int frame_nr)
 {
 	struct framebuf *frame = &cam->buffers[frame_nr];
@@ -526,61 +419,10 @@
 
 /******************************************************************************
  *
- *  ioctl_set_window_size
- *
- *****************************************************************************/
-static int ioctl_set_window_size(void *arg, struct camera_data *cam,
-				 struct cpia2_fh *fh)
-{
-	/* copy_from_user, check validity, copy to internal structure */
-	struct video_window *vw;
-	int frame, err;
-	vw = arg;
-
-	if (vw->clipcount != 0)	/* clipping not supported */
-		return -EINVAL;
-
-	if (vw->clips != NULL)	/* clipping not supported */
-		return -EINVAL;
-
-	/* Ensure that only this process can change the format. */
-	err = v4l2_prio_change(&cam->prio, &fh->prio, V4L2_PRIORITY_RECORD);
-	if(err != 0)
-		return err;
-
-	cam->pixelformat = V4L2_PIX_FMT_JPEG;
-
-	/* Be sure to supply the Huffman tables, this isn't MJPEG */
-	cam->params.compression.inhibit_htables = 0;
-
-	/* we set the video window to something smaller or equal to what
-	 * is requested by the user???
-	 */
-	DBG("Requested width = %d, height = %d\n", vw->width, vw->height);
-	if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
-		cam->vw.width = vw->width;
-		cam->vw.height = vw->height;
-		cam->params.roi.width = vw->width;
-		cam->params.roi.height = vw->height;
-		cpia2_set_format(cam);
-	}
-
-	for (frame = 0; frame < cam->num_frames; ++frame) {
-		if (cam->buffers[frame].status == FRAME_READING)
-			if ((err = sync(cam, frame)) < 0)
-				return err;
-
-		cam->buffers[frame].status = FRAME_EMPTY;
-	}
-
-	return 0;
-}
-
-/******************************************************************************
- *
  *  ioctl_get_mbuf
  *
  *****************************************************************************/
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
 static int ioctl_get_mbuf(void *arg, struct camera_data *cam)
 {
 	struct video_mbuf *vm;
@@ -595,66 +437,7 @@
 
 	return 0;
 }
-
-/******************************************************************************
- *
- *  ioctl_mcapture
- *
- *****************************************************************************/
-static int ioctl_mcapture(void *arg, struct camera_data *cam,
-			  struct cpia2_fh *fh)
-{
-	struct video_mmap *vm;
-	int video_size, err;
-	vm = arg;
-
-	if (vm->frame < 0 || vm->frame >= cam->num_frames)
-		return -EINVAL;
-
-	/* set video size */
-	video_size = cpia2_match_video_size(vm->width, vm->height);
-	if (cam->video_size < 0) {
-		return -EINVAL;
-	}
-
-	/* Ensure that only this process can change the format. */
-	err = v4l2_prio_change(&cam->prio, &fh->prio, V4L2_PRIORITY_RECORD);
-	if(err != 0)
-		return err;
-
-	if (video_size != cam->video_size) {
-		cam->video_size = video_size;
-		cam->params.roi.width = vm->width;
-		cam->params.roi.height = vm->height;
-		cpia2_set_format(cam);
-	}
-
-	if (cam->buffers[vm->frame].status == FRAME_READING)
-		if ((err=sync(cam, vm->frame)) < 0)
-			return err;
-
-	cam->buffers[vm->frame].status = FRAME_EMPTY;
-
-	return cpia2_usb_stream_start(cam,cam->params.camera_state.stream_mode);
-}
-
-/******************************************************************************
- *
- *  ioctl_sync
- *
- *****************************************************************************/
-static int ioctl_sync(void *arg, struct camera_data *cam)
-{
-	int frame;
-
-	frame = *(int*)arg;
-
-	if (frame < 0 || frame >= cam->num_frames)
-		return -EINVAL;
-
-	return sync(cam, frame);
-}
-
+#endif
 
 /******************************************************************************
  *
@@ -897,10 +680,10 @@
 	 */
 	DBG("Requested width = %d, height = %d\n",
 	    f->fmt.pix.width, f->fmt.pix.height);
-	if (f->fmt.pix.width != cam->vw.width ||
-	    f->fmt.pix.height != cam->vw.height) {
-		cam->vw.width = f->fmt.pix.width;
-		cam->vw.height = f->fmt.pix.height;
+	if (f->fmt.pix.width != cam->width ||
+	    f->fmt.pix.height != cam->height) {
+		cam->width = f->fmt.pix.width;
+		cam->height = f->fmt.pix.height;
 		cam->params.roi.width = f->fmt.pix.width;
 		cam->params.roi.height = f->fmt.pix.height;
 		cpia2_set_format(cam);
@@ -932,8 +715,8 @@
 	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 	       return -EINVAL;
 
-	f->fmt.pix.width = cam->vw.width;
-	f->fmt.pix.height = cam->vw.height;
+	f->fmt.pix.width = cam->width;
+	f->fmt.pix.height = cam->height;
 	f->fmt.pix.pixelformat = cam->pixelformat;
 	f->fmt.pix.field = V4L2_FIELD_NONE;
 	f->fmt.pix.bytesperline = 0;
@@ -962,12 +745,12 @@
 
 	c->bounds.left = 0;
 	c->bounds.top = 0;
-	c->bounds.width = cam->vw.width;
-	c->bounds.height = cam->vw.height;
+	c->bounds.width = cam->width;
+	c->bounds.height = cam->height;
 	c->defrect.left = 0;
 	c->defrect.top = 0;
-	c->defrect.width = cam->vw.width;
-	c->defrect.height = cam->vw.height;
+	c->defrect.width = cam->width;
+	c->defrect.height = cam->height;
 	c->pixelaspect.numerator = 1;
 	c->pixelaspect.denominator = 1;
 
@@ -1587,8 +1370,6 @@
 
 	/* Priority check */
 	switch (cmd) {
-	case VIDIOCSWIN:
-	case VIDIOCMCAPTURE:
 	case VIDIOC_S_FMT:
 	{
 		struct cpia2_fh *fh = file->private_data;
@@ -1599,8 +1380,8 @@
 		}
 		break;
 	}
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
 	case VIDIOCGMBUF:
-	case VIDIOCSYNC:
 	{
 		struct cpia2_fh *fh = file->private_data;
 		if(fh->prio != V4L2_PRIORITY_RECORD) {
@@ -1609,68 +1390,21 @@
 		}
 		break;
 	}
+#endif
 	default:
 		break;
 	}
 
 	switch (cmd) {
-	case VIDIOCGCAP:	/* query capabilities */
-		retval = ioctl_cap_query(arg, cam);
-		break;
-
-	case VIDIOCGCHAN:	/* get video source - we are a camera, nothing else */
-		retval = ioctl_get_channel(arg);
-		break;
-	case VIDIOCSCHAN:	/* set video source - we are a camera, nothing else */
-		retval = ioctl_set_channel(arg);
-		break;
-	case VIDIOCGPICT:	/* image properties */
-		memcpy(arg, &cam->vp, sizeof(struct video_picture));
-		break;
-	case VIDIOCSPICT:
-		retval = ioctl_set_image_prop(arg, cam);
-		break;
-	case VIDIOCGWIN:	/* get/set capture window */
-		memcpy(arg, &cam->vw, sizeof(struct video_window));
-		break;
-	case VIDIOCSWIN:
-		retval = ioctl_set_window_size(arg, cam, file->private_data);
-		break;
-	case VIDIOCGMBUF:	/* mmap interface */
-		retval = ioctl_get_mbuf(arg, cam);
-		break;
-	case VIDIOCMCAPTURE:
-		retval = ioctl_mcapture(arg, cam, file->private_data);
-		break;
-	case VIDIOCSYNC:
-		retval = ioctl_sync(arg, cam);
-		break;
-		/* pointless to implement overlay with this camera */
-	case VIDIOCCAPTURE:
-	case VIDIOCGFBUF:
-	case VIDIOCSFBUF:
-	case VIDIOCKEY:
-		retval = -EINVAL;
-		break;
-
-		/* tuner interface - we have none */
-	case VIDIOCGTUNER:
-	case VIDIOCSTUNER:
-	case VIDIOCGFREQ:
-	case VIDIOCSFREQ:
-		retval = -EINVAL;
-		break;
-
-		/* audio interface - we have none */
-	case VIDIOCGAUDIO:
-	case VIDIOCSAUDIO:
-		retval = -EINVAL;
-		break;
-
 	/* CPIA2 extension to Video4Linux API */
 	case CPIA2_IOC_SET_GPIO:
 		retval = ioctl_set_gpio(arg, cam);
 		break;
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+	case VIDIOCGMBUF:	/* mmap interface */
+		retval = ioctl_get_mbuf(arg, cam);
+		break;
+#endif
 	case VIDIOC_QUERYCAP:
 		retval = ioctl_querycap(arg,cam);
 		break;
@@ -1874,21 +1608,8 @@
  *****************************************************************************/
 static void reset_camera_struct_v4l(struct camera_data *cam)
 {
-	/***
-	 * Fill in the v4l structures.  video_cap is filled in inside the VIDIOCCAP
-	 * Ioctl.  Here, just do the window and picture stucts.
-	 ***/
-	cam->vp.palette = (u16) VIDEO_PALETTE_RGB24;	/* Is this right? */
-	cam->vp.brightness = (u16) cam->params.color_params.brightness * 256;
-	cam->vp.colour = (u16) cam->params.color_params.saturation * 256;
-	cam->vp.contrast = (u16) cam->params.color_params.contrast * 256;
-
-	cam->vw.x = 0;
-	cam->vw.y = 0;
-	cam->vw.width = cam->params.roi.width;
-	cam->vw.height = cam->params.roi.height;
-	cam->vw.flags = 0;
-	cam->vw.clipcount = 0;
+	cam->width = cam->params.roi.width;
+	cam->height = cam->params.roi.height;
 
 	cam->frame_size = buffer_size;
 	cam->num_frames = num_buffers;
@@ -1902,13 +1623,12 @@
 
 	cam->pixelformat = V4L2_PIX_FMT_JPEG;
 	v4l2_prio_init(&cam->prio);
-	return;
 }
 
 /***
  * The v4l video device structure initialized for this device
  ***/
-static const struct v4l2_file_operations fops_template = {
+static const struct v4l2_file_operations cpia2_fops = {
 	.owner		= THIS_MODULE,
 	.open		= cpia2_open,
 	.release	= cpia2_close,
@@ -1920,9 +1640,9 @@
 
 static struct video_device cpia2_template = {
 	/* I could not find any place for the old .initialize initializer?? */
-	.name=		"CPiA2 Camera",
-	.fops=		&fops_template,
-	.release=	video_device_release,
+	.name =		"CPiA2 Camera",
+	.fops =		&cpia2_fops,
+	.release =	video_device_release,
 };
 
 /******************************************************************************
diff --git a/drivers/media/video/cpia2/cpia2dev.h b/drivers/media/video/cpia2/cpia2dev.h
index d58097c..f66691f 100644
--- a/drivers/media/video/cpia2/cpia2dev.h
+++ b/drivers/media/video/cpia2/cpia2dev.h
@@ -29,14 +29,14 @@
 #ifndef CPIA2_DEV_HEADER
 #define CPIA2_DEV_HEADER
 
-#include <linux/videodev.h>
+#include <linux/videodev2.h>
 
 /***
  * The following defines are ioctl numbers based on video4linux private ioctls,
  * which can range from 192 (BASE_VIDIOCPRIVATE) to 255. All of these take int
  * args
  */
-#define CPIA2_IOC_SET_GPIO         _IOW('v', BASE_VIDIOCPRIVATE + 17, __u32)
+#define CPIA2_IOC_SET_GPIO         _IOW('v', BASE_VIDIOC_PRIVATE + 17, __u32)
 
 /* V4L2 driver specific controls */
 #define CPIA2_CID_TARGET_KB     (V4L2_CID_PRIVATE_BASE+0)
diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c
index 8362db5..9358fe7 100644
--- a/drivers/media/video/cs5345.c
+++ b/drivers/media/video/cs5345.c
@@ -25,7 +25,6 @@
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC");
 MODULE_AUTHOR("Hans Verkuil");
@@ -209,9 +208,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, cs5345_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "cs5345",
-	.probe = cs5345_probe,
-	.remove = cs5345_remove,
-	.id_table = cs5345_id,
+static struct i2c_driver cs5345_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "cs5345",
+	},
+	.probe		= cs5345_probe,
+	.remove		= cs5345_remove,
+	.id_table	= cs5345_id,
 };
+
+static __init int init_cs5345(void)
+{
+	return i2c_add_driver(&cs5345_driver);
+}
+
+static __exit void exit_cs5345(void)
+{
+	i2c_del_driver(&cs5345_driver);
+}
+
+module_init(init_cs5345);
+module_exit(exit_cs5345);
diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c
index cc9e84d..d93e5ab 100644
--- a/drivers/media/video/cs53l32a.c
+++ b/drivers/media/video/cs53l32a.c
@@ -30,7 +30,6 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-ctrls.h>
-#include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
 MODULE_AUTHOR("Martin Vaughan");
@@ -239,9 +238,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, cs53l32a_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "cs53l32a",
-	.remove = cs53l32a_remove,
-	.probe = cs53l32a_probe,
-	.id_table = cs53l32a_id,
+static struct i2c_driver cs53l32a_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "cs53l32a",
+	},
+	.probe		= cs53l32a_probe,
+	.remove		= cs53l32a_remove,
+	.id_table	= cs53l32a_id,
 };
+
+static __init int init_cs53l32a(void)
+{
+	return i2c_add_driver(&cs53l32a_driver);
+}
+
+static __exit void exit_cs53l32a(void)
+{
+	i2c_del_driver(&cs53l32a_driver);
+}
+
+module_init(init_cs53l32a);
+module_exit(exit_cs53l32a);
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index 9bc51a9..77be58c 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -674,18 +674,25 @@
 
 /* Call the specified callback for all subdevs with a grp_id bit matching the
  * mask in hw (if 0, then match them all). Ignore any errors. */
-#define cx18_call_hw(cx, hw, o, f, args...) \
-	__v4l2_device_call_subdevs(&(cx)->v4l2_dev, \
-				   !(hw) || (sd->grp_id & (hw)), o, f , ##args)
+#define cx18_call_hw(cx, hw, o, f, args...)				\
+	do {								\
+		struct v4l2_subdev *__sd;				\
+		__v4l2_device_call_subdevs_p(&(cx)->v4l2_dev, __sd,	\
+			!(hw) || (__sd->grp_id & (hw)), o, f , ##args);	\
+	} while (0)
 
 #define cx18_call_all(cx, o, f, args...) cx18_call_hw(cx, 0, o, f , ##args)
 
 /* Call the specified callback for all subdevs with a grp_id bit matching the
  * mask in hw (if 0, then match them all). If the callback returns an error
  * other than 0 or -ENOIOCTLCMD, then return with that error code. */
-#define cx18_call_hw_err(cx, hw, o, f, args...) \
-	__v4l2_device_call_subdevs_until_err( \
-		   &(cx)->v4l2_dev, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
+#define cx18_call_hw_err(cx, hw, o, f, args...)				\
+({									\
+	struct v4l2_subdev *__sd;					\
+	__v4l2_device_call_subdevs_until_err_p(&(cx)->v4l2_dev,		\
+			__sd, !(hw) || (__sd->grp_id & (hw)), o, f,	\
+			##args);					\
+})
 
 #define cx18_call_all_err(cx, o, f, args...) \
 	cx18_call_hw_err(cx, 0, o, f , ##args)
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
index 73ce90c..a09caf8 100644
--- a/drivers/media/video/cx18/cx18-i2c.c
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -71,19 +71,6 @@
 };
 
 /* This array should match the CX18_HW_ defines */
-static const char * const hw_modules[] = {
-	"tuner",	/* CX18_HW_TUNER */
-	NULL,		/* CX18_HW_TVEEPROM */
-	"cs5345",	/* CX18_HW_CS5345 */
-	NULL,		/* CX18_HW_DVB */
-	NULL,		/* CX18_HW_418_AV */
-	NULL,		/* CX18_HW_GPIO_MUX */
-	NULL,		/* CX18_HW_GPIO_RESET_CTRL */
-	NULL,		/* CX18_HW_Z8F0811_IR_TX_HAUP */
-	NULL,		/* CX18_HW_Z8F0811_IR_RX_HAUP */
-};
-
-/* This array should match the CX18_HW_ defines */
 static const char * const hw_devicenames[] = {
 	"tuner",
 	"tveeprom",
@@ -126,7 +113,6 @@
 	struct v4l2_subdev *sd;
 	int bus = hw_bus[idx];
 	struct i2c_adapter *adap = &cx->i2c_adap[bus];
-	const char *mod = hw_modules[idx];
 	const char *type = hw_devicenames[idx];
 	u32 hw = 1 << idx;
 
@@ -136,15 +122,15 @@
 	if (hw == CX18_HW_TUNER) {
 		/* special tuner group handling */
 		sd = v4l2_i2c_new_subdev(&cx->v4l2_dev,
-				adap, mod, type, 0, cx->card_i2c->radio);
+				adap, NULL, type, 0, cx->card_i2c->radio);
 		if (sd != NULL)
 			sd->grp_id = hw;
 		sd = v4l2_i2c_new_subdev(&cx->v4l2_dev,
-				adap, mod, type, 0, cx->card_i2c->demod);
+				adap, NULL, type, 0, cx->card_i2c->demod);
 		if (sd != NULL)
 			sd->grp_id = hw;
 		sd = v4l2_i2c_new_subdev(&cx->v4l2_dev,
-				adap, mod, type, 0, cx->card_i2c->tv);
+				adap, NULL, type, 0, cx->card_i2c->tv);
 		if (sd != NULL)
 			sd->grp_id = hw;
 		return sd != NULL ? 0 : -1;
@@ -158,7 +144,8 @@
 		return -1;
 
 	/* It's an I2C device other than an analog tuner or IR chip */
-	sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, adap, mod, type, hw_addrs[idx], NULL);
+	sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, adap, NULL, type, hw_addrs[idx],
+				 NULL);
 	if (sd != NULL)
 		sd->grp_id = hw;
 	return sd != NULL ? 0 : -1;
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index d679240..7150195 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -40,7 +40,6 @@
 #include "cx18-av-core.h"
 #include <media/tveeprom.h>
 #include <media/v4l2-chip-ident.h>
-#include <linux/i2c-id.h>
 
 u16 cx18_service2vbi(int type)
 {
diff --git a/drivers/media/video/cx231xx/Kconfig b/drivers/media/video/cx231xx/Kconfig
index 5ac7ece..bb04914 100644
--- a/drivers/media/video/cx231xx/Kconfig
+++ b/drivers/media/video/cx231xx/Kconfig
@@ -6,6 +6,7 @@
 	depends on VIDEO_IR
 	select VIDEOBUF_VMALLOC
 	select VIDEO_CX25840
+	select VIDEO_CX2341X
 
 	---help---
 	  This is a video4linux driver for Conexant 231xx USB based TV cards.
diff --git a/drivers/media/video/cx231xx/Makefile b/drivers/media/video/cx231xx/Makefile
index 6f2b573..a6bc4cc 100644
--- a/drivers/media/video/cx231xx/Makefile
+++ b/drivers/media/video/cx231xx/Makefile
@@ -1,5 +1,5 @@
 cx231xx-objs     := cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o \
-		    cx231xx-avcore.o cx231xx-pcb-cfg.o cx231xx-vbi.o
+		    cx231xx-avcore.o cx231xx-417.o cx231xx-pcb-cfg.o cx231xx-vbi.o
 
 cx231xx-alsa-objs := cx231xx-audio.o
 
diff --git a/drivers/media/video/cx231xx/cx231xx-417.c b/drivers/media/video/cx231xx/cx231xx-417.c
new file mode 100644
index 0000000..aab21f3
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-417.c
@@ -0,0 +1,2194 @@
+/*
+ *
+ *  Support for a cx23417 mpeg encoder via cx231xx host port.
+ *
+ *    (c) 2004 Jelle Foks <jelle@foks.us>
+ *    (c) 2004 Gerd Knorr <kraxel@bytesex.org>
+ *    (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *      - CX23885/7/8 support
+ *
+ *  Includes parts from the ivtv driver( http://ivtv.sourceforge.net/),
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/smp_lock.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/cx2341x.h>
+#include <linux/usb.h>
+
+#include "cx231xx.h"
+/*#include "cx23885-ioctl.h"*/
+
+#define CX231xx_FIRM_IMAGE_SIZE 376836
+#define CX231xx_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw"
+
+/* for polaris ITVC */
+#define ITVC_WRITE_DIR          0x03FDFC00
+#define ITVC_READ_DIR            0x0001FC00
+
+#define  MCI_MEMORY_DATA_BYTE0          0x00
+#define  MCI_MEMORY_DATA_BYTE1          0x08
+#define  MCI_MEMORY_DATA_BYTE2          0x10
+#define  MCI_MEMORY_DATA_BYTE3          0x18
+
+#define  MCI_MEMORY_ADDRESS_BYTE2       0x20
+#define  MCI_MEMORY_ADDRESS_BYTE1       0x28
+#define  MCI_MEMORY_ADDRESS_BYTE0       0x30
+
+#define  MCI_REGISTER_DATA_BYTE0        0x40
+#define  MCI_REGISTER_DATA_BYTE1        0x48
+#define  MCI_REGISTER_DATA_BYTE2        0x50
+#define  MCI_REGISTER_DATA_BYTE3        0x58
+
+#define  MCI_REGISTER_ADDRESS_BYTE0     0x60
+#define  MCI_REGISTER_ADDRESS_BYTE1     0x68
+
+#define  MCI_REGISTER_MODE              0x70
+
+/* Read and write modes for polaris ITVC */
+#define  MCI_MODE_REGISTER_READ         0x000
+#define  MCI_MODE_REGISTER_WRITE        0x100
+#define  MCI_MODE_MEMORY_READ           0x000
+#define  MCI_MODE_MEMORY_WRITE          0x4000
+
+static unsigned int mpegbufs = 8;
+module_param(mpegbufs, int, 0644);
+MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32");
+static unsigned int mpeglines = 128;
+module_param(mpeglines, int, 0644);
+MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32");
+static unsigned int mpeglinesize = 512;
+module_param(mpeglinesize, int, 0644);
+MODULE_PARM_DESC(mpeglinesize,
+	"number of bytes in each line of an MPEG buffer, range 512-1024");
+
+static unsigned int v4l_debug = 1;
+module_param(v4l_debug, int, 0644);
+MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages");
+struct cx231xx_dmaqueue *dma_qq;
+#define dprintk(level, fmt, arg...)\
+	do { if (v4l_debug >= level) \
+		printk(KERN_INFO "%s: " fmt, \
+		(dev) ? dev->name : "cx231xx[?]", ## arg); \
+	} while (0)
+
+static struct cx231xx_tvnorm cx231xx_tvnorms[] = {
+	{
+		.name      = "NTSC-M",
+		.id        = V4L2_STD_NTSC_M,
+	}, {
+		.name      = "NTSC-JP",
+		.id        = V4L2_STD_NTSC_M_JP,
+	}, {
+		.name      = "PAL-BG",
+		.id        = V4L2_STD_PAL_BG,
+	}, {
+		.name      = "PAL-DK",
+		.id        = V4L2_STD_PAL_DK,
+	}, {
+		.name      = "PAL-I",
+		.id        = V4L2_STD_PAL_I,
+	}, {
+		.name      = "PAL-M",
+		.id        = V4L2_STD_PAL_M,
+	}, {
+		.name      = "PAL-N",
+		.id        = V4L2_STD_PAL_N,
+	}, {
+		.name      = "PAL-Nc",
+		.id        = V4L2_STD_PAL_Nc,
+	}, {
+		.name      = "PAL-60",
+		.id        = V4L2_STD_PAL_60,
+	}, {
+		.name      = "SECAM-L",
+		.id        = V4L2_STD_SECAM_L,
+	}, {
+		.name      = "SECAM-DK",
+		.id        = V4L2_STD_SECAM_DK,
+	}
+};
+
+/* ------------------------------------------------------------------ */
+enum cx231xx_capture_type {
+	CX231xx_MPEG_CAPTURE,
+	CX231xx_RAW_CAPTURE,
+	CX231xx_RAW_PASSTHRU_CAPTURE
+};
+enum cx231xx_capture_bits {
+	CX231xx_RAW_BITS_NONE             = 0x00,
+	CX231xx_RAW_BITS_YUV_CAPTURE      = 0x01,
+	CX231xx_RAW_BITS_PCM_CAPTURE      = 0x02,
+	CX231xx_RAW_BITS_VBI_CAPTURE      = 0x04,
+	CX231xx_RAW_BITS_PASSTHRU_CAPTURE = 0x08,
+	CX231xx_RAW_BITS_TO_HOST_CAPTURE  = 0x10
+};
+enum cx231xx_capture_end {
+	CX231xx_END_AT_GOP, /* stop at the end of gop, generate irq */
+	CX231xx_END_NOW, /* stop immediately, no irq */
+};
+enum cx231xx_framerate {
+	CX231xx_FRAMERATE_NTSC_30, /* NTSC: 30fps */
+	CX231xx_FRAMERATE_PAL_25   /* PAL: 25fps */
+};
+enum cx231xx_stream_port {
+	CX231xx_OUTPUT_PORT_MEMORY,
+	CX231xx_OUTPUT_PORT_STREAMING,
+	CX231xx_OUTPUT_PORT_SERIAL
+};
+enum cx231xx_data_xfer_status {
+	CX231xx_MORE_BUFFERS_FOLLOW,
+	CX231xx_LAST_BUFFER,
+};
+enum cx231xx_picture_mask {
+	CX231xx_PICTURE_MASK_NONE,
+	CX231xx_PICTURE_MASK_I_FRAMES,
+	CX231xx_PICTURE_MASK_I_P_FRAMES = 0x3,
+	CX231xx_PICTURE_MASK_ALL_FRAMES = 0x7,
+};
+enum cx231xx_vbi_mode_bits {
+	CX231xx_VBI_BITS_SLICED,
+	CX231xx_VBI_BITS_RAW,
+};
+enum cx231xx_vbi_insertion_bits {
+	CX231xx_VBI_BITS_INSERT_IN_XTENSION_USR_DATA,
+	CX231xx_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1,
+	CX231xx_VBI_BITS_SEPARATE_STREAM = 0x2 << 1,
+	CX231xx_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1,
+	CX231xx_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1,
+};
+enum cx231xx_dma_unit {
+	CX231xx_DMA_BYTES,
+	CX231xx_DMA_FRAMES,
+};
+enum cx231xx_dma_transfer_status_bits {
+	CX231xx_DMA_TRANSFER_BITS_DONE = 0x01,
+	CX231xx_DMA_TRANSFER_BITS_ERROR = 0x04,
+	CX231xx_DMA_TRANSFER_BITS_LL_ERROR = 0x10,
+};
+enum cx231xx_pause {
+	CX231xx_PAUSE_ENCODING,
+	CX231xx_RESUME_ENCODING,
+};
+enum cx231xx_copyright {
+	CX231xx_COPYRIGHT_OFF,
+	CX231xx_COPYRIGHT_ON,
+};
+enum cx231xx_notification_type {
+	CX231xx_NOTIFICATION_REFRESH,
+};
+enum cx231xx_notification_status {
+	CX231xx_NOTIFICATION_OFF,
+	CX231xx_NOTIFICATION_ON,
+};
+enum cx231xx_notification_mailbox {
+	CX231xx_NOTIFICATION_NO_MAILBOX = -1,
+};
+enum cx231xx_field1_lines {
+	CX231xx_FIELD1_SAA7114 = 0x00EF, /* 239 */
+	CX231xx_FIELD1_SAA7115 = 0x00F0, /* 240 */
+	CX231xx_FIELD1_MICRONAS = 0x0105, /* 261 */
+};
+enum cx231xx_field2_lines {
+	CX231xx_FIELD2_SAA7114 = 0x00EF, /* 239 */
+	CX231xx_FIELD2_SAA7115 = 0x00F0, /* 240 */
+	CX231xx_FIELD2_MICRONAS = 0x0106, /* 262 */
+};
+enum cx231xx_custom_data_type {
+	CX231xx_CUSTOM_EXTENSION_USR_DATA,
+	CX231xx_CUSTOM_PRIVATE_PACKET,
+};
+enum cx231xx_mute {
+	CX231xx_UNMUTE,
+	CX231xx_MUTE,
+};
+enum cx231xx_mute_video_mask {
+	CX231xx_MUTE_VIDEO_V_MASK = 0x0000FF00,
+	CX231xx_MUTE_VIDEO_U_MASK = 0x00FF0000,
+	CX231xx_MUTE_VIDEO_Y_MASK = 0xFF000000,
+};
+enum cx231xx_mute_video_shift {
+	CX231xx_MUTE_VIDEO_V_SHIFT = 8,
+	CX231xx_MUTE_VIDEO_U_SHIFT = 16,
+	CX231xx_MUTE_VIDEO_Y_SHIFT = 24,
+};
+
+/* defines below are from ivtv-driver.h */
+#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
+
+/* Firmware API commands */
+#define IVTV_API_STD_TIMEOUT 500
+
+/* Registers */
+/* IVTV_REG_OFFSET */
+#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC)
+#define IVTV_REG_SPU (0x9050)
+#define IVTV_REG_HW_BLOCKS (0x9054)
+#define IVTV_REG_VPU (0x9058)
+#define IVTV_REG_APU (0xA064)
+
+/*
+ * Bit definitions for MC417_RWD and MC417_OEN registers
+ *
+ * bits 31-16
+ *+-----------+
+ *| Reserved  |
+ *|+-----------+
+ *|  bit 15  bit 14  bit 13 bit 12  bit 11  bit 10  bit 9   bit 8
+ *|+-------+-------+-------+-------+-------+-------+-------+-------+
+ *|| MIWR# | MIRD# | MICS# |MIRDY# |MIADDR3|MIADDR2|MIADDR1|MIADDR0|
+ *|+-------+-------+-------+-------+-------+-------+-------+-------+
+ *| bit 7   bit 6   bit 5   bit 4   bit 3   bit 2   bit 1   bit 0
+ *|+-------+-------+-------+-------+-------+-------+-------+-------+
+ *||MIDATA7|MIDATA6|MIDATA5|MIDATA4|MIDATA3|MIDATA2|MIDATA1|MIDATA0|
+ *|+-------+-------+-------+-------+-------+-------+-------+-------+
+ */
+#define MC417_MIWR	0x8000
+#define MC417_MIRD	0x4000
+#define MC417_MICS	0x2000
+#define MC417_MIRDY	0x1000
+#define MC417_MIADDR	0x0F00
+#define MC417_MIDATA	0x00FF
+
+
+/* Bit definitions for MC417_CTL register ****
+ *bits 31-6   bits 5-4   bit 3    bits 2-1       Bit 0
+ *+--------+-------------+--------+--------------+------------+
+ *|Reserved|MC417_SPD_CTL|Reserved|MC417_GPIO_SEL|UART_GPIO_EN|
+ *+--------+-------------+--------+--------------+------------+
+ */
+#define MC417_SPD_CTL(x)	(((x) << 4) & 0x00000030)
+#define MC417_GPIO_SEL(x)	(((x) << 1) & 0x00000006)
+#define MC417_UART_GPIO_EN	0x00000001
+
+/* Values for speed control */
+#define MC417_SPD_CTL_SLOW	0x1
+#define MC417_SPD_CTL_MEDIUM	0x0
+#define MC417_SPD_CTL_FAST	0x3     /* b'1x, but we use b'11 */
+
+/* Values for GPIO select */
+#define MC417_GPIO_SEL_GPIO3	0x3
+#define MC417_GPIO_SEL_GPIO2	0x2
+#define MC417_GPIO_SEL_GPIO1	0x1
+#define MC417_GPIO_SEL_GPIO0	0x0
+
+
+#define CX23417_GPIO_MASK 0xFC0003FF
+static int setITVCReg(struct cx231xx *dev, u32 gpio_direction, u32 value)
+{
+	int status = 0;
+	u32 _gpio_direction = 0;
+
+	_gpio_direction = _gpio_direction & CX23417_GPIO_MASK;
+	_gpio_direction = _gpio_direction|gpio_direction;
+	status = cx231xx_send_gpio_cmd(dev, _gpio_direction,
+			 (u8 *)&value, 4, 0, 0);
+	return status;
+}
+static int getITVCReg(struct cx231xx *dev, u32 gpio_direction, u32 *pValue)
+{
+	int status = 0;
+	u32 _gpio_direction = 0;
+
+	_gpio_direction = _gpio_direction & CX23417_GPIO_MASK;
+	_gpio_direction = _gpio_direction|gpio_direction;
+
+	status = cx231xx_send_gpio_cmd(dev, _gpio_direction,
+		 (u8 *)pValue, 4, 0, 1);
+	return status;
+}
+
+static int waitForMciComplete(struct cx231xx *dev)
+{
+	u32 gpio;
+	u32 gpio_driection = 0;
+	u8 count = 0;
+	getITVCReg(dev, gpio_driection, &gpio);
+
+	while (!(gpio&0x020000)) {
+		msleep(10);
+
+		getITVCReg(dev, gpio_driection, &gpio);
+
+		if (count++ > 100) {
+			dprintk(3, "ERROR: Timeout - gpio=%x\n", gpio);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int mc417_register_write(struct cx231xx *dev, u16 address, u32 value)
+{
+	u32 temp;
+	int status = 0;
+
+	temp = 0x82|MCI_REGISTER_DATA_BYTE0|((value&0x000000FF)<<8);
+	temp = temp<<10;
+	status = setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	if (status < 0)
+		return status;
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*write data byte 1;*/
+	temp = 0x82|MCI_REGISTER_DATA_BYTE1|(value&0x0000FF00);
+	temp = temp<<10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*write data byte 2;*/
+	temp = 0x82|MCI_REGISTER_DATA_BYTE2|((value&0x00FF0000)>>8);
+	temp = temp<<10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*write data byte 3;*/
+	temp = 0x82|MCI_REGISTER_DATA_BYTE3|((value&0xFF000000)>>16);
+	temp = temp<<10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*write address byte 0;*/
+	temp = 0x82|MCI_REGISTER_ADDRESS_BYTE0|((address&0x000000FF)<<8);
+	temp = temp<<10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*write address byte 1;*/
+	temp = 0x82|MCI_REGISTER_ADDRESS_BYTE1|(address&0x0000FF00);
+	temp = temp<<10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*Write that the mode is write.*/
+	temp = 0x82 | MCI_REGISTER_MODE | MCI_MODE_REGISTER_WRITE;
+	temp = temp<<10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	return waitForMciComplete(dev);
+}
+
+static int mc417_register_read(struct cx231xx *dev, u16 address, u32 *value)
+{
+	/*write address byte 0;*/
+	u32 temp;
+	u32 return_value = 0;
+	int ret = 0;
+
+	temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE0 | ((address & 0x00FF) << 8);
+	temp = temp << 10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp | ((0x05) << 10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*write address byte 1;*/
+	temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE1 | (address & 0xFF00);
+	temp = temp << 10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp | ((0x05) << 10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*write that the mode is read;*/
+	temp = 0x82 | MCI_REGISTER_MODE | MCI_MODE_REGISTER_READ;
+	temp = temp << 10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp | ((0x05) << 10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*wait for the MIRDY line to be asserted ,
+	signalling that the read is done;*/
+	ret = waitForMciComplete(dev);
+
+	/*switch the DATA- GPIO to input mode;*/
+
+	/*Read data byte 0;*/
+	temp = (0x82 | MCI_REGISTER_DATA_BYTE0) << 10;
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	temp = ((0x81 | MCI_REGISTER_DATA_BYTE0) << 10);
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	getITVCReg(dev, ITVC_READ_DIR, &temp);
+	return_value |= ((temp & 0x03FC0000) >> 18);
+	setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
+
+	/* Read data byte 1;*/
+	temp = (0x82 | MCI_REGISTER_DATA_BYTE1) << 10;
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	temp = ((0x81 | MCI_REGISTER_DATA_BYTE1) << 10);
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	getITVCReg(dev, ITVC_READ_DIR, &temp);
+
+	return_value |= ((temp & 0x03FC0000) >> 10);
+	setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
+
+	/*Read data byte 2;*/
+	temp = (0x82 | MCI_REGISTER_DATA_BYTE2) << 10;
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	temp = ((0x81 | MCI_REGISTER_DATA_BYTE2) << 10);
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	getITVCReg(dev, ITVC_READ_DIR, &temp);
+	return_value |= ((temp & 0x03FC0000) >> 2);
+	setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
+
+	/*Read data byte 3;*/
+	temp = (0x82 | MCI_REGISTER_DATA_BYTE3) << 10;
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	temp = ((0x81 | MCI_REGISTER_DATA_BYTE3) << 10);
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	getITVCReg(dev, ITVC_READ_DIR, &temp);
+	return_value |= ((temp & 0x03FC0000) << 6);
+	setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
+
+	*value  = return_value;
+
+
+	return ret;
+}
+
+static int mc417_memory_write(struct cx231xx *dev, u32 address, u32 value)
+{
+	/*write data byte 0;*/
+
+	u32 temp;
+	int ret = 0;
+
+	temp = 0x82 | MCI_MEMORY_DATA_BYTE0|((value & 0x000000FF) << 8);
+	temp = temp << 10;
+	ret = setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	if (ret < 0)
+		return ret;
+	temp = temp | ((0x05) << 10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*write data byte 1;*/
+	temp = 0x82 | MCI_MEMORY_DATA_BYTE1 | (value & 0x0000FF00);
+	temp = temp << 10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp | ((0x05) << 10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*write data byte 2;*/
+	temp = 0x82|MCI_MEMORY_DATA_BYTE2|((value&0x00FF0000)>>8);
+	temp = temp<<10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*write data byte 3;*/
+	temp = 0x82|MCI_MEMORY_DATA_BYTE3|((value&0xFF000000)>>16);
+	temp = temp<<10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/* write address byte 2;*/
+	temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE |
+		((address & 0x003F0000)>>8);
+	temp = temp<<10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/* write address byte 1;*/
+	temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00);
+	temp = temp<<10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/* write address byte 0;*/
+	temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0|((address & 0x00FF)<<8);
+	temp = temp<<10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*wait for MIRDY line;*/
+	waitForMciComplete(dev);
+
+	return 0;
+}
+
+static int mc417_memory_read(struct cx231xx *dev, u32 address, u32 *value)
+{
+	u32 temp = 0;
+	u32 return_value = 0;
+	int ret = 0;
+
+	/*write address byte 2;*/
+	temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_READ |
+		((address & 0x003F0000)>>8);
+	temp = temp<<10;
+	ret = setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	if (ret < 0)
+		return ret;
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*write address byte 1*/
+	temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00);
+	temp = temp<<10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*write address byte 0*/
+	temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0 | ((address & 0x00FF)<<8);
+	temp = temp<<10;
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+	temp = temp|((0x05)<<10);
+	setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+	/*Wait for MIRDY line*/
+	ret = waitForMciComplete(dev);
+
+
+	/*Read data byte 3;*/
+	temp = (0x82|MCI_MEMORY_DATA_BYTE3)<<10;
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	temp = ((0x81|MCI_MEMORY_DATA_BYTE3)<<10);
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	getITVCReg(dev, ITVC_READ_DIR, &temp);
+	return_value |= ((temp&0x03FC0000)<<6);
+	setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
+
+	/*Read data byte 2;*/
+	temp = (0x82|MCI_MEMORY_DATA_BYTE2)<<10;
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	temp = ((0x81|MCI_MEMORY_DATA_BYTE2)<<10);
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	getITVCReg(dev, ITVC_READ_DIR, &temp);
+	return_value |= ((temp&0x03FC0000)>>2);
+	setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
+
+	/* Read data byte 1;*/
+	temp = (0x82|MCI_MEMORY_DATA_BYTE1)<<10;
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	temp = ((0x81|MCI_MEMORY_DATA_BYTE1)<<10);
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	getITVCReg(dev, ITVC_READ_DIR, &temp);
+	return_value |= ((temp&0x03FC0000)>>10);
+	setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
+
+	/*Read data byte 0;*/
+	temp = (0x82|MCI_MEMORY_DATA_BYTE0)<<10;
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	temp = ((0x81|MCI_MEMORY_DATA_BYTE0)<<10);
+	setITVCReg(dev, ITVC_READ_DIR, temp);
+	getITVCReg(dev, ITVC_READ_DIR, &temp);
+	return_value |= ((temp&0x03FC0000)>>18);
+	setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
+
+	*value  = return_value;
+	return ret;
+}
+
+/* ------------------------------------------------------------------ */
+
+/* MPEG encoder API */
+static char *cmd_to_str(int cmd)
+{
+	switch (cmd) {
+	case CX2341X_ENC_PING_FW:
+		return  "PING_FW";
+	case CX2341X_ENC_START_CAPTURE:
+		return  "START_CAPTURE";
+	case CX2341X_ENC_STOP_CAPTURE:
+		return  "STOP_CAPTURE";
+	case CX2341X_ENC_SET_AUDIO_ID:
+		return  "SET_AUDIO_ID";
+	case CX2341X_ENC_SET_VIDEO_ID:
+		return  "SET_VIDEO_ID";
+	case CX2341X_ENC_SET_PCR_ID:
+		return  "SET_PCR_PID";
+	case CX2341X_ENC_SET_FRAME_RATE:
+		return  "SET_FRAME_RATE";
+	case CX2341X_ENC_SET_FRAME_SIZE:
+		return  "SET_FRAME_SIZE";
+	case CX2341X_ENC_SET_BIT_RATE:
+		return  "SET_BIT_RATE";
+	case CX2341X_ENC_SET_GOP_PROPERTIES:
+		return  "SET_GOP_PROPERTIES";
+	case CX2341X_ENC_SET_ASPECT_RATIO:
+		return  "SET_ASPECT_RATIO";
+	case CX2341X_ENC_SET_DNR_FILTER_MODE:
+		return  "SET_DNR_FILTER_PROPS";
+	case CX2341X_ENC_SET_DNR_FILTER_PROPS:
+		return  "SET_DNR_FILTER_PROPS";
+	case CX2341X_ENC_SET_CORING_LEVELS:
+		return  "SET_CORING_LEVELS";
+	case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
+		return  "SET_SPATIAL_FILTER_TYPE";
+	case CX2341X_ENC_SET_VBI_LINE:
+		return  "SET_VBI_LINE";
+	case CX2341X_ENC_SET_STREAM_TYPE:
+		return  "SET_STREAM_TYPE";
+	case CX2341X_ENC_SET_OUTPUT_PORT:
+		return  "SET_OUTPUT_PORT";
+	case CX2341X_ENC_SET_AUDIO_PROPERTIES:
+		return  "SET_AUDIO_PROPERTIES";
+	case CX2341X_ENC_HALT_FW:
+		return  "HALT_FW";
+	case CX2341X_ENC_GET_VERSION:
+		return  "GET_VERSION";
+	case CX2341X_ENC_SET_GOP_CLOSURE:
+		return  "SET_GOP_CLOSURE";
+	case CX2341X_ENC_GET_SEQ_END:
+		return  "GET_SEQ_END";
+	case CX2341X_ENC_SET_PGM_INDEX_INFO:
+		return  "SET_PGM_INDEX_INFO";
+	case CX2341X_ENC_SET_VBI_CONFIG:
+		return  "SET_VBI_CONFIG";
+	case CX2341X_ENC_SET_DMA_BLOCK_SIZE:
+		return  "SET_DMA_BLOCK_SIZE";
+	case CX2341X_ENC_GET_PREV_DMA_INFO_MB_10:
+		return  "GET_PREV_DMA_INFO_MB_10";
+	case CX2341X_ENC_GET_PREV_DMA_INFO_MB_9:
+		return  "GET_PREV_DMA_INFO_MB_9";
+	case CX2341X_ENC_SCHED_DMA_TO_HOST:
+		return  "SCHED_DMA_TO_HOST";
+	case CX2341X_ENC_INITIALIZE_INPUT:
+		return  "INITIALIZE_INPUT";
+	case CX2341X_ENC_SET_FRAME_DROP_RATE:
+		return  "SET_FRAME_DROP_RATE";
+	case CX2341X_ENC_PAUSE_ENCODER:
+		return  "PAUSE_ENCODER";
+	case CX2341X_ENC_REFRESH_INPUT:
+		return  "REFRESH_INPUT";
+	case CX2341X_ENC_SET_COPYRIGHT:
+		return  "SET_COPYRIGHT";
+	case CX2341X_ENC_SET_EVENT_NOTIFICATION:
+		return  "SET_EVENT_NOTIFICATION";
+	case CX2341X_ENC_SET_NUM_VSYNC_LINES:
+		return  "SET_NUM_VSYNC_LINES";
+	case CX2341X_ENC_SET_PLACEHOLDER:
+		return  "SET_PLACEHOLDER";
+	case CX2341X_ENC_MUTE_VIDEO:
+		return  "MUTE_VIDEO";
+	case CX2341X_ENC_MUTE_AUDIO:
+		return  "MUTE_AUDIO";
+	case CX2341X_ENC_MISC:
+		return  "MISC";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static int cx231xx_mbox_func(void *priv,
+			     u32 command,
+			     int in,
+			     int out,
+			     u32 data[CX2341X_MBOX_MAX_DATA])
+{
+	struct cx231xx *dev = priv;
+	unsigned long timeout;
+	u32 value, flag, retval = 0;
+	int i;
+
+	dprintk(3, "%s: command(0x%X) = %s\n", __func__, command,
+		cmd_to_str(command));
+
+	/* this may not be 100% safe if we can't read any memory location
+	   without side effects */
+	mc417_memory_read(dev, dev->cx23417_mailbox - 4, &value);
+	if (value != 0x12345678) {
+		dprintk(3,
+			"Firmware and/or mailbox pointer not initialized "
+			"or corrupted, signature = 0x%x, cmd = %s\n", value,
+			cmd_to_str(command));
+		return -1;
+	}
+
+	/* This read looks at 32 bits, but flag is only 8 bits.
+	 * Seems we also bail if CMD or TIMEOUT bytes are set???
+	 */
+	mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
+	if (flag) {
+		dprintk(3, "ERROR: Mailbox appears to be in use "
+			"(%x), cmd = %s\n", flag, cmd_to_str(command));
+		return -1;
+	}
+
+	flag |= 1; /* tell 'em we're working on it */
+	mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+	/* write command + args + fill remaining with zeros */
+	/* command code */
+	mc417_memory_write(dev, dev->cx23417_mailbox + 1, command);
+	mc417_memory_write(dev, dev->cx23417_mailbox + 3,
+		IVTV_API_STD_TIMEOUT); /* timeout */
+	for (i = 0; i < in; i++) {
+		mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, data[i]);
+		dprintk(3, "API Input %d = %d\n", i, data[i]);
+	}
+	for (; i < CX2341X_MBOX_MAX_DATA; i++)
+		mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, 0);
+
+	flag |= 3; /* tell 'em we're done writing */
+	mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+	/* wait for firmware to handle the API command */
+	timeout = jiffies + msecs_to_jiffies(10);
+	for (;;) {
+		mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
+		if (0 != (flag & 4))
+			break;
+		if (time_after(jiffies, timeout)) {
+			dprintk(3, "ERROR: API Mailbox timeout\n");
+			return -1;
+		}
+		udelay(10);
+	}
+
+	/* read output values */
+	for (i = 0; i < out; i++) {
+		mc417_memory_read(dev, dev->cx23417_mailbox + 4 + i, data + i);
+		dprintk(3, "API Output %d = %d\n", i, data[i]);
+	}
+
+	mc417_memory_read(dev, dev->cx23417_mailbox + 2, &retval);
+	dprintk(3, "API result = %d\n", retval);
+
+	flag = 0;
+	mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+	return retval;
+}
+
+/* We don't need to call the API often, so using just one
+ * mailbox will probably suffice
+ */
+static int cx231xx_api_cmd(struct cx231xx *dev,
+			   u32 command,
+			   u32 inputcnt,
+			   u32 outputcnt,
+			   ...)
+{
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	va_list vargs;
+	int i, err;
+
+	dprintk(3, "%s() cmds = 0x%08x\n", __func__, command);
+
+	va_start(vargs, outputcnt);
+	for (i = 0; i < inputcnt; i++)
+		data[i] = va_arg(vargs, int);
+
+	err = cx231xx_mbox_func(dev, command, inputcnt, outputcnt, data);
+	for (i = 0; i < outputcnt; i++) {
+		int *vptr = va_arg(vargs, int *);
+		*vptr = data[i];
+	}
+	va_end(vargs);
+
+	return err;
+}
+
+static int cx231xx_find_mailbox(struct cx231xx *dev)
+{
+	u32 signature[4] = {
+		0x12345678, 0x34567812, 0x56781234, 0x78123456
+	};
+	int signaturecnt = 0;
+	u32 value;
+	int i;
+	int ret = 0;
+
+	dprintk(2, "%s()\n", __func__);
+
+	for (i = 0; i < 0x100; i++) {/*CX231xx_FIRM_IMAGE_SIZE*/
+		ret = mc417_memory_read(dev, i, &value);
+		if (ret < 0)
+			return ret;
+		if (value == signature[signaturecnt])
+			signaturecnt++;
+		else
+			signaturecnt = 0;
+		if (4 == signaturecnt) {
+			dprintk(1, "Mailbox signature found at 0x%x\n", i+1);
+			return i+1;
+		}
+	}
+	dprintk(3, "Mailbox signature values not found!\n");
+	return -1;
+}
+
+static void mciWriteMemoryToGPIO(struct cx231xx *dev, u32 address, u32 value,
+		u32 *p_fw_image)
+{
+
+	u32 temp = 0;
+	int i = 0;
+
+	temp = 0x82|MCI_MEMORY_DATA_BYTE0|((value&0x000000FF)<<8);
+	temp = temp<<10;
+	*p_fw_image = temp;
+	p_fw_image++;
+	temp = temp|((0x05)<<10);
+	*p_fw_image = temp;
+	p_fw_image++;
+
+	/*write data byte 1;*/
+	temp = 0x82|MCI_MEMORY_DATA_BYTE1|(value&0x0000FF00);
+	temp = temp<<10;
+	*p_fw_image = temp;
+	p_fw_image++;
+	temp = temp|((0x05)<<10);
+	*p_fw_image = temp;
+	p_fw_image++;
+
+	/*write data byte 2;*/
+	temp = 0x82|MCI_MEMORY_DATA_BYTE2|((value&0x00FF0000)>>8);
+	temp = temp<<10;
+	*p_fw_image = temp;
+	p_fw_image++;
+	temp = temp|((0x05)<<10);
+	*p_fw_image = temp;
+	p_fw_image++;
+
+	/*write data byte 3;*/
+	temp = 0x82|MCI_MEMORY_DATA_BYTE3|((value&0xFF000000)>>16);
+	temp = temp<<10;
+	*p_fw_image = temp;
+	p_fw_image++;
+	temp = temp|((0x05)<<10);
+	*p_fw_image = temp;
+	p_fw_image++;
+
+	/* write address byte 2;*/
+	temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE |
+		((address & 0x003F0000)>>8);
+	temp = temp<<10;
+	*p_fw_image = temp;
+	p_fw_image++;
+	temp = temp|((0x05)<<10);
+	*p_fw_image = temp;
+	p_fw_image++;
+
+	/* write address byte 1;*/
+	temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00);
+	temp = temp<<10;
+	*p_fw_image = temp;
+	p_fw_image++;
+	temp = temp|((0x05)<<10);
+	*p_fw_image = temp;
+	p_fw_image++;
+
+	/* write address byte 0;*/
+	temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0|((address & 0x00FF)<<8);
+	temp = temp<<10;
+	*p_fw_image = temp;
+	p_fw_image++;
+	temp = temp|((0x05)<<10);
+	*p_fw_image = temp;
+	p_fw_image++;
+
+	for (i = 0; i < 6; i++) {
+		*p_fw_image = 0xFFFFFFFF;
+		p_fw_image++;
+	}
+}
+
+
+static int cx231xx_load_firmware(struct cx231xx *dev)
+{
+	static const unsigned char magic[8] = {
+		0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
+	};
+	const struct firmware *firmware;
+	int i, retval = 0;
+	u32 value = 0;
+	u32 gpio_output = 0;
+	/*u32 checksum = 0;*/
+	/*u32 *dataptr;*/
+	u32 transfer_size = 0;
+	u32 fw_data = 0;
+	u32 address = 0;
+	/*u32 current_fw[800];*/
+	u32 *p_current_fw, *p_fw;
+	u32 *p_fw_data;
+	int frame = 0;
+	u16 _buffer_size = 4096;
+	u8 *p_buffer;
+
+	p_current_fw = (u32 *)vmalloc(1884180*4);
+	p_fw = p_current_fw;
+	if (p_current_fw == 0) {
+		dprintk(2, "FAIL!!!\n");
+		return -1;
+	}
+
+	p_buffer = (u8 *)vmalloc(4096);
+	if (p_buffer == 0) {
+		dprintk(2, "FAIL!!!\n");
+		return -1;
+	}
+
+	dprintk(2, "%s()\n", __func__);
+
+	/* Save GPIO settings before reset of APU */
+	retval |= mc417_memory_read(dev, 0x9020, &gpio_output);
+	retval |= mc417_memory_read(dev, 0x900C, &value);
+
+	retval  = mc417_register_write(dev,
+		IVTV_REG_VPU, 0xFFFFFFED);
+	retval |= mc417_register_write(dev,
+		IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+	retval |= mc417_register_write(dev,
+		IVTV_REG_ENC_SDRAM_REFRESH, 0x80000800);
+	retval |= mc417_register_write(dev,
+		IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A);
+	retval |= mc417_register_write(dev,
+		IVTV_REG_APU, 0);
+
+	if (retval != 0) {
+		printk(KERN_ERR "%s: Error with mc417_register_write\n",
+			__func__);
+		return -1;
+	}
+
+	retval = request_firmware(&firmware, CX231xx_FIRM_IMAGE_NAME,
+				  &dev->udev->dev);
+
+	if (retval != 0) {
+		printk(KERN_ERR
+			"ERROR: Hotplug firmware request failed (%s).\n",
+			CX231xx_FIRM_IMAGE_NAME);
+		printk(KERN_ERR "Please fix your hotplug setup, the board will "
+			"not work without firmware loaded!\n");
+		return -1;
+	}
+
+	if (firmware->size != CX231xx_FIRM_IMAGE_SIZE) {
+		printk(KERN_ERR "ERROR: Firmware size mismatch "
+			"(have %zd, expected %d)\n",
+			firmware->size, CX231xx_FIRM_IMAGE_SIZE);
+		release_firmware(firmware);
+		return -1;
+	}
+
+	if (0 != memcmp(firmware->data, magic, 8)) {
+		printk(KERN_ERR
+			"ERROR: Firmware magic mismatch, wrong file?\n");
+		release_firmware(firmware);
+		return -1;
+	}
+
+	initGPIO(dev);
+
+	/* transfer to the chip */
+	dprintk(2, "Loading firmware to GPIO...\n");
+	p_fw_data = (u32 *)firmware->data;
+	dprintk(2, "firmware->size=%zd\n", firmware->size);
+	for (transfer_size = 0; transfer_size < firmware->size;
+		 transfer_size += 4) {
+		fw_data = *p_fw_data;
+
+		 mciWriteMemoryToGPIO(dev, address, fw_data, p_current_fw);
+		address = address + 1;
+		p_current_fw += 20;
+		p_fw_data += 1;
+	}
+
+	/*download the firmware by ep5-out*/
+
+	for (frame = 0; frame < (int)(CX231xx_FIRM_IMAGE_SIZE*20/_buffer_size);
+	     frame++) {
+		for (i = 0; i < _buffer_size; i++) {
+			*(p_buffer + i) = (u8)(*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x000000FF);
+			i++;
+			*(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x0000FF00) >> 8);
+			i++;
+			*(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x00FF0000) >> 16);
+			i++;
+			*(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0xFF000000) >> 24);
+		}
+		cx231xx_ep5_bulkout(dev, p_buffer, _buffer_size);
+	}
+
+	p_current_fw = p_fw;
+	vfree(p_current_fw);
+	p_current_fw = NULL;
+	uninitGPIO(dev);
+	release_firmware(firmware);
+	dprintk(1, "Firmware upload successful.\n");
+
+	retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS,
+		IVTV_CMD_HW_BLOCKS_RST);
+	if (retval < 0) {
+		printk(KERN_ERR "%s: Error with mc417_register_write\n",
+			__func__);
+		return retval;
+	}
+	/* F/W power up disturbs the GPIOs, restore state */
+	retval |= mc417_register_write(dev, 0x9020, gpio_output);
+	retval |= mc417_register_write(dev, 0x900C, value);
+
+	retval |= mc417_register_read(dev, IVTV_REG_VPU, &value);
+	retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8);
+
+	if (retval < 0) {
+		printk(KERN_ERR "%s: Error with mc417_register_write\n",
+			__func__);
+		return retval;
+	}
+	return 0;
+}
+
+static void cx231xx_417_check_encoder(struct cx231xx *dev)
+{
+	u32 status, seq;
+
+	status = 0;
+	seq = 0;
+	cx231xx_api_cmd(dev, CX2341X_ENC_GET_SEQ_END, 0, 2, &status, &seq);
+	dprintk(1, "%s() status = %d, seq = %d\n", __func__, status, seq);
+}
+
+static void cx231xx_codec_settings(struct cx231xx *dev)
+{
+	dprintk(1, "%s()\n", __func__);
+
+	/* assign frame size */
+	cx231xx_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
+				dev->ts1.height, dev->ts1.width);
+
+	dev->mpeg_params.width = dev->ts1.width;
+	dev->mpeg_params.height = dev->ts1.height;
+
+	cx2341x_update(dev, cx231xx_mbox_func, NULL, &dev->mpeg_params);
+
+	cx231xx_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1);
+	cx231xx_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1);
+}
+
+static int cx231xx_initialize_codec(struct cx231xx *dev)
+{
+	int version;
+	int retval;
+	u32 i, data[7];
+	u32 val = 0;
+
+	dprintk(1, "%s()\n", __func__);
+	cx231xx_disable656(dev);
+	retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
+	if (retval < 0) {
+		dprintk(2, "%s() PING OK\n", __func__);
+		retval = cx231xx_load_firmware(dev);
+		if (retval < 0) {
+			printk(KERN_ERR "%s() f/w load failed\n", __func__);
+			return retval;
+		}
+		retval = cx231xx_find_mailbox(dev);
+		if (retval < 0) {
+			printk(KERN_ERR "%s() mailbox < 0, error\n",
+				__func__);
+			return -1;
+		}
+		dev->cx23417_mailbox = retval;
+		retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0);
+		if (retval < 0) {
+			printk(KERN_ERR
+				"ERROR: cx23417 firmware ping failed!\n");
+			return -1;
+		}
+		retval = cx231xx_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1,
+			&version);
+		if (retval < 0) {
+			printk(KERN_ERR "ERROR: cx23417 firmware get encoder :"
+				"version failed!\n");
+			return -1;
+		}
+		dprintk(1, "cx23417 firmware version is 0x%08x\n", version);
+		msleep(200);
+	}
+
+	for (i = 0; i < 1; i++) {
+		retval = mc417_register_read(dev, 0x20f8, &val);
+		dprintk(3, "***before enable656() VIM Capture Lines =%d ***\n",
+				 val);
+		if (retval < 0)
+			return retval;
+	}
+
+	cx231xx_enable656(dev);
+			/* stop mpeg capture */
+			cx231xx_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE,
+				 3, 0, 1, 3, 4);
+
+	cx231xx_codec_settings(dev);
+	msleep(60);
+
+/*	cx231xx_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0,
+		CX231xx_FIELD1_SAA7115, CX231xx_FIELD2_SAA7115);
+	cx231xx_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0,
+		CX231xx_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0);
+*/
+	/* Setup to capture VBI */
+	data[0] = 0x0001BD00;
+	data[1] = 1;          /* frames per interrupt */
+	data[2] = 4;          /* total bufs */
+	data[3] = 0x91559155; /* start codes */
+	data[4] = 0x206080C0; /* stop codes */
+	data[5] = 6;          /* lines */
+	data[6] = 64;         /* BPL */
+/*
+	cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1],
+		data[2], data[3], data[4], data[5], data[6]);
+
+	for (i = 2; i <= 24; i++) {
+		int valid;
+
+		valid = ((i >= 19) && (i <= 21));
+		cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i,
+				valid, 0 , 0, 0);
+		cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0,
+				i | 0x80000000, valid, 0, 0, 0);
+	}
+*/
+/*	cx231xx_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX231xx_UNMUTE);
+	msleep(60);
+*/
+	/* initialize the video input */
+	retval = cx231xx_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
+	if (retval < 0)
+		return retval;
+	msleep(60);
+
+	/* Enable VIP style pixel invalidation so we work with scaled mode */
+	mc417_memory_write(dev, 2120, 0x00000080);
+
+	/* start capturing to the host interface */
+	retval = cx231xx_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
+		CX231xx_MPEG_CAPTURE, CX231xx_RAW_BITS_NONE);
+	if (retval < 0)
+		return retval;
+	msleep(10);
+
+	for (i = 0; i < 1; i++) {
+		mc417_register_read(dev, 0x20f8, &val);
+	dprintk(3, "***VIM Capture Lines =%d ***\n", val);
+	}
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int bb_buf_setup(struct videobuf_queue *q,
+	unsigned int *count, unsigned int *size)
+{
+	struct cx231xx_fh *fh = q->priv_data;
+
+	fh->dev->ts1.ts_packet_size  = mpeglinesize;
+	fh->dev->ts1.ts_packet_count = mpeglines;
+
+	*size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count;
+	*count = mpegbufs;
+
+	return 0;
+}
+static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
+{
+	struct cx231xx_fh *fh = vq->priv_data;
+	struct cx231xx *dev = fh->dev;
+	unsigned long flags = 0;
+
+	if (in_interrupt())
+		BUG();
+
+	spin_lock_irqsave(&dev->video_mode.slock, flags);
+	if (dev->USE_ISO) {
+		if (dev->video_mode.isoc_ctl.buf == buf)
+			dev->video_mode.isoc_ctl.buf = NULL;
+	} else {
+		if (dev->video_mode.bulk_ctl.buf == buf)
+			dev->video_mode.bulk_ctl.buf = NULL;
+	}
+	spin_unlock_irqrestore(&dev->video_mode.slock, flags);
+	videobuf_waiton(vq, &buf->vb, 0, 0);
+	videobuf_vmalloc_free(&buf->vb);
+	buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *urb,
+		struct cx231xx_dmaqueue *dma_q)
+{
+		void *vbuf;
+		struct cx231xx_buffer *buf;
+		u32 tail_data = 0;
+		char *p_data;
+
+		if (dma_q->mpeg_buffer_done == 0) {
+			if (list_empty(&dma_q->active))
+				return;
+
+			buf = list_entry(dma_q->active.next,
+					struct cx231xx_buffer, vb.queue);
+			dev->video_mode.isoc_ctl.buf = buf;
+			dma_q->mpeg_buffer_done = 1;
+		}
+		/* Fill buffer */
+		buf = dev->video_mode.isoc_ctl.buf;
+		vbuf = videobuf_to_vmalloc(&buf->vb);
+
+		if ((dma_q->mpeg_buffer_completed+len) <
+		   mpeglines*mpeglinesize) {
+			if (dma_q->add_ps_package_head ==
+			   CX231XX_NEED_ADD_PS_PACKAGE_HEAD) {
+				memcpy(vbuf+dma_q->mpeg_buffer_completed,
+				       dma_q->ps_head, 3);
+				dma_q->mpeg_buffer_completed =
+				  dma_q->mpeg_buffer_completed + 3;
+				dma_q->add_ps_package_head =
+				  CX231XX_NONEED_PS_PACKAGE_HEAD;
+			}
+			memcpy(vbuf+dma_q->mpeg_buffer_completed, data, len);
+			dma_q->mpeg_buffer_completed =
+			  dma_q->mpeg_buffer_completed + len;
+		} else {
+			dma_q->mpeg_buffer_done = 0;
+
+			tail_data =
+			  mpeglines*mpeglinesize - dma_q->mpeg_buffer_completed;
+			memcpy(vbuf+dma_q->mpeg_buffer_completed,
+			       data, tail_data);
+
+			buf->vb.state = VIDEOBUF_DONE;
+			buf->vb.field_count++;
+			do_gettimeofday(&buf->vb.ts);
+			list_del(&buf->vb.queue);
+			wake_up(&buf->vb.done);
+			dma_q->mpeg_buffer_completed = 0;
+
+			if (len - tail_data > 0) {
+				p_data = data + tail_data;
+				dma_q->left_data_count = len - tail_data;
+				memcpy(dma_q->p_left_data,
+				       p_data, len - tail_data);
+			}
+
+		}
+
+	    return;
+}
+
+static void buffer_filled(char *data, int len, struct urb *urb,
+		struct cx231xx_dmaqueue *dma_q)
+{
+		void *vbuf;
+		struct cx231xx_buffer *buf;
+
+		if (list_empty(&dma_q->active))
+			return;
+
+
+		buf = list_entry(dma_q->active.next,
+				 struct cx231xx_buffer, vb.queue);
+
+
+		/* Fill buffer */
+		vbuf = videobuf_to_vmalloc(&buf->vb);
+		memcpy(vbuf, data, len);
+		buf->vb.state = VIDEOBUF_DONE;
+		buf->vb.field_count++;
+		do_gettimeofday(&buf->vb.ts);
+		list_del(&buf->vb.queue);
+		wake_up(&buf->vb.done);
+
+	    return;
+}
+static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
+{
+	struct cx231xx_dmaqueue *dma_q = urb->context;
+	unsigned char *p_buffer;
+	u32 buffer_size = 0;
+	u32 i = 0;
+
+	for (i = 0; i < urb->number_of_packets; i++) {
+		if (dma_q->left_data_count > 0) {
+			buffer_copy(dev, dma_q->p_left_data,
+				    dma_q->left_data_count, urb, dma_q);
+			dma_q->mpeg_buffer_completed = dma_q->left_data_count;
+			dma_q->left_data_count = 0;
+		}
+
+		p_buffer = urb->transfer_buffer +
+				urb->iso_frame_desc[i].offset;
+		buffer_size = urb->iso_frame_desc[i].actual_length;
+
+		if (buffer_size > 0)
+			buffer_copy(dev, p_buffer, buffer_size, urb, dma_q);
+	}
+
+	return 0;
+}
+static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb)
+{
+
+	/*char *outp;*/
+	/*struct cx231xx_buffer *buf;*/
+	struct cx231xx_dmaqueue *dma_q = urb->context;
+	unsigned char *p_buffer, *buffer;
+	u32 buffer_size = 0;
+
+	p_buffer = urb->transfer_buffer;
+	buffer_size = urb->actual_length;
+
+	buffer = kmalloc(buffer_size, GFP_ATOMIC);
+
+	memcpy(buffer, dma_q->ps_head, 3);
+	memcpy(buffer+3, p_buffer, buffer_size-3);
+	memcpy(dma_q->ps_head, p_buffer+buffer_size-3, 3);
+
+	p_buffer = buffer;
+	buffer_filled(p_buffer, buffer_size, urb, dma_q);
+
+	kfree(buffer);
+	return 0;
+}
+
+static int bb_buf_prepare(struct videobuf_queue *q,
+	struct videobuf_buffer *vb, enum v4l2_field field)
+{
+	struct cx231xx_fh *fh = q->priv_data;
+	struct cx231xx_buffer *buf =
+	    container_of(vb, struct cx231xx_buffer, vb);
+	struct cx231xx *dev = fh->dev;
+	int rc = 0, urb_init = 0;
+	int size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count;
+
+	dma_qq = &dev->video_mode.vidq;
+
+	if (0 != buf->vb.baddr  &&  buf->vb.bsize < size)
+		return -EINVAL;
+	buf->vb.width = fh->dev->ts1.ts_packet_size;
+	buf->vb.height = fh->dev->ts1.ts_packet_count;
+	buf->vb.size = size;
+	buf->vb.field = field;
+
+	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+		rc = videobuf_iolock(q, &buf->vb, NULL);
+		if (rc < 0)
+			goto fail;
+	}
+
+	if (dev->USE_ISO) {
+		if (!dev->video_mode.isoc_ctl.num_bufs)
+			urb_init = 1;
+	} else {
+		if (!dev->video_mode.bulk_ctl.num_bufs)
+			urb_init = 1;
+	}
+	/*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n",
+		urb_init, dev->video_mode.max_pkt_size);*/
+	dev->mode_tv = 1;
+
+	if (urb_init) {
+		rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+		rc = cx231xx_unmute_audio(dev);
+		if (dev->USE_ISO) {
+			cx231xx_set_alt_setting(dev, INDEX_TS1, 4);
+			rc = cx231xx_init_isoc(dev, mpeglines,
+				       mpegbufs,
+				       dev->ts1_mode.max_pkt_size,
+				       cx231xx_isoc_copy);
+		} else {
+			cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
+			rc = cx231xx_init_bulk(dev, mpeglines,
+				       mpegbufs,
+				       dev->ts1_mode.max_pkt_size,
+				       cx231xx_bulk_copy);
+		}
+		if (rc < 0)
+			goto fail;
+	}
+
+	buf->vb.state = VIDEOBUF_PREPARED;
+	return 0;
+
+fail:
+	free_buffer(q, buf);
+	return rc;
+}
+
+static void bb_buf_queue(struct videobuf_queue *q,
+	struct videobuf_buffer *vb)
+{
+	struct cx231xx_fh *fh = q->priv_data;
+
+	struct cx231xx_buffer *buf =
+	    container_of(vb, struct cx231xx_buffer, vb);
+	struct cx231xx *dev = fh->dev;
+	struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
+
+	buf->vb.state = VIDEOBUF_QUEUED;
+	list_add_tail(&buf->vb.queue, &vidq->active);
+
+}
+
+static void bb_buf_release(struct videobuf_queue *q,
+	struct videobuf_buffer *vb)
+{
+	struct cx231xx_buffer *buf =
+	    container_of(vb, struct cx231xx_buffer, vb);
+	/*struct cx231xx_fh *fh = q->priv_data;*/
+	/*struct cx231xx *dev = (struct cx231xx *)fh->dev;*/
+
+	free_buffer(q, buf);
+}
+
+static struct videobuf_queue_ops cx231xx_qops = {
+	.buf_setup    = bb_buf_setup,
+	.buf_prepare  = bb_buf_prepare,
+	.buf_queue    = bb_buf_queue,
+	.buf_release  = bb_buf_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static const u32 *ctrl_classes[] = {
+	cx2341x_mpeg_ctrls,
+	NULL
+};
+
+static int cx231xx_queryctrl(struct cx231xx *dev,
+	struct v4l2_queryctrl *qctrl)
+{
+	qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+	if (qctrl->id == 0)
+		return -EINVAL;
+
+	/* MPEG V4L2 controls */
+	if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl))
+		qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+
+	return 0;
+}
+
+static int cx231xx_querymenu(struct cx231xx *dev,
+	struct v4l2_querymenu *qmenu)
+{
+	struct v4l2_queryctrl qctrl;
+
+	qctrl.id = qmenu->id;
+	cx231xx_queryctrl(dev, &qctrl);
+	return v4l2_ctrl_query_menu(qmenu, &qctrl,
+		cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id));
+}
+
+static int vidioc_g_std(struct file *file, void *fh0, v4l2_std_id *norm)
+{
+	struct cx231xx_fh  *fh  = file->private_data;
+	struct cx231xx *dev = fh->dev;
+
+	*norm = dev->encodernorm.id;
+	return 0;
+}
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+	struct cx231xx_fh  *fh  = file->private_data;
+	struct cx231xx *dev = fh->dev;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(cx231xx_tvnorms); i++)
+		if (*id & cx231xx_tvnorms[i].id)
+			break;
+	if (i == ARRAY_SIZE(cx231xx_tvnorms))
+		return -EINVAL;
+	dev->encodernorm = cx231xx_tvnorms[i];
+
+	if (dev->encodernorm.id & 0xb000) {
+		dprintk(3, "encodernorm set to NTSC\n");
+		dev->norm = V4L2_STD_NTSC;
+		dev->ts1.height = 480;
+		dev->mpeg_params.is_50hz = 0;
+	} else {
+		dprintk(3, "encodernorm set to PAL\n");
+		dev->norm = V4L2_STD_PAL_B;
+		dev->ts1.height = 576;
+		dev->mpeg_params.is_50hz = 1;
+	}
+	call_all(dev, core, s_std, dev->norm);
+	/* do mode control overrides */
+	cx231xx_do_mode_ctrl_overrides(dev);
+
+	dprintk(3, "exit vidioc_s_std() i=0x%x\n", i);
+	return 0;
+}
+static int vidioc_g_audio(struct file *file, void *fh,
+					struct v4l2_audio *a)
+{
+		struct v4l2_audio *vin = a;
+
+		int ret = -EINVAL;
+		if (vin->index > 0)
+			return ret;
+		strncpy(vin->name, "VideoGrabber Audio", 14);
+		vin->capability = V4L2_AUDCAP_STEREO;
+return 0;
+}
+static int vidioc_enumaudio(struct file *file, void *fh,
+					struct v4l2_audio *a)
+{
+		struct v4l2_audio *vin = a;
+
+		int ret = -EINVAL;
+
+		if (vin->index > 0)
+			return ret;
+		strncpy(vin->name, "VideoGrabber Audio", 14);
+		vin->capability = V4L2_AUDCAP_STEREO;
+
+
+return 0;
+}
+static const char *iname[] = {
+	[CX231XX_VMUX_COMPOSITE1] = "Composite1",
+	[CX231XX_VMUX_SVIDEO]     = "S-Video",
+	[CX231XX_VMUX_TELEVISION] = "Television",
+	[CX231XX_VMUX_CABLE]      = "Cable TV",
+	[CX231XX_VMUX_DVB]        = "DVB",
+	[CX231XX_VMUX_DEBUG]      = "for debug only",
+};
+static int vidioc_enum_input(struct file *file, void *priv,
+				struct v4l2_input *i)
+{
+	struct cx231xx_fh  *fh  = file->private_data;
+	struct cx231xx *dev = fh->dev;
+	struct cx231xx_input *input;
+	int n;
+	dprintk(3, "enter vidioc_enum_input()i->index=%d\n", i->index);
+
+	if (i->index >= 4)
+		return -EINVAL;
+
+
+	input = &cx231xx_boards[dev->model].input[i->index];
+
+	if (input->type == 0)
+		return -EINVAL;
+
+	/* FIXME
+	 * strcpy(i->name, input->name); */
+
+	n = i->index;
+	strcpy(i->name, iname[INPUT(n)->type]);
+
+	if (input->type == CX231XX_VMUX_TELEVISION ||
+	    input->type == CX231XX_VMUX_CABLE)
+		i->type = V4L2_INPUT_TYPE_TUNER;
+	else
+		i->type  = V4L2_INPUT_TYPE_CAMERA;
+
+
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return  0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	struct cx231xx_fh  *fh  = file->private_data;
+	struct cx231xx *dev = fh->dev;
+
+	dprintk(3, "enter vidioc_s_input() i=%d\n", i);
+
+	mutex_lock(&dev->lock);
+
+	video_mux(dev, i);
+
+	mutex_unlock(&dev->lock);
+
+	if (i >= 4)
+		return -EINVAL;
+	dev->input = i;
+	dprintk(3, "exit vidioc_s_input()\n");
+	return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+				struct v4l2_tuner *t)
+{
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+				struct v4l2_tuner *t)
+{
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+				struct v4l2_frequency *f)
+{
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+				struct v4l2_frequency *f)
+{
+
+
+	return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+				struct v4l2_control *ctl)
+{
+	struct cx231xx_fh  *fh  = file->private_data;
+	struct cx231xx *dev = fh->dev;
+	dprintk(3, "enter vidioc_s_ctrl()\n");
+	/* Update the A/V core */
+	call_all(dev, core, s_ctrl, ctl);
+	dprintk(3, "exit vidioc_s_ctrl()\n");
+	return 0;
+}
+static struct v4l2_capability pvr_capability = {
+	.driver         = "cx231xx",
+	.card           = "VideoGrabber",
+	.bus_info       = "usb",
+	.version        = 1,
+	.capabilities   = (V4L2_CAP_VIDEO_CAPTURE |
+			   V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
+			 V4L2_CAP_STREAMING | V4L2_CAP_READWRITE),
+	.reserved       = {0, 0, 0, 0}
+};
+static int vidioc_querycap(struct file *file, void  *priv,
+				struct v4l2_capability *cap)
+{
+
+
+
+		memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
+					struct v4l2_fmtdesc *f)
+{
+
+	if (f->index != 0)
+		return -EINVAL;
+
+	strlcpy(f->description, "MPEG", sizeof(f->description));
+	f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct cx231xx_fh  *fh  = file->private_data;
+	struct cx231xx *dev = fh->dev;
+	dprintk(3, "enter vidioc_g_fmt_vid_cap()\n");
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+	f->fmt.pix.width        = dev->ts1.width;
+	f->fmt.pix.height       = dev->ts1.height;
+	f->fmt.pix.field        = fh->vidq.field;
+	dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
+		dev->ts1.width, dev->ts1.height, fh->vidq.field);
+	dprintk(3, "exit vidioc_g_fmt_vid_cap()\n");
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct cx231xx_fh  *fh  = file->private_data;
+	struct cx231xx *dev = fh->dev;
+	dprintk(3, "enter vidioc_try_fmt_vid_cap()\n");
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+	dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
+		dev->ts1.width, dev->ts1.height, fh->vidq.field);
+	dprintk(3, "exit vidioc_try_fmt_vid_cap()\n");
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+
+	return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+				struct v4l2_requestbuffers *p)
+{
+	struct cx231xx_fh  *fh  = file->private_data;
+
+	return videobuf_reqbufs(&fh->vidq, p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+				struct v4l2_buffer *p)
+{
+	struct cx231xx_fh  *fh  = file->private_data;
+
+	return videobuf_querybuf(&fh->vidq, p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv,
+				struct v4l2_buffer *p)
+{
+	struct cx231xx_fh  *fh  = file->private_data;
+
+	return videobuf_qbuf(&fh->vidq, p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+	struct cx231xx_fh  *fh  = priv;
+
+	return videobuf_dqbuf(&fh->vidq, b, file->f_flags & O_NONBLOCK);
+}
+
+
+static int vidioc_streamon(struct file *file, void *priv,
+				enum v4l2_buf_type i)
+{
+	struct cx231xx_fh  *fh  = file->private_data;
+
+	struct cx231xx *dev = fh->dev;
+	int rc = 0;
+	dprintk(3, "enter vidioc_streamon()\n");
+		cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
+		rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+		if (dev->USE_ISO)
+			rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
+				       CX231XX_NUM_BUFS,
+				       dev->video_mode.max_pkt_size,
+				       cx231xx_isoc_copy);
+		else {
+			rc = cx231xx_init_bulk(dev, 320,
+				       5,
+				       dev->ts1_mode.max_pkt_size,
+				       cx231xx_bulk_copy);
+		}
+	dprintk(3, "exit vidioc_streamon()\n");
+	return videobuf_streamon(&fh->vidq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+	struct cx231xx_fh  *fh  = file->private_data;
+
+	return videobuf_streamoff(&fh->vidq);
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+				struct v4l2_ext_controls *f)
+{
+	struct cx231xx_fh  *fh  = priv;
+	struct cx231xx *dev = fh->dev;
+	dprintk(3, "enter vidioc_g_ext_ctrls()\n");
+	if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+		return -EINVAL;
+	dprintk(3, "exit vidioc_g_ext_ctrls()\n");
+	return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS);
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+				struct v4l2_ext_controls *f)
+{
+	struct cx231xx_fh  *fh  = priv;
+	struct cx231xx *dev = fh->dev;
+	struct cx2341x_mpeg_params p;
+	int err;
+	dprintk(3, "enter vidioc_s_ext_ctrls()\n");
+	if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+		return -EINVAL;
+
+	p = dev->mpeg_params;
+	err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
+	if (err == 0) {
+		err = cx2341x_update(dev, cx231xx_mbox_func,
+			&dev->mpeg_params, &p);
+		dev->mpeg_params = p;
+	}
+
+	return err;
+
+
+return 0;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+				struct v4l2_ext_controls *f)
+{
+	struct cx231xx_fh  *fh  = priv;
+	struct cx231xx *dev = fh->dev;
+	struct cx2341x_mpeg_params p;
+	int err;
+	dprintk(3, "enter vidioc_try_ext_ctrls()\n");
+	if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+		return -EINVAL;
+
+	p = dev->mpeg_params;
+	err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
+	dprintk(3, "exit vidioc_try_ext_ctrls() err=%d\n", err);
+	return err;
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+	struct cx231xx_fh  *fh  = priv;
+	struct cx231xx *dev = fh->dev;
+	char name[32 + 2];
+
+	snprintf(name, sizeof(name), "%s/2", dev->name);
+	dprintk(3,
+		"%s/2: ============  START LOG STATUS  ============\n",
+	       dev->name);
+	call_all(dev, core, log_status);
+	cx2341x_log_status(&dev->mpeg_params, name);
+	dprintk(3,
+		"%s/2: =============  END LOG STATUS  =============\n",
+	       dev->name);
+	return 0;
+}
+
+static int vidioc_querymenu(struct file *file, void *priv,
+				struct v4l2_querymenu *a)
+{
+	struct cx231xx_fh  *fh  = priv;
+	struct cx231xx *dev = fh->dev;
+	dprintk(3, "enter vidioc_querymenu()\n");
+	dprintk(3, "exit vidioc_querymenu()\n");
+	return cx231xx_querymenu(dev, a);
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+				struct v4l2_queryctrl *c)
+{
+	struct cx231xx_fh  *fh  = priv;
+	struct cx231xx *dev = fh->dev;
+	dprintk(3, "enter vidioc_queryctrl()\n");
+	dprintk(3, "exit vidioc_queryctrl()\n");
+	return cx231xx_queryctrl(dev, c);
+}
+
+static int mpeg_open(struct file *file)
+{
+	int minor = video_devdata(file)->minor;
+	struct cx231xx *h, *dev = NULL;
+	/*struct list_head *list;*/
+	struct cx231xx_fh *fh;
+	/*u32 value = 0;*/
+
+	dprintk(2, "%s()\n", __func__);
+
+	list_for_each_entry(h, &cx231xx_devlist, devlist) {
+		if (h->v4l_device->minor == minor)
+			dev = h;
+	}
+
+	if (dev == NULL) {
+		unlock_kernel();
+		return -ENODEV;
+	}
+	mutex_lock(&dev->lock);
+
+	/* allocate + initialize per filehandle data */
+	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+	if (NULL == fh) {
+		mutex_unlock(&dev->lock);
+		return -ENOMEM;
+	}
+
+	file->private_data = fh;
+	fh->dev      = dev;
+
+
+	videobuf_queue_vmalloc_init(&fh->vidq, &cx231xx_qops,
+			    NULL, &dev->video_mode.slock,
+			    V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED,
+			    sizeof(struct cx231xx_buffer), fh, NULL);
+/*
+	videobuf_queue_sg_init(&fh->vidq, &cx231xx_qops,
+			    &dev->udev->dev, &dev->ts1.slock,
+			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			    V4L2_FIELD_INTERLACED,
+			    sizeof(struct cx231xx_buffer),
+			    fh, NULL);
+*/
+
+
+	cx231xx_set_alt_setting(dev, INDEX_VANC, 1);
+	cx231xx_set_gpio_value(dev, 2, 0);
+
+	cx231xx_initialize_codec(dev);
+
+	mutex_unlock(&dev->lock);
+	cx231xx_start_TS1(dev);
+
+	return 0;
+}
+
+static int mpeg_release(struct file *file)
+{
+	struct cx231xx_fh  *fh  = file->private_data;
+	struct cx231xx *dev = fh->dev;
+
+	dprintk(3, "mpeg_release()! dev=0x%p\n", dev);
+
+	if (!dev) {
+		dprintk(3, "abort!!!\n");
+		return 0;
+	}
+
+	mutex_lock(&dev->lock);
+
+	cx231xx_stop_TS1(dev);
+
+		/* do this before setting alternate! */
+		if (dev->USE_ISO)
+			cx231xx_uninit_isoc(dev);
+		else
+			cx231xx_uninit_bulk(dev);
+		cx231xx_set_mode(dev, CX231XX_SUSPEND);
+
+		cx231xx_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
+				CX231xx_END_NOW, CX231xx_MPEG_CAPTURE,
+				CX231xx_RAW_BITS_NONE);
+
+	/* FIXME: Review this crap */
+	/* Shut device down on last close */
+	if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
+		if (atomic_dec_return(&dev->v4l_reader_count) == 0) {
+			/* stop mpeg capture */
+
+			msleep(500);
+			cx231xx_417_check_encoder(dev);
+
+		}
+	}
+
+	if (fh->vidq.streaming)
+		videobuf_streamoff(&fh->vidq);
+	if (fh->vidq.reading)
+		videobuf_read_stop(&fh->vidq);
+
+	videobuf_mmap_free(&fh->vidq);
+	file->private_data = NULL;
+	kfree(fh);
+	mutex_unlock(&dev->lock);
+	return 0;
+}
+
+static ssize_t mpeg_read(struct file *file, char __user *data,
+	size_t count, loff_t *ppos)
+{
+	struct cx231xx_fh *fh = file->private_data;
+	struct cx231xx *dev = fh->dev;
+
+
+	/* Deal w/ A/V decoder * and mpeg encoder sync issues. */
+	/* Start mpeg encoder on first read. */
+	if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+		if (atomic_inc_return(&dev->v4l_reader_count) == 1) {
+			if (cx231xx_initialize_codec(dev) < 0)
+				return -EINVAL;
+		}
+	}
+
+	return videobuf_read_stream(&fh->vidq, data, count, ppos, 0,
+				    file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int mpeg_poll(struct file *file,
+	struct poll_table_struct *wait)
+{
+	struct cx231xx_fh *fh = file->private_data;
+	/*struct cx231xx *dev = fh->dev;*/
+
+	/*dprintk(2, "%s\n", __func__);*/
+
+	return videobuf_poll_stream(file, &fh->vidq, wait);
+}
+
+static int mpeg_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct cx231xx_fh *fh = file->private_data;
+	struct cx231xx *dev = fh->dev;
+
+	dprintk(2, "%s()\n", __func__);
+
+	return videobuf_mmap_mapper(&fh->vidq, vma);
+}
+
+static struct v4l2_file_operations mpeg_fops = {
+	.owner	       = THIS_MODULE,
+	.open	       = mpeg_open,
+	.release       = mpeg_release,
+	.read	       = mpeg_read,
+	.poll          = mpeg_poll,
+	.mmap	       = mpeg_mmap,
+	.ioctl	       = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
+	.vidioc_s_std		 = vidioc_s_std,
+	.vidioc_g_std		 = vidioc_g_std,
+	.vidioc_enum_input	 = vidioc_enum_input,
+	.vidioc_enumaudio	 = vidioc_enumaudio,
+	.vidioc_g_audio		 = vidioc_g_audio,
+	.vidioc_g_input		 = vidioc_g_input,
+	.vidioc_s_input		 = vidioc_s_input,
+	.vidioc_g_tuner		 = vidioc_g_tuner,
+	.vidioc_s_tuner		 = vidioc_s_tuner,
+	.vidioc_g_frequency	 = vidioc_g_frequency,
+	.vidioc_s_frequency	 = vidioc_s_frequency,
+	.vidioc_s_ctrl		 = vidioc_s_ctrl,
+	.vidioc_querycap	 = vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap	 = vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap	 = vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap	 = vidioc_s_fmt_vid_cap,
+	.vidioc_reqbufs		 = vidioc_reqbufs,
+	.vidioc_querybuf	 = vidioc_querybuf,
+	.vidioc_qbuf		 = vidioc_qbuf,
+	.vidioc_dqbuf		 = vidioc_dqbuf,
+	.vidioc_streamon	 = vidioc_streamon,
+	.vidioc_streamoff	 = vidioc_streamoff,
+	.vidioc_g_ext_ctrls	 = vidioc_g_ext_ctrls,
+	.vidioc_s_ext_ctrls	 = vidioc_s_ext_ctrls,
+	.vidioc_try_ext_ctrls	 = vidioc_try_ext_ctrls,
+	.vidioc_log_status	 = vidioc_log_status,
+	.vidioc_querymenu	 = vidioc_querymenu,
+	.vidioc_queryctrl	 = vidioc_queryctrl,
+/*	.vidioc_g_chip_ident	 = cx231xx_g_chip_ident,*/
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+/*	.vidioc_g_register	 = cx231xx_g_register,*/
+/*	.vidioc_s_register	 = cx231xx_s_register,*/
+#endif
+};
+
+static struct video_device cx231xx_mpeg_template = {
+	.name          = "cx231xx",
+	.fops          = &mpeg_fops,
+	.ioctl_ops     = &mpeg_ioctl_ops,
+	.minor         = -1,
+	.tvnorms       = CX231xx_NORMS,
+	.current_norm  = V4L2_STD_NTSC_M,
+};
+
+void cx231xx_417_unregister(struct cx231xx *dev)
+{
+	dprintk(1, "%s()\n", __func__);
+	dprintk(3, "%s()\n", __func__);
+
+	if (dev->v4l_device) {
+		if (-1 != dev->v4l_device->minor)
+			video_unregister_device(dev->v4l_device);
+		else
+			video_device_release(dev->v4l_device);
+		dev->v4l_device = NULL;
+	}
+}
+
+static struct video_device *cx231xx_video_dev_alloc(
+	struct cx231xx *dev,
+	struct usb_device *usbdev,
+	struct video_device *template,
+	char *type)
+{
+	struct video_device *vfd;
+
+	dprintk(1, "%s()\n", __func__);
+	vfd = video_device_alloc();
+	if (NULL == vfd)
+		return NULL;
+	*vfd = *template;
+	vfd->minor = -1;
+	snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
+		type, cx231xx_boards[dev->model].name);
+
+	vfd->v4l2_dev = &dev->v4l2_dev;
+	vfd->release = video_device_release;
+
+	return vfd;
+
+}
+
+int cx231xx_417_register(struct cx231xx *dev)
+{
+	/* FIXME: Port1 hardcoded here */
+	int err = -ENODEV;
+	struct cx231xx_tsport *tsport = &dev->ts1;
+
+	dprintk(1, "%s()\n", __func__);
+
+	/* Set default TV standard */
+	dev->encodernorm = cx231xx_tvnorms[0];
+
+	if (dev->encodernorm.id & V4L2_STD_525_60)
+		tsport->height = 480;
+	else
+		tsport->height = 576;
+
+	tsport->width = 720;
+	cx2341x_fill_defaults(&dev->mpeg_params);
+	dev->norm = V4L2_STD_NTSC;
+
+	dev->mpeg_params.port = CX2341X_PORT_SERIAL;
+
+	/* Allocate and initialize V4L video device */
+	dev->v4l_device = cx231xx_video_dev_alloc(dev,
+		dev->udev, &cx231xx_mpeg_template, "mpeg");
+	err = video_register_device(dev->v4l_device,
+		VFL_TYPE_GRABBER, -1);
+	if (err < 0) {
+		dprintk(3, "%s: can't register mpeg device\n", dev->name);
+		return err;
+	}
+
+	dprintk(3, "%s: registered device video%d [mpeg]\n",
+	       dev->name, dev->v4l_device->num);
+
+	return 0;
+}
diff --git a/drivers/media/video/cx231xx/cx231xx-audio.c b/drivers/media/video/cx231xx/cx231xx-audio.c
index 7cae95a..30d13c1 100644
--- a/drivers/media/video/cx231xx/cx231xx-audio.c
+++ b/drivers/media/video/cx231xx/cx231xx-audio.c
@@ -75,6 +75,30 @@
 	return 0;
 }
 
+static int cx231xx_bulk_audio_deinit(struct cx231xx *dev)
+{
+	int i;
+
+	dprintk("Stopping bulk\n");
+
+	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
+		if (dev->adev.urb[i]) {
+			if (!irqs_disabled())
+				usb_kill_urb(dev->adev.urb[i]);
+			else
+				usb_unlink_urb(dev->adev.urb[i]);
+
+			usb_free_urb(dev->adev.urb[i]);
+			dev->adev.urb[i] = NULL;
+
+			kfree(dev->adev.transfer_buffer[i]);
+			dev->adev.transfer_buffer[i] = NULL;
+		}
+	}
+
+	return 0;
+}
+
 static void cx231xx_audio_isocirq(struct urb *urb)
 {
 	struct cx231xx *dev = urb->context;
@@ -100,6 +124,9 @@
 		break;
 	}
 
+	if (atomic_read(&dev->stream_started) == 0)
+		return;
+
 	if (dev->adev.capture_pcm_substream) {
 		substream = dev->adev.capture_pcm_substream;
 		runtime = substream->runtime;
@@ -158,14 +185,95 @@
 	return;
 }
 
+static void cx231xx_audio_bulkirq(struct urb *urb)
+{
+	struct cx231xx *dev = urb->context;
+	unsigned int oldptr;
+	int period_elapsed = 0;
+	int status;
+	unsigned char *cp;
+	unsigned int stride;
+	struct snd_pcm_substream *substream;
+	struct snd_pcm_runtime *runtime;
+
+	switch (urb->status) {
+	case 0:		/* success */
+	case -ETIMEDOUT:	/* NAK */
+		break;
+	case -ECONNRESET:	/* kill */
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+	default:		/* error */
+		dprintk("urb completition error %d.\n", urb->status);
+		break;
+	}
+
+	if (atomic_read(&dev->stream_started) == 0)
+		return;
+
+	if (dev->adev.capture_pcm_substream) {
+		substream = dev->adev.capture_pcm_substream;
+		runtime = substream->runtime;
+		stride = runtime->frame_bits >> 3;
+
+		if (1) {
+			int length = urb->actual_length /
+				     stride;
+			cp = (unsigned char *)urb->transfer_buffer;
+
+			oldptr = dev->adev.hwptr_done_capture;
+			if (oldptr + length >= runtime->buffer_size) {
+				unsigned int cnt;
+
+				cnt = runtime->buffer_size - oldptr;
+				memcpy(runtime->dma_area + oldptr * stride, cp,
+				       cnt * stride);
+				memcpy(runtime->dma_area, cp + cnt * stride,
+				       length * stride - cnt * stride);
+			} else {
+				memcpy(runtime->dma_area + oldptr * stride, cp,
+				       length * stride);
+			}
+
+			snd_pcm_stream_lock(substream);
+
+			dev->adev.hwptr_done_capture += length;
+			if (dev->adev.hwptr_done_capture >=
+						runtime->buffer_size)
+				dev->adev.hwptr_done_capture -=
+						runtime->buffer_size;
+
+			dev->adev.capture_transfer_done += length;
+			if (dev->adev.capture_transfer_done >=
+				runtime->period_size) {
+				dev->adev.capture_transfer_done -=
+						runtime->period_size;
+				period_elapsed = 1;
+			}
+			snd_pcm_stream_unlock(substream);
+		}
+		if (period_elapsed)
+			snd_pcm_period_elapsed(substream);
+	}
+	urb->status = 0;
+
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status < 0) {
+		cx231xx_errdev("resubmit of audio urb failed (error=%i)\n",
+			       status);
+	}
+	return;
+}
+
 static int cx231xx_init_audio_isoc(struct cx231xx *dev)
 {
 	int i, errCode;
 	int sb_size;
 
-	cx231xx_info("%s: Starting AUDIO transfers\n", __func__);
+	cx231xx_info("%s: Starting ISO AUDIO transfers\n", __func__);
 
-	sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
+	sb_size = CX231XX_ISO_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
 
 	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
 		struct urb *urb;
@@ -176,7 +284,7 @@
 			return -ENOMEM;
 
 		memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
-		urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
+		urb = usb_alloc_urb(CX231XX_ISO_NUM_AUDIO_PACKETS, GFP_ATOMIC);
 		if (!urb) {
 			cx231xx_errdev("usb_alloc_urb failed!\n");
 			for (j = 0; j < i; j++) {
@@ -194,10 +302,10 @@
 		urb->transfer_buffer = dev->adev.transfer_buffer[i];
 		urb->interval = 1;
 		urb->complete = cx231xx_audio_isocirq;
-		urb->number_of_packets = CX231XX_NUM_AUDIO_PACKETS;
+		urb->number_of_packets = CX231XX_ISO_NUM_AUDIO_PACKETS;
 		urb->transfer_buffer_length = sb_size;
 
-		for (j = k = 0; j < CX231XX_NUM_AUDIO_PACKETS;
+		for (j = k = 0; j < CX231XX_ISO_NUM_AUDIO_PACKETS;
 			j++, k += dev->adev.max_pkt_size) {
 			urb->iso_frame_desc[j].offset = k;
 			urb->iso_frame_desc[j].length = dev->adev.max_pkt_size;
@@ -216,27 +324,56 @@
 	return errCode;
 }
 
-static int cx231xx_cmd(struct cx231xx *dev, int cmd, int arg)
+static int cx231xx_init_audio_bulk(struct cx231xx *dev)
 {
-	dprintk("%s transfer\n", (dev->adev.capture_stream == STREAM_ON) ?
-		"stop" : "start");
+	int i, errCode;
+	int sb_size;
 
-	switch (cmd) {
-	case CX231XX_CAPTURE_STREAM_EN:
-		if (dev->adev.capture_stream == STREAM_OFF && arg == 1) {
-			dev->adev.capture_stream = STREAM_ON;
-			cx231xx_init_audio_isoc(dev);
-		} else if (dev->adev.capture_stream == STREAM_ON && arg == 0) {
-			dev->adev.capture_stream = STREAM_OFF;
-			cx231xx_isoc_audio_deinit(dev);
-		} else {
-			cx231xx_errdev("An underrun very likely occurred. "
-				       "Ignoring it.\n");
+	cx231xx_info("%s: Starting BULK AUDIO transfers\n", __func__);
+
+	sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
+
+	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
+		struct urb *urb;
+		int j;
+
+		dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
+		if (!dev->adev.transfer_buffer[i])
+			return -ENOMEM;
+
+		memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
+		urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
+		if (!urb) {
+			cx231xx_errdev("usb_alloc_urb failed!\n");
+			for (j = 0; j < i; j++) {
+				usb_free_urb(dev->adev.urb[j]);
+				kfree(dev->adev.transfer_buffer[j]);
+			}
+			return -ENOMEM;
 		}
-		return 0;
-	default:
-		return -EINVAL;
+
+		urb->dev = dev->udev;
+		urb->context = dev;
+		urb->pipe = usb_rcvbulkpipe(dev->udev,
+						dev->adev.end_point_addr);
+		urb->transfer_flags = 0;
+		urb->transfer_buffer = dev->adev.transfer_buffer[i];
+		urb->complete = cx231xx_audio_bulkirq;
+		urb->transfer_buffer_length = sb_size;
+
+		dev->adev.urb[i] = urb;
+
 	}
+
+	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
+		errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
+		if (errCode < 0) {
+			cx231xx_bulk_audio_deinit(dev);
+			return errCode;
+		}
+	}
+
+	return errCode;
 }
 
 static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
@@ -300,19 +437,24 @@
 
 	/* set alternate setting for audio interface */
 	/* 1 - 48000 samples per sec */
-	ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1);
+	mutex_lock(&dev->lock);
+	if (dev->USE_ISO)
+		ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1);
+	else
+		ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
+	mutex_unlock(&dev->lock);
 	if (ret < 0) {
 		cx231xx_errdev("failed to set alternate setting !\n");
 
 		return ret;
 	}
 
-	/* inform hardware to start streaming */
-	ret = cx231xx_capture_start(dev, 1, Audio);
-
 	runtime->hw = snd_cx231xx_hw_capture;
 
 	mutex_lock(&dev->lock);
+	/* inform hardware to start streaming */
+	ret = cx231xx_capture_start(dev, 1, Audio);
+
 	dev->adev.users++;
 	mutex_unlock(&dev->lock);
 
@@ -330,20 +472,21 @@
 
 	dprintk("closing device\n");
 
+	/* inform hardware to stop streaming */
+	mutex_lock(&dev->lock);
+	ret = cx231xx_capture_start(dev, 0, Audio);
+
 	/* set alternate setting for audio interface */
 	/* 1 - 48000 samples per sec */
 	ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
 	if (ret < 0) {
 		cx231xx_errdev("failed to set alternate setting !\n");
 
+		mutex_unlock(&dev->lock);
 		return ret;
 	}
 
-	/* inform hardware to start streaming */
-	ret = cx231xx_capture_start(dev, 0, Audio);
-
 	dev->mute = 1;
-	mutex_lock(&dev->lock);
 	dev->adev.users--;
 	mutex_unlock(&dev->lock);
 
@@ -352,7 +495,10 @@
 		dprintk("disabling audio stream!\n");
 		dev->adev.shutdown = 0;
 		dprintk("released lock\n");
-		cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, 0);
+		if (atomic_read(&dev->stream_started) > 0) {
+			atomic_set(&dev->stream_started, 0);
+			schedule_work(&dev->wq_trigger);
+		}
 	}
 	return 0;
 }
@@ -383,43 +529,64 @@
 
 	dprintk("Stop capture, if needed\n");
 
-	if (dev->adev.capture_stream == STREAM_ON)
-		cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, CX231XX_STOP_AUDIO);
+	if (atomic_read(&dev->stream_started) > 0) {
+		atomic_set(&dev->stream_started, 0);
+		schedule_work(&dev->wq_trigger);
+	}
 
 	return 0;
 }
 
 static int snd_cx231xx_prepare(struct snd_pcm_substream *substream)
 {
+	struct cx231xx *dev = snd_pcm_substream_chip(substream);
+
+	dev->adev.hwptr_done_capture = 0;
+	dev->adev.capture_transfer_done = 0;
+
 	return 0;
 }
 
+static void audio_trigger(struct work_struct *work)
+{
+	struct cx231xx *dev = container_of(work, struct cx231xx, wq_trigger);
+
+	if (atomic_read(&dev->stream_started)) {
+		dprintk("starting capture");
+		if (is_fw_load(dev) == 0)
+			cx25840_call(dev, core, load_fw);
+		if (dev->USE_ISO)
+			cx231xx_init_audio_isoc(dev);
+		else
+			cx231xx_init_audio_bulk(dev);
+	} else {
+		dprintk("stopping capture");
+		cx231xx_isoc_audio_deinit(dev);
+	}
+}
+
 static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream,
 				       int cmd)
 {
 	struct cx231xx *dev = snd_pcm_substream_chip(substream);
 	int retval;
 
-	dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START) ?
-		"start" : "stop");
-
 	spin_lock(&dev->adev.slock);
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
-		cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN,
-			    CX231XX_START_AUDIO);
-		retval = 0;
+		atomic_set(&dev->stream_started, 1);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
-		cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, CX231XX_STOP_AUDIO);
-		retval = 0;
+		atomic_set(&dev->stream_started, 0);
 		break;
 	default:
 		retval = -EINVAL;
 	}
-
 	spin_unlock(&dev->adev.slock);
-	return retval;
+
+	schedule_work(&dev->wq_trigger);
+
+	return 0;
 }
 
 static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream
@@ -495,10 +662,13 @@
 	pcm->info_flags = 0;
 	pcm->private_data = dev;
 	strcpy(pcm->name, "Conexant cx231xx Capture");
+	snd_card_set_dev(card, &dev->udev->dev);
 	strcpy(card->driver, "Cx231xx-Audio");
 	strcpy(card->shortname, "Cx231xx Audio");
 	strcpy(card->longname, "Conexant cx231xx Audio");
 
+	INIT_WORK(&dev->wq_trigger, audio_trigger);
+
 	err = snd_card_register(card);
 	if (err < 0) {
 		snd_card_free(card);
diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c
index c217441..cf50faf 100644
--- a/drivers/media/video/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/video/cx231xx/cx231xx-avcore.c
@@ -31,13 +31,16 @@
 #include <linux/i2c.h>
 #include <linux/mm.h>
 #include <linux/mutex.h>
+#include <media/tuner.h>
 
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-chip-ident.h>
 
 #include "cx231xx.h"
+#include "cx231xx-dif.h"
 
+#define TUNER_MODE_FM_RADIO 0
 /******************************************************************************
 			-: BLOCK ARRANGEMENT :-
 	I2S block ----------------------|
@@ -50,6 +53,57 @@
 					    [Video]
 
 *******************************************************************************/
+/******************************************************************************
+ *                    VERVE REGISTER                                          *
+ *									      *
+ ******************************************************************************/
+static int verve_write_byte(struct cx231xx *dev, u8 saddr, u8 data)
+{
+	return cx231xx_write_i2c_data(dev, VERVE_I2C_ADDRESS,
+					saddr, 1, data, 1);
+}
+
+static int verve_read_byte(struct cx231xx *dev, u8 saddr, u8 *data)
+{
+	int status;
+	u32 temp = 0;
+
+	status = cx231xx_read_i2c_data(dev, VERVE_I2C_ADDRESS,
+					saddr, 1, &temp, 1);
+	*data = (u8) temp;
+	return status;
+}
+void initGPIO(struct cx231xx *dev)
+{
+	u32 _gpio_direction = 0;
+	u32 value = 0;
+	u8 val = 0;
+
+	_gpio_direction = _gpio_direction & 0xFC0003FF;
+	_gpio_direction = _gpio_direction | 0x03FDFC00;
+	cx231xx_send_gpio_cmd(dev, _gpio_direction, (u8 *)&value, 4, 0, 0);
+
+	verve_read_byte(dev, 0x07, &val);
+	cx231xx_info(" verve_read_byte address0x07=0x%x\n", val);
+	verve_write_byte(dev, 0x07, 0xF4);
+	verve_read_byte(dev, 0x07, &val);
+	cx231xx_info(" verve_read_byte address0x07=0x%x\n", val);
+
+	cx231xx_capture_start(dev, 1, 2);
+
+	cx231xx_mode_register(dev, EP_MODE_SET, 0x0500FE00);
+	cx231xx_mode_register(dev, GBULK_BIT_EN, 0xFFFDFFFF);
+
+}
+void uninitGPIO(struct cx231xx *dev)
+{
+	u8 value[4] = { 0, 0, 0, 0 };
+
+	cx231xx_capture_start(dev, 0, 2);
+	verve_write_byte(dev, 0x07, 0x14);
+	cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+			0x68, value, 4);
+}
 
 /******************************************************************************
  *                    A F E - B L O C K    C O N T R O L   functions          *
@@ -258,7 +312,7 @@
 
 	switch (mode) {
 	case AFE_MODE_LOW_IF:
-		/* SetupAFEforLowIF();  */
+		cx231xx_Setup_AFE_for_LowIF(dev);
 		break;
 	case AFE_MODE_BASEBAND:
 		status = cx231xx_afe_setup_AFE_for_baseband(dev);
@@ -291,8 +345,15 @@
 	int status = 0;
 
 	switch (dev->model) {
+	case CX231XX_BOARD_CNXT_CARRAERA:
 	case CX231XX_BOARD_CNXT_RDE_250:
+	case CX231XX_BOARD_CNXT_SHELBY:
 	case CX231XX_BOARD_CNXT_RDU_250:
+	case CX231XX_BOARD_CNXT_RDE_253S:
+	case CX231XX_BOARD_CNXT_RDU_253S:
+	case CX231XX_BOARD_CNXT_VIDEO_GRABBER:
+	case CX231XX_BOARD_HAUPPAUGE_EXETER:
+	case CX231XX_BOARD_HAUPPAUGE_USBLIVE2:
 		if (avmode == POLARIS_AVMODE_ANALOGT_TV) {
 			while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
 						FLD_PWRDN_ENABLE_PLL)) {
@@ -483,6 +544,17 @@
 	return cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
 					saddr, 2, data, 4);
 }
+int cx231xx_check_fw(struct cx231xx *dev)
+{
+	u8 temp = 0;
+	int status = 0;
+	status = vid_blk_read_byte(dev, DL_CTL_ADDRESS_LOW, &temp);
+	if (status < 0)
+		return status;
+	else
+		return temp;
+
+}
 
 int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input)
 {
@@ -521,9 +593,15 @@
 				return status;
 			}
 		}
-		status = cx231xx_set_decoder_video_input(dev,
+		if (dev->tuner_type == TUNER_NXP_TDA18271)
+			status = cx231xx_set_decoder_video_input(dev,
+							CX231XX_VMUX_TELEVISION,
+							INPUT(input)->vmux);
+		else
+			status = cx231xx_set_decoder_video_input(dev,
 							CX231XX_VMUX_COMPOSITE1,
 							INPUT(input)->vmux);
+
 		break;
 	default:
 		cx231xx_errdev("%s: set_power_mode : Unknown Input %d !\n",
@@ -578,12 +656,12 @@
 		value |= (1 << 7);
 		status = vid_blk_write_word(dev, OUT_CTRL1, value);
 
-		/* Set vip 1.1 output mode */
+		/* Set output mode */
 		status = cx231xx_read_modify_write_i2c_dword(dev,
 							VID_BLK_I2C_ADDRESS,
 							OUT_CTRL1,
 							FLD_OUT_MODE,
-							OUT_MODE_VIP11);
+							dev->board.output_mode);
 
 		/* Tell DIF object to go to baseband mode  */
 		status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
@@ -681,7 +759,9 @@
 	case CX231XX_VMUX_CABLE:
 	default:
 		switch (dev->model) {
+		case CX231XX_BOARD_CNXT_CARRAERA:
 		case CX231XX_BOARD_CNXT_RDE_250:
+		case CX231XX_BOARD_CNXT_SHELBY:
 		case CX231XX_BOARD_CNXT_RDU_250:
 			/* Disable the use of  DIF   */
 
@@ -699,11 +779,11 @@
 			value |= (1 << 7);
 			status = vid_blk_write_word(dev, OUT_CTRL1, value);
 
-			/* Set vip 1.1 output mode */
+			/* Set output mode */
 			status = cx231xx_read_modify_write_i2c_dword(dev,
 							VID_BLK_I2C_ADDRESS,
 							OUT_CTRL1, FLD_OUT_MODE,
-							OUT_MODE_VIP11);
+							dev->board.output_mode);
 
 			/* Tell DIF object to go to baseband mode */
 			status = cx231xx_dif_set_standard(dev,
@@ -790,11 +870,11 @@
 				 (FLD_OEF_AGC_IF);
 			status = vid_blk_write_word(dev, PIN_CTRL, value);
 
-			/* Set vip 1.1 output mode */
+			/* Set output mode */
 			status = cx231xx_read_modify_write_i2c_dword(dev,
 						VID_BLK_I2C_ADDRESS,
 						OUT_CTRL1, FLD_OUT_MODE,
-						OUT_MODE_VIP11);
+						dev->board.output_mode);
 
 			/* Disable auto config of registers */
 			status = cx231xx_read_modify_write_i2c_dword(dev,
@@ -816,9 +896,21 @@
 			/* Set VGA_SEL (for audio control)       (bit 7-8) */
 			status = vid_blk_read_word(dev, AFE_CTRL, &value);
 
+			/*Set Func mode:01-DIF 10-baseband 11-YUV*/
+			value &= (~(FLD_FUNC_MODE));
+			value |= 0x800000;
+
 			value |= FLD_VGA_SEL_CH3 | FLD_VGA_SEL_CH2;
 
 			status = vid_blk_write_word(dev, AFE_CTRL, value);
+
+			if (dev->tuner_type == TUNER_NXP_TDA18271) {
+				status = vid_blk_read_word(dev, PIN_CTRL,
+				 &value);
+				status = vid_blk_write_word(dev, PIN_CTRL,
+				 (value & 0xFFFFFFEF));
+			}
+
 			break;
 
 		}
@@ -840,6 +932,39 @@
 	return status;
 }
 
+void cx231xx_enable656(struct cx231xx *dev)
+{
+	u8 temp = 0;
+	int status;
+	/*enable TS1 data[0:7] as output to export 656*/
+
+	status = vid_blk_write_byte(dev, TS1_PIN_CTL0, 0xFF);
+
+	/*enable TS1 clock as output to export 656*/
+
+	status = vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp);
+	temp = temp|0x04;
+
+	status = vid_blk_write_byte(dev, TS1_PIN_CTL1, temp);
+
+}
+EXPORT_SYMBOL_GPL(cx231xx_enable656);
+
+void cx231xx_disable656(struct cx231xx *dev)
+{
+	u8 temp = 0;
+	int status;
+
+
+	status = vid_blk_write_byte(dev, TS1_PIN_CTL0, 0x00);
+
+	status = vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp);
+	temp = temp&0xFB;
+
+	status = vid_blk_write_byte(dev, TS1_PIN_CTL1, temp);
+}
+EXPORT_SYMBOL_GPL(cx231xx_disable656);
+
 /*
  * Handle any video-mode specific overrides that are different
  * on a per video standards basis after touching the MODE_CTRL
@@ -868,12 +993,12 @@
 							VID_BLK_I2C_ADDRESS,
 							VERT_TIM_CTRL,
 							FLD_VACTIVE_CNT,
-							0x1E6000);
+							0x1E7000);
 		status = cx231xx_read_modify_write_i2c_dword(dev,
 							VID_BLK_I2C_ADDRESS,
 							VERT_TIM_CTRL,
 							FLD_V656BLANK_CNT,
-							0x1E000000);
+							0x1C000000);
 
 		status = cx231xx_read_modify_write_i2c_dword(dev,
 							VID_BLK_I2C_ADDRESS,
@@ -881,12 +1006,27 @@
 							FLD_HBLANK_CNT,
 							cx231xx_set_field
 							(FLD_HBLANK_CNT, 0x79));
+
 	} else if (dev->norm & V4L2_STD_SECAM) {
 		cx231xx_info("do_mode_ctrl_overrides SECAM\n");
 		status =  cx231xx_read_modify_write_i2c_dword(dev,
 							VID_BLK_I2C_ADDRESS,
 							VERT_TIM_CTRL,
-							FLD_VBLANK_CNT, 0x24);
+							FLD_VBLANK_CNT, 0x20);
+		status = cx231xx_read_modify_write_i2c_dword(dev,
+							VID_BLK_I2C_ADDRESS,
+							VERT_TIM_CTRL,
+							FLD_VACTIVE_CNT,
+							cx231xx_set_field
+							(FLD_VACTIVE_CNT,
+							 0x244));
+		status = cx231xx_read_modify_write_i2c_dword(dev,
+							VID_BLK_I2C_ADDRESS,
+							VERT_TIM_CTRL,
+							FLD_V656BLANK_CNT,
+							cx231xx_set_field
+							(FLD_V656BLANK_CNT,
+							0x24));
 		/* Adjust the active video horizontal start point */
 		status = cx231xx_read_modify_write_i2c_dword(dev,
 							VID_BLK_I2C_ADDRESS,
@@ -899,7 +1039,21 @@
 		status = cx231xx_read_modify_write_i2c_dword(dev,
 							VID_BLK_I2C_ADDRESS,
 							VERT_TIM_CTRL,
-							FLD_VBLANK_CNT, 0x24);
+							FLD_VBLANK_CNT, 0x20);
+		status = cx231xx_read_modify_write_i2c_dword(dev,
+							VID_BLK_I2C_ADDRESS,
+							VERT_TIM_CTRL,
+							FLD_VACTIVE_CNT,
+							cx231xx_set_field
+							(FLD_VACTIVE_CNT,
+							 0x244));
+		status = cx231xx_read_modify_write_i2c_dword(dev,
+							VID_BLK_I2C_ADDRESS,
+							VERT_TIM_CTRL,
+							FLD_V656BLANK_CNT,
+							cx231xx_set_field
+							(FLD_V656BLANK_CNT,
+							0x24));
 		/* Adjust the active video horizontal start point */
 		status = cx231xx_read_modify_write_i2c_dword(dev,
 							VID_BLK_I2C_ADDRESS,
@@ -907,11 +1061,28 @@
 							FLD_HBLANK_CNT,
 							cx231xx_set_field
 							(FLD_HBLANK_CNT, 0x85));
+
 	}
 
 	return status;
 }
 
+int cx231xx_unmute_audio(struct cx231xx *dev)
+{
+	return vid_blk_write_byte(dev, PATH1_VOL_CTL, 0x24);
+}
+EXPORT_SYMBOL_GPL(cx231xx_unmute_audio);
+
+int stopAudioFirmware(struct cx231xx *dev)
+{
+	return vid_blk_write_byte(dev, DL_CTL_CONTROL, 0x03);
+}
+
+int restartAudioFirmware(struct cx231xx *dev)
+{
+	return vid_blk_write_byte(dev, DL_CTL_CONTROL, 0x13);
+}
+
 int cx231xx_set_audio_input(struct cx231xx *dev, u8 input)
 {
 	int status = 0;
@@ -970,6 +1141,7 @@
 
 		/* unmute all, AC97 in, independence mode
 		   adr 08d0, data 0x00063073 */
+		status = vid_blk_write_word(dev, DL_CTL, 0x3000001);
 		status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063073);
 
 		/* set AVC maximum threshold, adr 08d4, dat ffff0024 */
@@ -985,7 +1157,7 @@
 
 	case AUDIO_INPUT_TUNER_TV:
 	default:
-
+		status = stopAudioFirmware(dev);
 		/* Setup SRC sources and clocks */
 		status = vid_blk_write_word(dev, BAND_OUT_SEL,
 			cx231xx_set_field(FLD_SRC6_IN_SEL, 0x00)         |
@@ -1013,18 +1185,32 @@
 		status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F063870);
 
 		/* setAudioStandard(_audio_standard); */
-
 		status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063870);
-		switch (dev->model) {
-		case CX231XX_BOARD_CNXT_RDE_250:
-		case CX231XX_BOARD_CNXT_RDU_250:
+
+		status = restartAudioFirmware(dev);
+
+		switch (dev->board.tuner_type) {
+		case TUNER_XC5000:
+			/* SIF passthrough at 28.6363 MHz sample rate */
 			status = cx231xx_read_modify_write_i2c_dword(dev,
 					VID_BLK_I2C_ADDRESS,
 					CHIP_CTRL,
 					FLD_SIF_EN,
 					cx231xx_set_field(FLD_SIF_EN, 1));
 			break;
+		case TUNER_NXP_TDA18271:
+			/* Normal mode: SIF passthrough at 14.32 MHz */
+			status = cx231xx_read_modify_write_i2c_dword(dev,
+					VID_BLK_I2C_ADDRESS,
+					CHIP_CTRL,
+					FLD_SIF_EN,
+					cx231xx_set_field(FLD_SIF_EN, 0));
+			break;
 		default:
+			/* This is just a casual suggestion to people adding
+			   new boards in case they use a tuner type we don't
+			   currently know about */
+			printk(KERN_INFO "Unknown tuner type configuring SIF");
 			break;
 		}
 		break;
@@ -1049,18 +1235,6 @@
 	return status;
 }
 
-/* Set resolution of the video */
-int cx231xx_resolution_set(struct cx231xx *dev)
-{
-	/* set horzontal scale */
-	int status = vid_blk_write_word(dev, HSCALE_CTRL, dev->hscale);
-	if (status)
-		return status;
-
-	/* set vertical scale */
-	return vid_blk_write_word(dev, VSCALE_CTRL, dev->vscale);
-}
-
 /******************************************************************************
  *                    C H I P Specific  C O N T R O L   functions             *
  ******************************************************************************/
@@ -1094,35 +1268,351 @@
 	return status;
 }
 
-int cx231xx_enable_i2c_for_tuner(struct cx231xx *dev, u8 I2CIndex)
+int cx231xx_enable_i2c_port_3(struct cx231xx *dev, bool is_port_3)
 {
 	u8 value[4] = { 0, 0, 0, 0 };
 	int status = 0;
-
-	cx231xx_info("Changing the i2c port for tuner to %d\n", I2CIndex);
+	bool current_is_port_3;
 
 	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
 				       PWR_CTL_EN, value, 4);
 	if (status < 0)
 		return status;
 
-	if (I2CIndex == I2C_1) {
-		if (value[0] & I2C_DEMOD_EN) {
-			value[0] &= ~I2C_DEMOD_EN;
-			status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
-						   PWR_CTL_EN, value, 4);
-		}
-	} else {
-		if (!(value[0] & I2C_DEMOD_EN)) {
-			value[0] |= I2C_DEMOD_EN;
-			status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
-						   PWR_CTL_EN, value, 4);
-		}
-	}
+	current_is_port_3 = value[0] & I2C_DEMOD_EN ? true : false;
+
+	/* Just return, if already using the right port */
+	if (current_is_port_3 == is_port_3)
+		return 0;
+
+	if (is_port_3)
+		value[0] |= I2C_DEMOD_EN;
+	else
+		value[0] &= ~I2C_DEMOD_EN;
+
+	cx231xx_info("Changing the i2c master port to %d\n",
+		     is_port_3 ?  3 : 1);
+
+	status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+					PWR_CTL_EN, value, 4);
 
 	return status;
 
 }
+EXPORT_SYMBOL_GPL(cx231xx_enable_i2c_port_3);
+
+void update_HH_register_after_set_DIF(struct cx231xx *dev)
+{
+/*
+	u8 status = 0;
+	u32 value = 0;
+
+	vid_blk_write_word(dev, PIN_CTRL, 0xA0FFF82F);
+	vid_blk_write_word(dev, DIF_MISC_CTRL, 0x0A203F11);
+	vid_blk_write_word(dev, DIF_SRC_PHASE_INC, 0x1BEFBF06);
+
+	status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value);
+	vid_blk_write_word(dev, AFE_CTRL_C2HH_SRC_CTRL, 0x4485D390);
+	status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL,  &value);
+*/
+}
+
+void cx231xx_dump_HH_reg(struct cx231xx *dev)
+{
+	u8 status = 0;
+	u32 value = 0;
+	u16  i = 0;
+
+	value = 0x45005390;
+	status = vid_blk_write_word(dev, 0x104, value);
+
+	for (i = 0x100; i < 0x140; i++) {
+		status = vid_blk_read_word(dev, i, &value);
+		cx231xx_info("reg0x%x=0x%x\n", i, value);
+		i = i+3;
+	}
+
+	for (i = 0x300; i < 0x400; i++) {
+		status = vid_blk_read_word(dev, i, &value);
+		cx231xx_info("reg0x%x=0x%x\n", i, value);
+		i = i+3;
+	}
+
+	for (i = 0x400; i < 0x440; i++) {
+		status = vid_blk_read_word(dev, i,  &value);
+		cx231xx_info("reg0x%x=0x%x\n", i, value);
+		i = i+3;
+	}
+
+	status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value);
+	cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value);
+	vid_blk_write_word(dev, AFE_CTRL_C2HH_SRC_CTRL, 0x4485D390);
+	status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value);
+	cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value);
+}
+
+void cx231xx_dump_SC_reg(struct cx231xx *dev)
+{
+	u8 value[4] = { 0, 0, 0, 0 };
+	int status = 0;
+	cx231xx_info("cx231xx_dump_SC_reg %s!\n", __TIME__);
+
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", BOARD_CFG_STAT, value[0],
+				 value[1], value[2], value[3]);
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS_MODE_REG,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS_MODE_REG, value[0],
+				 value[1], value[2], value[3]);
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_CFG_REG,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_CFG_REG, value[0],
+				 value[1], value[2], value[3]);
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_LENGTH_REG,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_LENGTH_REG, value[0],
+				 value[1], value[2], value[3]);
+
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_CFG_REG,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_CFG_REG, value[0],
+				 value[1], value[2], value[3]);
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_LENGTH_REG,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_LENGTH_REG, value[0],
+				 value[1], value[2], value[3]);
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", EP_MODE_SET, value[0],
+				 value[1], value[2], value[3]);
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN1,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN1, value[0],
+				 value[1], value[2], value[3]);
+
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN2,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN2, value[0],
+				 value[1], value[2], value[3]);
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN3,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN3, value[0],
+				 value[1], value[2], value[3]);
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK0,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK0, value[0],
+				 value[1], value[2], value[3]);
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK1,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK1, value[0],
+				 value[1], value[2], value[3]);
+
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK2,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK2, value[0],
+				 value[1], value[2], value[3]);
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_GAIN,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_GAIN, value[0],
+				 value[1], value[2], value[3]);
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_CAR_REG,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_CAR_REG, value[0],
+				 value[1], value[2], value[3]);
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG1,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG1, value[0],
+				 value[1], value[2], value[3]);
+
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG2,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG2, value[0],
+				 value[1], value[2], value[3]);
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN,
+				 value, 4);
+	cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN, value[0],
+				 value[1], value[2], value[3]);
+
+
+}
+
+void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev)
+
+{
+	u8 status = 0;
+	u8 value = 0;
+
+
+
+	status = afe_read_byte(dev, ADC_STATUS2_CH3, &value);
+	value = (value & 0xFE)|0x01;
+	status = afe_write_byte(dev, ADC_STATUS2_CH3, value);
+
+	status = afe_read_byte(dev, ADC_STATUS2_CH3, &value);
+	value = (value & 0xFE)|0x00;
+	status = afe_write_byte(dev, ADC_STATUS2_CH3, value);
+
+
+/*
+	config colibri to lo-if mode
+
+	FIXME: ntf_mode = 2'b00 by default. But set 0x1 would reduce
+		the diff IF input by half,
+
+		for low-if agc defect
+*/
+
+	status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, &value);
+	value = (value & 0xFC)|0x00;
+	status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, value);
+
+	status = afe_read_byte(dev, ADC_INPUT_CH3, &value);
+	value = (value & 0xF9)|0x02;
+	status = afe_write_byte(dev, ADC_INPUT_CH3, value);
+
+	status = afe_read_byte(dev, ADC_FB_FRCRST_CH3, &value);
+	value = (value & 0xFB)|0x04;
+	status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, value);
+
+	status = afe_read_byte(dev, ADC_DCSERVO_DEM_CH3, &value);
+	value = (value & 0xFC)|0x03;
+	status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, value);
+
+	status = afe_read_byte(dev, ADC_CTRL_DAC1_CH3, &value);
+	value = (value & 0xFB)|0x04;
+	status = afe_write_byte(dev, ADC_CTRL_DAC1_CH3, value);
+
+	status = afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value);
+	value = (value & 0xF8)|0x06;
+	status = afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value);
+
+	status = afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value);
+	value = (value & 0x8F)|0x40;
+	status = afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value);
+
+	status = afe_read_byte(dev, ADC_PWRDN_CLAMP_CH3, &value);
+	value = (value & 0xDF)|0x20;
+	status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, value);
+}
+
+void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq,
+		 u8 spectral_invert, u32 mode)
+{
+	u32 colibri_carrier_offset = 0;
+	u8 status = 0;
+	u32 func_mode = 0x01; /* Device has a DIF if this function is called */
+	u32 standard = 0;
+	u8 value[4] = { 0, 0, 0, 0 };
+
+	cx231xx_info("Enter cx231xx_set_Colibri_For_LowIF()\n");
+	value[0] = (u8) 0x6F;
+	value[1] = (u8) 0x6F;
+	value[2] = (u8) 0x6F;
+	value[3] = (u8) 0x6F;
+	status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+					PWR_CTL_EN, value, 4);
+
+	/*Set colibri for low IF*/
+	status = cx231xx_afe_set_mode(dev, AFE_MODE_LOW_IF);
+
+	/* Set C2HH for low IF operation.*/
+	standard = dev->norm;
+	status = cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode,
+						       func_mode, standard);
+
+	/* Get colibri offsets.*/
+	colibri_carrier_offset = cx231xx_Get_Colibri_CarrierOffset(mode,
+								   standard);
+
+	cx231xx_info("colibri_carrier_offset=%d, standard=0x%x\n",
+		     colibri_carrier_offset, standard);
+
+	/* Set the band Pass filter for DIF*/
+	cx231xx_set_DIF_bandpass(dev, (if_freq+colibri_carrier_offset),
+				 spectral_invert, mode);
+}
+
+u32 cx231xx_Get_Colibri_CarrierOffset(u32 mode, u32 standerd)
+{
+	u32 colibri_carrier_offset = 0;
+
+	if (mode == TUNER_MODE_FM_RADIO) {
+		colibri_carrier_offset = 1100000;
+	} else if (standerd & (V4L2_STD_MN | V4L2_STD_NTSC_M_JP)) {
+		colibri_carrier_offset = 4832000;  /*4.83MHz	*/
+	} else if (standerd & (V4L2_STD_PAL_B | V4L2_STD_PAL_G)) {
+		colibri_carrier_offset = 2700000;  /*2.70MHz       */
+	} else if (standerd & (V4L2_STD_PAL_D | V4L2_STD_PAL_I
+			| V4L2_STD_SECAM)) {
+		colibri_carrier_offset = 2100000;  /*2.10MHz	*/
+	}
+
+	return colibri_carrier_offset;
+}
+
+void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq,
+		 u8 spectral_invert, u32 mode)
+{
+	unsigned long pll_freq_word;
+	int status = 0;
+	u32 dif_misc_ctrl_value = 0;
+	u64 pll_freq_u64 = 0;
+	u32 i = 0;
+
+	cx231xx_info("if_freq=%d;spectral_invert=0x%x;mode=0x%x\n",
+			 if_freq, spectral_invert, mode);
+
+
+	if (mode == TUNER_MODE_FM_RADIO) {
+		pll_freq_word = 0x905A1CAC;
+		status = vid_blk_write_word(dev, DIF_PLL_FREQ_WORD,  pll_freq_word);
+
+	} else /*KSPROPERTY_TUNER_MODE_TV*/{
+		/* Calculate the PLL frequency word based on the adjusted if_freq*/
+		pll_freq_word = if_freq;
+		pll_freq_u64 = (u64)pll_freq_word << 28L;
+		do_div(pll_freq_u64, 50000000);
+		pll_freq_word = (u32)pll_freq_u64;
+		/*pll_freq_word = 0x3463497;*/
+		status = vid_blk_write_word(dev, DIF_PLL_FREQ_WORD,  pll_freq_word);
+
+	if (spectral_invert) {
+		if_freq -= 400000;
+		/* Enable Spectral Invert*/
+		status = vid_blk_read_word(dev, DIF_MISC_CTRL,
+					&dif_misc_ctrl_value);
+		dif_misc_ctrl_value = dif_misc_ctrl_value | 0x00200000;
+		status = vid_blk_write_word(dev, DIF_MISC_CTRL,
+					dif_misc_ctrl_value);
+	} else {
+		if_freq += 400000;
+		/* Disable Spectral Invert*/
+		status = vid_blk_read_word(dev, DIF_MISC_CTRL,
+					&dif_misc_ctrl_value);
+		dif_misc_ctrl_value = dif_misc_ctrl_value & 0xFFDFFFFF;
+		status = vid_blk_write_word(dev, DIF_MISC_CTRL,
+					dif_misc_ctrl_value);
+	}
+
+	if_freq = (if_freq/100000)*100000;
+
+	if (if_freq < 3000000)
+		if_freq = 3000000;
+
+	if (if_freq > 16000000)
+		if_freq = 16000000;
+	}
+
+	cx231xx_info("Enter IF=%zd\n",
+			sizeof(Dif_set_array)/sizeof(struct dif_settings));
+	for (i = 0; i < sizeof(Dif_set_array)/sizeof(struct dif_settings); i++) {
+		if (Dif_set_array[i].if_freq == if_freq) {
+			status = vid_blk_write_word(dev,
+			Dif_set_array[i].register_address, Dif_set_array[i].value);
+		}
+	}
+}
 
 /******************************************************************************
  *                 D I F - B L O C K    C O N T R O L   functions             *
@@ -1132,6 +1622,7 @@
 {
 	int status = 0;
 
+
 	if (mode == V4L2_TUNER_RADIO) {
 		/* C2HH */
 		/* lo if big signal */
@@ -1174,6 +1665,7 @@
 					VID_BLK_I2C_ADDRESS, 32,
 					AUD_IO_CTRL, 0, 31, 0x00000003);
 		} else if ((standard == V4L2_STD_PAL_I) |
+			(standard & V4L2_STD_PAL_D) |
 			(standard & V4L2_STD_SECAM)) {
 			/* C2HH setup */
 			/* lo if big signal */
@@ -1232,10 +1724,18 @@
 		dev->norm = standard;
 
 	switch (dev->model) {
+	case CX231XX_BOARD_CNXT_CARRAERA:
 	case CX231XX_BOARD_CNXT_RDE_250:
+	case CX231XX_BOARD_CNXT_SHELBY:
 	case CX231XX_BOARD_CNXT_RDU_250:
+	case CX231XX_BOARD_CNXT_VIDEO_GRABBER:
+	case CX231XX_BOARD_HAUPPAUGE_EXETER:
 		func_mode = 0x03;
 		break;
+	case CX231XX_BOARD_CNXT_RDE_253S:
+	case CX231XX_BOARD_CNXT_RDU_253S:
+		func_mode = 0x01;
+		break;
 	default:
 		func_mode = 0x01;
 	}
@@ -1617,17 +2117,27 @@
 {
 	int status = 0;
 	u32 dwval;
-
+	cx231xx_info("cx231xx_tuner_post_channel_change  dev->tuner_type =0%d\n",
+		     dev->tuner_type);
 	/* Set the RF and IF k_agc values to 4 for PAL/NTSC and 8 for
 	 * SECAM L/B/D standards */
 	status = vid_blk_read_word(dev, DIF_AGC_IF_REF, &dwval);
 	dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF);
 
 	if (dev->norm & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_B |
-			 V4L2_STD_SECAM_D))
-		dwval |= 0x88000000;
-	else
-		dwval |= 0x44000000;
+			 V4L2_STD_SECAM_D)) {
+			if (dev->tuner_type == TUNER_NXP_TDA18271) {
+				dwval &= ~FLD_DIF_IF_REF;
+				dwval |= 0x88000300;
+			} else
+				dwval |= 0x88000000;
+		} else {
+			if (dev->tuner_type == TUNER_NXP_TDA18271) {
+				dwval &= ~FLD_DIF_IF_REF;
+				dwval |= 0xCC000300;
+			} else
+				dwval |= 0x44000000;
+		}
 
 	status = vid_blk_write_word(dev, DIF_AGC_IF_REF, dwval);
 
@@ -1714,8 +2224,6 @@
 		return 0;
 	}
 
-	cx231xx_info(" setPowerMode::mode = %d\n", mode);
-
 	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value,
 				       4);
 	if (status < 0)
@@ -1761,7 +2269,7 @@
 
 	case POLARIS_AVMODE_ANALOGT_TV:
 
-		tmp &= (~PWR_DEMOD_EN);
+		tmp |= PWR_DEMOD_EN;
 		tmp |= (I2C_DEMOD_EN);
 		value[0] = (u8) tmp;
 		value[1] = (u8) (tmp >> 8);
@@ -1814,14 +2322,18 @@
 			msleep(PWR_SLEEP_INTERVAL);
 		}
 
-		if ((dev->model == CX231XX_BOARD_CNXT_RDE_250) ||
-		    (dev->model == CX231XX_BOARD_CNXT_RDU_250)) {
-			/* tuner path to channel 1 from port 3 */
-			cx231xx_enable_i2c_for_tuner(dev, I2C_3);
+		if (dev->board.tuner_type != TUNER_ABSENT) {
+			/* Enable tuner */
+			cx231xx_enable_i2c_port_3(dev, true);
+
+			/* reset the Tuner */
+			if (dev->board.tuner_gpio)
+				cx231xx_gpio_set(dev, dev->board.tuner_gpio);
 
 			if (dev->cx231xx_reset_analog_tuner)
 				dev->cx231xx_reset_analog_tuner(dev);
 		}
+
 		break;
 
 	case POLARIS_AVMODE_DIGITAL:
@@ -1856,6 +2368,7 @@
 			msleep(PWR_SLEEP_INTERVAL);
 		}
 
+		tmp &= (~PWR_AV_MODE);
 		tmp |= POLARIS_AVMODE_DIGITAL | I2C_DEMOD_EN;
 		value[0] = (u8) tmp;
 		value[1] = (u8) (tmp >> 8);
@@ -1876,10 +2389,19 @@
 			msleep(PWR_SLEEP_INTERVAL);
 		}
 
-		if ((dev->model == CX231XX_BOARD_CNXT_RDE_250) ||
-		    (dev->model == CX231XX_BOARD_CNXT_RDU_250)) {
-			/* tuner path to channel 1 from port 3 */
-			cx231xx_enable_i2c_for_tuner(dev, I2C_3);
+		if (dev->board.tuner_type != TUNER_ABSENT) {
+			/*
+			 * Enable tuner
+			 *	Hauppauge Exeter seems to need to do something different!
+			 */
+			if (dev->model == CX231XX_BOARD_HAUPPAUGE_EXETER)
+				cx231xx_enable_i2c_port_3(dev, false);
+			else
+				cx231xx_enable_i2c_port_3(dev, true);
+
+			/* reset the Tuner */
+			if (dev->board.tuner_gpio)
+				cx231xx_gpio_set(dev, dev->board.tuner_gpio);
 
 			if (dev->cx231xx_reset_analog_tuner)
 				dev->cx231xx_reset_analog_tuner(dev);
@@ -1913,9 +2435,6 @@
 
 	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value,
 				       4);
-	cx231xx_info(" The data of PWR_CTL_EN register 0x74"
-				 "=0x%0x,0x%0x,0x%0x,0x%0x\n",
-		     value[0], value[1], value[2], value[3]);
 
 	return status;
 }
@@ -2000,6 +2519,8 @@
 int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type)
 {
 	int status = 0;
+	u32 value = 0;
+	u8 val[4] = { 0, 0, 0, 0 };
 
 	if (dev->udev->speed == USB_SPEED_HIGH) {
 		switch (media_type) {
@@ -2026,10 +2547,36 @@
 			break;
 
 		case 4:	/* ts1 */
-			cx231xx_info("%s: set ts1 registers\n", __func__);
+			cx231xx_info("%s: set ts1 registers", __func__);
+
+		if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER) {
+			cx231xx_info(" MPEG\n");
+			value &= 0xFFFFFFFC;
+			value |= 0x3;
+
+			status = cx231xx_mode_register(dev, TS_MODE_REG, value);
+
+			val[0] = 0x04;
+			val[1] = 0xA3;
+			val[2] = 0x3B;
+			val[3] = 0x00;
+			status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+				 TS1_CFG_REG, val, 4);
+
+			val[0] = 0x00;
+			val[1] = 0x08;
+			val[2] = 0x00;
+			val[3] = 0x08;
+			status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+				 TS1_LENGTH_REG, val, 4);
+
+		} else {
+			cx231xx_info(" BDA\n");
 			status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101);
-			status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x400);
+			status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x010);
+		}
 			break;
+
 		case 6:	/* ts1 parallel mode */
 			cx231xx_info("%s: set ts1 parrallel mode registers\n",
 				     __func__);
@@ -2128,7 +2675,7 @@
 /*****************************************************************************
 *                   G P I O   B I T control functions                        *
 ******************************************************************************/
-int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val)
+int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val)
 {
 	int status = 0;
 
@@ -2137,7 +2684,7 @@
 	return status;
 }
 
-int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val)
+int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val)
 {
 	int status = 0;
 
@@ -2344,7 +2891,7 @@
 	return status;
 }
 
-int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 * buf)
+int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf)
 {
 	u8 value = 0;
 	int status = 0;
@@ -2494,7 +3041,7 @@
 /* cx231xx_gpio_i2c_read
  * Function to read data from gpio based I2C interface
  */
-int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 * buf, u8 len)
+int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len)
 {
 	int status = 0;
 	int i = 0;
@@ -2538,7 +3085,7 @@
 /* cx231xx_gpio_i2c_write
  * Function to write data to gpio based I2C interface
  */
-int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 * buf, u8 len)
+int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len)
 {
 	int status = 0;
 	int i = 0;
diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c
index f2a4900..56c2d81 100644
--- a/drivers/media/video/cx231xx/cx231xx-cards.c
+++ b/drivers/media/video/cx231xx/cx231xx-cards.c
@@ -41,6 +41,10 @@
 module_param(tuner, int, 0444);
 MODULE_PARM_DESC(tuner, "tuner type");
 
+static int transfer_mode = 1;
+module_param(transfer_mode, int, 0444);
+MODULE_PARM_DESC(transfer_mode, "transfer mode (1-ISO or 0-BULK)");
+
 static unsigned int disable_ir;
 module_param(disable_ir, int, 0444);
 MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
@@ -86,8 +90,8 @@
 			}
 		},
 	},
-	[CX231XX_BOARD_CNXT_RDE_250] = {
-		.name = "Conexant Hybrid TV - RDE250",
+	[CX231XX_BOARD_CNXT_CARRAERA] = {
+		.name = "Conexant Hybrid TV - CARRAERA",
 		.tuner_type = TUNER_XC5000,
 		.tuner_addr = 0x61,
 		.tuner_gpio = RDE250_XCV_TUNER,
@@ -95,6 +99,7 @@
 		.tuner_scl_gpio = 0x1a,
 		.tuner_sda_gpio = 0x1b,
 		.decoder = CX231XX_AVDECODER,
+		.output_mode = OUT_MODE_VIP11,
 		.demod_xfer_mode = 0,
 		.ctl_pin_status_mask = 0xFFFFFFC4,
 		.agc_analog_digital_select_gpio = 0x0c,
@@ -125,9 +130,8 @@
 			}
 		},
 	},
-
-	[CX231XX_BOARD_CNXT_RDU_250] = {
-		.name = "Conexant Hybrid TV - RDU250",
+	[CX231XX_BOARD_CNXT_SHELBY] = {
+		.name = "Conexant Hybrid TV - SHELBY",
 		.tuner_type = TUNER_XC5000,
 		.tuner_addr = 0x61,
 		.tuner_gpio = RDE250_XCV_TUNER,
@@ -135,6 +139,7 @@
 		.tuner_scl_gpio = 0x1a,
 		.tuner_sda_gpio = 0x1b,
 		.decoder = CX231XX_AVDECODER,
+		.output_mode = OUT_MODE_VIP11,
 		.demod_xfer_mode = 0,
 		.ctl_pin_status_mask = 0xFFFFFFC4,
 		.agc_analog_digital_select_gpio = 0x0c,
@@ -165,6 +170,231 @@
 			}
 		},
 	},
+	[CX231XX_BOARD_CNXT_RDE_253S] = {
+		.name = "Conexant Hybrid TV - RDE253S",
+		.tuner_type = TUNER_NXP_TDA18271,
+		.tuner_addr = 0x60,
+		.tuner_gpio = RDE250_XCV_TUNER,
+		.tuner_sif_gpio = 0x05,
+		.tuner_scl_gpio = 0x1a,
+		.tuner_sda_gpio = 0x1b,
+		.decoder = CX231XX_AVDECODER,
+		.output_mode = OUT_MODE_VIP11,
+		.demod_xfer_mode = 0,
+		.ctl_pin_status_mask = 0xFFFFFFC4,
+		.agc_analog_digital_select_gpio = 0x1c,
+		.gpio_pin_status_mask = 0x4001000,
+		.tuner_i2c_master = 1,
+		.demod_i2c_master = 2,
+		.has_dvb = 1,
+		.demod_addr = 0x02,
+		.norm = V4L2_STD_PAL,
+
+		.input = {{
+				.type = CX231XX_VMUX_TELEVISION,
+				.vmux = CX231XX_VIN_3_1,
+				.amux = CX231XX_AMUX_VIDEO,
+				.gpio = NULL,
+			}, {
+				.type = CX231XX_VMUX_COMPOSITE1,
+				.vmux = CX231XX_VIN_2_1,
+				.amux = CX231XX_AMUX_LINE_IN,
+				.gpio = NULL,
+			}, {
+				.type = CX231XX_VMUX_SVIDEO,
+				.vmux = CX231XX_VIN_1_1 |
+					(CX231XX_VIN_1_2 << 8) |
+					CX25840_SVIDEO_ON,
+				.amux = CX231XX_AMUX_LINE_IN,
+				.gpio = NULL,
+			}
+		},
+	},
+
+	[CX231XX_BOARD_CNXT_RDU_253S] = {
+		.name = "Conexant Hybrid TV - RDU253S",
+		.tuner_type = TUNER_NXP_TDA18271,
+		.tuner_addr = 0x60,
+		.tuner_gpio = RDE250_XCV_TUNER,
+		.tuner_sif_gpio = 0x05,
+		.tuner_scl_gpio = 0x1a,
+		.tuner_sda_gpio = 0x1b,
+		.decoder = CX231XX_AVDECODER,
+		.output_mode = OUT_MODE_VIP11,
+		.demod_xfer_mode = 0,
+		.ctl_pin_status_mask = 0xFFFFFFC4,
+		.agc_analog_digital_select_gpio = 0x1c,
+		.gpio_pin_status_mask = 0x4001000,
+		.tuner_i2c_master = 1,
+		.demod_i2c_master = 2,
+		.has_dvb = 1,
+		.demod_addr = 0x02,
+		.norm = V4L2_STD_PAL,
+
+		.input = {{
+				.type = CX231XX_VMUX_TELEVISION,
+				.vmux = CX231XX_VIN_3_1,
+				.amux = CX231XX_AMUX_VIDEO,
+				.gpio = NULL,
+			}, {
+				.type = CX231XX_VMUX_COMPOSITE1,
+				.vmux = CX231XX_VIN_2_1,
+				.amux = CX231XX_AMUX_LINE_IN,
+				.gpio = NULL,
+			}, {
+				.type = CX231XX_VMUX_SVIDEO,
+				.vmux = CX231XX_VIN_1_1 |
+					(CX231XX_VIN_1_2 << 8) |
+					CX25840_SVIDEO_ON,
+				.amux = CX231XX_AMUX_LINE_IN,
+				.gpio = NULL,
+			}
+		},
+	},
+	[CX231XX_BOARD_CNXT_VIDEO_GRABBER] = {
+		.name = "Conexant VIDEO GRABBER",
+		.tuner_type = TUNER_ABSENT,
+		.decoder = CX231XX_AVDECODER,
+		.output_mode = OUT_MODE_VIP11,
+		.ctl_pin_status_mask = 0xFFFFFFC4,
+		.agc_analog_digital_select_gpio = 0x1c,
+		.gpio_pin_status_mask = 0x4001000,
+		.norm = V4L2_STD_PAL,
+
+		.input = {{
+				.type = CX231XX_VMUX_COMPOSITE1,
+				.vmux = CX231XX_VIN_2_1,
+				.amux = CX231XX_AMUX_LINE_IN,
+				.gpio = NULL,
+			}, {
+				.type = CX231XX_VMUX_SVIDEO,
+				.vmux = CX231XX_VIN_1_1 |
+					(CX231XX_VIN_1_2 << 8) |
+					CX25840_SVIDEO_ON,
+				.amux = CX231XX_AMUX_LINE_IN,
+				.gpio = NULL,
+			}
+		},
+	},
+	[CX231XX_BOARD_CNXT_RDE_250] = {
+		.name = "Conexant Hybrid TV - rde 250",
+		.tuner_type = TUNER_XC5000,
+		.tuner_addr = 0x61,
+		.tuner_gpio = RDE250_XCV_TUNER,
+		.tuner_sif_gpio = 0x05,
+		.tuner_scl_gpio = 0x1a,
+		.tuner_sda_gpio = 0x1b,
+		.decoder = CX231XX_AVDECODER,
+		.output_mode = OUT_MODE_VIP11,
+		.demod_xfer_mode = 0,
+		.ctl_pin_status_mask = 0xFFFFFFC4,
+		.agc_analog_digital_select_gpio = 0x0c,
+		.gpio_pin_status_mask = 0x4001000,
+		.tuner_i2c_master = 1,
+		.demod_i2c_master = 2,
+		.has_dvb = 1,
+		.demod_addr = 0x02,
+		.norm = V4L2_STD_PAL,
+
+		.input = {{
+				.type = CX231XX_VMUX_TELEVISION,
+				.vmux = CX231XX_VIN_2_1,
+				.amux = CX231XX_AMUX_VIDEO,
+				.gpio = NULL,
+			}
+		},
+	},
+	[CX231XX_BOARD_CNXT_RDU_250] = {
+		.name = "Conexant Hybrid TV - RDU 250",
+		.tuner_type = TUNER_XC5000,
+		.tuner_addr = 0x61,
+		.tuner_gpio = RDE250_XCV_TUNER,
+		.tuner_sif_gpio = 0x05,
+		.tuner_scl_gpio = 0x1a,
+		.tuner_sda_gpio = 0x1b,
+		.decoder = CX231XX_AVDECODER,
+		.output_mode = OUT_MODE_VIP11,
+		.demod_xfer_mode = 0,
+		.ctl_pin_status_mask = 0xFFFFFFC4,
+		.agc_analog_digital_select_gpio = 0x0c,
+		.gpio_pin_status_mask = 0x4001000,
+		.tuner_i2c_master = 1,
+		.demod_i2c_master = 2,
+		.has_dvb = 1,
+		.demod_addr = 0x32,
+		.norm = V4L2_STD_NTSC,
+
+		.input = {{
+				.type = CX231XX_VMUX_TELEVISION,
+				.vmux = CX231XX_VIN_2_1,
+				.amux = CX231XX_AMUX_VIDEO,
+				.gpio = NULL,
+			}
+		},
+	},
+	[CX231XX_BOARD_HAUPPAUGE_EXETER] = {
+		.name = "Hauppauge EXETER",
+		.tuner_type = TUNER_NXP_TDA18271,
+		.tuner_addr = 0x60,
+		.tuner_gpio = RDE250_XCV_TUNER,
+		.tuner_sif_gpio = 0x05,
+		.tuner_scl_gpio = 0x1a,
+		.tuner_sda_gpio = 0x1b,
+		.decoder = CX231XX_AVDECODER,
+		.output_mode = OUT_MODE_VIP11,
+		.demod_xfer_mode = 0,
+		.ctl_pin_status_mask = 0xFFFFFFC4,
+		.agc_analog_digital_select_gpio = 0x0c,
+		.gpio_pin_status_mask = 0x4001000,
+		.tuner_i2c_master = 1,
+		.demod_i2c_master = 2,
+		.has_dvb = 1,
+		.demod_addr = 0x0e,
+		.norm = V4L2_STD_NTSC,
+
+		.input = {{
+			.type = CX231XX_VMUX_TELEVISION,
+			.vmux = CX231XX_VIN_3_1,
+			.amux = CX231XX_AMUX_VIDEO,
+			.gpio = 0,
+		}, {
+			.type = CX231XX_VMUX_COMPOSITE1,
+			.vmux = CX231XX_VIN_2_1,
+			.amux = CX231XX_AMUX_LINE_IN,
+			.gpio = 0,
+		}, {
+			.type = CX231XX_VMUX_SVIDEO,
+			.vmux = CX231XX_VIN_1_1 |
+				(CX231XX_VIN_1_2 << 8) |
+				CX25840_SVIDEO_ON,
+			.amux = CX231XX_AMUX_LINE_IN,
+			.gpio = 0,
+		} },
+	},
+	[CX231XX_BOARD_HAUPPAUGE_USBLIVE2] = {
+		.name = "Hauppauge USB Live 2",
+		.tuner_type = TUNER_ABSENT,
+		.decoder = CX231XX_AVDECODER,
+		.output_mode = OUT_MODE_VIP11,
+		.demod_xfer_mode = 0,
+		.ctl_pin_status_mask = 0xFFFFFFC4,
+		.agc_analog_digital_select_gpio = 0x0c,
+		.gpio_pin_status_mask = 0x4001000,
+		.norm = V4L2_STD_NTSC,
+		.input = {{
+			.type = CX231XX_VMUX_COMPOSITE1,
+			.vmux = CX231XX_VIN_2_1,
+			.amux = CX231XX_AMUX_LINE_IN,
+			.gpio = 0,
+		}, {
+			.type = CX231XX_VMUX_SVIDEO,
+			.vmux = CX231XX_VIN_1_1 |
+				(CX231XX_VIN_1_2 << 8) |
+				CX25840_SVIDEO_ON,
+			.amux = CX231XX_AMUX_LINE_IN,
+			.gpio = 0,
+		} },
+	},
 };
 const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
 
@@ -172,12 +402,28 @@
 struct usb_device_id cx231xx_id_table[] = {
 	{USB_DEVICE(0x0572, 0x5A3C),
 	 .driver_info = CX231XX_BOARD_UNKNOWN},
-	{USB_DEVICE(0x0572, 0x58A2),
-	 .driver_info = CX231XX_BOARD_CNXT_RDE_250},
-	{USB_DEVICE(0x0572, 0x58A1),
-	 .driver_info = CX231XX_BOARD_CNXT_RDU_250},
 	{USB_DEVICE_VER(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD, 0x4000,0x4fff),
 	 .driver_info = CX231XX_BOARD_UNKNOWN},
+	{USB_DEVICE(0x0572, 0x58A2),
+	 .driver_info = CX231XX_BOARD_CNXT_CARRAERA},
+	{USB_DEVICE(0x0572, 0x58A1),
+	 .driver_info = CX231XX_BOARD_CNXT_SHELBY},
+	{USB_DEVICE(0x0572, 0x58A4),
+	 .driver_info = CX231XX_BOARD_CNXT_RDE_253S},
+	{USB_DEVICE(0x0572, 0x58A5),
+	 .driver_info = CX231XX_BOARD_CNXT_RDU_253S},
+	{USB_DEVICE(0x0572, 0x58A6),
+	 .driver_info = CX231XX_BOARD_CNXT_VIDEO_GRABBER},
+	{USB_DEVICE(0x0572, 0x589E),
+	 .driver_info = CX231XX_BOARD_CNXT_RDE_250},
+	{USB_DEVICE(0x0572, 0x58A0),
+	 .driver_info = CX231XX_BOARD_CNXT_RDU_250},
+	{USB_DEVICE(0x2040, 0xb120),
+	 .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER},
+	{USB_DEVICE(0x2040, 0xb140),
+	 .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER},
+	{USB_DEVICE(0x2040, 0xc200),
+	 .driver_info = CX231XX_BOARD_HAUPPAUGE_USBLIVE2},
 	{},
 };
 
@@ -212,6 +458,23 @@
 }
 EXPORT_SYMBOL_GPL(cx231xx_tuner_callback);
 
+void cx231xx_reset_out(struct cx231xx *dev)
+{
+	cx231xx_set_gpio_value(dev, CX23417_RESET, 1);
+	msleep(200);
+	cx231xx_set_gpio_value(dev, CX23417_RESET, 0);
+	msleep(200);
+	cx231xx_set_gpio_value(dev, CX23417_RESET, 1);
+}
+void cx231xx_enable_OSC(struct cx231xx *dev)
+{
+	cx231xx_set_gpio_value(dev, CX23417_OSC_EN, 1);
+}
+void cx231xx_sleep_s5h1432(struct cx231xx *dev)
+{
+	cx231xx_set_gpio_value(dev, SLEEP_S5H1432, 0);
+}
+
 static inline void cx231xx_set_model(struct cx231xx *dev)
 {
 	memcpy(&dev->board, &cx231xx_boards[dev->model], sizeof(dev->board));
@@ -232,13 +495,11 @@
 	if (dev->board.tuner_gpio) {
 		cx231xx_set_gpio_direction(dev, dev->board.tuner_gpio->bit, 1);
 		cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, 1);
+	}
+	if (dev->board.tuner_sif_gpio >= 0)
 		cx231xx_set_gpio_direction(dev, dev->board.tuner_sif_gpio, 1);
 
-		/* request some modules if any required */
-
-		/* reset the Tuner */
-		cx231xx_gpio_set(dev, dev->board.tuner_gpio);
-	}
+	/* request some modules if any required */
 
 	/* set the mode to Analog mode initially */
 	cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
@@ -286,26 +547,6 @@
 
 }
 
-/* ----------------------------------------------------------------------- */
-void cx231xx_register_i2c_ir(struct cx231xx *dev)
-{
-	if (disable_ir)
-		return;
-
-	/* REVISIT: instantiate IR device */
-
-	/* detect & configure */
-	switch (dev->model) {
-
-	case CX231XX_BOARD_CNXT_RDE_250:
-		break;
-	case CX231XX_BOARD_CNXT_RDU_250:
-		break;
-	default:
-		break;
-	}
-}
-
 void cx231xx_card_setup(struct cx231xx *dev)
 {
 
@@ -319,29 +560,24 @@
 	if (dev->board.decoder == CX231XX_AVDECODER) {
 		dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
 					&dev->i2c_bus[0].i2c_adap,
-					"cx25840", "cx25840", 0x88 >> 1, NULL);
+					NULL, "cx25840", 0x88 >> 1, NULL);
 		if (dev->sd_cx25840 == NULL)
 			cx231xx_info("cx25840 subdev registration failure\n");
 		cx25840_call(dev, core, load_fw);
 
 	}
 
+	/* Initialize the tuner */
 	if (dev->board.tuner_type != TUNER_ABSENT) {
-		dev->sd_tuner =	v4l2_i2c_new_subdev(&dev->v4l2_dev,
-				&dev->i2c_bus[1].i2c_adap,
-				"tuner", "tuner", 0xc2 >> 1, NULL);
+		dev->sd_tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+						    &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+						    NULL, "tuner",
+						    dev->tuner_addr, NULL);
 		if (dev->sd_tuner == NULL)
 			cx231xx_info("tuner subdev registration failure\n");
-
-		cx231xx_config_tuner(dev);
+		else
+			cx231xx_config_tuner(dev);
 	}
-
-	cx231xx_config_tuner(dev);
-
-#if 0
-	/* TBD  IR will be added later */
-	cx231xx_ir_init(dev);
-#endif
 }
 
 /*
@@ -375,12 +611,6 @@
 */
 void cx231xx_release_resources(struct cx231xx *dev)
 {
-
-#if 0		/* TBD IR related  */
-	if (dev->ir)
-		cx231xx_ir_fini(dev);
-#endif
-
 	cx231xx_release_analog_resources(dev);
 
 	cx231xx_remove_from_devlist(dev);
@@ -409,6 +639,7 @@
 	mutex_init(&dev->lock);
 	mutex_init(&dev->ctrl_urb_lock);
 	mutex_init(&dev->gpio_i2c_lock);
+	mutex_init(&dev->i2c_lock);
 
 	spin_lock_init(&dev->video_mode.slock);
 	spin_lock_init(&dev->vbi_mode.slock);
@@ -427,6 +658,13 @@
 	/* Query cx231xx to find what pcb config it is related to */
 	initialize_cx231xx(dev);
 
+	/*To workaround error number=-71 on EP0 for VideoGrabber,
+		 need set alt here.*/
+	if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER ||
+	    dev->model == CX231XX_BOARD_HAUPPAUGE_USBLIVE2) {
+		cx231xx_set_alt_setting(dev, INDEX_VIDEO, 3);
+		cx231xx_set_alt_setting(dev, INDEX_VANC, 1);
+	}
 	/* Cx231xx pre card setup */
 	cx231xx_pre_card_setup(dev);
 
@@ -442,6 +680,7 @@
 	/* register i2c bus */
 	errCode = cx231xx_dev_init(dev);
 	if (errCode < 0) {
+		cx231xx_dev_uninit(dev);
 		cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n",
 			       __func__, errCode);
 		return errCode;
@@ -460,8 +699,6 @@
 	dev->width = maxw;
 	dev->height = maxh;
 	dev->interlaced = 0;
-	dev->hscale = 0;
-	dev->vscale = 0;
 	dev->video_input = 0;
 
 	errCode = cx231xx_config(dev);
@@ -480,9 +717,17 @@
 	INIT_LIST_HEAD(&dev->vbi_mode.vidq.queued);
 
 	/* Reset other chips required if they are tied up with GPIO pins */
-
 	cx231xx_add_into_devlist(dev);
 
+	if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER) {
+		printk(KERN_INFO "attach 417 %d\n", dev->model);
+		if (cx231xx_417_register(dev) < 0) {
+			printk(KERN_ERR
+				"%s() Failed to register 417 on VID_B\n",
+			       __func__);
+		}
+	}
+
 	retval = cx231xx_register_analog_devices(dev);
 	if (retval < 0) {
 		cx231xx_release_resources(dev);
@@ -537,13 +782,12 @@
 	char *speed;
 	char descr[255] = "";
 	struct usb_interface *lif = NULL;
-	int skip_interface = 0;
 	struct usb_interface_assoc_descriptor *assoc_desc;
 
 	udev = usb_get_dev(interface_to_usbdev(interface));
 	ifnum = interface->altsetting[0].desc.bInterfaceNumber;
 
-	if (!ifnum) {
+	if (ifnum == 1) {
 		/*
 		 * Interface number 0 - IR interface
 		 */
@@ -552,8 +796,8 @@
 		cx231xx_devused |= 1 << nr;
 
 		if (nr >= CX231XX_MAXBOARDS) {
-			cx231xx_err(DRIVER_NAME ": Supports only %i cx231xx boards.\n",
-				     CX231XX_MAXBOARDS);
+			cx231xx_err(DRIVER_NAME
+		 ": Supports only %i cx231xx boards.\n", CX231XX_MAXBOARDS);
 			cx231xx_devused &= ~(1 << nr);
 			return -ENOMEM;
 		}
@@ -578,6 +822,7 @@
 		dev->xc_fw_load_done = 0;
 		dev->has_alsa_audio = 1;
 		dev->power_mode = -1;
+		atomic_set(&dev->devlist_count, 0);
 
 		/* 0 - vbi ; 1 -sliced cc mode */
 		dev->vbi_or_sliced_cc_mode = 0;
@@ -591,6 +836,11 @@
 		/* store the current interface */
 		lif = interface;
 
+		/*mode_tv: digital=1 or analog=0*/
+		dev->mode_tv = 0;
+
+		dev->USE_ISO = transfer_mode;
+
 		switch (udev->speed) {
 		case USB_SPEED_LOW:
 			speed = "1.5";
@@ -624,13 +874,6 @@
 		     le16_to_cpu(udev->descriptor.idVendor),
 		     le16_to_cpu(udev->descriptor.idProduct),
 		     dev->max_iad_interface_count);
-	} else {
-		/* Get dev structure first */
-		dev = usb_get_intfdata(udev->actconfig->interface[0]);
-		if (dev == NULL) {
-			cx231xx_err(DRIVER_NAME ": out of first interface!\n");
-			return -ENODEV;
-		}
 
 		/* store the interface 0 back */
 		lif = udev->actconfig->interface[0];
@@ -641,35 +884,21 @@
 		/* get device number */
 		nr = dev->devno;
 
-		/*
-		 * set skip interface, for all interfaces but
-		 * interface 1 and the last one
-		 */
-		if ((ifnum != 1) && ((dev->interface_count - 1)
-				     != dev->max_iad_interface_count))
-			skip_interface = 1;
-
-		if (ifnum == 1) {
-			assoc_desc = udev->actconfig->intf_assoc[0];
-			if (assoc_desc->bFirstInterface != ifnum) {
-				cx231xx_err(DRIVER_NAME ": Not found "
-					    "matching IAD interface\n");
-				return -ENODEV;
-			}
+		assoc_desc = udev->actconfig->intf_assoc[0];
+		if (assoc_desc->bFirstInterface != ifnum) {
+			cx231xx_err(DRIVER_NAME ": Not found "
+				    "matching IAD interface\n");
+			return -ENODEV;
 		}
-	}
-
-	if (skip_interface)
+	} else {
 		return -ENODEV;
+	}
 
 	cx231xx_info("registering interface %d\n", ifnum);
 
 	/* save our data pointer in this interface device */
 	usb_set_intfdata(lif, dev);
 
-	if ((dev->interface_count - 1) != dev->max_iad_interface_count)
-		return 0;
-
 	/*
 	 * AV device initialization - only done at the last interface
 	 */
@@ -680,15 +909,18 @@
 		cx231xx_errdev("v4l2_device_register failed\n");
 		cx231xx_devused &= ~(1 << nr);
 		kfree(dev);
+		dev = NULL;
 		return -EIO;
 	}
-
 	/* allocate device struct */
 	retval = cx231xx_init_dev(&dev, udev, nr);
 	if (retval) {
 		cx231xx_devused &= ~(1 << dev->devno);
 		v4l2_device_unregister(&dev->v4l2_dev);
 		kfree(dev);
+		dev = NULL;
+		usb_set_intfdata(lif, NULL);
+
 		return retval;
 	}
 
@@ -711,6 +943,7 @@
 		cx231xx_devused &= ~(1 << nr);
 		v4l2_device_unregister(&dev->v4l2_dev);
 		kfree(dev);
+		dev = NULL;
 		return -ENOMEM;
 	}
 
@@ -744,6 +977,7 @@
 		cx231xx_devused &= ~(1 << nr);
 		v4l2_device_unregister(&dev->v4l2_dev);
 		kfree(dev);
+		dev = NULL;
 		return -ENOMEM;
 	}
 
@@ -778,6 +1012,7 @@
 		cx231xx_devused &= ~(1 << nr);
 		v4l2_device_unregister(&dev->v4l2_dev);
 		kfree(dev);
+		dev = NULL;
 		return -ENOMEM;
 	}
 
@@ -813,6 +1048,7 @@
 			cx231xx_devused &= ~(1 << nr);
 			v4l2_device_unregister(&dev->v4l2_dev);
 			kfree(dev);
+			dev = NULL;
 			return -ENOMEM;
 		}
 
@@ -827,6 +1063,15 @@
 		}
 	}
 
+	if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER) {
+		cx231xx_enable_OSC(dev);
+		cx231xx_reset_out(dev);
+		cx231xx_set_alt_setting(dev, INDEX_VIDEO, 3);
+	}
+
+	if (dev->model == CX231XX_BOARD_CNXT_RDE_253S)
+		cx231xx_sleep_s5h1432(dev);
+
 	/* load other modules required */
 	request_modules(dev);
 
@@ -867,7 +1112,10 @@
 		     video_device_node_name(dev->vdev));
 
 		dev->state |= DEV_MISCONFIGURED;
-		cx231xx_uninit_isoc(dev);
+		if (dev->USE_ISO)
+			cx231xx_uninit_isoc(dev);
+		else
+			cx231xx_uninit_bulk(dev);
 		dev->state |= DEV_DISCONNECTED;
 		wake_up_interruptible(&dev->wait_frame);
 		wake_up_interruptible(&dev->wait_stream);
@@ -886,6 +1134,7 @@
 		kfree(dev->sliced_cc_mode.alt_max_pkt_size);
 		kfree(dev->ts1_mode.alt_max_pkt_size);
 		kfree(dev);
+		dev = NULL;
 	}
 }
 
diff --git a/drivers/media/video/cx231xx/cx231xx-conf-reg.h b/drivers/media/video/cx231xx/cx231xx-conf-reg.h
index 31a8759..25593f2 100644
--- a/drivers/media/video/cx231xx/cx231xx-conf-reg.h
+++ b/drivers/media/video/cx231xx/cx231xx-conf-reg.h
@@ -39,6 +39,7 @@
 #define CIR_CAR_REG             0x38
 #define CIR_OT_CFG1             0x40
 #define CIR_OT_CFG2             0x44
+#define GBULK_BIT_EN            0x68
 #define PWR_CTL_EN              0x74
 
 /* Polaris Endpoints capture mask for register EP_MODE_SET */
diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c
index 912a4d7..4af46fc 100644
--- a/drivers/media/video/cx231xx/cx231xx-core.c
+++ b/drivers/media/video/cx231xx/cx231xx-core.c
@@ -27,6 +27,7 @@
 #include <linux/usb.h>
 #include <linux/vmalloc.h>
 #include <media/v4l2-common.h>
+#include <media/tuner.h>
 
 #include "cx231xx.h"
 #include "cx231xx-reg.h"
@@ -46,11 +47,6 @@
 module_param(reg_debug, int, 0644);
 MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]");
 
-#define cx231xx_regdbg(fmt, arg...) do {\
-	if (reg_debug) \
-		printk(KERN_INFO "%s %s :"fmt, \
-			 dev->name, __func__ , ##arg); } while (0)
-
 static int alt = CX231XX_PINOUT;
 module_param(alt, int, 0644);
 MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
@@ -64,7 +60,7 @@
 *             Device control list functions     				 *
 ******************************************************************/
 
-static LIST_HEAD(cx231xx_devlist);
+LIST_HEAD(cx231xx_devlist);
 static DEFINE_MUTEX(cx231xx_devlist_mutex);
 
 /*
@@ -74,33 +70,39 @@
 */
 void cx231xx_remove_from_devlist(struct cx231xx *dev)
 {
-	mutex_lock(&cx231xx_devlist_mutex);
-	list_del(&dev->devlist);
-	mutex_unlock(&cx231xx_devlist_mutex);
+	if (dev == NULL)
+		return;
+	if (dev->udev == NULL)
+		return;
+
+	if (atomic_read(&dev->devlist_count) > 0) {
+		mutex_lock(&cx231xx_devlist_mutex);
+		list_del(&dev->devlist);
+		atomic_dec(&dev->devlist_count);
+		mutex_unlock(&cx231xx_devlist_mutex);
+	}
 };
 
 void cx231xx_add_into_devlist(struct cx231xx *dev)
 {
 	mutex_lock(&cx231xx_devlist_mutex);
 	list_add_tail(&dev->devlist, &cx231xx_devlist);
+	atomic_inc(&dev->devlist_count);
 	mutex_unlock(&cx231xx_devlist_mutex);
 };
 
 static LIST_HEAD(cx231xx_extension_devlist);
-static DEFINE_MUTEX(cx231xx_extension_devlist_lock);
 
 int cx231xx_register_extension(struct cx231xx_ops *ops)
 {
 	struct cx231xx *dev = NULL;
 
 	mutex_lock(&cx231xx_devlist_mutex);
-	mutex_lock(&cx231xx_extension_devlist_lock);
 	list_add_tail(&ops->next, &cx231xx_extension_devlist);
 	list_for_each_entry(dev, &cx231xx_devlist, devlist)
 		ops->init(dev);
 
 	printk(KERN_INFO DRIVER_NAME ": %s initialized\n", ops->name);
-	mutex_unlock(&cx231xx_extension_devlist_lock);
 	mutex_unlock(&cx231xx_devlist_mutex);
 	return 0;
 }
@@ -114,10 +116,9 @@
 	list_for_each_entry(dev, &cx231xx_devlist, devlist)
 		ops->fini(dev);
 
-	mutex_lock(&cx231xx_extension_devlist_lock);
+
 	printk(KERN_INFO DRIVER_NAME ": %s removed\n", ops->name);
 	list_del(&ops->next);
-	mutex_unlock(&cx231xx_extension_devlist_lock);
 	mutex_unlock(&cx231xx_devlist_mutex);
 }
 EXPORT_SYMBOL(cx231xx_unregister_extension);
@@ -126,28 +127,28 @@
 {
 	struct cx231xx_ops *ops = NULL;
 
-	mutex_lock(&cx231xx_extension_devlist_lock);
+	mutex_lock(&cx231xx_devlist_mutex);
 	if (!list_empty(&cx231xx_extension_devlist)) {
 		list_for_each_entry(ops, &cx231xx_extension_devlist, next) {
 			if (ops->init)
 				ops->init(dev);
 		}
 	}
-	mutex_unlock(&cx231xx_extension_devlist_lock);
+	mutex_unlock(&cx231xx_devlist_mutex);
 }
 
 void cx231xx_close_extension(struct cx231xx *dev)
 {
 	struct cx231xx_ops *ops = NULL;
 
-	mutex_lock(&cx231xx_extension_devlist_lock);
+	mutex_lock(&cx231xx_devlist_mutex);
 	if (!list_empty(&cx231xx_extension_devlist)) {
 		list_for_each_entry(ops, &cx231xx_extension_devlist, next) {
 			if (ops->fini)
 				ops->fini(dev);
 		}
 	}
-	mutex_unlock(&cx231xx_extension_devlist_lock);
+	mutex_unlock(&cx231xx_devlist_mutex);
 }
 
 /****************************************************************
@@ -234,6 +235,66 @@
 EXPORT_SYMBOL_GPL(cx231xx_send_usb_command);
 
 /*
+ * Sends/Receives URB control messages, assuring to use a kalloced buffer
+ * for all operations (dev->urb_buf), to avoid using stacked buffers, as
+ * they aren't safe for usage with USB, due to DMA restrictions.
+ * Also implements the debug code for control URB's.
+ */
+static int __usb_control_msg(struct cx231xx *dev, unsigned int pipe,
+	__u8 request, __u8 requesttype, __u16 value, __u16 index,
+	void *data, __u16 size, int timeout)
+{
+	int rc, i;
+
+	if (reg_debug) {
+		printk(KERN_DEBUG "%s: (pipe 0x%08x): "
+				"%s:  %02x %02x %02x %02x %02x %02x %02x %02x ",
+				dev->name,
+				pipe,
+				(requesttype & USB_DIR_IN) ? "IN" : "OUT",
+				requesttype,
+				request,
+				value & 0xff, value >> 8,
+				index & 0xff, index >> 8,
+				size & 0xff, size >> 8);
+		if (!(requesttype & USB_DIR_IN)) {
+			printk(KERN_CONT ">>>");
+			for (i = 0; i < size; i++)
+				printk(KERN_CONT " %02x",
+				       ((unsigned char *)data)[i]);
+		}
+	}
+
+	/* Do the real call to usb_control_msg */
+	mutex_lock(&dev->ctrl_urb_lock);
+	if (!(requesttype & USB_DIR_IN) && size)
+		memcpy(dev->urb_buf, data, size);
+	rc = usb_control_msg(dev->udev, pipe, request, requesttype, value,
+			     index, dev->urb_buf, size, timeout);
+	if ((requesttype & USB_DIR_IN) && size)
+		memcpy(data, dev->urb_buf, size);
+	mutex_unlock(&dev->ctrl_urb_lock);
+
+	if (reg_debug) {
+		if (unlikely(rc < 0)) {
+			printk(KERN_CONT "FAILED!\n");
+			return rc;
+		}
+
+		if ((requesttype & USB_DIR_IN)) {
+			printk(KERN_CONT "<<<");
+			for (i = 0; i < size; i++)
+				printk(KERN_CONT " %02x",
+				       ((unsigned char *)data)[i]);
+		}
+		printk(KERN_CONT "\n");
+	}
+
+	return rc;
+}
+
+
+/*
  * cx231xx_read_ctrl_reg()
  * reads data from the usb device specifying bRequest and wValue
  */
@@ -270,39 +331,9 @@
 	if (val == 0xFF)
 		return -EINVAL;
 
-	if (reg_debug) {
-		cx231xx_isocdbg("(pipe 0x%08x): "
-				"IN:  %02x %02x %02x %02x %02x %02x %02x %02x ",
-				pipe,
-				USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-				req, 0, val,
-				reg & 0xff, reg >> 8, len & 0xff, len >> 8);
-	}
-
-	mutex_lock(&dev->ctrl_urb_lock);
-	ret = usb_control_msg(dev->udev, pipe, req,
+	ret = __usb_control_msg(dev, pipe, req,
 			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-			      val, reg, dev->urb_buf, len, HZ);
-	if (ret < 0) {
-		cx231xx_isocdbg(" failed!\n");
-		/* mutex_unlock(&dev->ctrl_urb_lock); */
-		return ret;
-	}
-
-	if (len)
-		memcpy(buf, dev->urb_buf, len);
-
-	mutex_unlock(&dev->ctrl_urb_lock);
-
-	if (reg_debug) {
-		int byte;
-
-		cx231xx_isocdbg("<<<");
-		for (byte = 0; byte < len; byte++)
-			cx231xx_isocdbg(" %02x", (unsigned char)buf[byte]);
-		cx231xx_isocdbg("\n");
-	}
-
+			      val, reg, buf, len, HZ);
 	return ret;
 }
 
@@ -311,6 +342,8 @@
 {
 	int ret;
 	int pipe = 0;
+	int unsend_size = 0;
+	u8 *pdata;
 
 	if (dev->state & DEV_DISCONNECTED)
 		return -ENODEV;
@@ -323,31 +356,54 @@
 	else
 		pipe = usb_sndctrlpipe(dev->udev, 0);
 
-	if (reg_debug) {
-		int byte;
+	/*
+	 * If the cx23102 read more than 4 bytes with i2c bus,
+	 * need chop to 4 byte per request
+	 */
+	if ((ven_req->wLength > 4) && ((ven_req->bRequest == 0x4) ||
+					(ven_req->bRequest == 0x5) ||
+					(ven_req->bRequest == 0x6))) {
+		unsend_size = 0;
+		pdata = ven_req->pBuff;
 
-		cx231xx_isocdbg("(pipe 0x%08x): "
-				"OUT: %02x %02x %02x %04x %04x %04x >>>",
-				pipe,
-				ven_req->
-				direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-				ven_req->bRequest, 0, ven_req->wValue,
-				ven_req->wIndex, ven_req->wLength);
 
-		for (byte = 0; byte < ven_req->wLength; byte++)
-			cx231xx_isocdbg(" %02x",
-					(unsigned char)ven_req->pBuff[byte]);
-		cx231xx_isocdbg("\n");
+		unsend_size = ven_req->wLength;
+
+		/* the first package */
+		ven_req->wValue = ven_req->wValue & 0xFFFB;
+		ven_req->wValue = (ven_req->wValue & 0xFFBD) | 0x2;
+		ret = __usb_control_msg(dev, pipe, ven_req->bRequest,
+			ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			ven_req->wValue, ven_req->wIndex, pdata,
+			0x0004, HZ);
+		unsend_size = unsend_size - 4;
+
+		/* the middle package */
+		ven_req->wValue = (ven_req->wValue & 0xFFBD) | 0x42;
+		while (unsend_size - 4 > 0) {
+			pdata = pdata + 4;
+			ret = __usb_control_msg(dev, pipe,
+				ven_req->bRequest,
+				ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+				ven_req->wValue, ven_req->wIndex, pdata,
+				0x0004, HZ);
+			unsend_size = unsend_size - 4;
+		}
+
+		/* the last package */
+		ven_req->wValue = (ven_req->wValue & 0xFFBD) | 0x40;
+		pdata = pdata + 4;
+		ret = __usb_control_msg(dev, pipe, ven_req->bRequest,
+			ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			ven_req->wValue, ven_req->wIndex, pdata,
+			unsend_size, HZ);
+	} else {
+		ret = __usb_control_msg(dev, pipe, ven_req->bRequest,
+				ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+				ven_req->wValue, ven_req->wIndex,
+				ven_req->pBuff, ven_req->wLength, HZ);
 	}
 
-	mutex_lock(&dev->ctrl_urb_lock);
-	ret = usb_control_msg(dev->udev, pipe, ven_req->bRequest,
-			      ven_req->
-			      direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-			      ven_req->wValue, ven_req->wIndex, ven_req->pBuff,
-			      ven_req->wLength, HZ);
-	mutex_unlock(&dev->ctrl_urb_lock);
-
 	return ret;
 }
 
@@ -403,12 +459,9 @@
 		cx231xx_isocdbg("\n");
 	}
 
-	mutex_lock(&dev->ctrl_urb_lock);
-	memcpy(dev->urb_buf, buf, len);
-	ret = usb_control_msg(dev->udev, pipe, req,
+	ret = __usb_control_msg(dev, pipe, req,
 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-			      val, reg, dev->urb_buf, len, HZ);
-	mutex_unlock(&dev->ctrl_urb_lock);
+			      val, reg, buf, len, HZ);
 
 	return ret;
 }
@@ -444,6 +497,11 @@
 		dev->video_mode.alt = 0;
 	}
 
+	if (dev->USE_ISO == 0)
+		dev->video_mode.alt = 0;
+
+	cx231xx_coredbg("dev->video_mode.alt= %d\n", dev->video_mode.alt);
+
 	/* Get the correct video interface Index */
 	usb_interface_index =
 	    dev->current_pcb_config.hs_config_info[0].interface_info.
@@ -452,15 +510,13 @@
 	if (dev->video_mode.alt != prev_alt) {
 		cx231xx_coredbg("minimum isoc packet size: %u (alt=%d)\n",
 				min_pkt_size, dev->video_mode.alt);
-		dev->video_mode.max_pkt_size =
-		    dev->video_mode.alt_max_pkt_size[dev->video_mode.alt];
+
+		if (dev->video_mode.alt_max_pkt_size != NULL)
+			dev->video_mode.max_pkt_size =
+			dev->video_mode.alt_max_pkt_size[dev->video_mode.alt];
 		cx231xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n",
 				dev->video_mode.alt,
 				dev->video_mode.max_pkt_size);
-		cx231xx_info
-		    (" setting alt %d with wMaxPktSize=%u , Interface = %d\n",
-		     dev->video_mode.alt, dev->video_mode.max_pkt_size,
-		     usb_interface_index);
 		errCode =
 		    usb_set_interface(dev->udev, usb_interface_index,
 				      dev->video_mode.alt);
@@ -485,7 +541,7 @@
 		usb_interface_index =
 		    dev->current_pcb_config.hs_config_info[0].interface_info.
 		    ts1_index + 1;
-		dev->video_mode.alt = alt;
+		dev->ts1_mode.alt = alt;
 		if (dev->ts1_mode.alt_max_pkt_size != NULL)
 			max_pkt_size = dev->ts1_mode.max_pkt_size =
 			    dev->ts1_mode.alt_max_pkt_size[dev->ts1_mode.alt];
@@ -542,12 +598,16 @@
 		cx231xx_errdev
 		("can't change interface %d alt no. to %d: Max. Pkt size = 0\n",
 		usb_interface_index, alt);
-		return -1;
+		/*To workaround error number=-71 on EP0 for videograbber,
+		 need add following codes.*/
+		if (dev->model != CX231XX_BOARD_CNXT_VIDEO_GRABBER &&
+		    dev->model != CX231XX_BOARD_HAUPPAUGE_USBLIVE2)
+			return -1;
 	}
 
-	cx231xx_info
-	    (" setting alternate %d with wMaxPacketSize=%u , Interface = %d\n",
-	     alt, max_pkt_size, usb_interface_index);
+	cx231xx_coredbg("setting alternate %d with wMaxPacketSize=%u,"
+			"Interface = %d\n", alt, max_pkt_size,
+			usb_interface_index);
 
 	if (usb_interface_index > 0) {
 		status = usb_set_interface(dev->udev, usb_interface_index, alt);
@@ -584,8 +644,56 @@
 	return rc;
 }
 
+int cx231xx_demod_reset(struct cx231xx *dev)
+{
+
+	u8 status = 0;
+	u8 value[4] = { 0, 0, 0, 0 };
+
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN,
+				 value, 4);
+
+	cx231xx_coredbg("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN,
+			value[0], value[1], value[2], value[3]);
+
+	cx231xx_coredbg("Enter cx231xx_demod_reset()\n");
+
+		value[1] = (u8) 0x3;
+		status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+						PWR_CTL_EN, value, 4);
+			msleep(10);
+
+		value[1] = (u8) 0x0;
+		status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+						PWR_CTL_EN, value, 4);
+			msleep(10);
+
+		value[1] = (u8) 0x3;
+		status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+						PWR_CTL_EN, value, 4);
+			msleep(10);
+
+
+
+	status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN,
+				 value, 4);
+
+	cx231xx_coredbg("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN,
+			value[0], value[1], value[2], value[3]);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(cx231xx_demod_reset);
+int is_fw_load(struct cx231xx *dev)
+{
+	return cx231xx_check_fw(dev);
+}
+EXPORT_SYMBOL_GPL(is_fw_load);
+
 int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
 {
+	int errCode = 0;
+
 	if (dev->mode == set_mode)
 		return 0;
 
@@ -600,15 +708,75 @@
 
 	dev->mode = set_mode;
 
-	if (dev->mode == CX231XX_DIGITAL_MODE)
-		;/* Set Digital power mode */
-	else
-		;/* Set Analog Power mode */
+	if (dev->mode == CX231XX_DIGITAL_MODE)/* Set Digital power mode */ {
+	/* set AGC mode to Digital */
+		switch (dev->model) {
+		case CX231XX_BOARD_CNXT_CARRAERA:
+		case CX231XX_BOARD_CNXT_RDE_250:
+		case CX231XX_BOARD_CNXT_SHELBY:
+		case CX231XX_BOARD_CNXT_RDU_250:
+		errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
+			break;
+		case CX231XX_BOARD_CNXT_RDE_253S:
+		case CX231XX_BOARD_CNXT_RDU_253S:
+			errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1);
+			break;
+		case CX231XX_BOARD_HAUPPAUGE_EXETER:
+			errCode = cx231xx_set_power_mode(dev,
+						POLARIS_AVMODE_DIGITAL);
+			break;
+		default:
+			break;
+		}
+	} else/* Set Analog Power mode */ {
+	/* set AGC mode to Analog */
+		switch (dev->model) {
+		case CX231XX_BOARD_CNXT_CARRAERA:
+		case CX231XX_BOARD_CNXT_RDE_250:
+		case CX231XX_BOARD_CNXT_SHELBY:
+		case CX231XX_BOARD_CNXT_RDU_250:
+		errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1);
+			break;
+		case CX231XX_BOARD_CNXT_RDE_253S:
+		case CX231XX_BOARD_CNXT_RDU_253S:
+		case CX231XX_BOARD_HAUPPAUGE_EXETER:
+		errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
+			break;
+		default:
+			break;
+		}
+	}
 
 	return 0;
 }
 EXPORT_SYMBOL_GPL(cx231xx_set_mode);
 
+int cx231xx_ep5_bulkout(struct cx231xx *dev, u8 *firmware, u16 size)
+{
+	int errCode = 0;
+	int actlen, ret = -ENOMEM;
+	u32 *buffer;
+
+buffer = kzalloc(4096, GFP_KERNEL);
+	if (buffer == NULL) {
+		cx231xx_info("out of mem\n");
+		return -ENOMEM;
+	}
+	memcpy(&buffer[0], firmware, 4096);
+
+	ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 5),
+				 buffer, 4096, &actlen, 2000);
+
+	if (ret)
+		cx231xx_info("bulk message failed: %d (%d/%d)", ret,
+				 size, actlen);
+	else {
+		errCode = actlen != size ? -1 : 0;
+	}
+kfree(buffer);
+	return 0;
+}
+
 /*****************************************************************
 *                URB Streaming functions                         *
 ******************************************************************/
@@ -616,7 +784,7 @@
 /*
  * IRQ callback, called by URB callback
  */
-static void cx231xx_irq_callback(struct urb *urb)
+static void cx231xx_isoc_irq_callback(struct urb *urb)
 {
 	struct cx231xx_dmaqueue *dma_q = urb->context;
 	struct cx231xx_video_mode *vmode =
@@ -655,12 +823,54 @@
 				urb->status);
 	}
 }
+/*****************************************************************
+*                URB Streaming functions                         *
+******************************************************************/
 
 /*
+ * IRQ callback, called by URB callback
+ */
+static void cx231xx_bulk_irq_callback(struct urb *urb)
+{
+	struct cx231xx_dmaqueue *dma_q = urb->context;
+	struct cx231xx_video_mode *vmode =
+	    container_of(dma_q, struct cx231xx_video_mode, vidq);
+	struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
+	int rc;
+
+	switch (urb->status) {
+	case 0:		/* success */
+	case -ETIMEDOUT:	/* NAK */
+		break;
+	case -ECONNRESET:	/* kill */
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+	default:		/* error */
+		cx231xx_isocdbg("urb completition error %d.\n", urb->status);
+		break;
+	}
+
+	/* Copy data from URB */
+	spin_lock(&dev->video_mode.slock);
+	rc = dev->video_mode.bulk_ctl.bulk_copy(dev, urb);
+	spin_unlock(&dev->video_mode.slock);
+
+	/* Reset urb buffers */
+	urb->status = 0;
+
+	urb->status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (urb->status) {
+		cx231xx_isocdbg("urb resubmit failed (error=%i)\n",
+				urb->status);
+	}
+}
+/*
  * Stop and Deallocate URBs
  */
 void cx231xx_uninit_isoc(struct cx231xx *dev)
 {
+	struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq;
 	struct urb *urb;
 	int i;
 
@@ -690,16 +900,71 @@
 
 	kfree(dev->video_mode.isoc_ctl.urb);
 	kfree(dev->video_mode.isoc_ctl.transfer_buffer);
+	kfree(dma_q->p_left_data);
 
 	dev->video_mode.isoc_ctl.urb = NULL;
 	dev->video_mode.isoc_ctl.transfer_buffer = NULL;
 	dev->video_mode.isoc_ctl.num_bufs = 0;
+	dma_q->p_left_data = NULL;
 
-	cx231xx_capture_start(dev, 0, Raw_Video);
+	if (dev->mode_tv == 0)
+		cx231xx_capture_start(dev, 0, Raw_Video);
+	else
+		cx231xx_capture_start(dev, 0, TS1_serial_mode);
+
+
 }
 EXPORT_SYMBOL_GPL(cx231xx_uninit_isoc);
 
 /*
+ * Stop and Deallocate URBs
+ */
+void cx231xx_uninit_bulk(struct cx231xx *dev)
+{
+	struct urb *urb;
+	int i;
+
+	cx231xx_isocdbg("cx231xx: called cx231xx_uninit_bulk\n");
+
+	dev->video_mode.bulk_ctl.nfields = -1;
+	for (i = 0; i < dev->video_mode.bulk_ctl.num_bufs; i++) {
+		urb = dev->video_mode.bulk_ctl.urb[i];
+		if (urb) {
+			if (!irqs_disabled())
+				usb_kill_urb(urb);
+			else
+				usb_unlink_urb(urb);
+
+			if (dev->video_mode.bulk_ctl.transfer_buffer[i]) {
+				usb_free_coherent(dev->udev,
+						urb->transfer_buffer_length,
+						dev->video_mode.isoc_ctl.
+						transfer_buffer[i],
+						urb->transfer_dma);
+			}
+			usb_free_urb(urb);
+			dev->video_mode.bulk_ctl.urb[i] = NULL;
+		}
+		dev->video_mode.bulk_ctl.transfer_buffer[i] = NULL;
+	}
+
+	kfree(dev->video_mode.bulk_ctl.urb);
+	kfree(dev->video_mode.bulk_ctl.transfer_buffer);
+
+	dev->video_mode.bulk_ctl.urb = NULL;
+	dev->video_mode.bulk_ctl.transfer_buffer = NULL;
+	dev->video_mode.bulk_ctl.num_bufs = 0;
+
+	if (dev->mode_tv == 0)
+		cx231xx_capture_start(dev, 0, Raw_Video);
+	else
+		cx231xx_capture_start(dev, 0, TS1_serial_mode);
+
+
+}
+EXPORT_SYMBOL_GPL(cx231xx_uninit_bulk);
+
+/*
  * Allocate URBs and start IRQ
  */
 int cx231xx_init_isoc(struct cx231xx *dev, int max_packets,
@@ -713,16 +978,17 @@
 	int j, k;
 	int rc;
 
-	cx231xx_isocdbg("cx231xx: called cx231xx_prepare_isoc\n");
-
-	dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
-
-	cx231xx_info("Setting Video mux to %d\n", dev->video_input);
-	video_mux(dev, dev->video_input);
-
 	/* De-allocates all pending stuff */
 	cx231xx_uninit_isoc(dev);
 
+	dma_q->p_left_data = kzalloc(4096, GFP_KERNEL);
+	if (dma_q->p_left_data == NULL) {
+		cx231xx_info("out of mem\n");
+		return -ENOMEM;
+	}
+
+
+
 	dev->video_mode.isoc_ctl.isoc_copy = isoc_copy;
 	dev->video_mode.isoc_ctl.num_bufs = num_bufs;
 	dma_q->pos = 0;
@@ -733,6 +999,14 @@
 	dma_q->lines_per_field = dev->height / 2;
 	dma_q->bytes_left_in_line = dev->width << 1;
 	dma_q->lines_completed = 0;
+	dma_q->mpeg_buffer_done = 0;
+	dma_q->left_data_count = 0;
+	dma_q->mpeg_buffer_completed = 0;
+	dma_q->add_ps_package_head = CX231XX_NEED_ADD_PS_PACKAGE_HEAD;
+	dma_q->ps_head[0] = 0x00;
+	dma_q->ps_head[1] = 0x00;
+	dma_q->ps_head[2] = 0x01;
+	dma_q->ps_head[3] = 0xBA;
 	for (i = 0; i < 8; i++)
 		dma_q->partial_buf[i] = 0;
 
@@ -756,6 +1030,12 @@
 
 	sb_size = max_packets * dev->video_mode.isoc_ctl.max_pkt_size;
 
+	if (dev->mode_tv == 1)
+		dev->video_mode.end_point_addr = 0x81;
+	else
+		dev->video_mode.end_point_addr = 0x84;
+
+
 	/* allocate urbs and transfer buffers */
 	for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
 		urb = usb_alloc_urb(max_packets, GFP_KERNEL);
@@ -784,7 +1064,7 @@
 
 		usb_fill_int_urb(urb, dev->udev, pipe,
 				 dev->video_mode.isoc_ctl.transfer_buffer[i],
-				 sb_size, cx231xx_irq_callback, dma_q, 1);
+				 sb_size, cx231xx_isoc_irq_callback, dma_q, 1);
 
 		urb->number_of_packets = max_packets;
 		urb->transfer_flags = URB_ISO_ASAP;
@@ -812,12 +1092,176 @@
 		}
 	}
 
-	cx231xx_capture_start(dev, 1, Raw_Video);
+	if (dev->mode_tv == 0)
+		cx231xx_capture_start(dev, 1, Raw_Video);
+	else
+		cx231xx_capture_start(dev, 1, TS1_serial_mode);
 
 	return 0;
 }
 EXPORT_SYMBOL_GPL(cx231xx_init_isoc);
 
+/*
+ * Allocate URBs and start IRQ
+ */
+int cx231xx_init_bulk(struct cx231xx *dev, int max_packets,
+		      int num_bufs, int max_pkt_size,
+		      int (*bulk_copy) (struct cx231xx *dev, struct urb *urb))
+{
+	struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq;
+	int i;
+	int sb_size, pipe;
+	struct urb *urb;
+	int rc;
+
+	dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
+
+	cx231xx_coredbg("Setting Video mux to %d\n", dev->video_input);
+
+	video_mux(dev, dev->video_input);
+
+	/* De-allocates all pending stuff */
+	cx231xx_uninit_bulk(dev);
+
+	dev->video_mode.bulk_ctl.bulk_copy = bulk_copy;
+	dev->video_mode.bulk_ctl.num_bufs = num_bufs;
+	dma_q->pos = 0;
+	dma_q->is_partial_line = 0;
+	dma_q->last_sav = 0;
+	dma_q->current_field = -1;
+	dma_q->field1_done = 0;
+	dma_q->lines_per_field = dev->height / 2;
+	dma_q->bytes_left_in_line = dev->width << 1;
+	dma_q->lines_completed = 0;
+	dma_q->mpeg_buffer_done = 0;
+	dma_q->left_data_count = 0;
+	dma_q->mpeg_buffer_completed = 0;
+	dma_q->ps_head[0] = 0x00;
+	dma_q->ps_head[1] = 0x00;
+	dma_q->ps_head[2] = 0x01;
+	dma_q->ps_head[3] = 0xBA;
+	for (i = 0; i < 8; i++)
+		dma_q->partial_buf[i] = 0;
+
+	dev->video_mode.bulk_ctl.urb =
+	    kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
+	if (!dev->video_mode.bulk_ctl.urb) {
+		cx231xx_errdev("cannot alloc memory for usb buffers\n");
+		return -ENOMEM;
+	}
+
+	dev->video_mode.bulk_ctl.transfer_buffer =
+	    kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
+	if (!dev->video_mode.bulk_ctl.transfer_buffer) {
+		cx231xx_errdev("cannot allocate memory for usbtransfer\n");
+		kfree(dev->video_mode.bulk_ctl.urb);
+		return -ENOMEM;
+	}
+
+	dev->video_mode.bulk_ctl.max_pkt_size = max_pkt_size;
+	dev->video_mode.bulk_ctl.buf = NULL;
+
+	sb_size = max_packets * dev->video_mode.bulk_ctl.max_pkt_size;
+
+	if (dev->mode_tv == 1)
+		dev->video_mode.end_point_addr = 0x81;
+	else
+		dev->video_mode.end_point_addr = 0x84;
+
+
+	/* allocate urbs and transfer buffers */
+	for (i = 0; i < dev->video_mode.bulk_ctl.num_bufs; i++) {
+		urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!urb) {
+			cx231xx_err("cannot alloc bulk_ctl.urb %i\n", i);
+			cx231xx_uninit_bulk(dev);
+			return -ENOMEM;
+		}
+		dev->video_mode.bulk_ctl.urb[i] = urb;
+		urb->transfer_flags = 0;
+
+		dev->video_mode.bulk_ctl.transfer_buffer[i] =
+		    usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL,
+				     &urb->transfer_dma);
+		if (!dev->video_mode.bulk_ctl.transfer_buffer[i]) {
+			cx231xx_err("unable to allocate %i bytes for transfer"
+				    " buffer %i%s\n",
+				    sb_size, i,
+				    in_interrupt() ? " while in int" : "");
+			cx231xx_uninit_bulk(dev);
+			return -ENOMEM;
+		}
+		memset(dev->video_mode.bulk_ctl.transfer_buffer[i], 0, sb_size);
+
+		pipe = usb_rcvbulkpipe(dev->udev,
+				 dev->video_mode.end_point_addr);
+		usb_fill_bulk_urb(urb, dev->udev, pipe,
+				  dev->video_mode.bulk_ctl.transfer_buffer[i],
+				  sb_size, cx231xx_bulk_irq_callback, dma_q);
+	}
+
+	init_waitqueue_head(&dma_q->wq);
+
+	/* submit urbs and enables IRQ */
+	for (i = 0; i < dev->video_mode.bulk_ctl.num_bufs; i++) {
+		rc = usb_submit_urb(dev->video_mode.bulk_ctl.urb[i],
+				    GFP_ATOMIC);
+		if (rc) {
+			cx231xx_err("submit of urb %i failed (error=%i)\n", i,
+				    rc);
+			cx231xx_uninit_bulk(dev);
+			return rc;
+		}
+	}
+
+	if (dev->mode_tv == 0)
+		cx231xx_capture_start(dev, 1, Raw_Video);
+	else
+		cx231xx_capture_start(dev, 1, TS1_serial_mode);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cx231xx_init_bulk);
+void cx231xx_stop_TS1(struct cx231xx *dev)
+{
+	int status = 0;
+	u8 val[4] = { 0, 0, 0, 0 };
+
+			val[0] = 0x00;
+			val[1] = 0x03;
+			val[2] = 0x00;
+			val[3] = 0x00;
+			status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+				 TS_MODE_REG, val, 4);
+
+			val[0] = 0x00;
+			val[1] = 0x70;
+			val[2] = 0x04;
+			val[3] = 0x00;
+			status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+				 TS1_CFG_REG, val, 4);
+}
+/* EXPORT_SYMBOL_GPL(cx231xx_stop_TS1); */
+void cx231xx_start_TS1(struct cx231xx *dev)
+{
+	int status = 0;
+	u8 val[4] = { 0, 0, 0, 0 };
+
+			val[0] = 0x03;
+			val[1] = 0x03;
+			val[2] = 0x00;
+			val[3] = 0x00;
+			status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+				 TS_MODE_REG, val, 4);
+
+			val[0] = 0x04;
+			val[1] = 0xA3;
+			val[2] = 0x3B;
+			val[3] = 0x00;
+			status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+				 TS1_CFG_REG, val, 4);
+}
+/* EXPORT_SYMBOL_GPL(cx231xx_start_TS1); */
 /*****************************************************************
 *             Device Init/UnInit functions                       *
 ******************************************************************/
@@ -830,14 +1274,14 @@
 	/* External Master 1 Bus */
 	dev->i2c_bus[0].nr = 0;
 	dev->i2c_bus[0].dev = dev;
-	dev->i2c_bus[0].i2c_period = I2C_SPEED_1M;	/* 1MHz */
+	dev->i2c_bus[0].i2c_period = I2C_SPEED_100K;	/* 100 KHz */
 	dev->i2c_bus[0].i2c_nostop = 0;
 	dev->i2c_bus[0].i2c_reserve = 0;
 
 	/* External Master 2 Bus */
 	dev->i2c_bus[1].nr = 1;
 	dev->i2c_bus[1].dev = dev;
-	dev->i2c_bus[1].i2c_period = I2C_SPEED_1M;	/* 1MHz */
+	dev->i2c_bus[1].i2c_period = I2C_SPEED_100K;	/* 100 KHz */
 	dev->i2c_bus[1].i2c_nostop = 0;
 	dev->i2c_bus[1].i2c_reserve = 0;
 
@@ -856,14 +1300,34 @@
 	/* init hardware */
 	/* Note : with out calling set power mode function,
 	afe can not be set up correctly */
-	errCode = cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);
-	if (errCode < 0) {
-		cx231xx_errdev
-		    ("%s: Failed to set Power - errCode [%d]!\n",
-		     __func__, errCode);
-		return errCode;
+	if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER ||
+	    dev->model == CX231XX_BOARD_HAUPPAUGE_USBLIVE2) {
+		errCode = cx231xx_set_power_mode(dev,
+				 POLARIS_AVMODE_ENXTERNAL_AV);
+		if (errCode < 0) {
+			cx231xx_errdev
+			("%s: Failed to set Power - errCode [%d]!\n",
+			__func__, errCode);
+			return errCode;
+		}
+	} else {
+		errCode = cx231xx_set_power_mode(dev,
+				 POLARIS_AVMODE_ANALOGT_TV);
+		if (errCode < 0) {
+			cx231xx_errdev
+			("%s: Failed to set Power - errCode [%d]!\n",
+			__func__, errCode);
+			return errCode;
+		}
 	}
 
+	/* reset the Tuner */
+	if ((dev->model == CX231XX_BOARD_CNXT_CARRAERA) ||
+		(dev->model == CX231XX_BOARD_CNXT_RDE_250) ||
+		(dev->model == CX231XX_BOARD_CNXT_SHELBY) ||
+		(dev->model == CX231XX_BOARD_CNXT_RDU_250))
+			cx231xx_gpio_set(dev, dev->board.tuner_gpio);
+
 	/* initialize Colibri block */
 	errCode = cx231xx_afe_init_super_block(dev, 0x23c);
 	if (errCode < 0) {
@@ -907,7 +1371,21 @@
 	}
 
 	/* set AGC mode to Analog */
+	switch (dev->model) {
+	case CX231XX_BOARD_CNXT_CARRAERA:
+	case CX231XX_BOARD_CNXT_RDE_250:
+	case CX231XX_BOARD_CNXT_SHELBY:
+	case CX231XX_BOARD_CNXT_RDU_250:
 	errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1);
+		break;
+	case CX231XX_BOARD_CNXT_RDE_253S:
+	case CX231XX_BOARD_CNXT_RDU_253S:
+	case CX231XX_BOARD_HAUPPAUGE_EXETER:
+	errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
+		break;
+	default:
+		break;
+	}
 	if (errCode < 0) {
 		cx231xx_errdev
 		    ("%s: cx231xx_AGC mode to Analog - errCode [%d]!\n",
@@ -923,7 +1401,7 @@
 		cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
 
 	/* set the I2C master port to 3 on channel 1 */
-	errCode = cx231xx_enable_i2c_for_tuner(dev, I2C_3);
+	errCode = cx231xx_enable_i2c_port_3(dev, true);
 
 	return errCode;
 }
@@ -941,7 +1419,7 @@
 /*****************************************************************
 *              G P I O related functions                         *
 ******************************************************************/
-int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val,
+int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val,
 			  u8 len, u8 request, u8 direction)
 {
 	int status = 0;
@@ -1026,6 +1504,91 @@
 /*****************************************************************
  *            I 2 C Internal C O N T R O L   functions           *
  *****************************************************************/
+int cx231xx_read_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr,
+			  u8 saddr_len, u32 *data, u8 data_len, int master)
+{
+	int status = 0;
+	struct cx231xx_i2c_xfer_data req_data;
+	u8 value[64] = "0";
+
+	if (saddr_len == 0)
+		saddr = 0;
+	else if (saddr_len == 0)
+		saddr &= 0xff;
+
+	/* prepare xfer_data struct */
+	req_data.dev_addr = dev_addr >> 1;
+	req_data.direction = I2C_M_RD;
+	req_data.saddr_len = saddr_len;
+	req_data.saddr_dat = saddr;
+	req_data.buf_size = data_len;
+	req_data.p_buffer = (u8 *) value;
+
+	/* usb send command */
+	if (master == 0)
+		status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0],
+					 &req_data);
+	else if (master == 1)
+		status = dev->cx231xx_send_usb_command(&dev->i2c_bus[1],
+					 &req_data);
+	else if (master == 2)
+		status = dev->cx231xx_send_usb_command(&dev->i2c_bus[2],
+					 &req_data);
+
+	if (status >= 0) {
+		/* Copy the data read back to main buffer */
+		if (data_len == 1)
+			*data = value[0];
+		else if (data_len == 4)
+			*data =
+			    value[0] | value[1] << 8 | value[2] << 16 | value[3]
+			    << 24;
+		else if (data_len > 4)
+			*data = value[saddr];
+	}
+
+	return status;
+}
+
+int cx231xx_write_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr,
+			   u8 saddr_len, u32 data, u8 data_len, int master)
+{
+	int status = 0;
+	u8 value[4] = { 0, 0, 0, 0 };
+	struct cx231xx_i2c_xfer_data req_data;
+
+	value[0] = (u8) data;
+	value[1] = (u8) (data >> 8);
+	value[2] = (u8) (data >> 16);
+	value[3] = (u8) (data >> 24);
+
+	if (saddr_len == 0)
+		saddr = 0;
+	else if (saddr_len == 0)
+		saddr &= 0xff;
+
+	/* prepare xfer_data struct */
+	req_data.dev_addr = dev_addr >> 1;
+	req_data.direction = 0;
+	req_data.saddr_len = saddr_len;
+	req_data.saddr_dat = saddr;
+	req_data.buf_size = data_len;
+	req_data.p_buffer = value;
+
+	/* usb send command */
+	if (master == 0)
+		status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0],
+				 &req_data);
+	else if (master == 1)
+		status = dev->cx231xx_send_usb_command(&dev->i2c_bus[1],
+				 &req_data);
+	else if (master == 2)
+		status = dev->cx231xx_send_usb_command(&dev->i2c_bus[2],
+				 &req_data);
+
+	return status;
+}
+
 int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr,
 			  u8 saddr_len, u32 *data, u8 data_len)
 {
diff --git a/drivers/media/video/cx231xx/cx231xx-dif.h b/drivers/media/video/cx231xx/cx231xx-dif.h
new file mode 100644
index 0000000..2b63c2f
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-dif.h
@@ -0,0 +1,3178 @@
+/*
+ *  cx231xx-dif.h - driver for Conexant Cx23100/101/102 USB video capture devices
+ *
+ *  Copyright {C} 2009 <Bill.Liu@conexant.com>
+ *
+ *  This program is free software, you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY, without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program, if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CX231XX_DIF_H
+#define _CX231XX_DIF_H
+
+#include "cx231xx-reg.h"
+
+struct dif_settings{
+	u32 if_freq;
+	u32 register_address;
+	u32 value;
+};
+
+static struct dif_settings Dif_set_array[] = {
+
+/*case 3000000:*/
+/* BEGIN - DIF BPF register values from 30_quant.dat*/
+{3000000, DIF_BPF_COEFF01,    0x00000002},
+{3000000, DIF_BPF_COEFF23,    0x00080012},
+{3000000, DIF_BPF_COEFF45,    0x001e0024},
+{3000000, DIF_BPF_COEFF67,    0x001bfff8},
+{3000000, DIF_BPF_COEFF89,    0xffb4ff50},
+{3000000, DIF_BPF_COEFF1011,  0xfed8fe68},
+{3000000, DIF_BPF_COEFF1213,  0xfe24fe34},
+{3000000, DIF_BPF_COEFF1415,  0xfebaffc7},
+{3000000, DIF_BPF_COEFF1617,  0x014d031f},
+{3000000, DIF_BPF_COEFF1819,  0x04f0065d},
+{3000000, DIF_BPF_COEFF2021,  0x07010688},
+{3000000, DIF_BPF_COEFF2223,  0x04c901d6},
+{3000000, DIF_BPF_COEFF2425,  0xfe00f9d3},
+{3000000, DIF_BPF_COEFF2627,  0xf600f342},
+{3000000, DIF_BPF_COEFF2829,  0xf235f337},
+{3000000, DIF_BPF_COEFF3031,  0xf64efb22},
+{3000000, DIF_BPF_COEFF3233,  0x0105070f},
+{3000000, DIF_BPF_COEFF3435,  0x0c460fce},
+{3000000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 30_quant.dat*/
+
+
+/*case 3100000:*/
+/* BEGIN - DIF BPF register values from 31_quant.dat*/
+{3100000, DIF_BPF_COEFF01,    0x00000001},
+{3100000, DIF_BPF_COEFF23,    0x00070012},
+{3100000, DIF_BPF_COEFF45,    0x00220032},
+{3100000, DIF_BPF_COEFF67,    0x00370026},
+{3100000, DIF_BPF_COEFF89,    0xfff0ff91},
+{3100000, DIF_BPF_COEFF1011,  0xff0efe7c},
+{3100000, DIF_BPF_COEFF1213,  0xfe01fdcc},
+{3100000, DIF_BPF_COEFF1415,  0xfe0afedb},
+{3100000, DIF_BPF_COEFF1617,  0x00440224},
+{3100000, DIF_BPF_COEFF1819,  0x0434060c},
+{3100000, DIF_BPF_COEFF2021,  0x0738074e},
+{3100000, DIF_BPF_COEFF2223,  0x06090361},
+{3100000, DIF_BPF_COEFF2425,  0xff99fb39},
+{3100000, DIF_BPF_COEFF2627,  0xf6fef3b6},
+{3100000, DIF_BPF_COEFF2829,  0xf21af2a5},
+{3100000, DIF_BPF_COEFF3031,  0xf573fa33},
+{3100000, DIF_BPF_COEFF3233,  0x0034067d},
+{3100000, DIF_BPF_COEFF3435,  0x0bfb0fb9},
+{3100000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 31_quant.dat*/
+
+
+/*case 3200000:*/
+/* BEGIN - DIF BPF register values from 32_quant.dat*/
+{3200000, DIF_BPF_COEFF01,    0x00000000},
+{3200000, DIF_BPF_COEFF23,    0x0004000e},
+{3200000, DIF_BPF_COEFF45,    0x00200038},
+{3200000, DIF_BPF_COEFF67,    0x004c004f},
+{3200000, DIF_BPF_COEFF89,    0x002fffdf},
+{3200000, DIF_BPF_COEFF1011,  0xff5cfeb6},
+{3200000, DIF_BPF_COEFF1213,  0xfe0dfd92},
+{3200000, DIF_BPF_COEFF1415,  0xfd7ffe03},
+{3200000, DIF_BPF_COEFF1617,  0xff36010a},
+{3200000, DIF_BPF_COEFF1819,  0x03410575},
+{3200000, DIF_BPF_COEFF2021,  0x072607d2},
+{3200000, DIF_BPF_COEFF2223,  0x071804d5},
+{3200000, DIF_BPF_COEFF2425,  0x0134fcb7},
+{3200000, DIF_BPF_COEFF2627,  0xf81ff451},
+{3200000, DIF_BPF_COEFF2829,  0xf223f22e},
+{3200000, DIF_BPF_COEFF3031,  0xf4a7f94b},
+{3200000, DIF_BPF_COEFF3233,  0xff6405e8},
+{3200000, DIF_BPF_COEFF3435,  0x0bae0fa4},
+{3200000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 32_quant.dat*/
+
+
+/*case 3300000:*/
+/* BEGIN - DIF BPF register values from 33_quant.dat*/
+{3300000, DIF_BPF_COEFF01,    0x0000ffff},
+{3300000, DIF_BPF_COEFF23,    0x00000008},
+{3300000, DIF_BPF_COEFF45,    0x001a0036},
+{3300000, DIF_BPF_COEFF67,    0x0056006d},
+{3300000, DIF_BPF_COEFF89,    0x00670030},
+{3300000, DIF_BPF_COEFF1011,  0xffbdff10},
+{3300000, DIF_BPF_COEFF1213,  0xfe46fd8d},
+{3300000, DIF_BPF_COEFF1415,  0xfd25fd4f},
+{3300000, DIF_BPF_COEFF1617,  0xfe35ffe0},
+{3300000, DIF_BPF_COEFF1819,  0x0224049f},
+{3300000, DIF_BPF_COEFF2021,  0x06c9080e},
+{3300000, DIF_BPF_COEFF2223,  0x07ef0627},
+{3300000, DIF_BPF_COEFF2425,  0x02c9fe45},
+{3300000, DIF_BPF_COEFF2627,  0xf961f513},
+{3300000, DIF_BPF_COEFF2829,  0xf250f1d2},
+{3300000, DIF_BPF_COEFF3031,  0xf3ecf869},
+{3300000, DIF_BPF_COEFF3233,  0xfe930552},
+{3300000, DIF_BPF_COEFF3435,  0x0b5f0f8f},
+{3300000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 33_quant.dat*/
+
+
+/*case 3400000:*/
+/* BEGIN - DIF BPF register values from 34_quant.dat*/
+{3400000, DIF_BPF_COEFF01,    0xfffffffe},
+{3400000, DIF_BPF_COEFF23,    0xfffd0001},
+{3400000, DIF_BPF_COEFF45,    0x000f002c},
+{3400000, DIF_BPF_COEFF67,    0x0054007d},
+{3400000, DIF_BPF_COEFF89,    0x0093007c},
+{3400000, DIF_BPF_COEFF1011,  0x0024ff82},
+{3400000, DIF_BPF_COEFF1213,  0xfea6fdbb},
+{3400000, DIF_BPF_COEFF1415,  0xfd03fcca},
+{3400000, DIF_BPF_COEFF1617,  0xfd51feb9},
+{3400000, DIF_BPF_COEFF1819,  0x00eb0392},
+{3400000, DIF_BPF_COEFF2021,  0x06270802},
+{3400000, DIF_BPF_COEFF2223,  0x08880750},
+{3400000, DIF_BPF_COEFF2425,  0x044dffdb},
+{3400000, DIF_BPF_COEFF2627,  0xfabdf5f8},
+{3400000, DIF_BPF_COEFF2829,  0xf2a0f193},
+{3400000, DIF_BPF_COEFF3031,  0xf342f78f},
+{3400000, DIF_BPF_COEFF3233,  0xfdc404b9},
+{3400000, DIF_BPF_COEFF3435,  0x0b0e0f78},
+{3400000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 34_quant.dat*/
+
+
+/*case 3500000:*/
+/* BEGIN - DIF BPF register values from 35_quant.dat*/
+{3500000, DIF_BPF_COEFF01,    0xfffffffd},
+{3500000, DIF_BPF_COEFF23,    0xfffafff9},
+{3500000, DIF_BPF_COEFF45,    0x0002001b},
+{3500000, DIF_BPF_COEFF67,    0x0046007d},
+{3500000, DIF_BPF_COEFF89,    0x00ad00ba},
+{3500000, DIF_BPF_COEFF1011,  0x00870000},
+{3500000, DIF_BPF_COEFF1213,  0xff26fe1a},
+{3500000, DIF_BPF_COEFF1415,  0xfd1bfc7e},
+{3500000, DIF_BPF_COEFF1617,  0xfc99fda4},
+{3500000, DIF_BPF_COEFF1819,  0xffa5025c},
+{3500000, DIF_BPF_COEFF2021,  0x054507ad},
+{3500000, DIF_BPF_COEFF2223,  0x08dd0847},
+{3500000, DIF_BPF_COEFF2425,  0x05b80172},
+{3500000, DIF_BPF_COEFF2627,  0xfc2ef6ff},
+{3500000, DIF_BPF_COEFF2829,  0xf313f170},
+{3500000, DIF_BPF_COEFF3031,  0xf2abf6bd},
+{3500000, DIF_BPF_COEFF3233,  0xfcf6041f},
+{3500000, DIF_BPF_COEFF3435,  0x0abc0f61},
+{3500000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 35_quant.dat*/
+
+
+/*case 3600000:*/
+/* BEGIN - DIF BPF register values from 36_quant.dat*/
+{3600000, DIF_BPF_COEFF01,    0xfffffffd},
+{3600000, DIF_BPF_COEFF23,    0xfff8fff3},
+{3600000, DIF_BPF_COEFF45,    0xfff50006},
+{3600000, DIF_BPF_COEFF67,    0x002f006c},
+{3600000, DIF_BPF_COEFF89,    0x00b200e3},
+{3600000, DIF_BPF_COEFF1011,  0x00dc007e},
+{3600000, DIF_BPF_COEFF1213,  0xffb9fea0},
+{3600000, DIF_BPF_COEFF1415,  0xfd6bfc71},
+{3600000, DIF_BPF_COEFF1617,  0xfc17fcb1},
+{3600000, DIF_BPF_COEFF1819,  0xfe65010b},
+{3600000, DIF_BPF_COEFF2021,  0x042d0713},
+{3600000, DIF_BPF_COEFF2223,  0x08ec0906},
+{3600000, DIF_BPF_COEFF2425,  0x07020302},
+{3600000, DIF_BPF_COEFF2627,  0xfdaff823},
+{3600000, DIF_BPF_COEFF2829,  0xf3a7f16a},
+{3600000, DIF_BPF_COEFF3031,  0xf228f5f5},
+{3600000, DIF_BPF_COEFF3233,  0xfc2a0384},
+{3600000, DIF_BPF_COEFF3435,  0x0a670f4a},
+{3600000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 36_quant.dat*/
+
+
+/*case 3700000:*/
+/* BEGIN - DIF BPF register values from 37_quant.dat*/
+{3700000, DIF_BPF_COEFF01,    0x0000fffd},
+{3700000, DIF_BPF_COEFF23,    0xfff7ffef},
+{3700000, DIF_BPF_COEFF45,    0xffe9fff1},
+{3700000, DIF_BPF_COEFF67,    0x0010004d},
+{3700000, DIF_BPF_COEFF89,    0x00a100f2},
+{3700000, DIF_BPF_COEFF1011,  0x011a00f0},
+{3700000, DIF_BPF_COEFF1213,  0x0053ff44},
+{3700000, DIF_BPF_COEFF1415,  0xfdedfca2},
+{3700000, DIF_BPF_COEFF1617,  0xfbd3fbef},
+{3700000, DIF_BPF_COEFF1819,  0xfd39ffae},
+{3700000, DIF_BPF_COEFF2021,  0x02ea0638},
+{3700000, DIF_BPF_COEFF2223,  0x08b50987},
+{3700000, DIF_BPF_COEFF2425,  0x08230483},
+{3700000, DIF_BPF_COEFF2627,  0xff39f960},
+{3700000, DIF_BPF_COEFF2829,  0xf45bf180},
+{3700000, DIF_BPF_COEFF3031,  0xf1b8f537},
+{3700000, DIF_BPF_COEFF3233,  0xfb6102e7},
+{3700000, DIF_BPF_COEFF3435,  0x0a110f32},
+{3700000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 37_quant.dat*/
+
+
+/*case 3800000:*/
+/* BEGIN - DIF BPF register values from 38_quant.dat*/
+{3800000, DIF_BPF_COEFF01,    0x0000fffe},
+{3800000, DIF_BPF_COEFF23,    0xfff9ffee},
+{3800000, DIF_BPF_COEFF45,    0xffe1ffdd},
+{3800000, DIF_BPF_COEFF67,    0xfff00024},
+{3800000, DIF_BPF_COEFF89,    0x007c00e5},
+{3800000, DIF_BPF_COEFF1011,  0x013a014a},
+{3800000, DIF_BPF_COEFF1213,  0x00e6fff8},
+{3800000, DIF_BPF_COEFF1415,  0xfe98fd0f},
+{3800000, DIF_BPF_COEFF1617,  0xfbd3fb67},
+{3800000, DIF_BPF_COEFF1819,  0xfc32fe54},
+{3800000, DIF_BPF_COEFF2021,  0x01880525},
+{3800000, DIF_BPF_COEFF2223,  0x083909c7},
+{3800000, DIF_BPF_COEFF2425,  0x091505ee},
+{3800000, DIF_BPF_COEFF2627,  0x00c7fab3},
+{3800000, DIF_BPF_COEFF2829,  0xf52df1b4},
+{3800000, DIF_BPF_COEFF3031,  0xf15df484},
+{3800000, DIF_BPF_COEFF3233,  0xfa9b0249},
+{3800000, DIF_BPF_COEFF3435,  0x09ba0f19},
+{3800000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 38_quant.dat*/
+
+
+/*case 3900000:*/
+/* BEGIN - DIF BPF register values from 39_quant.dat*/
+{3900000, DIF_BPF_COEFF01,    0x00000000},
+{3900000, DIF_BPF_COEFF23,    0xfffbfff0},
+{3900000, DIF_BPF_COEFF45,    0xffdeffcf},
+{3900000, DIF_BPF_COEFF67,    0xffd1fff6},
+{3900000, DIF_BPF_COEFF89,    0x004800be},
+{3900000, DIF_BPF_COEFF1011,  0x01390184},
+{3900000, DIF_BPF_COEFF1213,  0x016300ac},
+{3900000, DIF_BPF_COEFF1415,  0xff5efdb1},
+{3900000, DIF_BPF_COEFF1617,  0xfc17fb23},
+{3900000, DIF_BPF_COEFF1819,  0xfb5cfd0d},
+{3900000, DIF_BPF_COEFF2021,  0x001703e4},
+{3900000, DIF_BPF_COEFF2223,  0x077b09c4},
+{3900000, DIF_BPF_COEFF2425,  0x09d2073c},
+{3900000, DIF_BPF_COEFF2627,  0x0251fc18},
+{3900000, DIF_BPF_COEFF2829,  0xf61cf203},
+{3900000, DIF_BPF_COEFF3031,  0xf118f3dc},
+{3900000, DIF_BPF_COEFF3233,  0xf9d801aa},
+{3900000, DIF_BPF_COEFF3435,  0x09600eff},
+{3900000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 39_quant.dat*/
+
+
+/*case 4000000:*/
+/* BEGIN - DIF BPF register values from 40_quant.dat*/
+{4000000, DIF_BPF_COEFF01,    0x00000001},
+{4000000, DIF_BPF_COEFF23,    0xfffefff4},
+{4000000, DIF_BPF_COEFF45,    0xffe1ffc8},
+{4000000, DIF_BPF_COEFF67,    0xffbaffca},
+{4000000, DIF_BPF_COEFF89,    0x000b0082},
+{4000000, DIF_BPF_COEFF1011,  0x01170198},
+{4000000, DIF_BPF_COEFF1213,  0x01c10152},
+{4000000, DIF_BPF_COEFF1415,  0x0030fe7b},
+{4000000, DIF_BPF_COEFF1617,  0xfc99fb24},
+{4000000, DIF_BPF_COEFF1819,  0xfac3fbe9},
+{4000000, DIF_BPF_COEFF2021,  0xfea5027f},
+{4000000, DIF_BPF_COEFF2223,  0x0683097f},
+{4000000, DIF_BPF_COEFF2425,  0x0a560867},
+{4000000, DIF_BPF_COEFF2627,  0x03d2fd89},
+{4000000, DIF_BPF_COEFF2829,  0xf723f26f},
+{4000000, DIF_BPF_COEFF3031,  0xf0e8f341},
+{4000000, DIF_BPF_COEFF3233,  0xf919010a},
+{4000000, DIF_BPF_COEFF3435,  0x09060ee5},
+{4000000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 40_quant.dat*/
+
+
+/*case 4100000:*/
+/* BEGIN - DIF BPF register values from 41_quant.dat*/
+{4100000, DIF_BPF_COEFF01,    0x00010002},
+{4100000, DIF_BPF_COEFF23,    0x0002fffb},
+{4100000, DIF_BPF_COEFF45,    0xffe8ffca},
+{4100000, DIF_BPF_COEFF67,    0xffacffa4},
+{4100000, DIF_BPF_COEFF89,    0xffcd0036},
+{4100000, DIF_BPF_COEFF1011,  0x00d70184},
+{4100000, DIF_BPF_COEFF1213,  0x01f601dc},
+{4100000, DIF_BPF_COEFF1415,  0x00ffff60},
+{4100000, DIF_BPF_COEFF1617,  0xfd51fb6d},
+{4100000, DIF_BPF_COEFF1819,  0xfa6efaf5},
+{4100000, DIF_BPF_COEFF2021,  0xfd410103},
+{4100000, DIF_BPF_COEFF2223,  0x055708f9},
+{4100000, DIF_BPF_COEFF2425,  0x0a9e0969},
+{4100000, DIF_BPF_COEFF2627,  0x0543ff02},
+{4100000, DIF_BPF_COEFF2829,  0xf842f2f5},
+{4100000, DIF_BPF_COEFF3031,  0xf0cef2b2},
+{4100000, DIF_BPF_COEFF3233,  0xf85e006b},
+{4100000, DIF_BPF_COEFF3435,  0x08aa0ecb},
+{4100000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 41_quant.dat*/
+
+
+/*case 4200000:*/
+/* BEGIN - DIF BPF register values from 42_quant.dat*/
+{4200000, DIF_BPF_COEFF01,    0x00010003},
+{4200000, DIF_BPF_COEFF23,    0x00050003},
+{4200000, DIF_BPF_COEFF45,    0xfff3ffd3},
+{4200000, DIF_BPF_COEFF67,    0xffaaff8b},
+{4200000, DIF_BPF_COEFF89,    0xff95ffe5},
+{4200000, DIF_BPF_COEFF1011,  0x0080014a},
+{4200000, DIF_BPF_COEFF1213,  0x01fe023f},
+{4200000, DIF_BPF_COEFF1415,  0x01ba0050},
+{4200000, DIF_BPF_COEFF1617,  0xfe35fbf8},
+{4200000, DIF_BPF_COEFF1819,  0xfa62fa3b},
+{4200000, DIF_BPF_COEFF2021,  0xfbf9ff7e},
+{4200000, DIF_BPF_COEFF2223,  0x04010836},
+{4200000, DIF_BPF_COEFF2425,  0x0aa90a3d},
+{4200000, DIF_BPF_COEFF2627,  0x069f007f},
+{4200000, DIF_BPF_COEFF2829,  0xf975f395},
+{4200000, DIF_BPF_COEFF3031,  0xf0cbf231},
+{4200000, DIF_BPF_COEFF3233,  0xf7a9ffcb},
+{4200000, DIF_BPF_COEFF3435,  0x084c0eaf},
+{4200000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 42_quant.dat*/
+
+
+/*case 4300000:*/
+/* BEGIN - DIF BPF register values from 43_quant.dat*/
+{4300000, DIF_BPF_COEFF01,    0x00010003},
+{4300000, DIF_BPF_COEFF23,    0x0008000a},
+{4300000, DIF_BPF_COEFF45,    0x0000ffe4},
+{4300000, DIF_BPF_COEFF67,    0xffb4ff81},
+{4300000, DIF_BPF_COEFF89,    0xff6aff96},
+{4300000, DIF_BPF_COEFF1011,  0x001c00f0},
+{4300000, DIF_BPF_COEFF1213,  0x01d70271},
+{4300000, DIF_BPF_COEFF1415,  0x0254013b},
+{4300000, DIF_BPF_COEFF1617,  0xff36fcbd},
+{4300000, DIF_BPF_COEFF1819,  0xfa9ff9c5},
+{4300000, DIF_BPF_COEFF2021,  0xfadbfdfe},
+{4300000, DIF_BPF_COEFF2223,  0x028c073b},
+{4300000, DIF_BPF_COEFF2425,  0x0a750adf},
+{4300000, DIF_BPF_COEFF2627,  0x07e101fa},
+{4300000, DIF_BPF_COEFF2829,  0xfab8f44e},
+{4300000, DIF_BPF_COEFF3031,  0xf0ddf1be},
+{4300000, DIF_BPF_COEFF3233,  0xf6f9ff2b},
+{4300000, DIF_BPF_COEFF3435,  0x07ed0e94},
+{4300000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 43_quant.dat*/
+
+
+/*case 4400000:*/
+/* BEGIN - DIF BPF register values from 44_quant.dat*/
+{4400000, DIF_BPF_COEFF01,    0x00000003},
+{4400000, DIF_BPF_COEFF23,    0x0009000f},
+{4400000, DIF_BPF_COEFF45,    0x000efff8},
+{4400000, DIF_BPF_COEFF67,    0xffc9ff87},
+{4400000, DIF_BPF_COEFF89,    0xff52ff54},
+{4400000, DIF_BPF_COEFF1011,  0xffb5007e},
+{4400000, DIF_BPF_COEFF1213,  0x01860270},
+{4400000, DIF_BPF_COEFF1415,  0x02c00210},
+{4400000, DIF_BPF_COEFF1617,  0x0044fdb2},
+{4400000, DIF_BPF_COEFF1819,  0xfb22f997},
+{4400000, DIF_BPF_COEFF2021,  0xf9f2fc90},
+{4400000, DIF_BPF_COEFF2223,  0x0102060f},
+{4400000, DIF_BPF_COEFF2425,  0x0a050b4c},
+{4400000, DIF_BPF_COEFF2627,  0x0902036e},
+{4400000, DIF_BPF_COEFF2829,  0xfc0af51e},
+{4400000, DIF_BPF_COEFF3031,  0xf106f15a},
+{4400000, DIF_BPF_COEFF3233,  0xf64efe8b},
+{4400000, DIF_BPF_COEFF3435,  0x078d0e77},
+{4400000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 44_quant.dat*/
+
+
+/*case 4500000:*/
+/* BEGIN - DIF BPF register values from 45_quant.dat*/
+{4500000, DIF_BPF_COEFF01,    0x00000002},
+{4500000, DIF_BPF_COEFF23,    0x00080012},
+{4500000, DIF_BPF_COEFF45,    0x0019000e},
+{4500000, DIF_BPF_COEFF67,    0xffe5ff9e},
+{4500000, DIF_BPF_COEFF89,    0xff4fff25},
+{4500000, DIF_BPF_COEFF1011,  0xff560000},
+{4500000, DIF_BPF_COEFF1213,  0x0112023b},
+{4500000, DIF_BPF_COEFF1415,  0x02f702c0},
+{4500000, DIF_BPF_COEFF1617,  0x014dfec8},
+{4500000, DIF_BPF_COEFF1819,  0xfbe5f9b3},
+{4500000, DIF_BPF_COEFF2021,  0xf947fb41},
+{4500000, DIF_BPF_COEFF2223,  0xff7004b9},
+{4500000, DIF_BPF_COEFF2425,  0x095a0b81},
+{4500000, DIF_BPF_COEFF2627,  0x0a0004d8},
+{4500000, DIF_BPF_COEFF2829,  0xfd65f603},
+{4500000, DIF_BPF_COEFF3031,  0xf144f104},
+{4500000, DIF_BPF_COEFF3233,  0xf5aafdec},
+{4500000, DIF_BPF_COEFF3435,  0x072b0e5a},
+{4500000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 45_quant.dat*/
+
+
+/*case 4600000:*/
+/* BEGIN - DIF BPF register values from 46_quant.dat*/
+{4600000, DIF_BPF_COEFF01,    0x00000001},
+{4600000, DIF_BPF_COEFF23,    0x00060012},
+{4600000, DIF_BPF_COEFF45,    0x00200022},
+{4600000, DIF_BPF_COEFF67,    0x0005ffc1},
+{4600000, DIF_BPF_COEFF89,    0xff61ff10},
+{4600000, DIF_BPF_COEFF1011,  0xff09ff82},
+{4600000, DIF_BPF_COEFF1213,  0x008601d7},
+{4600000, DIF_BPF_COEFF1415,  0x02f50340},
+{4600000, DIF_BPF_COEFF1617,  0x0241fff0},
+{4600000, DIF_BPF_COEFF1819,  0xfcddfa19},
+{4600000, DIF_BPF_COEFF2021,  0xf8e2fa1e},
+{4600000, DIF_BPF_COEFF2223,  0xfde30343},
+{4600000, DIF_BPF_COEFF2425,  0x08790b7f},
+{4600000, DIF_BPF_COEFF2627,  0x0ad50631},
+{4600000, DIF_BPF_COEFF2829,  0xfec7f6fc},
+{4600000, DIF_BPF_COEFF3031,  0xf198f0bd},
+{4600000, DIF_BPF_COEFF3233,  0xf50dfd4e},
+{4600000, DIF_BPF_COEFF3435,  0x06c90e3d},
+{4600000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 46_quant.dat*/
+
+
+/*case 4700000:*/
+/* BEGIN - DIF BPF register values from 47_quant.dat*/
+{4700000, DIF_BPF_COEFF01,    0x0000ffff},
+{4700000, DIF_BPF_COEFF23,    0x0003000f},
+{4700000, DIF_BPF_COEFF45,    0x00220030},
+{4700000, DIF_BPF_COEFF67,    0x0025ffed},
+{4700000, DIF_BPF_COEFF89,    0xff87ff15},
+{4700000, DIF_BPF_COEFF1011,  0xfed6ff10},
+{4700000, DIF_BPF_COEFF1213,  0xffed014c},
+{4700000, DIF_BPF_COEFF1415,  0x02b90386},
+{4700000, DIF_BPF_COEFF1617,  0x03110119},
+{4700000, DIF_BPF_COEFF1819,  0xfdfefac4},
+{4700000, DIF_BPF_COEFF2021,  0xf8c6f92f},
+{4700000, DIF_BPF_COEFF2223,  0xfc6701b7},
+{4700000, DIF_BPF_COEFF2425,  0x07670b44},
+{4700000, DIF_BPF_COEFF2627,  0x0b7e0776},
+{4700000, DIF_BPF_COEFF2829,  0x002df807},
+{4700000, DIF_BPF_COEFF3031,  0xf200f086},
+{4700000, DIF_BPF_COEFF3233,  0xf477fcb1},
+{4700000, DIF_BPF_COEFF3435,  0x06650e1e},
+{4700000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 47_quant.dat*/
+
+
+/*case 4800000:*/
+/* BEGIN - DIF BPF register values from 48_quant.dat*/
+{4800000, DIF_BPF_COEFF01,    0xfffffffe},
+{4800000, DIF_BPF_COEFF23,    0xffff0009},
+{4800000, DIF_BPF_COEFF45,    0x001e0038},
+{4800000, DIF_BPF_COEFF67,    0x003f001b},
+{4800000, DIF_BPF_COEFF89,    0xffbcff36},
+{4800000, DIF_BPF_COEFF1011,  0xfec2feb6},
+{4800000, DIF_BPF_COEFF1213,  0xff5600a5},
+{4800000, DIF_BPF_COEFF1415,  0x0248038d},
+{4800000, DIF_BPF_COEFF1617,  0x03b00232},
+{4800000, DIF_BPF_COEFF1819,  0xff39fbab},
+{4800000, DIF_BPF_COEFF2021,  0xf8f4f87f},
+{4800000, DIF_BPF_COEFF2223,  0xfb060020},
+{4800000, DIF_BPF_COEFF2425,  0x062a0ad2},
+{4800000, DIF_BPF_COEFF2627,  0x0bf908a3},
+{4800000, DIF_BPF_COEFF2829,  0x0192f922},
+{4800000, DIF_BPF_COEFF3031,  0xf27df05e},
+{4800000, DIF_BPF_COEFF3233,  0xf3e8fc14},
+{4800000, DIF_BPF_COEFF3435,  0x06000e00},
+{4800000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 48_quant.dat*/
+
+
+/*case 4900000:*/
+/* BEGIN - DIF BPF register values from 49_quant.dat*/
+{4900000, DIF_BPF_COEFF01,    0xfffffffd},
+{4900000, DIF_BPF_COEFF23,    0xfffc0002},
+{4900000, DIF_BPF_COEFF45,    0x00160037},
+{4900000, DIF_BPF_COEFF67,    0x00510046},
+{4900000, DIF_BPF_COEFF89,    0xfff9ff6d},
+{4900000, DIF_BPF_COEFF1011,  0xfed0fe7c},
+{4900000, DIF_BPF_COEFF1213,  0xfecefff0},
+{4900000, DIF_BPF_COEFF1415,  0x01aa0356},
+{4900000, DIF_BPF_COEFF1617,  0x0413032b},
+{4900000, DIF_BPF_COEFF1819,  0x007ffcc5},
+{4900000, DIF_BPF_COEFF2021,  0xf96cf812},
+{4900000, DIF_BPF_COEFF2223,  0xf9cefe87},
+{4900000, DIF_BPF_COEFF2425,  0x04c90a2c},
+{4900000, DIF_BPF_COEFF2627,  0x0c4309b4},
+{4900000, DIF_BPF_COEFF2829,  0x02f3fa4a},
+{4900000, DIF_BPF_COEFF3031,  0xf30ef046},
+{4900000, DIF_BPF_COEFF3233,  0xf361fb7a},
+{4900000, DIF_BPF_COEFF3435,  0x059b0de0},
+{4900000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 49_quant.dat*/
+
+
+/*case 5000000:*/
+/* BEGIN - DIF BPF register values from 50_quant.dat*/
+{5000000, DIF_BPF_COEFF01,    0xfffffffd},
+{5000000, DIF_BPF_COEFF23,    0xfff9fffa},
+{5000000, DIF_BPF_COEFF45,    0x000a002d},
+{5000000, DIF_BPF_COEFF67,    0x00570067},
+{5000000, DIF_BPF_COEFF89,    0x0037ffb5},
+{5000000, DIF_BPF_COEFF1011,  0xfefffe68},
+{5000000, DIF_BPF_COEFF1213,  0xfe62ff3d},
+{5000000, DIF_BPF_COEFF1415,  0x00ec02e3},
+{5000000, DIF_BPF_COEFF1617,  0x043503f6},
+{5000000, DIF_BPF_COEFF1819,  0x01befe05},
+{5000000, DIF_BPF_COEFF2021,  0xfa27f7ee},
+{5000000, DIF_BPF_COEFF2223,  0xf8c6fcf8},
+{5000000, DIF_BPF_COEFF2425,  0x034c0954},
+{5000000, DIF_BPF_COEFF2627,  0x0c5c0aa4},
+{5000000, DIF_BPF_COEFF2829,  0x044cfb7e},
+{5000000, DIF_BPF_COEFF3031,  0xf3b1f03f},
+{5000000, DIF_BPF_COEFF3233,  0xf2e2fae1},
+{5000000, DIF_BPF_COEFF3435,  0x05340dc0},
+{5000000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 50_quant.dat*/
+
+
+/*case 5100000:*/
+/* BEGIN - DIF BPF register values from 51_quant.dat*/
+{5100000, DIF_BPF_COEFF01,    0x0000fffd},
+{5100000, DIF_BPF_COEFF23,    0xfff8fff4},
+{5100000, DIF_BPF_COEFF45,    0xfffd001e},
+{5100000, DIF_BPF_COEFF67,    0x0051007b},
+{5100000, DIF_BPF_COEFF89,    0x006e0006},
+{5100000, DIF_BPF_COEFF1011,  0xff48fe7c},
+{5100000, DIF_BPF_COEFF1213,  0xfe1bfe9a},
+{5100000, DIF_BPF_COEFF1415,  0x001d023e},
+{5100000, DIF_BPF_COEFF1617,  0x04130488},
+{5100000, DIF_BPF_COEFF1819,  0x02e6ff5b},
+{5100000, DIF_BPF_COEFF2021,  0xfb1ef812},
+{5100000, DIF_BPF_COEFF2223,  0xf7f7fb7f},
+{5100000, DIF_BPF_COEFF2425,  0x01bc084e},
+{5100000, DIF_BPF_COEFF2627,  0x0c430b72},
+{5100000, DIF_BPF_COEFF2829,  0x059afcba},
+{5100000, DIF_BPF_COEFF3031,  0xf467f046},
+{5100000, DIF_BPF_COEFF3233,  0xf26cfa4a},
+{5100000, DIF_BPF_COEFF3435,  0x04cd0da0},
+{5100000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 51_quant.dat*/
+
+
+/*case 5200000:*/
+/* BEGIN - DIF BPF register values from 52_quant.dat*/
+{5200000, DIF_BPF_COEFF01,    0x0000fffe},
+{5200000, DIF_BPF_COEFF23,    0xfff8ffef},
+{5200000, DIF_BPF_COEFF45,    0xfff00009},
+{5200000, DIF_BPF_COEFF67,    0x003f007f},
+{5200000, DIF_BPF_COEFF89,    0x00980056},
+{5200000, DIF_BPF_COEFF1011,  0xffa5feb6},
+{5200000, DIF_BPF_COEFF1213,  0xfe00fe15},
+{5200000, DIF_BPF_COEFF1415,  0xff4b0170},
+{5200000, DIF_BPF_COEFF1617,  0x03b004d7},
+{5200000, DIF_BPF_COEFF1819,  0x03e800b9},
+{5200000, DIF_BPF_COEFF2021,  0xfc48f87f},
+{5200000, DIF_BPF_COEFF2223,  0xf768fa23},
+{5200000, DIF_BPF_COEFF2425,  0x0022071f},
+{5200000, DIF_BPF_COEFF2627,  0x0bf90c1b},
+{5200000, DIF_BPF_COEFF2829,  0x06dafdfd},
+{5200000, DIF_BPF_COEFF3031,  0xf52df05e},
+{5200000, DIF_BPF_COEFF3233,  0xf1fef9b5},
+{5200000, DIF_BPF_COEFF3435,  0x04640d7f},
+{5200000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 52_quant.dat*/
+
+
+/*case 5300000:*/
+/* BEGIN - DIF BPF register values from 53_quant.dat*/
+{5300000, DIF_BPF_COEFF01,    0x0000ffff},
+{5300000, DIF_BPF_COEFF23,    0xfff9ffee},
+{5300000, DIF_BPF_COEFF45,    0xffe6fff3},
+{5300000, DIF_BPF_COEFF67,    0x00250072},
+{5300000, DIF_BPF_COEFF89,    0x00af009c},
+{5300000, DIF_BPF_COEFF1011,  0x000cff10},
+{5300000, DIF_BPF_COEFF1213,  0xfe13fdb8},
+{5300000, DIF_BPF_COEFF1415,  0xfe870089},
+{5300000, DIF_BPF_COEFF1617,  0x031104e1},
+{5300000, DIF_BPF_COEFF1819,  0x04b8020f},
+{5300000, DIF_BPF_COEFF2021,  0xfd98f92f},
+{5300000, DIF_BPF_COEFF2223,  0xf71df8f0},
+{5300000, DIF_BPF_COEFF2425,  0xfe8805ce},
+{5300000, DIF_BPF_COEFF2627,  0x0b7e0c9c},
+{5300000, DIF_BPF_COEFF2829,  0x0808ff44},
+{5300000, DIF_BPF_COEFF3031,  0xf603f086},
+{5300000, DIF_BPF_COEFF3233,  0xf19af922},
+{5300000, DIF_BPF_COEFF3435,  0x03fb0d5e},
+{5300000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 53_quant.dat*/
+
+
+/*case 5400000:*/
+/* BEGIN - DIF BPF register values from 54_quant.dat*/
+{5400000, DIF_BPF_COEFF01,    0x00000001},
+{5400000, DIF_BPF_COEFF23,    0xfffcffef},
+{5400000, DIF_BPF_COEFF45,    0xffe0ffe0},
+{5400000, DIF_BPF_COEFF67,    0x00050056},
+{5400000, DIF_BPF_COEFF89,    0x00b000d1},
+{5400000, DIF_BPF_COEFF1011,  0x0071ff82},
+{5400000, DIF_BPF_COEFF1213,  0xfe53fd8c},
+{5400000, DIF_BPF_COEFF1415,  0xfddfff99},
+{5400000, DIF_BPF_COEFF1617,  0x024104a3},
+{5400000, DIF_BPF_COEFF1819,  0x054a034d},
+{5400000, DIF_BPF_COEFF2021,  0xff01fa1e},
+{5400000, DIF_BPF_COEFF2223,  0xf717f7ed},
+{5400000, DIF_BPF_COEFF2425,  0xfcf50461},
+{5400000, DIF_BPF_COEFF2627,  0x0ad50cf4},
+{5400000, DIF_BPF_COEFF2829,  0x0921008d},
+{5400000, DIF_BPF_COEFF3031,  0xf6e7f0bd},
+{5400000, DIF_BPF_COEFF3233,  0xf13ff891},
+{5400000, DIF_BPF_COEFF3435,  0x03920d3b},
+{5400000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 54_quant.dat*/
+
+
+/*case 5500000:*/
+/* BEGIN - DIF BPF register values from 55_quant.dat*/
+{5500000, DIF_BPF_COEFF01,    0x00010002},
+{5500000, DIF_BPF_COEFF23,    0xfffffff3},
+{5500000, DIF_BPF_COEFF45,    0xffdeffd1},
+{5500000, DIF_BPF_COEFF67,    0xffe5002f},
+{5500000, DIF_BPF_COEFF89,    0x009c00ed},
+{5500000, DIF_BPF_COEFF1011,  0x00cb0000},
+{5500000, DIF_BPF_COEFF1213,  0xfebafd94},
+{5500000, DIF_BPF_COEFF1415,  0xfd61feb0},
+{5500000, DIF_BPF_COEFF1617,  0x014d0422},
+{5500000, DIF_BPF_COEFF1819,  0x05970464},
+{5500000, DIF_BPF_COEFF2021,  0x0074fb41},
+{5500000, DIF_BPF_COEFF2223,  0xf759f721},
+{5500000, DIF_BPF_COEFF2425,  0xfb7502de},
+{5500000, DIF_BPF_COEFF2627,  0x0a000d21},
+{5500000, DIF_BPF_COEFF2829,  0x0a2201d4},
+{5500000, DIF_BPF_COEFF3031,  0xf7d9f104},
+{5500000, DIF_BPF_COEFF3233,  0xf0edf804},
+{5500000, DIF_BPF_COEFF3435,  0x03280d19},
+{5500000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 55_quant.dat*/
+
+
+/*case 5600000:*/
+/* BEGIN - DIF BPF register values from 56_quant.dat*/
+{5600000, DIF_BPF_COEFF01,    0x00010003},
+{5600000, DIF_BPF_COEFF23,    0x0003fffa},
+{5600000, DIF_BPF_COEFF45,    0xffe3ffc9},
+{5600000, DIF_BPF_COEFF67,    0xffc90002},
+{5600000, DIF_BPF_COEFF89,    0x007500ef},
+{5600000, DIF_BPF_COEFF1011,  0x010e007e},
+{5600000, DIF_BPF_COEFF1213,  0xff3dfdcf},
+{5600000, DIF_BPF_COEFF1415,  0xfd16fddd},
+{5600000, DIF_BPF_COEFF1617,  0x00440365},
+{5600000, DIF_BPF_COEFF1819,  0x059b0548},
+{5600000, DIF_BPF_COEFF2021,  0x01e3fc90},
+{5600000, DIF_BPF_COEFF2223,  0xf7dff691},
+{5600000, DIF_BPF_COEFF2425,  0xfa0f014d},
+{5600000, DIF_BPF_COEFF2627,  0x09020d23},
+{5600000, DIF_BPF_COEFF2829,  0x0b0a0318},
+{5600000, DIF_BPF_COEFF3031,  0xf8d7f15a},
+{5600000, DIF_BPF_COEFF3233,  0xf0a5f779},
+{5600000, DIF_BPF_COEFF3435,  0x02bd0cf6},
+{5600000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 56_quant.dat*/
+
+
+/*case 5700000:*/
+/* BEGIN - DIF BPF register values from 57_quant.dat*/
+{5700000, DIF_BPF_COEFF01,    0x00010003},
+{5700000, DIF_BPF_COEFF23,    0x00060001},
+{5700000, DIF_BPF_COEFF45,    0xffecffc9},
+{5700000, DIF_BPF_COEFF67,    0xffb4ffd4},
+{5700000, DIF_BPF_COEFF89,    0x004000d5},
+{5700000, DIF_BPF_COEFF1011,  0x013600f0},
+{5700000, DIF_BPF_COEFF1213,  0xffd3fe39},
+{5700000, DIF_BPF_COEFF1415,  0xfd04fd31},
+{5700000, DIF_BPF_COEFF1617,  0xff360277},
+{5700000, DIF_BPF_COEFF1819,  0x055605ef},
+{5700000, DIF_BPF_COEFF2021,  0x033efdfe},
+{5700000, DIF_BPF_COEFF2223,  0xf8a5f642},
+{5700000, DIF_BPF_COEFF2425,  0xf8cbffb6},
+{5700000, DIF_BPF_COEFF2627,  0x07e10cfb},
+{5700000, DIF_BPF_COEFF2829,  0x0bd50456},
+{5700000, DIF_BPF_COEFF3031,  0xf9dff1be},
+{5700000, DIF_BPF_COEFF3233,  0xf067f6f2},
+{5700000, DIF_BPF_COEFF3435,  0x02520cd2},
+{5700000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 57_quant.dat*/
+
+
+/*case 5800000:*/
+/* BEGIN - DIF BPF register values from 58_quant.dat*/
+{5800000, DIF_BPF_COEFF01,    0x00000003},
+{5800000, DIF_BPF_COEFF23,    0x00080009},
+{5800000, DIF_BPF_COEFF45,    0xfff8ffd2},
+{5800000, DIF_BPF_COEFF67,    0xffaaffac},
+{5800000, DIF_BPF_COEFF89,    0x000200a3},
+{5800000, DIF_BPF_COEFF1011,  0x013c014a},
+{5800000, DIF_BPF_COEFF1213,  0x006dfec9},
+{5800000, DIF_BPF_COEFF1415,  0xfd2bfcb7},
+{5800000, DIF_BPF_COEFF1617,  0xfe350165},
+{5800000, DIF_BPF_COEFF1819,  0x04cb0651},
+{5800000, DIF_BPF_COEFF2021,  0x0477ff7e},
+{5800000, DIF_BPF_COEFF2223,  0xf9a5f635},
+{5800000, DIF_BPF_COEFF2425,  0xf7b1fe20},
+{5800000, DIF_BPF_COEFF2627,  0x069f0ca8},
+{5800000, DIF_BPF_COEFF2829,  0x0c81058b},
+{5800000, DIF_BPF_COEFF3031,  0xfaf0f231},
+{5800000, DIF_BPF_COEFF3233,  0xf033f66d},
+{5800000, DIF_BPF_COEFF3435,  0x01e60cae},
+{5800000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 58_quant.dat*/
+
+
+/*case 5900000:*/
+/* BEGIN - DIF BPF register values from 59_quant.dat*/
+{5900000, DIF_BPF_COEFF01,    0x00000002},
+{5900000, DIF_BPF_COEFF23,    0x0009000e},
+{5900000, DIF_BPF_COEFF45,    0x0005ffe1},
+{5900000, DIF_BPF_COEFF67,    0xffacff90},
+{5900000, DIF_BPF_COEFF89,    0xffc5005f},
+{5900000, DIF_BPF_COEFF1011,  0x01210184},
+{5900000, DIF_BPF_COEFF1213,  0x00fcff72},
+{5900000, DIF_BPF_COEFF1415,  0xfd8afc77},
+{5900000, DIF_BPF_COEFF1617,  0xfd51003f},
+{5900000, DIF_BPF_COEFF1819,  0x04020669},
+{5900000, DIF_BPF_COEFF2021,  0x05830103},
+{5900000, DIF_BPF_COEFF2223,  0xfad7f66b},
+{5900000, DIF_BPF_COEFF2425,  0xf6c8fc93},
+{5900000, DIF_BPF_COEFF2627,  0x05430c2b},
+{5900000, DIF_BPF_COEFF2829,  0x0d0d06b5},
+{5900000, DIF_BPF_COEFF3031,  0xfc08f2b2},
+{5900000, DIF_BPF_COEFF3233,  0xf00af5ec},
+{5900000, DIF_BPF_COEFF3435,  0x017b0c89},
+{5900000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 59_quant.dat*/
+
+
+/*case 6000000:*/
+/* BEGIN - DIF BPF register values from 60_quant.dat*/
+{6000000, DIF_BPF_COEFF01,    0x00000001},
+{6000000, DIF_BPF_COEFF23,    0x00070012},
+{6000000, DIF_BPF_COEFF45,    0x0012fff5},
+{6000000, DIF_BPF_COEFF67,    0xffbaff82},
+{6000000, DIF_BPF_COEFF89,    0xff8e000f},
+{6000000, DIF_BPF_COEFF1011,  0x00e80198},
+{6000000, DIF_BPF_COEFF1213,  0x01750028},
+{6000000, DIF_BPF_COEFF1415,  0xfe18fc75},
+{6000000, DIF_BPF_COEFF1617,  0xfc99ff15},
+{6000000, DIF_BPF_COEFF1819,  0x03050636},
+{6000000, DIF_BPF_COEFF2021,  0x0656027f},
+{6000000, DIF_BPF_COEFF2223,  0xfc32f6e2},
+{6000000, DIF_BPF_COEFF2425,  0xf614fb17},
+{6000000, DIF_BPF_COEFF2627,  0x03d20b87},
+{6000000, DIF_BPF_COEFF2829,  0x0d7707d2},
+{6000000, DIF_BPF_COEFF3031,  0xfd26f341},
+{6000000, DIF_BPF_COEFF3233,  0xefeaf56f},
+{6000000, DIF_BPF_COEFF3435,  0x010f0c64},
+{6000000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 60_quant.dat*/
+
+
+/*case 6100000:*/
+/* BEGIN - DIF BPF register values from 61_quant.dat*/
+{6100000, DIF_BPF_COEFF01,    0xffff0000},
+{6100000, DIF_BPF_COEFF23,    0x00050012},
+{6100000, DIF_BPF_COEFF45,    0x001c000b},
+{6100000, DIF_BPF_COEFF67,    0xffd1ff84},
+{6100000, DIF_BPF_COEFF89,    0xff66ffbe},
+{6100000, DIF_BPF_COEFF1011,  0x00960184},
+{6100000, DIF_BPF_COEFF1213,  0x01cd00da},
+{6100000, DIF_BPF_COEFF1415,  0xfeccfcb2},
+{6100000, DIF_BPF_COEFF1617,  0xfc17fdf9},
+{6100000, DIF_BPF_COEFF1819,  0x01e005bc},
+{6100000, DIF_BPF_COEFF2021,  0x06e703e4},
+{6100000, DIF_BPF_COEFF2223,  0xfdabf798},
+{6100000, DIF_BPF_COEFF2425,  0xf599f9b3},
+{6100000, DIF_BPF_COEFF2627,  0x02510abd},
+{6100000, DIF_BPF_COEFF2829,  0x0dbf08df},
+{6100000, DIF_BPF_COEFF3031,  0xfe48f3dc},
+{6100000, DIF_BPF_COEFF3233,  0xefd5f4f6},
+{6100000, DIF_BPF_COEFF3435,  0x00a20c3e},
+{6100000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 61_quant.dat*/
+
+
+/*case 6200000:*/
+/* BEGIN - DIF BPF register values from 62_quant.dat*/
+{6200000, DIF_BPF_COEFF01,    0xfffffffe},
+{6200000, DIF_BPF_COEFF23,    0x0002000f},
+{6200000, DIF_BPF_COEFF45,    0x0021001f},
+{6200000, DIF_BPF_COEFF67,    0xfff0ff97},
+{6200000, DIF_BPF_COEFF89,    0xff50ff74},
+{6200000, DIF_BPF_COEFF1011,  0x0034014a},
+{6200000, DIF_BPF_COEFF1213,  0x01fa0179},
+{6200000, DIF_BPF_COEFF1415,  0xff97fd2a},
+{6200000, DIF_BPF_COEFF1617,  0xfbd3fcfa},
+{6200000, DIF_BPF_COEFF1819,  0x00a304fe},
+{6200000, DIF_BPF_COEFF2021,  0x07310525},
+{6200000, DIF_BPF_COEFF2223,  0xff37f886},
+{6200000, DIF_BPF_COEFF2425,  0xf55cf86e},
+{6200000, DIF_BPF_COEFF2627,  0x00c709d0},
+{6200000, DIF_BPF_COEFF2829,  0x0de209db},
+{6200000, DIF_BPF_COEFF3031,  0xff6df484},
+{6200000, DIF_BPF_COEFF3233,  0xefcbf481},
+{6200000, DIF_BPF_COEFF3435,  0x00360c18},
+{6200000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 62_quant.dat*/
+
+
+/*case 6300000:*/
+/* BEGIN - DIF BPF register values from 63_quant.dat*/
+{6300000, DIF_BPF_COEFF01,    0xfffffffd},
+{6300000, DIF_BPF_COEFF23,    0xfffe000a},
+{6300000, DIF_BPF_COEFF45,    0x0021002f},
+{6300000, DIF_BPF_COEFF67,    0x0010ffb8},
+{6300000, DIF_BPF_COEFF89,    0xff50ff3b},
+{6300000, DIF_BPF_COEFF1011,  0xffcc00f0},
+{6300000, DIF_BPF_COEFF1213,  0x01fa01fa},
+{6300000, DIF_BPF_COEFF1415,  0x0069fdd4},
+{6300000, DIF_BPF_COEFF1617,  0xfbd3fc26},
+{6300000, DIF_BPF_COEFF1819,  0xff5d0407},
+{6300000, DIF_BPF_COEFF2021,  0x07310638},
+{6300000, DIF_BPF_COEFF2223,  0x00c9f9a8},
+{6300000, DIF_BPF_COEFF2425,  0xf55cf74e},
+{6300000, DIF_BPF_COEFF2627,  0xff3908c3},
+{6300000, DIF_BPF_COEFF2829,  0x0de20ac3},
+{6300000, DIF_BPF_COEFF3031,  0x0093f537},
+{6300000, DIF_BPF_COEFF3233,  0xefcbf410},
+{6300000, DIF_BPF_COEFF3435,  0xffca0bf2},
+{6300000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 63_quant.dat*/
+
+
+/*case 6400000:*/
+/* BEGIN - DIF BPF register values from 64_quant.dat*/
+{6400000, DIF_BPF_COEFF01,    0xfffffffd},
+{6400000, DIF_BPF_COEFF23,    0xfffb0003},
+{6400000, DIF_BPF_COEFF45,    0x001c0037},
+{6400000, DIF_BPF_COEFF67,    0x002fffe2},
+{6400000, DIF_BPF_COEFF89,    0xff66ff17},
+{6400000, DIF_BPF_COEFF1011,  0xff6a007e},
+{6400000, DIF_BPF_COEFF1213,  0x01cd0251},
+{6400000, DIF_BPF_COEFF1415,  0x0134fea5},
+{6400000, DIF_BPF_COEFF1617,  0xfc17fb8b},
+{6400000, DIF_BPF_COEFF1819,  0xfe2002e0},
+{6400000, DIF_BPF_COEFF2021,  0x06e70713},
+{6400000, DIF_BPF_COEFF2223,  0x0255faf5},
+{6400000, DIF_BPF_COEFF2425,  0xf599f658},
+{6400000, DIF_BPF_COEFF2627,  0xfdaf0799},
+{6400000, DIF_BPF_COEFF2829,  0x0dbf0b96},
+{6400000, DIF_BPF_COEFF3031,  0x01b8f5f5},
+{6400000, DIF_BPF_COEFF3233,  0xefd5f3a3},
+{6400000, DIF_BPF_COEFF3435,  0xff5e0bca},
+{6400000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 64_quant.dat*/
+
+
+/*case 6500000:*/
+/* BEGIN - DIF BPF register values from 65_quant.dat*/
+{6500000, DIF_BPF_COEFF01,    0x0000fffd},
+{6500000, DIF_BPF_COEFF23,    0xfff9fffb},
+{6500000, DIF_BPF_COEFF45,    0x00120037},
+{6500000, DIF_BPF_COEFF67,    0x00460010},
+{6500000, DIF_BPF_COEFF89,    0xff8eff0f},
+{6500000, DIF_BPF_COEFF1011,  0xff180000},
+{6500000, DIF_BPF_COEFF1213,  0x01750276},
+{6500000, DIF_BPF_COEFF1415,  0x01e8ff8d},
+{6500000, DIF_BPF_COEFF1617,  0xfc99fb31},
+{6500000, DIF_BPF_COEFF1819,  0xfcfb0198},
+{6500000, DIF_BPF_COEFF2021,  0x065607ad},
+{6500000, DIF_BPF_COEFF2223,  0x03cefc64},
+{6500000, DIF_BPF_COEFF2425,  0xf614f592},
+{6500000, DIF_BPF_COEFF2627,  0xfc2e0656},
+{6500000, DIF_BPF_COEFF2829,  0x0d770c52},
+{6500000, DIF_BPF_COEFF3031,  0x02daf6bd},
+{6500000, DIF_BPF_COEFF3233,  0xefeaf33b},
+{6500000, DIF_BPF_COEFF3435,  0xfef10ba3},
+{6500000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 65_quant.dat*/
+
+
+/*case 6600000:*/
+/* BEGIN - DIF BPF register values from 66_quant.dat*/
+{6600000, DIF_BPF_COEFF01,    0x0000fffe},
+{6600000, DIF_BPF_COEFF23,    0xfff7fff5},
+{6600000, DIF_BPF_COEFF45,    0x0005002f},
+{6600000, DIF_BPF_COEFF67,    0x0054003c},
+{6600000, DIF_BPF_COEFF89,    0xffc5ff22},
+{6600000, DIF_BPF_COEFF1011,  0xfedfff82},
+{6600000, DIF_BPF_COEFF1213,  0x00fc0267},
+{6600000, DIF_BPF_COEFF1415,  0x0276007e},
+{6600000, DIF_BPF_COEFF1617,  0xfd51fb1c},
+{6600000, DIF_BPF_COEFF1819,  0xfbfe003e},
+{6600000, DIF_BPF_COEFF2021,  0x05830802},
+{6600000, DIF_BPF_COEFF2223,  0x0529fdec},
+{6600000, DIF_BPF_COEFF2425,  0xf6c8f4fe},
+{6600000, DIF_BPF_COEFF2627,  0xfabd04ff},
+{6600000, DIF_BPF_COEFF2829,  0x0d0d0cf6},
+{6600000, DIF_BPF_COEFF3031,  0x03f8f78f},
+{6600000, DIF_BPF_COEFF3233,  0xf00af2d7},
+{6600000, DIF_BPF_COEFF3435,  0xfe850b7b},
+{6600000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 66_quant.dat*/
+
+
+/*case 6700000:*/
+/* BEGIN - DIF BPF register values from 67_quant.dat*/
+{6700000, DIF_BPF_COEFF01,    0x0000ffff},
+{6700000, DIF_BPF_COEFF23,    0xfff8fff0},
+{6700000, DIF_BPF_COEFF45,    0xfff80020},
+{6700000, DIF_BPF_COEFF67,    0x00560060},
+{6700000, DIF_BPF_COEFF89,    0x0002ff4e},
+{6700000, DIF_BPF_COEFF1011,  0xfec4ff10},
+{6700000, DIF_BPF_COEFF1213,  0x006d0225},
+{6700000, DIF_BPF_COEFF1415,  0x02d50166},
+{6700000, DIF_BPF_COEFF1617,  0xfe35fb4e},
+{6700000, DIF_BPF_COEFF1819,  0xfb35fee1},
+{6700000, DIF_BPF_COEFF2021,  0x0477080e},
+{6700000, DIF_BPF_COEFF2223,  0x065bff82},
+{6700000, DIF_BPF_COEFF2425,  0xf7b1f4a0},
+{6700000, DIF_BPF_COEFF2627,  0xf9610397},
+{6700000, DIF_BPF_COEFF2829,  0x0c810d80},
+{6700000, DIF_BPF_COEFF3031,  0x0510f869},
+{6700000, DIF_BPF_COEFF3233,  0xf033f278},
+{6700000, DIF_BPF_COEFF3435,  0xfe1a0b52},
+{6700000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 67_quant.dat*/
+
+
+/*case 6800000:*/
+/* BEGIN - DIF BPF register values from 68_quant.dat*/
+{6800000, DIF_BPF_COEFF01,    0x00010000},
+{6800000, DIF_BPF_COEFF23,    0xfffaffee},
+{6800000, DIF_BPF_COEFF45,    0xffec000c},
+{6800000, DIF_BPF_COEFF67,    0x004c0078},
+{6800000, DIF_BPF_COEFF89,    0x0040ff8e},
+{6800000, DIF_BPF_COEFF1011,  0xfecafeb6},
+{6800000, DIF_BPF_COEFF1213,  0xffd301b6},
+{6800000, DIF_BPF_COEFF1415,  0x02fc0235},
+{6800000, DIF_BPF_COEFF1617,  0xff36fbc5},
+{6800000, DIF_BPF_COEFF1819,  0xfaaafd90},
+{6800000, DIF_BPF_COEFF2021,  0x033e07d2},
+{6800000, DIF_BPF_COEFF2223,  0x075b011b},
+{6800000, DIF_BPF_COEFF2425,  0xf8cbf47a},
+{6800000, DIF_BPF_COEFF2627,  0xf81f0224},
+{6800000, DIF_BPF_COEFF2829,  0x0bd50def},
+{6800000, DIF_BPF_COEFF3031,  0x0621f94b},
+{6800000, DIF_BPF_COEFF3233,  0xf067f21e},
+{6800000, DIF_BPF_COEFF3435,  0xfdae0b29},
+{6800000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 68_quant.dat*/
+
+
+/*case 6900000:*/
+/* BEGIN - DIF BPF register values from 69_quant.dat*/
+{6900000, DIF_BPF_COEFF01,    0x00010001},
+{6900000, DIF_BPF_COEFF23,    0xfffdffef},
+{6900000, DIF_BPF_COEFF45,    0xffe3fff6},
+{6900000, DIF_BPF_COEFF67,    0x0037007f},
+{6900000, DIF_BPF_COEFF89,    0x0075ffdc},
+{6900000, DIF_BPF_COEFF1011,  0xfef2fe7c},
+{6900000, DIF_BPF_COEFF1213,  0xff3d0122},
+{6900000, DIF_BPF_COEFF1415,  0x02ea02dd},
+{6900000, DIF_BPF_COEFF1617,  0x0044fc79},
+{6900000, DIF_BPF_COEFF1819,  0xfa65fc5d},
+{6900000, DIF_BPF_COEFF2021,  0x01e3074e},
+{6900000, DIF_BPF_COEFF2223,  0x082102ad},
+{6900000, DIF_BPF_COEFF2425,  0xfa0ff48c},
+{6900000, DIF_BPF_COEFF2627,  0xf6fe00a9},
+{6900000, DIF_BPF_COEFF2829,  0x0b0a0e43},
+{6900000, DIF_BPF_COEFF3031,  0x0729fa33},
+{6900000, DIF_BPF_COEFF3233,  0xf0a5f1c9},
+{6900000, DIF_BPF_COEFF3435,  0xfd430b00},
+{6900000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 69_quant.dat*/
+
+
+/*case 7000000:*/
+/* BEGIN - DIF BPF register values from 70_quant.dat*/
+{7000000, DIF_BPF_COEFF01,    0x00010002},
+{7000000, DIF_BPF_COEFF23,    0x0001fff3},
+{7000000, DIF_BPF_COEFF45,    0xffdeffe2},
+{7000000, DIF_BPF_COEFF67,    0x001b0076},
+{7000000, DIF_BPF_COEFF89,    0x009c002d},
+{7000000, DIF_BPF_COEFF1011,  0xff35fe68},
+{7000000, DIF_BPF_COEFF1213,  0xfeba0076},
+{7000000, DIF_BPF_COEFF1415,  0x029f0352},
+{7000000, DIF_BPF_COEFF1617,  0x014dfd60},
+{7000000, DIF_BPF_COEFF1819,  0xfa69fb53},
+{7000000, DIF_BPF_COEFF2021,  0x00740688},
+{7000000, DIF_BPF_COEFF2223,  0x08a7042d},
+{7000000, DIF_BPF_COEFF2425,  0xfb75f4d6},
+{7000000, DIF_BPF_COEFF2627,  0xf600ff2d},
+{7000000, DIF_BPF_COEFF2829,  0x0a220e7a},
+{7000000, DIF_BPF_COEFF3031,  0x0827fb22},
+{7000000, DIF_BPF_COEFF3233,  0xf0edf17a},
+{7000000, DIF_BPF_COEFF3435,  0xfcd80ad6},
+{7000000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 70_quant.dat*/
+
+
+/*case 7100000:*/
+/* BEGIN - DIF BPF register values from 71_quant.dat*/
+{7100000, DIF_BPF_COEFF01,    0x00000003},
+{7100000, DIF_BPF_COEFF23,    0x0004fff9},
+{7100000, DIF_BPF_COEFF45,    0xffe0ffd2},
+{7100000, DIF_BPF_COEFF67,    0xfffb005e},
+{7100000, DIF_BPF_COEFF89,    0x00b0007a},
+{7100000, DIF_BPF_COEFF1011,  0xff8ffe7c},
+{7100000, DIF_BPF_COEFF1213,  0xfe53ffc1},
+{7100000, DIF_BPF_COEFF1415,  0x0221038c},
+{7100000, DIF_BPF_COEFF1617,  0x0241fe6e},
+{7100000, DIF_BPF_COEFF1819,  0xfab6fa80},
+{7100000, DIF_BPF_COEFF2021,  0xff010587},
+{7100000, DIF_BPF_COEFF2223,  0x08e90590},
+{7100000, DIF_BPF_COEFF2425,  0xfcf5f556},
+{7100000, DIF_BPF_COEFF2627,  0xf52bfdb3},
+{7100000, DIF_BPF_COEFF2829,  0x09210e95},
+{7100000, DIF_BPF_COEFF3031,  0x0919fc15},
+{7100000, DIF_BPF_COEFF3233,  0xf13ff12f},
+{7100000, DIF_BPF_COEFF3435,  0xfc6e0aab},
+{7100000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 71_quant.dat*/
+
+
+/*case 7200000:*/
+/* BEGIN - DIF BPF register values from 72_quant.dat*/
+{7200000, DIF_BPF_COEFF01,    0x00000003},
+{7200000, DIF_BPF_COEFF23,    0x00070000},
+{7200000, DIF_BPF_COEFF45,    0xffe6ffc9},
+{7200000, DIF_BPF_COEFF67,    0xffdb0039},
+{7200000, DIF_BPF_COEFF89,    0x00af00b8},
+{7200000, DIF_BPF_COEFF1011,  0xfff4feb6},
+{7200000, DIF_BPF_COEFF1213,  0xfe13ff10},
+{7200000, DIF_BPF_COEFF1415,  0x01790388},
+{7200000, DIF_BPF_COEFF1617,  0x0311ff92},
+{7200000, DIF_BPF_COEFF1819,  0xfb48f9ed},
+{7200000, DIF_BPF_COEFF2021,  0xfd980453},
+{7200000, DIF_BPF_COEFF2223,  0x08e306cd},
+{7200000, DIF_BPF_COEFF2425,  0xfe88f60a},
+{7200000, DIF_BPF_COEFF2627,  0xf482fc40},
+{7200000, DIF_BPF_COEFF2829,  0x08080e93},
+{7200000, DIF_BPF_COEFF3031,  0x09fdfd0c},
+{7200000, DIF_BPF_COEFF3233,  0xf19af0ea},
+{7200000, DIF_BPF_COEFF3435,  0xfc050a81},
+{7200000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 72_quant.dat*/
+
+
+/*case 7300000:*/
+/* BEGIN - DIF BPF register values from 73_quant.dat*/
+{7300000, DIF_BPF_COEFF01,    0x00000002},
+{7300000, DIF_BPF_COEFF23,    0x00080008},
+{7300000, DIF_BPF_COEFF45,    0xfff0ffc9},
+{7300000, DIF_BPF_COEFF67,    0xffc1000d},
+{7300000, DIF_BPF_COEFF89,    0x009800e2},
+{7300000, DIF_BPF_COEFF1011,  0x005bff10},
+{7300000, DIF_BPF_COEFF1213,  0xfe00fe74},
+{7300000, DIF_BPF_COEFF1415,  0x00b50345},
+{7300000, DIF_BPF_COEFF1617,  0x03b000bc},
+{7300000, DIF_BPF_COEFF1819,  0xfc18f9a1},
+{7300000, DIF_BPF_COEFF2021,  0xfc4802f9},
+{7300000, DIF_BPF_COEFF2223,  0x089807dc},
+{7300000, DIF_BPF_COEFF2425,  0x0022f6f0},
+{7300000, DIF_BPF_COEFF2627,  0xf407fada},
+{7300000, DIF_BPF_COEFF2829,  0x06da0e74},
+{7300000, DIF_BPF_COEFF3031,  0x0ad3fe06},
+{7300000, DIF_BPF_COEFF3233,  0xf1fef0ab},
+{7300000, DIF_BPF_COEFF3435,  0xfb9c0a55},
+{7300000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 73_quant.dat*/
+
+
+/*case 7400000:*/
+/* BEGIN - DIF BPF register values from 74_quant.dat*/
+{7400000, DIF_BPF_COEFF01,    0x00000001},
+{7400000, DIF_BPF_COEFF23,    0x0008000e},
+{7400000, DIF_BPF_COEFF45,    0xfffdffd0},
+{7400000, DIF_BPF_COEFF67,    0xffafffdf},
+{7400000, DIF_BPF_COEFF89,    0x006e00f2},
+{7400000, DIF_BPF_COEFF1011,  0x00b8ff82},
+{7400000, DIF_BPF_COEFF1213,  0xfe1bfdf8},
+{7400000, DIF_BPF_COEFF1415,  0xffe302c8},
+{7400000, DIF_BPF_COEFF1617,  0x041301dc},
+{7400000, DIF_BPF_COEFF1819,  0xfd1af99e},
+{7400000, DIF_BPF_COEFF2021,  0xfb1e0183},
+{7400000, DIF_BPF_COEFF2223,  0x080908b5},
+{7400000, DIF_BPF_COEFF2425,  0x01bcf801},
+{7400000, DIF_BPF_COEFF2627,  0xf3bdf985},
+{7400000, DIF_BPF_COEFF2829,  0x059a0e38},
+{7400000, DIF_BPF_COEFF3031,  0x0b99ff03},
+{7400000, DIF_BPF_COEFF3233,  0xf26cf071},
+{7400000, DIF_BPF_COEFF3435,  0xfb330a2a},
+{7400000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 74_quant.dat*/
+
+
+/*case 7500000:*/
+/* BEGIN - DIF BPF register values from 75_quant.dat*/
+{7500000, DIF_BPF_COEFF01,    0xffff0000},
+{7500000, DIF_BPF_COEFF23,    0x00070011},
+{7500000, DIF_BPF_COEFF45,    0x000affdf},
+{7500000, DIF_BPF_COEFF67,    0xffa9ffb5},
+{7500000, DIF_BPF_COEFF89,    0x003700e6},
+{7500000, DIF_BPF_COEFF1011,  0x01010000},
+{7500000, DIF_BPF_COEFF1213,  0xfe62fda8},
+{7500000, DIF_BPF_COEFF1415,  0xff140219},
+{7500000, DIF_BPF_COEFF1617,  0x043502e1},
+{7500000, DIF_BPF_COEFF1819,  0xfe42f9e6},
+{7500000, DIF_BPF_COEFF2021,  0xfa270000},
+{7500000, DIF_BPF_COEFF2223,  0x073a0953},
+{7500000, DIF_BPF_COEFF2425,  0x034cf939},
+{7500000, DIF_BPF_COEFF2627,  0xf3a4f845},
+{7500000, DIF_BPF_COEFF2829,  0x044c0de1},
+{7500000, DIF_BPF_COEFF3031,  0x0c4f0000},
+{7500000, DIF_BPF_COEFF3233,  0xf2e2f03c},
+{7500000, DIF_BPF_COEFF3435,  0xfacc09fe},
+{7500000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 75_quant.dat*/
+
+
+/*case 7600000:*/
+/* BEGIN - DIF BPF register values from 76_quant.dat*/
+{7600000, DIF_BPF_COEFF01,    0xffffffff},
+{7600000, DIF_BPF_COEFF23,    0x00040012},
+{7600000, DIF_BPF_COEFF45,    0x0016fff3},
+{7600000, DIF_BPF_COEFF67,    0xffafff95},
+{7600000, DIF_BPF_COEFF89,    0xfff900c0},
+{7600000, DIF_BPF_COEFF1011,  0x0130007e},
+{7600000, DIF_BPF_COEFF1213,  0xfecefd89},
+{7600000, DIF_BPF_COEFF1415,  0xfe560146},
+{7600000, DIF_BPF_COEFF1617,  0x041303bc},
+{7600000, DIF_BPF_COEFF1819,  0xff81fa76},
+{7600000, DIF_BPF_COEFF2021,  0xf96cfe7d},
+{7600000, DIF_BPF_COEFF2223,  0x063209b1},
+{7600000, DIF_BPF_COEFF2425,  0x04c9fa93},
+{7600000, DIF_BPF_COEFF2627,  0xf3bdf71e},
+{7600000, DIF_BPF_COEFF2829,  0x02f30d6e},
+{7600000, DIF_BPF_COEFF3031,  0x0cf200fd},
+{7600000, DIF_BPF_COEFF3233,  0xf361f00e},
+{7600000, DIF_BPF_COEFF3435,  0xfa6509d1},
+{7600000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 76_quant.dat*/
+
+
+/*case 7700000:*/
+/* BEGIN - DIF BPF register values from 77_quant.dat*/
+{7700000, DIF_BPF_COEFF01,    0xfffffffe},
+{7700000, DIF_BPF_COEFF23,    0x00010010},
+{7700000, DIF_BPF_COEFF45,    0x001e0008},
+{7700000, DIF_BPF_COEFF67,    0xffc1ff84},
+{7700000, DIF_BPF_COEFF89,    0xffbc0084},
+{7700000, DIF_BPF_COEFF1011,  0x013e00f0},
+{7700000, DIF_BPF_COEFF1213,  0xff56fd9f},
+{7700000, DIF_BPF_COEFF1415,  0xfdb8005c},
+{7700000, DIF_BPF_COEFF1617,  0x03b00460},
+{7700000, DIF_BPF_COEFF1819,  0x00c7fb45},
+{7700000, DIF_BPF_COEFF2021,  0xf8f4fd07},
+{7700000, DIF_BPF_COEFF2223,  0x04fa09ce},
+{7700000, DIF_BPF_COEFF2425,  0x062afc07},
+{7700000, DIF_BPF_COEFF2627,  0xf407f614},
+{7700000, DIF_BPF_COEFF2829,  0x01920ce0},
+{7700000, DIF_BPF_COEFF3031,  0x0d8301fa},
+{7700000, DIF_BPF_COEFF3233,  0xf3e8efe5},
+{7700000, DIF_BPF_COEFF3435,  0xfa0009a4},
+{7700000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 77_quant.dat*/
+
+
+/*case 7800000:*/
+/* BEGIN - DIF BPF register values from 78_quant.dat*/
+{7800000, DIF_BPF_COEFF01,    0x0000fffd},
+{7800000, DIF_BPF_COEFF23,    0xfffd000b},
+{7800000, DIF_BPF_COEFF45,    0x0022001d},
+{7800000, DIF_BPF_COEFF67,    0xffdbff82},
+{7800000, DIF_BPF_COEFF89,    0xff870039},
+{7800000, DIF_BPF_COEFF1011,  0x012a014a},
+{7800000, DIF_BPF_COEFF1213,  0xffedfde7},
+{7800000, DIF_BPF_COEFF1415,  0xfd47ff6b},
+{7800000, DIF_BPF_COEFF1617,  0x031104c6},
+{7800000, DIF_BPF_COEFF1819,  0x0202fc4c},
+{7800000, DIF_BPF_COEFF2021,  0xf8c6fbad},
+{7800000, DIF_BPF_COEFF2223,  0x039909a7},
+{7800000, DIF_BPF_COEFF2425,  0x0767fd8e},
+{7800000, DIF_BPF_COEFF2627,  0xf482f52b},
+{7800000, DIF_BPF_COEFF2829,  0x002d0c39},
+{7800000, DIF_BPF_COEFF3031,  0x0e0002f4},
+{7800000, DIF_BPF_COEFF3233,  0xf477efc2},
+{7800000, DIF_BPF_COEFF3435,  0xf99b0977},
+{7800000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 78_quant.dat*/
+
+
+/*case 7900000:*/
+/* BEGIN - DIF BPF register values from 79_quant.dat*/
+{7900000, DIF_BPF_COEFF01,    0x0000fffd},
+{7900000, DIF_BPF_COEFF23,    0xfffa0004},
+{7900000, DIF_BPF_COEFF45,    0x0020002d},
+{7900000, DIF_BPF_COEFF67,    0xfffbff91},
+{7900000, DIF_BPF_COEFF89,    0xff61ffe8},
+{7900000, DIF_BPF_COEFF1011,  0x00f70184},
+{7900000, DIF_BPF_COEFF1213,  0x0086fe5c},
+{7900000, DIF_BPF_COEFF1415,  0xfd0bfe85},
+{7900000, DIF_BPF_COEFF1617,  0x024104e5},
+{7900000, DIF_BPF_COEFF1819,  0x0323fd7d},
+{7900000, DIF_BPF_COEFF2021,  0xf8e2fa79},
+{7900000, DIF_BPF_COEFF2223,  0x021d093f},
+{7900000, DIF_BPF_COEFF2425,  0x0879ff22},
+{7900000, DIF_BPF_COEFF2627,  0xf52bf465},
+{7900000, DIF_BPF_COEFF2829,  0xfec70b79},
+{7900000, DIF_BPF_COEFF3031,  0x0e6803eb},
+{7900000, DIF_BPF_COEFF3233,  0xf50defa5},
+{7900000, DIF_BPF_COEFF3435,  0xf937094a},
+{7900000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 79_quant.dat*/
+
+
+/*case 8000000:*/
+/* BEGIN - DIF BPF register values from 80_quant.dat*/
+{8000000, DIF_BPF_COEFF01,    0x0000fffe},
+{8000000, DIF_BPF_COEFF23,    0xfff8fffd},
+{8000000, DIF_BPF_COEFF45,    0x00190036},
+{8000000, DIF_BPF_COEFF67,    0x001bffaf},
+{8000000, DIF_BPF_COEFF89,    0xff4fff99},
+{8000000, DIF_BPF_COEFF1011,  0x00aa0198},
+{8000000, DIF_BPF_COEFF1213,  0x0112fef3},
+{8000000, DIF_BPF_COEFF1415,  0xfd09fdb9},
+{8000000, DIF_BPF_COEFF1617,  0x014d04be},
+{8000000, DIF_BPF_COEFF1819,  0x041bfecc},
+{8000000, DIF_BPF_COEFF2021,  0xf947f978},
+{8000000, DIF_BPF_COEFF2223,  0x00900897},
+{8000000, DIF_BPF_COEFF2425,  0x095a00b9},
+{8000000, DIF_BPF_COEFF2627,  0xf600f3c5},
+{8000000, DIF_BPF_COEFF2829,  0xfd650aa3},
+{8000000, DIF_BPF_COEFF3031,  0x0ebc04de},
+{8000000, DIF_BPF_COEFF3233,  0xf5aaef8e},
+{8000000, DIF_BPF_COEFF3435,  0xf8d5091c},
+{8000000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 80_quant.dat*/
+
+
+/*case 8100000:*/
+/* BEGIN - DIF BPF register values from 81_quant.dat*/
+{8100000, DIF_BPF_COEFF01,    0x0000ffff},
+{8100000, DIF_BPF_COEFF23,    0xfff7fff6},
+{8100000, DIF_BPF_COEFF45,    0x000e0038},
+{8100000, DIF_BPF_COEFF67,    0x0037ffd7},
+{8100000, DIF_BPF_COEFF89,    0xff52ff56},
+{8100000, DIF_BPF_COEFF1011,  0x004b0184},
+{8100000, DIF_BPF_COEFF1213,  0x0186ffa1},
+{8100000, DIF_BPF_COEFF1415,  0xfd40fd16},
+{8100000, DIF_BPF_COEFF1617,  0x00440452},
+{8100000, DIF_BPF_COEFF1819,  0x04de0029},
+{8100000, DIF_BPF_COEFF2021,  0xf9f2f8b2},
+{8100000, DIF_BPF_COEFF2223,  0xfefe07b5},
+{8100000, DIF_BPF_COEFF2425,  0x0a05024d},
+{8100000, DIF_BPF_COEFF2627,  0xf6fef34d},
+{8100000, DIF_BPF_COEFF2829,  0xfc0a09b8},
+{8100000, DIF_BPF_COEFF3031,  0x0efa05cd},
+{8100000, DIF_BPF_COEFF3233,  0xf64eef7d},
+{8100000, DIF_BPF_COEFF3435,  0xf87308ed},
+{8100000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 81_quant.dat*/
+
+
+/*case 8200000:*/
+/* BEGIN - DIF BPF register values from 82_quant.dat*/
+{8200000, DIF_BPF_COEFF01,    0x00010000},
+{8200000, DIF_BPF_COEFF23,    0xfff8fff0},
+{8200000, DIF_BPF_COEFF45,    0x00000031},
+{8200000, DIF_BPF_COEFF67,    0x004c0005},
+{8200000, DIF_BPF_COEFF89,    0xff6aff27},
+{8200000, DIF_BPF_COEFF1011,  0xffe4014a},
+{8200000, DIF_BPF_COEFF1213,  0x01d70057},
+{8200000, DIF_BPF_COEFF1415,  0xfdacfca6},
+{8200000, DIF_BPF_COEFF1617,  0xff3603a7},
+{8200000, DIF_BPF_COEFF1819,  0x05610184},
+{8200000, DIF_BPF_COEFF2021,  0xfadbf82e},
+{8200000, DIF_BPF_COEFF2223,  0xfd74069f},
+{8200000, DIF_BPF_COEFF2425,  0x0a7503d6},
+{8200000, DIF_BPF_COEFF2627,  0xf81ff2ff},
+{8200000, DIF_BPF_COEFF2829,  0xfab808b9},
+{8200000, DIF_BPF_COEFF3031,  0x0f2306b5},
+{8200000, DIF_BPF_COEFF3233,  0xf6f9ef72},
+{8200000, DIF_BPF_COEFF3435,  0xf81308bf},
+{8200000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 82_quant.dat*/
+
+
+/*case 8300000:*/
+/* BEGIN - DIF BPF register values from 83_quant.dat*/
+{8300000, DIF_BPF_COEFF01,    0x00010001},
+{8300000, DIF_BPF_COEFF23,    0xfffbffee},
+{8300000, DIF_BPF_COEFF45,    0xfff30022},
+{8300000, DIF_BPF_COEFF67,    0x00560032},
+{8300000, DIF_BPF_COEFF89,    0xff95ff10},
+{8300000, DIF_BPF_COEFF1011,  0xff8000f0},
+{8300000, DIF_BPF_COEFF1213,  0x01fe0106},
+{8300000, DIF_BPF_COEFF1415,  0xfe46fc71},
+{8300000, DIF_BPF_COEFF1617,  0xfe3502c7},
+{8300000, DIF_BPF_COEFF1819,  0x059e02ce},
+{8300000, DIF_BPF_COEFF2021,  0xfbf9f7f2},
+{8300000, DIF_BPF_COEFF2223,  0xfbff055b},
+{8300000, DIF_BPF_COEFF2425,  0x0aa9054c},
+{8300000, DIF_BPF_COEFF2627,  0xf961f2db},
+{8300000, DIF_BPF_COEFF2829,  0xf97507aa},
+{8300000, DIF_BPF_COEFF3031,  0x0f350797},
+{8300000, DIF_BPF_COEFF3233,  0xf7a9ef6d},
+{8300000, DIF_BPF_COEFF3435,  0xf7b40890},
+{8300000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 83_quant.dat*/
+
+
+/*case 8400000:*/
+/* BEGIN - DIF BPF register values from 84_quant.dat*/
+{8400000, DIF_BPF_COEFF01,    0x00010002},
+{8400000, DIF_BPF_COEFF23,    0xfffeffee},
+{8400000, DIF_BPF_COEFF45,    0xffe8000f},
+{8400000, DIF_BPF_COEFF67,    0x00540058},
+{8400000, DIF_BPF_COEFF89,    0xffcdff14},
+{8400000, DIF_BPF_COEFF1011,  0xff29007e},
+{8400000, DIF_BPF_COEFF1213,  0x01f6019e},
+{8400000, DIF_BPF_COEFF1415,  0xff01fc7c},
+{8400000, DIF_BPF_COEFF1617,  0xfd5101bf},
+{8400000, DIF_BPF_COEFF1819,  0x059203f6},
+{8400000, DIF_BPF_COEFF2021,  0xfd41f7fe},
+{8400000, DIF_BPF_COEFF2223,  0xfaa903f3},
+{8400000, DIF_BPF_COEFF2425,  0x0a9e06a9},
+{8400000, DIF_BPF_COEFF2627,  0xfabdf2e2},
+{8400000, DIF_BPF_COEFF2829,  0xf842068b},
+{8400000, DIF_BPF_COEFF3031,  0x0f320871},
+{8400000, DIF_BPF_COEFF3233,  0xf85eef6e},
+{8400000, DIF_BPF_COEFF3435,  0xf7560860},
+{8400000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 84_quant.dat*/
+
+
+/*case 8500000:*/
+/* BEGIN - DIF BPF register values from 85_quant.dat*/
+{8500000, DIF_BPF_COEFF01,    0x00000003},
+{8500000, DIF_BPF_COEFF23,    0x0002fff2},
+{8500000, DIF_BPF_COEFF45,    0xffe1fff9},
+{8500000, DIF_BPF_COEFF67,    0x00460073},
+{8500000, DIF_BPF_COEFF89,    0x000bff34},
+{8500000, DIF_BPF_COEFF1011,  0xfee90000},
+{8500000, DIF_BPF_COEFF1213,  0x01c10215},
+{8500000, DIF_BPF_COEFF1415,  0xffd0fcc5},
+{8500000, DIF_BPF_COEFF1617,  0xfc99009d},
+{8500000, DIF_BPF_COEFF1819,  0x053d04f1},
+{8500000, DIF_BPF_COEFF2021,  0xfea5f853},
+{8500000, DIF_BPF_COEFF2223,  0xf97d0270},
+{8500000, DIF_BPF_COEFF2425,  0x0a5607e4},
+{8500000, DIF_BPF_COEFF2627,  0xfc2ef314},
+{8500000, DIF_BPF_COEFF2829,  0xf723055f},
+{8500000, DIF_BPF_COEFF3031,  0x0f180943},
+{8500000, DIF_BPF_COEFF3233,  0xf919ef75},
+{8500000, DIF_BPF_COEFF3435,  0xf6fa0830},
+{8500000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 85_quant.dat*/
+
+
+/*case 8600000:*/
+/* BEGIN - DIF BPF register values from 86_quant.dat*/
+{8600000, DIF_BPF_COEFF01,    0x00000003},
+{8600000, DIF_BPF_COEFF23,    0x0005fff8},
+{8600000, DIF_BPF_COEFF45,    0xffdeffe4},
+{8600000, DIF_BPF_COEFF67,    0x002f007f},
+{8600000, DIF_BPF_COEFF89,    0x0048ff6b},
+{8600000, DIF_BPF_COEFF1011,  0xfec7ff82},
+{8600000, DIF_BPF_COEFF1213,  0x0163025f},
+{8600000, DIF_BPF_COEFF1415,  0x00a2fd47},
+{8600000, DIF_BPF_COEFF1617,  0xfc17ff73},
+{8600000, DIF_BPF_COEFF1819,  0x04a405b2},
+{8600000, DIF_BPF_COEFF2021,  0x0017f8ed},
+{8600000, DIF_BPF_COEFF2223,  0xf88500dc},
+{8600000, DIF_BPF_COEFF2425,  0x09d208f9},
+{8600000, DIF_BPF_COEFF2627,  0xfdaff370},
+{8600000, DIF_BPF_COEFF2829,  0xf61c0429},
+{8600000, DIF_BPF_COEFF3031,  0x0ee80a0b},
+{8600000, DIF_BPF_COEFF3233,  0xf9d8ef82},
+{8600000, DIF_BPF_COEFF3435,  0xf6a00800},
+{8600000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 86_quant.dat*/
+
+
+/*case 8700000:*/
+/* BEGIN - DIF BPF register values from 87_quant.dat*/
+{8700000, DIF_BPF_COEFF01,    0x00000003},
+{8700000, DIF_BPF_COEFF23,    0x0007ffff},
+{8700000, DIF_BPF_COEFF45,    0xffe1ffd4},
+{8700000, DIF_BPF_COEFF67,    0x0010007a},
+{8700000, DIF_BPF_COEFF89,    0x007cffb2},
+{8700000, DIF_BPF_COEFF1011,  0xfec6ff10},
+{8700000, DIF_BPF_COEFF1213,  0x00e60277},
+{8700000, DIF_BPF_COEFF1415,  0x0168fdf9},
+{8700000, DIF_BPF_COEFF1617,  0xfbd3fe50},
+{8700000, DIF_BPF_COEFF1819,  0x03ce0631},
+{8700000, DIF_BPF_COEFF2021,  0x0188f9c8},
+{8700000, DIF_BPF_COEFF2223,  0xf7c7ff43},
+{8700000, DIF_BPF_COEFF2425,  0x091509e3},
+{8700000, DIF_BPF_COEFF2627,  0xff39f3f6},
+{8700000, DIF_BPF_COEFF2829,  0xf52d02ea},
+{8700000, DIF_BPF_COEFF3031,  0x0ea30ac9},
+{8700000, DIF_BPF_COEFF3233,  0xfa9bef95},
+{8700000, DIF_BPF_COEFF3435,  0xf64607d0},
+{8700000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 87_quant.dat*/
+
+
+/*case 8800000:*/
+/* BEGIN - DIF BPF register values from 88_quant.dat*/
+{8800000, DIF_BPF_COEFF01,    0x00000002},
+{8800000, DIF_BPF_COEFF23,    0x00090007},
+{8800000, DIF_BPF_COEFF45,    0xffe9ffca},
+{8800000, DIF_BPF_COEFF67,    0xfff00065},
+{8800000, DIF_BPF_COEFF89,    0x00a10003},
+{8800000, DIF_BPF_COEFF1011,  0xfee6feb6},
+{8800000, DIF_BPF_COEFF1213,  0x0053025b},
+{8800000, DIF_BPF_COEFF1415,  0x0213fed0},
+{8800000, DIF_BPF_COEFF1617,  0xfbd3fd46},
+{8800000, DIF_BPF_COEFF1819,  0x02c70668},
+{8800000, DIF_BPF_COEFF2021,  0x02eafadb},
+{8800000, DIF_BPF_COEFF2223,  0xf74bfdae},
+{8800000, DIF_BPF_COEFF2425,  0x08230a9c},
+{8800000, DIF_BPF_COEFF2627,  0x00c7f4a3},
+{8800000, DIF_BPF_COEFF2829,  0xf45b01a6},
+{8800000, DIF_BPF_COEFF3031,  0x0e480b7c},
+{8800000, DIF_BPF_COEFF3233,  0xfb61efae},
+{8800000, DIF_BPF_COEFF3435,  0xf5ef079f},
+{8800000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 88_quant.dat*/
+
+
+/*case 8900000:*/
+/* BEGIN - DIF BPF register values from 89_quant.dat*/
+{8900000, DIF_BPF_COEFF01,    0xffff0000},
+{8900000, DIF_BPF_COEFF23,    0x0008000d},
+{8900000, DIF_BPF_COEFF45,    0xfff5ffc8},
+{8900000, DIF_BPF_COEFF67,    0xffd10043},
+{8900000, DIF_BPF_COEFF89,    0x00b20053},
+{8900000, DIF_BPF_COEFF1011,  0xff24fe7c},
+{8900000, DIF_BPF_COEFF1213,  0xffb9020c},
+{8900000, DIF_BPF_COEFF1415,  0x0295ffbb},
+{8900000, DIF_BPF_COEFF1617,  0xfc17fc64},
+{8900000, DIF_BPF_COEFF1819,  0x019b0654},
+{8900000, DIF_BPF_COEFF2021,  0x042dfc1c},
+{8900000, DIF_BPF_COEFF2223,  0xf714fc2a},
+{8900000, DIF_BPF_COEFF2425,  0x07020b21},
+{8900000, DIF_BPF_COEFF2627,  0x0251f575},
+{8900000, DIF_BPF_COEFF2829,  0xf3a7005e},
+{8900000, DIF_BPF_COEFF3031,  0x0dd80c24},
+{8900000, DIF_BPF_COEFF3233,  0xfc2aefcd},
+{8900000, DIF_BPF_COEFF3435,  0xf599076e},
+{8900000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 89_quant.dat*/
+
+
+/*case 9000000:*/
+/* BEGIN - DIF BPF register values from 90_quant.dat*/
+{9000000, DIF_BPF_COEFF01,    0xffffffff},
+{9000000, DIF_BPF_COEFF23,    0x00060011},
+{9000000, DIF_BPF_COEFF45,    0x0002ffcf},
+{9000000, DIF_BPF_COEFF67,    0xffba0018},
+{9000000, DIF_BPF_COEFF89,    0x00ad009a},
+{9000000, DIF_BPF_COEFF1011,  0xff79fe68},
+{9000000, DIF_BPF_COEFF1213,  0xff260192},
+{9000000, DIF_BPF_COEFF1415,  0x02e500ab},
+{9000000, DIF_BPF_COEFF1617,  0xfc99fbb6},
+{9000000, DIF_BPF_COEFF1819,  0x005b05f7},
+{9000000, DIF_BPF_COEFF2021,  0x0545fd81},
+{9000000, DIF_BPF_COEFF2223,  0xf723fabf},
+{9000000, DIF_BPF_COEFF2425,  0x05b80b70},
+{9000000, DIF_BPF_COEFF2627,  0x03d2f669},
+{9000000, DIF_BPF_COEFF2829,  0xf313ff15},
+{9000000, DIF_BPF_COEFF3031,  0x0d550cbf},
+{9000000, DIF_BPF_COEFF3233,  0xfcf6eff2},
+{9000000, DIF_BPF_COEFF3435,  0xf544073d},
+{9000000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 90_quant.dat*/
+
+
+/*case 9100000:*/
+/* BEGIN - DIF BPF register values from 91_quant.dat*/
+{9100000, DIF_BPF_COEFF01,    0xfffffffe},
+{9100000, DIF_BPF_COEFF23,    0x00030012},
+{9100000, DIF_BPF_COEFF45,    0x000fffdd},
+{9100000, DIF_BPF_COEFF67,    0xffacffea},
+{9100000, DIF_BPF_COEFF89,    0x009300cf},
+{9100000, DIF_BPF_COEFF1011,  0xffdcfe7c},
+{9100000, DIF_BPF_COEFF1213,  0xfea600f7},
+{9100000, DIF_BPF_COEFF1415,  0x02fd0190},
+{9100000, DIF_BPF_COEFF1617,  0xfd51fb46},
+{9100000, DIF_BPF_COEFF1819,  0xff150554},
+{9100000, DIF_BPF_COEFF2021,  0x0627fefd},
+{9100000, DIF_BPF_COEFF2223,  0xf778f978},
+{9100000, DIF_BPF_COEFF2425,  0x044d0b87},
+{9100000, DIF_BPF_COEFF2627,  0x0543f77d},
+{9100000, DIF_BPF_COEFF2829,  0xf2a0fdcf},
+{9100000, DIF_BPF_COEFF3031,  0x0cbe0d4e},
+{9100000, DIF_BPF_COEFF3233,  0xfdc4f01d},
+{9100000, DIF_BPF_COEFF3435,  0xf4f2070b},
+{9100000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 91_quant.dat*/
+
+
+/*case 9200000:*/
+/* BEGIN - DIF BPF register values from 92_quant.dat*/
+{9200000, DIF_BPF_COEFF01,    0x0000fffd},
+{9200000, DIF_BPF_COEFF23,    0x00000010},
+{9200000, DIF_BPF_COEFF45,    0x001afff0},
+{9200000, DIF_BPF_COEFF67,    0xffaaffbf},
+{9200000, DIF_BPF_COEFF89,    0x006700ed},
+{9200000, DIF_BPF_COEFF1011,  0x0043feb6},
+{9200000, DIF_BPF_COEFF1213,  0xfe460047},
+{9200000, DIF_BPF_COEFF1415,  0x02db0258},
+{9200000, DIF_BPF_COEFF1617,  0xfe35fb1b},
+{9200000, DIF_BPF_COEFF1819,  0xfddc0473},
+{9200000, DIF_BPF_COEFF2021,  0x06c90082},
+{9200000, DIF_BPF_COEFF2223,  0xf811f85e},
+{9200000, DIF_BPF_COEFF2425,  0x02c90b66},
+{9200000, DIF_BPF_COEFF2627,  0x069ff8ad},
+{9200000, DIF_BPF_COEFF2829,  0xf250fc8d},
+{9200000, DIF_BPF_COEFF3031,  0x0c140dcf},
+{9200000, DIF_BPF_COEFF3233,  0xfe93f04d},
+{9200000, DIF_BPF_COEFF3435,  0xf4a106d9},
+{9200000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 92_quant.dat*/
+
+
+/*case 9300000:*/
+/* BEGIN - DIF BPF register values from 93_quant.dat*/
+{9300000, DIF_BPF_COEFF01,    0x0000fffd},
+{9300000, DIF_BPF_COEFF23,    0xfffc000c},
+{9300000, DIF_BPF_COEFF45,    0x00200006},
+{9300000, DIF_BPF_COEFF67,    0xffb4ff9c},
+{9300000, DIF_BPF_COEFF89,    0x002f00ef},
+{9300000, DIF_BPF_COEFF1011,  0x00a4ff10},
+{9300000, DIF_BPF_COEFF1213,  0xfe0dff92},
+{9300000, DIF_BPF_COEFF1415,  0x028102f7},
+{9300000, DIF_BPF_COEFF1617,  0xff36fb37},
+{9300000, DIF_BPF_COEFF1819,  0xfcbf035e},
+{9300000, DIF_BPF_COEFF2021,  0x07260202},
+{9300000, DIF_BPF_COEFF2223,  0xf8e8f778},
+{9300000, DIF_BPF_COEFF2425,  0x01340b0d},
+{9300000, DIF_BPF_COEFF2627,  0x07e1f9f4},
+{9300000, DIF_BPF_COEFF2829,  0xf223fb51},
+{9300000, DIF_BPF_COEFF3031,  0x0b590e42},
+{9300000, DIF_BPF_COEFF3233,  0xff64f083},
+{9300000, DIF_BPF_COEFF3435,  0xf45206a7},
+{9300000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 93_quant.dat*/
+
+
+/*case 9400000:*/
+/* BEGIN - DIF BPF register values from 94_quant.dat*/
+{9400000, DIF_BPF_COEFF01,    0x0000fffd},
+{9400000, DIF_BPF_COEFF23,    0xfff90005},
+{9400000, DIF_BPF_COEFF45,    0x0022001a},
+{9400000, DIF_BPF_COEFF67,    0xffc9ff86},
+{9400000, DIF_BPF_COEFF89,    0xfff000d7},
+{9400000, DIF_BPF_COEFF1011,  0x00f2ff82},
+{9400000, DIF_BPF_COEFF1213,  0xfe01fee5},
+{9400000, DIF_BPF_COEFF1415,  0x01f60362},
+{9400000, DIF_BPF_COEFF1617,  0x0044fb99},
+{9400000, DIF_BPF_COEFF1819,  0xfbcc0222},
+{9400000, DIF_BPF_COEFF2021,  0x07380370},
+{9400000, DIF_BPF_COEFF2223,  0xf9f7f6cc},
+{9400000, DIF_BPF_COEFF2425,  0xff990a7e},
+{9400000, DIF_BPF_COEFF2627,  0x0902fb50},
+{9400000, DIF_BPF_COEFF2829,  0xf21afa1f},
+{9400000, DIF_BPF_COEFF3031,  0x0a8d0ea6},
+{9400000, DIF_BPF_COEFF3233,  0x0034f0bf},
+{9400000, DIF_BPF_COEFF3435,  0xf4050675},
+{9400000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 94_quant.dat*/
+
+
+/*case 9500000:*/
+/* BEGIN - DIF BPF register values from 95_quant.dat*/
+{9500000, DIF_BPF_COEFF01,    0x0000fffe},
+{9500000, DIF_BPF_COEFF23,    0xfff8fffe},
+{9500000, DIF_BPF_COEFF45,    0x001e002b},
+{9500000, DIF_BPF_COEFF67,    0xffe5ff81},
+{9500000, DIF_BPF_COEFF89,    0xffb400a5},
+{9500000, DIF_BPF_COEFF1011,  0x01280000},
+{9500000, DIF_BPF_COEFF1213,  0xfe24fe50},
+{9500000, DIF_BPF_COEFF1415,  0x01460390},
+{9500000, DIF_BPF_COEFF1617,  0x014dfc3a},
+{9500000, DIF_BPF_COEFF1819,  0xfb1000ce},
+{9500000, DIF_BPF_COEFF2021,  0x070104bf},
+{9500000, DIF_BPF_COEFF2223,  0xfb37f65f},
+{9500000, DIF_BPF_COEFF2425,  0xfe0009bc},
+{9500000, DIF_BPF_COEFF2627,  0x0a00fcbb},
+{9500000, DIF_BPF_COEFF2829,  0xf235f8f8},
+{9500000, DIF_BPF_COEFF3031,  0x09b20efc},
+{9500000, DIF_BPF_COEFF3233,  0x0105f101},
+{9500000, DIF_BPF_COEFF3435,  0xf3ba0642},
+{9500000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 95_quant.dat*/
+
+
+/*case 9600000:*/
+/* BEGIN - DIF BPF register values from 96_quant.dat*/
+{9600000, DIF_BPF_COEFF01,    0x0001ffff},
+{9600000, DIF_BPF_COEFF23,    0xfff8fff7},
+{9600000, DIF_BPF_COEFF45,    0x00150036},
+{9600000, DIF_BPF_COEFF67,    0x0005ff8c},
+{9600000, DIF_BPF_COEFF89,    0xff810061},
+{9600000, DIF_BPF_COEFF1011,  0x013d007e},
+{9600000, DIF_BPF_COEFF1213,  0xfe71fddf},
+{9600000, DIF_BPF_COEFF1415,  0x007c0380},
+{9600000, DIF_BPF_COEFF1617,  0x0241fd13},
+{9600000, DIF_BPF_COEFF1819,  0xfa94ff70},
+{9600000, DIF_BPF_COEFF2021,  0x068005e2},
+{9600000, DIF_BPF_COEFF2223,  0xfc9bf633},
+{9600000, DIF_BPF_COEFF2425,  0xfc7308ca},
+{9600000, DIF_BPF_COEFF2627,  0x0ad5fe30},
+{9600000, DIF_BPF_COEFF2829,  0xf274f7e0},
+{9600000, DIF_BPF_COEFF3031,  0x08c90f43},
+{9600000, DIF_BPF_COEFF3233,  0x01d4f147},
+{9600000, DIF_BPF_COEFF3435,  0xf371060f},
+{9600000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 96_quant.dat*/
+
+
+/*case 9700000:*/
+/* BEGIN - DIF BPF register values from 97_quant.dat*/
+{9700000, DIF_BPF_COEFF01,    0x00010001},
+{9700000, DIF_BPF_COEFF23,    0xfff9fff1},
+{9700000, DIF_BPF_COEFF45,    0x00090038},
+{9700000, DIF_BPF_COEFF67,    0x0025ffa7},
+{9700000, DIF_BPF_COEFF89,    0xff5e0012},
+{9700000, DIF_BPF_COEFF1011,  0x013200f0},
+{9700000, DIF_BPF_COEFF1213,  0xfee3fd9b},
+{9700000, DIF_BPF_COEFF1415,  0xffaa0331},
+{9700000, DIF_BPF_COEFF1617,  0x0311fe15},
+{9700000, DIF_BPF_COEFF1819,  0xfa60fe18},
+{9700000, DIF_BPF_COEFF2021,  0x05bd06d1},
+{9700000, DIF_BPF_COEFF2223,  0xfe1bf64a},
+{9700000, DIF_BPF_COEFF2425,  0xfafa07ae},
+{9700000, DIF_BPF_COEFF2627,  0x0b7effab},
+{9700000, DIF_BPF_COEFF2829,  0xf2d5f6d7},
+{9700000, DIF_BPF_COEFF3031,  0x07d30f7a},
+{9700000, DIF_BPF_COEFF3233,  0x02a3f194},
+{9700000, DIF_BPF_COEFF3435,  0xf32905dc},
+{9700000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 97_quant.dat*/
+
+
+/*case 9800000:*/
+/* BEGIN - DIF BPF register values from 98_quant.dat*/
+{9800000, DIF_BPF_COEFF01,    0x00010002},
+{9800000, DIF_BPF_COEFF23,    0xfffcffee},
+{9800000, DIF_BPF_COEFF45,    0xfffb0032},
+{9800000, DIF_BPF_COEFF67,    0x003fffcd},
+{9800000, DIF_BPF_COEFF89,    0xff4effc1},
+{9800000, DIF_BPF_COEFF1011,  0x0106014a},
+{9800000, DIF_BPF_COEFF1213,  0xff6efd8a},
+{9800000, DIF_BPF_COEFF1415,  0xfedd02aa},
+{9800000, DIF_BPF_COEFF1617,  0x03b0ff34},
+{9800000, DIF_BPF_COEFF1819,  0xfa74fcd7},
+{9800000, DIF_BPF_COEFF2021,  0x04bf0781},
+{9800000, DIF_BPF_COEFF2223,  0xffaaf6a3},
+{9800000, DIF_BPF_COEFF2425,  0xf99e066b},
+{9800000, DIF_BPF_COEFF2627,  0x0bf90128},
+{9800000, DIF_BPF_COEFF2829,  0xf359f5e1},
+{9800000, DIF_BPF_COEFF3031,  0x06d20fa2},
+{9800000, DIF_BPF_COEFF3233,  0x0370f1e5},
+{9800000, DIF_BPF_COEFF3435,  0xf2e405a8},
+{9800000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 98_quant.dat*/
+
+
+/*case 9900000:*/
+/* BEGIN - DIF BPF register values from 99_quant.dat*/
+{9900000, DIF_BPF_COEFF01,    0x00000003},
+{9900000, DIF_BPF_COEFF23,    0xffffffee},
+{9900000, DIF_BPF_COEFF45,    0xffef0024},
+{9900000, DIF_BPF_COEFF67,    0x0051fffa},
+{9900000, DIF_BPF_COEFF89,    0xff54ff77},
+{9900000, DIF_BPF_COEFF1011,  0x00be0184},
+{9900000, DIF_BPF_COEFF1213,  0x0006fdad},
+{9900000, DIF_BPF_COEFF1415,  0xfe2701f3},
+{9900000, DIF_BPF_COEFF1617,  0x0413005e},
+{9900000, DIF_BPF_COEFF1819,  0xfad1fbba},
+{9900000, DIF_BPF_COEFF2021,  0x039007ee},
+{9900000, DIF_BPF_COEFF2223,  0x013bf73d},
+{9900000, DIF_BPF_COEFF2425,  0xf868050a},
+{9900000, DIF_BPF_COEFF2627,  0x0c4302a1},
+{9900000, DIF_BPF_COEFF2829,  0xf3fdf4fe},
+{9900000, DIF_BPF_COEFF3031,  0x05c70fba},
+{9900000, DIF_BPF_COEFF3233,  0x043bf23c},
+{9900000, DIF_BPF_COEFF3435,  0xf2a10575},
+{9900000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 99_quant.dat*/
+
+
+/*case 10000000:*/
+/* BEGIN - DIF BPF register values from 100_quant.dat*/
+{10000000, DIF_BPF_COEFF01,    0x00000003},
+{10000000, DIF_BPF_COEFF23,    0x0003fff1},
+{10000000, DIF_BPF_COEFF45,    0xffe50011},
+{10000000, DIF_BPF_COEFF67,    0x00570027},
+{10000000, DIF_BPF_COEFF89,    0xff70ff3c},
+{10000000, DIF_BPF_COEFF1011,  0x00620198},
+{10000000, DIF_BPF_COEFF1213,  0x009efe01},
+{10000000, DIF_BPF_COEFF1415,  0xfd95011a},
+{10000000, DIF_BPF_COEFF1617,  0x04350183},
+{10000000, DIF_BPF_COEFF1819,  0xfb71fad0},
+{10000000, DIF_BPF_COEFF2021,  0x023c0812},
+{10000000, DIF_BPF_COEFF2223,  0x02c3f811},
+{10000000, DIF_BPF_COEFF2425,  0xf75e0390},
+{10000000, DIF_BPF_COEFF2627,  0x0c5c0411},
+{10000000, DIF_BPF_COEFF2829,  0xf4c1f432},
+{10000000, DIF_BPF_COEFF3031,  0x04b30fc1},
+{10000000, DIF_BPF_COEFF3233,  0x0503f297},
+{10000000, DIF_BPF_COEFF3435,  0xf2610541},
+{10000000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 100_quant.dat*/
+
+
+/*case 10100000:*/
+/* BEGIN - DIF BPF register values from 101_quant.dat*/
+{10100000, DIF_BPF_COEFF01,    0x00000003},
+{10100000, DIF_BPF_COEFF23,    0x0006fff7},
+{10100000, DIF_BPF_COEFF45,    0xffdffffc},
+{10100000, DIF_BPF_COEFF67,    0x00510050},
+{10100000, DIF_BPF_COEFF89,    0xff9dff18},
+{10100000, DIF_BPF_COEFF1011,  0xfffc0184},
+{10100000, DIF_BPF_COEFF1213,  0x0128fe80},
+{10100000, DIF_BPF_COEFF1415,  0xfd32002e},
+{10100000, DIF_BPF_COEFF1617,  0x04130292},
+{10100000, DIF_BPF_COEFF1819,  0xfc4dfa21},
+{10100000, DIF_BPF_COEFF2021,  0x00d107ee},
+{10100000, DIF_BPF_COEFF2223,  0x0435f91c},
+{10100000, DIF_BPF_COEFF2425,  0xf6850205},
+{10100000, DIF_BPF_COEFF2627,  0x0c430573},
+{10100000, DIF_BPF_COEFF2829,  0xf5a1f37d},
+{10100000, DIF_BPF_COEFF3031,  0x03990fba},
+{10100000, DIF_BPF_COEFF3233,  0x05c7f2f8},
+{10100000, DIF_BPF_COEFF3435,  0xf222050d},
+{10100000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 101_quant.dat*/
+
+
+/*case 10200000:*/
+/* BEGIN - DIF BPF register values from 102_quant.dat*/
+{10200000, DIF_BPF_COEFF01,    0x00000002},
+{10200000, DIF_BPF_COEFF23,    0x0008fffe},
+{10200000, DIF_BPF_COEFF45,    0xffdfffe7},
+{10200000, DIF_BPF_COEFF67,    0x003f006e},
+{10200000, DIF_BPF_COEFF89,    0xffd6ff0f},
+{10200000, DIF_BPF_COEFF1011,  0xff96014a},
+{10200000, DIF_BPF_COEFF1213,  0x0197ff1f},
+{10200000, DIF_BPF_COEFF1415,  0xfd05ff3e},
+{10200000, DIF_BPF_COEFF1617,  0x03b0037c},
+{10200000, DIF_BPF_COEFF1819,  0xfd59f9b7},
+{10200000, DIF_BPF_COEFF2021,  0xff5d0781},
+{10200000, DIF_BPF_COEFF2223,  0x0585fa56},
+{10200000, DIF_BPF_COEFF2425,  0xf5e4006f},
+{10200000, DIF_BPF_COEFF2627,  0x0bf906c4},
+{10200000, DIF_BPF_COEFF2829,  0xf69df2e0},
+{10200000, DIF_BPF_COEFF3031,  0x02790fa2},
+{10200000, DIF_BPF_COEFF3233,  0x0688f35d},
+{10200000, DIF_BPF_COEFF3435,  0xf1e604d8},
+{10200000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 102_quant.dat*/
+
+
+/*case 10300000:*/
+/* BEGIN - DIF BPF register values from 103_quant.dat*/
+{10300000, DIF_BPF_COEFF01,    0xffff0001},
+{10300000, DIF_BPF_COEFF23,    0x00090005},
+{10300000, DIF_BPF_COEFF45,    0xffe4ffd6},
+{10300000, DIF_BPF_COEFF67,    0x0025007e},
+{10300000, DIF_BPF_COEFF89,    0x0014ff20},
+{10300000, DIF_BPF_COEFF1011,  0xff3c00f0},
+{10300000, DIF_BPF_COEFF1213,  0x01e1ffd0},
+{10300000, DIF_BPF_COEFF1415,  0xfd12fe5c},
+{10300000, DIF_BPF_COEFF1617,  0x03110433},
+{10300000, DIF_BPF_COEFF1819,  0xfe88f996},
+{10300000, DIF_BPF_COEFF2021,  0xfdf106d1},
+{10300000, DIF_BPF_COEFF2223,  0x06aafbb7},
+{10300000, DIF_BPF_COEFF2425,  0xf57efed8},
+{10300000, DIF_BPF_COEFF2627,  0x0b7e07ff},
+{10300000, DIF_BPF_COEFF2829,  0xf7b0f25e},
+{10300000, DIF_BPF_COEFF3031,  0x01560f7a},
+{10300000, DIF_BPF_COEFF3233,  0x0745f3c7},
+{10300000, DIF_BPF_COEFF3435,  0xf1ac04a4},
+{10300000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 103_quant.dat*/
+
+
+/*case 10400000:*/
+/* BEGIN - DIF BPF register values from 104_quant.dat*/
+{10400000, DIF_BPF_COEFF01,    0xffffffff},
+{10400000, DIF_BPF_COEFF23,    0x0008000c},
+{10400000, DIF_BPF_COEFF45,    0xffedffcb},
+{10400000, DIF_BPF_COEFF67,    0x0005007d},
+{10400000, DIF_BPF_COEFF89,    0x0050ff4c},
+{10400000, DIF_BPF_COEFF1011,  0xfef6007e},
+{10400000, DIF_BPF_COEFF1213,  0x01ff0086},
+{10400000, DIF_BPF_COEFF1415,  0xfd58fd97},
+{10400000, DIF_BPF_COEFF1617,  0x024104ad},
+{10400000, DIF_BPF_COEFF1819,  0xffcaf9c0},
+{10400000, DIF_BPF_COEFF2021,  0xfc9905e2},
+{10400000, DIF_BPF_COEFF2223,  0x079afd35},
+{10400000, DIF_BPF_COEFF2425,  0xf555fd46},
+{10400000, DIF_BPF_COEFF2627,  0x0ad50920},
+{10400000, DIF_BPF_COEFF2829,  0xf8d9f1f6},
+{10400000, DIF_BPF_COEFF3031,  0x00310f43},
+{10400000, DIF_BPF_COEFF3233,  0x07fdf435},
+{10400000, DIF_BPF_COEFF3435,  0xf174046f},
+{10400000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 104_quant.dat*/
+
+
+/*case 10500000:*/
+/* BEGIN - DIF BPF register values from 105_quant.dat*/
+{10500000, DIF_BPF_COEFF01,    0xfffffffe},
+{10500000, DIF_BPF_COEFF23,    0x00050011},
+{10500000, DIF_BPF_COEFF45,    0xfffaffc8},
+{10500000, DIF_BPF_COEFF67,    0xffe5006b},
+{10500000, DIF_BPF_COEFF89,    0x0082ff8c},
+{10500000, DIF_BPF_COEFF1011,  0xfecc0000},
+{10500000, DIF_BPF_COEFF1213,  0x01f00130},
+{10500000, DIF_BPF_COEFF1415,  0xfdd2fcfc},
+{10500000, DIF_BPF_COEFF1617,  0x014d04e3},
+{10500000, DIF_BPF_COEFF1819,  0x010efa32},
+{10500000, DIF_BPF_COEFF2021,  0xfb6404bf},
+{10500000, DIF_BPF_COEFF2223,  0x084efec5},
+{10500000, DIF_BPF_COEFF2425,  0xf569fbc2},
+{10500000, DIF_BPF_COEFF2627,  0x0a000a23},
+{10500000, DIF_BPF_COEFF2829,  0xfa15f1ab},
+{10500000, DIF_BPF_COEFF3031,  0xff0b0efc},
+{10500000, DIF_BPF_COEFF3233,  0x08b0f4a7},
+{10500000, DIF_BPF_COEFF3435,  0xf13f043a},
+{10500000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 105_quant.dat*/
+
+
+/*case 10600000:*/
+/* BEGIN - DIF BPF register values from 106_quant.dat*/
+{10600000, DIF_BPF_COEFF01,    0x0000fffd},
+{10600000, DIF_BPF_COEFF23,    0x00020012},
+{10600000, DIF_BPF_COEFF45,    0x0007ffcd},
+{10600000, DIF_BPF_COEFF67,    0xffc9004c},
+{10600000, DIF_BPF_COEFF89,    0x00a4ffd9},
+{10600000, DIF_BPF_COEFF1011,  0xfec3ff82},
+{10600000, DIF_BPF_COEFF1213,  0x01b401c1},
+{10600000, DIF_BPF_COEFF1415,  0xfe76fc97},
+{10600000, DIF_BPF_COEFF1617,  0x004404d2},
+{10600000, DIF_BPF_COEFF1819,  0x0245fae8},
+{10600000, DIF_BPF_COEFF2021,  0xfa5f0370},
+{10600000, DIF_BPF_COEFF2223,  0x08c1005f},
+{10600000, DIF_BPF_COEFF2425,  0xf5bcfa52},
+{10600000, DIF_BPF_COEFF2627,  0x09020b04},
+{10600000, DIF_BPF_COEFF2829,  0xfb60f17b},
+{10600000, DIF_BPF_COEFF3031,  0xfde70ea6},
+{10600000, DIF_BPF_COEFF3233,  0x095df51e},
+{10600000, DIF_BPF_COEFF3435,  0xf10c0405},
+{10600000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 106_quant.dat*/
+
+
+/*case 10700000:*/
+/* BEGIN - DIF BPF register values from 107_quant.dat*/
+{10700000, DIF_BPF_COEFF01,    0x0000fffd},
+{10700000, DIF_BPF_COEFF23,    0xffff0011},
+{10700000, DIF_BPF_COEFF45,    0x0014ffdb},
+{10700000, DIF_BPF_COEFF67,    0xffb40023},
+{10700000, DIF_BPF_COEFF89,    0x00b2002a},
+{10700000, DIF_BPF_COEFF1011,  0xfedbff10},
+{10700000, DIF_BPF_COEFF1213,  0x0150022d},
+{10700000, DIF_BPF_COEFF1415,  0xff38fc6f},
+{10700000, DIF_BPF_COEFF1617,  0xff36047b},
+{10700000, DIF_BPF_COEFF1819,  0x035efbda},
+{10700000, DIF_BPF_COEFF2021,  0xf9940202},
+{10700000, DIF_BPF_COEFF2223,  0x08ee01f5},
+{10700000, DIF_BPF_COEFF2425,  0xf649f8fe},
+{10700000, DIF_BPF_COEFF2627,  0x07e10bc2},
+{10700000, DIF_BPF_COEFF2829,  0xfcb6f169},
+{10700000, DIF_BPF_COEFF3031,  0xfcc60e42},
+{10700000, DIF_BPF_COEFF3233,  0x0a04f599},
+{10700000, DIF_BPF_COEFF3435,  0xf0db03d0},
+{10700000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 107_quant.dat*/
+
+
+/*case 10800000:*/
+/* BEGIN - DIF BPF register values from 108_quant.dat*/
+{10800000, DIF_BPF_COEFF01,    0x0000fffd},
+{10800000, DIF_BPF_COEFF23,    0xfffb000d},
+{10800000, DIF_BPF_COEFF45,    0x001dffed},
+{10800000, DIF_BPF_COEFF67,    0xffaafff5},
+{10800000, DIF_BPF_COEFF89,    0x00aa0077},
+{10800000, DIF_BPF_COEFF1011,  0xff13feb6},
+{10800000, DIF_BPF_COEFF1213,  0x00ce026b},
+{10800000, DIF_BPF_COEFF1415,  0x000afc85},
+{10800000, DIF_BPF_COEFF1617,  0xfe3503e3},
+{10800000, DIF_BPF_COEFF1819,  0x044cfcfb},
+{10800000, DIF_BPF_COEFF2021,  0xf90c0082},
+{10800000, DIF_BPF_COEFF2223,  0x08d5037f},
+{10800000, DIF_BPF_COEFF2425,  0xf710f7cc},
+{10800000, DIF_BPF_COEFF2627,  0x069f0c59},
+{10800000, DIF_BPF_COEFF2829,  0xfe16f173},
+{10800000, DIF_BPF_COEFF3031,  0xfbaa0dcf},
+{10800000, DIF_BPF_COEFF3233,  0x0aa5f617},
+{10800000, DIF_BPF_COEFF3435,  0xf0ad039b},
+{10800000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 108_quant.dat*/
+
+
+/*case 10900000:*/
+/* BEGIN - DIF BPF register values from 109_quant.dat*/
+{10900000, DIF_BPF_COEFF01,    0x0000fffe},
+{10900000, DIF_BPF_COEFF23,    0xfff90006},
+{10900000, DIF_BPF_COEFF45,    0x00210003},
+{10900000, DIF_BPF_COEFF67,    0xffacffc8},
+{10900000, DIF_BPF_COEFF89,    0x008e00b6},
+{10900000, DIF_BPF_COEFF1011,  0xff63fe7c},
+{10900000, DIF_BPF_COEFF1213,  0x003a0275},
+{10900000, DIF_BPF_COEFF1415,  0x00dafcda},
+{10900000, DIF_BPF_COEFF1617,  0xfd510313},
+{10900000, DIF_BPF_COEFF1819,  0x0501fe40},
+{10900000, DIF_BPF_COEFF2021,  0xf8cbfefd},
+{10900000, DIF_BPF_COEFF2223,  0x087604f0},
+{10900000, DIF_BPF_COEFF2425,  0xf80af6c2},
+{10900000, DIF_BPF_COEFF2627,  0x05430cc8},
+{10900000, DIF_BPF_COEFF2829,  0xff7af19a},
+{10900000, DIF_BPF_COEFF3031,  0xfa940d4e},
+{10900000, DIF_BPF_COEFF3233,  0x0b3ff699},
+{10900000, DIF_BPF_COEFF3435,  0xf0810365},
+{10900000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 109_quant.dat*/
+
+
+/*case 11000000:*/
+/* BEGIN - DIF BPF register values from 110_quant.dat*/
+{11000000, DIF_BPF_COEFF01,    0x0001ffff},
+{11000000, DIF_BPF_COEFF23,    0xfff8ffff},
+{11000000, DIF_BPF_COEFF45,    0x00210018},
+{11000000, DIF_BPF_COEFF67,    0xffbaffa3},
+{11000000, DIF_BPF_COEFF89,    0x006000e1},
+{11000000, DIF_BPF_COEFF1011,  0xffc4fe68},
+{11000000, DIF_BPF_COEFF1213,  0xffa0024b},
+{11000000, DIF_BPF_COEFF1415,  0x019afd66},
+{11000000, DIF_BPF_COEFF1617,  0xfc990216},
+{11000000, DIF_BPF_COEFF1819,  0x0575ff99},
+{11000000, DIF_BPF_COEFF2021,  0xf8d4fd81},
+{11000000, DIF_BPF_COEFF2223,  0x07d40640},
+{11000000, DIF_BPF_COEFF2425,  0xf932f5e6},
+{11000000, DIF_BPF_COEFF2627,  0x03d20d0d},
+{11000000, DIF_BPF_COEFF2829,  0x00dff1de},
+{11000000, DIF_BPF_COEFF3031,  0xf9860cbf},
+{11000000, DIF_BPF_COEFF3233,  0x0bd1f71e},
+{11000000, DIF_BPF_COEFF3435,  0xf058032f},
+{11000000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 110_quant.dat*/
+
+
+/*case 11100000:*/
+/* BEGIN - DIF BPF register values from 111_quant.dat*/
+{11100000, DIF_BPF_COEFF01,    0x00010000},
+{11100000, DIF_BPF_COEFF23,    0xfff8fff8},
+{11100000, DIF_BPF_COEFF45,    0x001b0029},
+{11100000, DIF_BPF_COEFF67,    0xffd1ff8a},
+{11100000, DIF_BPF_COEFF89,    0x002600f2},
+{11100000, DIF_BPF_COEFF1011,  0x002cfe7c},
+{11100000, DIF_BPF_COEFF1213,  0xff0f01f0},
+{11100000, DIF_BPF_COEFF1415,  0x023bfe20},
+{11100000, DIF_BPF_COEFF1617,  0xfc1700fa},
+{11100000, DIF_BPF_COEFF1819,  0x05a200f7},
+{11100000, DIF_BPF_COEFF2021,  0xf927fc1c},
+{11100000, DIF_BPF_COEFF2223,  0x06f40765},
+{11100000, DIF_BPF_COEFF2425,  0xfa82f53b},
+{11100000, DIF_BPF_COEFF2627,  0x02510d27},
+{11100000, DIF_BPF_COEFF2829,  0x0243f23d},
+{11100000, DIF_BPF_COEFF3031,  0xf8810c24},
+{11100000, DIF_BPF_COEFF3233,  0x0c5cf7a7},
+{11100000, DIF_BPF_COEFF3435,  0xf03102fa},
+{11100000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 111_quant.dat*/
+
+
+/*case 11200000:*/
+/* BEGIN - DIF BPF register values from 112_quant.dat*/
+{11200000, DIF_BPF_COEFF01,    0x00010002},
+{11200000, DIF_BPF_COEFF23,    0xfffafff2},
+{11200000, DIF_BPF_COEFF45,    0x00110035},
+{11200000, DIF_BPF_COEFF67,    0xfff0ff81},
+{11200000, DIF_BPF_COEFF89,    0xffe700e7},
+{11200000, DIF_BPF_COEFF1011,  0x008ffeb6},
+{11200000, DIF_BPF_COEFF1213,  0xfe94016d},
+{11200000, DIF_BPF_COEFF1415,  0x02b0fefb},
+{11200000, DIF_BPF_COEFF1617,  0xfbd3ffd1},
+{11200000, DIF_BPF_COEFF1819,  0x05850249},
+{11200000, DIF_BPF_COEFF2021,  0xf9c1fadb},
+{11200000, DIF_BPF_COEFF2223,  0x05de0858},
+{11200000, DIF_BPF_COEFF2425,  0xfbf2f4c4},
+{11200000, DIF_BPF_COEFF2627,  0x00c70d17},
+{11200000, DIF_BPF_COEFF2829,  0x03a0f2b8},
+{11200000, DIF_BPF_COEFF3031,  0xf7870b7c},
+{11200000, DIF_BPF_COEFF3233,  0x0cdff833},
+{11200000, DIF_BPF_COEFF3435,  0xf00d02c4},
+{11200000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 112_quant.dat*/
+
+
+/*case 11300000:*/
+/* BEGIN - DIF BPF register values from 113_quant.dat*/
+{11300000, DIF_BPF_COEFF01,    0x00000003},
+{11300000, DIF_BPF_COEFF23,    0xfffdffee},
+{11300000, DIF_BPF_COEFF45,    0x00040038},
+{11300000, DIF_BPF_COEFF67,    0x0010ff88},
+{11300000, DIF_BPF_COEFF89,    0xffac00c2},
+{11300000, DIF_BPF_COEFF1011,  0x00e2ff10},
+{11300000, DIF_BPF_COEFF1213,  0xfe3900cb},
+{11300000, DIF_BPF_COEFF1415,  0x02f1ffe9},
+{11300000, DIF_BPF_COEFF1617,  0xfbd3feaa},
+{11300000, DIF_BPF_COEFF1819,  0x05210381},
+{11300000, DIF_BPF_COEFF2021,  0xfa9cf9c8},
+{11300000, DIF_BPF_COEFF2223,  0x04990912},
+{11300000, DIF_BPF_COEFF2425,  0xfd7af484},
+{11300000, DIF_BPF_COEFF2627,  0xff390cdb},
+{11300000, DIF_BPF_COEFF2829,  0x04f4f34d},
+{11300000, DIF_BPF_COEFF3031,  0xf69a0ac9},
+{11300000, DIF_BPF_COEFF3233,  0x0d5af8c1},
+{11300000, DIF_BPF_COEFF3435,  0xefec028e},
+{11300000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 113_quant.dat*/
+
+
+/*case 11400000:*/
+/* BEGIN - DIF BPF register values from 114_quant.dat*/
+{11400000, DIF_BPF_COEFF01,    0x00000003},
+{11400000, DIF_BPF_COEFF23,    0x0000ffee},
+{11400000, DIF_BPF_COEFF45,    0xfff60033},
+{11400000, DIF_BPF_COEFF67,    0x002fff9f},
+{11400000, DIF_BPF_COEFF89,    0xff7b0087},
+{11400000, DIF_BPF_COEFF1011,  0x011eff82},
+{11400000, DIF_BPF_COEFF1213,  0xfe080018},
+{11400000, DIF_BPF_COEFF1415,  0x02f900d8},
+{11400000, DIF_BPF_COEFF1617,  0xfc17fd96},
+{11400000, DIF_BPF_COEFF1819,  0x04790490},
+{11400000, DIF_BPF_COEFF2021,  0xfbadf8ed},
+{11400000, DIF_BPF_COEFF2223,  0x032f098e},
+{11400000, DIF_BPF_COEFF2425,  0xff10f47d},
+{11400000, DIF_BPF_COEFF2627,  0xfdaf0c75},
+{11400000, DIF_BPF_COEFF2829,  0x063cf3fc},
+{11400000, DIF_BPF_COEFF3031,  0xf5ba0a0b},
+{11400000, DIF_BPF_COEFF3233,  0x0dccf952},
+{11400000, DIF_BPF_COEFF3435,  0xefcd0258},
+{11400000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 114_quant.dat*/
+
+
+/*case 11500000:*/
+/* BEGIN - DIF BPF register values from 115_quant.dat*/
+{11500000, DIF_BPF_COEFF01,    0x00000003},
+{11500000, DIF_BPF_COEFF23,    0x0004fff1},
+{11500000, DIF_BPF_COEFF45,    0xffea0026},
+{11500000, DIF_BPF_COEFF67,    0x0046ffc3},
+{11500000, DIF_BPF_COEFF89,    0xff5a003c},
+{11500000, DIF_BPF_COEFF1011,  0x013b0000},
+{11500000, DIF_BPF_COEFF1213,  0xfe04ff63},
+{11500000, DIF_BPF_COEFF1415,  0x02c801b8},
+{11500000, DIF_BPF_COEFF1617,  0xfc99fca6},
+{11500000, DIF_BPF_COEFF1819,  0x0397056a},
+{11500000, DIF_BPF_COEFF2021,  0xfcecf853},
+{11500000, DIF_BPF_COEFF2223,  0x01ad09c9},
+{11500000, DIF_BPF_COEFF2425,  0x00acf4ad},
+{11500000, DIF_BPF_COEFF2627,  0xfc2e0be7},
+{11500000, DIF_BPF_COEFF2829,  0x0773f4c2},
+{11500000, DIF_BPF_COEFF3031,  0xf4e90943},
+{11500000, DIF_BPF_COEFF3233,  0x0e35f9e6},
+{11500000, DIF_BPF_COEFF3435,  0xefb10221},
+{11500000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 115_quant.dat*/
+
+
+/*case 11600000:*/
+/* BEGIN - DIF BPF register values from 116_quant.dat*/
+{11600000, DIF_BPF_COEFF01,    0x00000002},
+{11600000, DIF_BPF_COEFF23,    0x0007fff6},
+{11600000, DIF_BPF_COEFF45,    0xffe20014},
+{11600000, DIF_BPF_COEFF67,    0x0054ffee},
+{11600000, DIF_BPF_COEFF89,    0xff4effeb},
+{11600000, DIF_BPF_COEFF1011,  0x0137007e},
+{11600000, DIF_BPF_COEFF1213,  0xfe2efebb},
+{11600000, DIF_BPF_COEFF1415,  0x0260027a},
+{11600000, DIF_BPF_COEFF1617,  0xfd51fbe6},
+{11600000, DIF_BPF_COEFF1819,  0x02870605},
+{11600000, DIF_BPF_COEFF2021,  0xfe4af7fe},
+{11600000, DIF_BPF_COEFF2223,  0x001d09c1},
+{11600000, DIF_BPF_COEFF2425,  0x0243f515},
+{11600000, DIF_BPF_COEFF2627,  0xfabd0b32},
+{11600000, DIF_BPF_COEFF2829,  0x0897f59e},
+{11600000, DIF_BPF_COEFF3031,  0xf4280871},
+{11600000, DIF_BPF_COEFF3233,  0x0e95fa7c},
+{11600000, DIF_BPF_COEFF3435,  0xef9701eb},
+{11600000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 116_quant.dat*/
+
+
+/*case 11700000:*/
+/* BEGIN - DIF BPF register values from 117_quant.dat*/
+{11700000, DIF_BPF_COEFF01,    0xffff0001},
+{11700000, DIF_BPF_COEFF23,    0x0008fffd},
+{11700000, DIF_BPF_COEFF45,    0xffdeffff},
+{11700000, DIF_BPF_COEFF67,    0x0056001d},
+{11700000, DIF_BPF_COEFF89,    0xff57ff9c},
+{11700000, DIF_BPF_COEFF1011,  0x011300f0},
+{11700000, DIF_BPF_COEFF1213,  0xfe82fe2e},
+{11700000, DIF_BPF_COEFF1415,  0x01ca0310},
+{11700000, DIF_BPF_COEFF1617,  0xfe35fb62},
+{11700000, DIF_BPF_COEFF1819,  0x0155065a},
+{11700000, DIF_BPF_COEFF2021,  0xffbaf7f2},
+{11700000, DIF_BPF_COEFF2223,  0xfe8c0977},
+{11700000, DIF_BPF_COEFF2425,  0x03cef5b2},
+{11700000, DIF_BPF_COEFF2627,  0xf9610a58},
+{11700000, DIF_BPF_COEFF2829,  0x09a5f68f},
+{11700000, DIF_BPF_COEFF3031,  0xf3790797},
+{11700000, DIF_BPF_COEFF3233,  0x0eebfb14},
+{11700000, DIF_BPF_COEFF3435,  0xef8001b5},
+{11700000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 117_quant.dat*/
+
+
+/*case 11800000:*/
+/* BEGIN - DIF BPF register values from 118_quant.dat*/
+{11800000, DIF_BPF_COEFF01,    0xffff0000},
+{11800000, DIF_BPF_COEFF23,    0x00080004},
+{11800000, DIF_BPF_COEFF45,    0xffe0ffe9},
+{11800000, DIF_BPF_COEFF67,    0x004c0047},
+{11800000, DIF_BPF_COEFF89,    0xff75ff58},
+{11800000, DIF_BPF_COEFF1011,  0x00d1014a},
+{11800000, DIF_BPF_COEFF1213,  0xfef9fdc8},
+{11800000, DIF_BPF_COEFF1415,  0x0111036f},
+{11800000, DIF_BPF_COEFF1617,  0xff36fb21},
+{11800000, DIF_BPF_COEFF1819,  0x00120665},
+{11800000, DIF_BPF_COEFF2021,  0x012df82e},
+{11800000, DIF_BPF_COEFF2223,  0xfd0708ec},
+{11800000, DIF_BPF_COEFF2425,  0x0542f682},
+{11800000, DIF_BPF_COEFF2627,  0xf81f095c},
+{11800000, DIF_BPF_COEFF2829,  0x0a9af792},
+{11800000, DIF_BPF_COEFF3031,  0xf2db06b5},
+{11800000, DIF_BPF_COEFF3233,  0x0f38fbad},
+{11800000, DIF_BPF_COEFF3435,  0xef6c017e},
+{11800000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 118_quant.dat*/
+
+
+/*case 11900000:*/
+/* BEGIN - DIF BPF register values from 119_quant.dat*/
+{11900000, DIF_BPF_COEFF01,    0xffffffff},
+{11900000, DIF_BPF_COEFF23,    0x0007000b},
+{11900000, DIF_BPF_COEFF45,    0xffe7ffd8},
+{11900000, DIF_BPF_COEFF67,    0x00370068},
+{11900000, DIF_BPF_COEFF89,    0xffa4ff28},
+{11900000, DIF_BPF_COEFF1011,  0x00790184},
+{11900000, DIF_BPF_COEFF1213,  0xff87fd91},
+{11900000, DIF_BPF_COEFF1415,  0x00430392},
+{11900000, DIF_BPF_COEFF1617,  0x0044fb26},
+{11900000, DIF_BPF_COEFF1819,  0xfece0626},
+{11900000, DIF_BPF_COEFF2021,  0x0294f8b2},
+{11900000, DIF_BPF_COEFF2223,  0xfb990825},
+{11900000, DIF_BPF_COEFF2425,  0x0698f77f},
+{11900000, DIF_BPF_COEFF2627,  0xf6fe0842},
+{11900000, DIF_BPF_COEFF2829,  0x0b73f8a7},
+{11900000, DIF_BPF_COEFF3031,  0xf25105cd},
+{11900000, DIF_BPF_COEFF3233,  0x0f7bfc48},
+{11900000, DIF_BPF_COEFF3435,  0xef5a0148},
+{11900000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 119_quant.dat*/
+
+
+/*case 12000000:*/
+/* BEGIN - DIF BPF register values from 120_quant.dat*/
+{12000000, DIF_BPF_COEFF01,    0x0000fffe},
+{12000000, DIF_BPF_COEFF23,    0x00050010},
+{12000000, DIF_BPF_COEFF45,    0xfff2ffcc},
+{12000000, DIF_BPF_COEFF67,    0x001b007b},
+{12000000, DIF_BPF_COEFF89,    0xffdfff10},
+{12000000, DIF_BPF_COEFF1011,  0x00140198},
+{12000000, DIF_BPF_COEFF1213,  0x0020fd8e},
+{12000000, DIF_BPF_COEFF1415,  0xff710375},
+{12000000, DIF_BPF_COEFF1617,  0x014dfb73},
+{12000000, DIF_BPF_COEFF1819,  0xfd9a059f},
+{12000000, DIF_BPF_COEFF2021,  0x03e0f978},
+{12000000, DIF_BPF_COEFF2223,  0xfa4e0726},
+{12000000, DIF_BPF_COEFF2425,  0x07c8f8a7},
+{12000000, DIF_BPF_COEFF2627,  0xf600070c},
+{12000000, DIF_BPF_COEFF2829,  0x0c2ff9c9},
+{12000000, DIF_BPF_COEFF3031,  0xf1db04de},
+{12000000, DIF_BPF_COEFF3233,  0x0fb4fce5},
+{12000000, DIF_BPF_COEFF3435,  0xef4b0111},
+{12000000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 120_quant.dat*/
+
+
+/*case 12100000:*/
+/* BEGIN - DIF BPF register values from 121_quant.dat*/
+{12100000, DIF_BPF_COEFF01,    0x0000fffd},
+{12100000, DIF_BPF_COEFF23,    0x00010012},
+{12100000, DIF_BPF_COEFF45,    0xffffffc8},
+{12100000, DIF_BPF_COEFF67,    0xfffb007e},
+{12100000, DIF_BPF_COEFF89,    0x001dff14},
+{12100000, DIF_BPF_COEFF1011,  0xffad0184},
+{12100000, DIF_BPF_COEFF1213,  0x00b7fdbe},
+{12100000, DIF_BPF_COEFF1415,  0xfea9031b},
+{12100000, DIF_BPF_COEFF1617,  0x0241fc01},
+{12100000, DIF_BPF_COEFF1819,  0xfc8504d6},
+{12100000, DIF_BPF_COEFF2021,  0x0504fa79},
+{12100000, DIF_BPF_COEFF2223,  0xf93005f6},
+{12100000, DIF_BPF_COEFF2425,  0x08caf9f2},
+{12100000, DIF_BPF_COEFF2627,  0xf52b05c0},
+{12100000, DIF_BPF_COEFF2829,  0x0ccbfaf9},
+{12100000, DIF_BPF_COEFF3031,  0xf17903eb},
+{12100000, DIF_BPF_COEFF3233,  0x0fe3fd83},
+{12100000, DIF_BPF_COEFF3435,  0xef3f00db},
+{12100000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 121_quant.dat*/
+
+
+/*case 12200000:*/
+/* BEGIN - DIF BPF register values from 122_quant.dat*/
+{12200000, DIF_BPF_COEFF01,    0x0000fffd},
+{12200000, DIF_BPF_COEFF23,    0xfffe0011},
+{12200000, DIF_BPF_COEFF45,    0x000cffcc},
+{12200000, DIF_BPF_COEFF67,    0xffdb0071},
+{12200000, DIF_BPF_COEFF89,    0x0058ff32},
+{12200000, DIF_BPF_COEFF1011,  0xff4f014a},
+{12200000, DIF_BPF_COEFF1213,  0x013cfe1f},
+{12200000, DIF_BPF_COEFF1415,  0xfdfb028a},
+{12200000, DIF_BPF_COEFF1617,  0x0311fcc9},
+{12200000, DIF_BPF_COEFF1819,  0xfb9d03d6},
+{12200000, DIF_BPF_COEFF2021,  0x05f4fbad},
+{12200000, DIF_BPF_COEFF2223,  0xf848049d},
+{12200000, DIF_BPF_COEFF2425,  0x0999fb5b},
+{12200000, DIF_BPF_COEFF2627,  0xf4820461},
+{12200000, DIF_BPF_COEFF2829,  0x0d46fc32},
+{12200000, DIF_BPF_COEFF3031,  0xf12d02f4},
+{12200000, DIF_BPF_COEFF3233,  0x1007fe21},
+{12200000, DIF_BPF_COEFF3435,  0xef3600a4},
+{12200000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 122_quant.dat*/
+
+
+/*case 12300000:*/
+/* BEGIN - DIF BPF register values from 123_quant.dat*/
+{12300000, DIF_BPF_COEFF01,    0x0000fffe},
+{12300000, DIF_BPF_COEFF23,    0xfffa000e},
+{12300000, DIF_BPF_COEFF45,    0x0017ffd9},
+{12300000, DIF_BPF_COEFF67,    0xffc10055},
+{12300000, DIF_BPF_COEFF89,    0x0088ff68},
+{12300000, DIF_BPF_COEFF1011,  0xff0400f0},
+{12300000, DIF_BPF_COEFF1213,  0x01a6fea7},
+{12300000, DIF_BPF_COEFF1415,  0xfd7501cc},
+{12300000, DIF_BPF_COEFF1617,  0x03b0fdc0},
+{12300000, DIF_BPF_COEFF1819,  0xfaef02a8},
+{12300000, DIF_BPF_COEFF2021,  0x06a7fd07},
+{12300000, DIF_BPF_COEFF2223,  0xf79d0326},
+{12300000, DIF_BPF_COEFF2425,  0x0a31fcda},
+{12300000, DIF_BPF_COEFF2627,  0xf40702f3},
+{12300000, DIF_BPF_COEFF2829,  0x0d9ffd72},
+{12300000, DIF_BPF_COEFF3031,  0xf0f601fa},
+{12300000, DIF_BPF_COEFF3233,  0x1021fec0},
+{12300000, DIF_BPF_COEFF3435,  0xef2f006d},
+{12300000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 123_quant.dat*/
+
+
+/*case 12400000:*/
+/* BEGIN - DIF BPF register values from 124_quant.dat*/
+{12400000, DIF_BPF_COEFF01,    0x0001ffff},
+{12400000, DIF_BPF_COEFF23,    0xfff80007},
+{12400000, DIF_BPF_COEFF45,    0x001fffeb},
+{12400000, DIF_BPF_COEFF67,    0xffaf002d},
+{12400000, DIF_BPF_COEFF89,    0x00a8ffb0},
+{12400000, DIF_BPF_COEFF1011,  0xfed3007e},
+{12400000, DIF_BPF_COEFF1213,  0x01e9ff4c},
+{12400000, DIF_BPF_COEFF1415,  0xfd2000ee},
+{12400000, DIF_BPF_COEFF1617,  0x0413fed8},
+{12400000, DIF_BPF_COEFF1819,  0xfa82015c},
+{12400000, DIF_BPF_COEFF2021,  0x0715fe7d},
+{12400000, DIF_BPF_COEFF2223,  0xf7340198},
+{12400000, DIF_BPF_COEFF2425,  0x0a8dfe69},
+{12400000, DIF_BPF_COEFF2627,  0xf3bd017c},
+{12400000, DIF_BPF_COEFF2829,  0x0dd5feb8},
+{12400000, DIF_BPF_COEFF3031,  0xf0d500fd},
+{12400000, DIF_BPF_COEFF3233,  0x1031ff60},
+{12400000, DIF_BPF_COEFF3435,  0xef2b0037},
+{12400000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 124_quant.dat*/
+
+
+/*case 12500000:*/
+/* BEGIN - DIF BPF register values from 125_quant.dat*/
+{12500000, DIF_BPF_COEFF01,    0x00010000},
+{12500000, DIF_BPF_COEFF23,    0xfff70000},
+{12500000, DIF_BPF_COEFF45,    0x00220000},
+{12500000, DIF_BPF_COEFF67,    0xffa90000},
+{12500000, DIF_BPF_COEFF89,    0x00b30000},
+{12500000, DIF_BPF_COEFF1011,  0xfec20000},
+{12500000, DIF_BPF_COEFF1213,  0x02000000},
+{12500000, DIF_BPF_COEFF1415,  0xfd030000},
+{12500000, DIF_BPF_COEFF1617,  0x04350000},
+{12500000, DIF_BPF_COEFF1819,  0xfa5e0000},
+{12500000, DIF_BPF_COEFF2021,  0x073b0000},
+{12500000, DIF_BPF_COEFF2223,  0xf7110000},
+{12500000, DIF_BPF_COEFF2425,  0x0aac0000},
+{12500000, DIF_BPF_COEFF2627,  0xf3a40000},
+{12500000, DIF_BPF_COEFF2829,  0x0de70000},
+{12500000, DIF_BPF_COEFF3031,  0xf0c90000},
+{12500000, DIF_BPF_COEFF3233,  0x10360000},
+{12500000, DIF_BPF_COEFF3435,  0xef290000},
+{12500000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 125_quant.dat*/
+
+
+/*case 12600000:*/
+/* BEGIN - DIF BPF register values from 126_quant.dat*/
+{12600000, DIF_BPF_COEFF01,    0x00010001},
+{12600000, DIF_BPF_COEFF23,    0xfff8fff9},
+{12600000, DIF_BPF_COEFF45,    0x001f0015},
+{12600000, DIF_BPF_COEFF67,    0xffafffd3},
+{12600000, DIF_BPF_COEFF89,    0x00a80050},
+{12600000, DIF_BPF_COEFF1011,  0xfed3ff82},
+{12600000, DIF_BPF_COEFF1213,  0x01e900b4},
+{12600000, DIF_BPF_COEFF1415,  0xfd20ff12},
+{12600000, DIF_BPF_COEFF1617,  0x04130128},
+{12600000, DIF_BPF_COEFF1819,  0xfa82fea4},
+{12600000, DIF_BPF_COEFF2021,  0x07150183},
+{12600000, DIF_BPF_COEFF2223,  0xf734fe68},
+{12600000, DIF_BPF_COEFF2425,  0x0a8d0197},
+{12600000, DIF_BPF_COEFF2627,  0xf3bdfe84},
+{12600000, DIF_BPF_COEFF2829,  0x0dd50148},
+{12600000, DIF_BPF_COEFF3031,  0xf0d5ff03},
+{12600000, DIF_BPF_COEFF3233,  0x103100a0},
+{12600000, DIF_BPF_COEFF3435,  0xef2bffc9},
+{12600000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 126_quant.dat*/
+
+
+/*case 12700000:*/
+/* BEGIN - DIF BPF register values from 127_quant.dat*/
+{12700000, DIF_BPF_COEFF01,    0x00000002},
+{12700000, DIF_BPF_COEFF23,    0xfffafff2},
+{12700000, DIF_BPF_COEFF45,    0x00170027},
+{12700000, DIF_BPF_COEFF67,    0xffc1ffab},
+{12700000, DIF_BPF_COEFF89,    0x00880098},
+{12700000, DIF_BPF_COEFF1011,  0xff04ff10},
+{12700000, DIF_BPF_COEFF1213,  0x01a60159},
+{12700000, DIF_BPF_COEFF1415,  0xfd75fe34},
+{12700000, DIF_BPF_COEFF1617,  0x03b00240},
+{12700000, DIF_BPF_COEFF1819,  0xfaeffd58},
+{12700000, DIF_BPF_COEFF2021,  0x06a702f9},
+{12700000, DIF_BPF_COEFF2223,  0xf79dfcda},
+{12700000, DIF_BPF_COEFF2425,  0x0a310326},
+{12700000, DIF_BPF_COEFF2627,  0xf407fd0d},
+{12700000, DIF_BPF_COEFF2829,  0x0d9f028e},
+{12700000, DIF_BPF_COEFF3031,  0xf0f6fe06},
+{12700000, DIF_BPF_COEFF3233,  0x10210140},
+{12700000, DIF_BPF_COEFF3435,  0xef2fff93},
+{12700000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 127_quant.dat*/
+
+
+/*case 12800000:*/
+/* BEGIN - DIF BPF register values from 128_quant.dat*/
+{12800000, DIF_BPF_COEFF01,    0x00000003},
+{12800000, DIF_BPF_COEFF23,    0xfffeffef},
+{12800000, DIF_BPF_COEFF45,    0x000c0034},
+{12800000, DIF_BPF_COEFF67,    0xffdbff8f},
+{12800000, DIF_BPF_COEFF89,    0x005800ce},
+{12800000, DIF_BPF_COEFF1011,  0xff4ffeb6},
+{12800000, DIF_BPF_COEFF1213,  0x013c01e1},
+{12800000, DIF_BPF_COEFF1415,  0xfdfbfd76},
+{12800000, DIF_BPF_COEFF1617,  0x03110337},
+{12800000, DIF_BPF_COEFF1819,  0xfb9dfc2a},
+{12800000, DIF_BPF_COEFF2021,  0x05f40453},
+{12800000, DIF_BPF_COEFF2223,  0xf848fb63},
+{12800000, DIF_BPF_COEFF2425,  0x099904a5},
+{12800000, DIF_BPF_COEFF2627,  0xf482fb9f},
+{12800000, DIF_BPF_COEFF2829,  0x0d4603ce},
+{12800000, DIF_BPF_COEFF3031,  0xf12dfd0c},
+{12800000, DIF_BPF_COEFF3233,  0x100701df},
+{12800000, DIF_BPF_COEFF3435,  0xef36ff5c},
+{12800000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 128_quant.dat*/
+
+
+/*case 12900000:*/
+/* BEGIN - DIF BPF register values from 129_quant.dat*/
+{12900000, DIF_BPF_COEFF01,    0x00000003},
+{12900000, DIF_BPF_COEFF23,    0x0001ffee},
+{12900000, DIF_BPF_COEFF45,    0xffff0038},
+{12900000, DIF_BPF_COEFF67,    0xfffbff82},
+{12900000, DIF_BPF_COEFF89,    0x001d00ec},
+{12900000, DIF_BPF_COEFF1011,  0xffadfe7c},
+{12900000, DIF_BPF_COEFF1213,  0x00b70242},
+{12900000, DIF_BPF_COEFF1415,  0xfea9fce5},
+{12900000, DIF_BPF_COEFF1617,  0x024103ff},
+{12900000, DIF_BPF_COEFF1819,  0xfc85fb2a},
+{12900000, DIF_BPF_COEFF2021,  0x05040587},
+{12900000, DIF_BPF_COEFF2223,  0xf930fa0a},
+{12900000, DIF_BPF_COEFF2425,  0x08ca060e},
+{12900000, DIF_BPF_COEFF2627,  0xf52bfa40},
+{12900000, DIF_BPF_COEFF2829,  0x0ccb0507},
+{12900000, DIF_BPF_COEFF3031,  0xf179fc15},
+{12900000, DIF_BPF_COEFF3233,  0x0fe3027d},
+{12900000, DIF_BPF_COEFF3435,  0xef3fff25},
+{12900000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 129_quant.dat*/
+
+
+/*case 113000000:*/
+/* BEGIN - DIF BPF register values from 130_quant.dat*/
+{13000000, DIF_BPF_COEFF01,    0x00000002},
+{13000000, DIF_BPF_COEFF23,    0x0005fff0},
+{13000000, DIF_BPF_COEFF45,    0xfff20034},
+{13000000, DIF_BPF_COEFF67,    0x001bff85},
+{13000000, DIF_BPF_COEFF89,    0xffdf00f0},
+{13000000, DIF_BPF_COEFF1011,  0x0014fe68},
+{13000000, DIF_BPF_COEFF1213,  0x00200272},
+{13000000, DIF_BPF_COEFF1415,  0xff71fc8b},
+{13000000, DIF_BPF_COEFF1617,  0x014d048d},
+{13000000, DIF_BPF_COEFF1819,  0xfd9afa61},
+{13000000, DIF_BPF_COEFF2021,  0x03e00688},
+{13000000, DIF_BPF_COEFF2223,  0xfa4ef8da},
+{13000000, DIF_BPF_COEFF2425,  0x07c80759},
+{13000000, DIF_BPF_COEFF2627,  0xf600f8f4},
+{13000000, DIF_BPF_COEFF2829,  0x0c2f0637},
+{13000000, DIF_BPF_COEFF3031,  0xf1dbfb22},
+{13000000, DIF_BPF_COEFF3233,  0x0fb4031b},
+{13000000, DIF_BPF_COEFF3435,  0xef4bfeef},
+{13000000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 130_quant.dat*/
+
+
+/*case 13100000:*/
+/* BEGIN - DIF BPF register values from 131_quant.dat*/
+{13100000, DIF_BPF_COEFF01,    0xffff0001},
+{13100000, DIF_BPF_COEFF23,    0x0007fff5},
+{13100000, DIF_BPF_COEFF45,    0xffe70028},
+{13100000, DIF_BPF_COEFF67,    0x0037ff98},
+{13100000, DIF_BPF_COEFF89,    0xffa400d8},
+{13100000, DIF_BPF_COEFF1011,  0x0079fe7c},
+{13100000, DIF_BPF_COEFF1213,  0xff87026f},
+{13100000, DIF_BPF_COEFF1415,  0x0043fc6e},
+{13100000, DIF_BPF_COEFF1617,  0x004404da},
+{13100000, DIF_BPF_COEFF1819,  0xfecef9da},
+{13100000, DIF_BPF_COEFF2021,  0x0294074e},
+{13100000, DIF_BPF_COEFF2223,  0xfb99f7db},
+{13100000, DIF_BPF_COEFF2425,  0x06980881},
+{13100000, DIF_BPF_COEFF2627,  0xf6fef7be},
+{13100000, DIF_BPF_COEFF2829,  0x0b730759},
+{13100000, DIF_BPF_COEFF3031,  0xf251fa33},
+{13100000, DIF_BPF_COEFF3233,  0x0f7b03b8},
+{13100000, DIF_BPF_COEFF3435,  0xef5afeb8},
+{13100000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 131_quant.dat*/
+
+
+/*case 13200000:*/
+/* BEGIN - DIF BPF register values from 132_quant.dat*/
+{13200000, DIF_BPF_COEFF01,    0xffff0000},
+{13200000, DIF_BPF_COEFF23,    0x0008fffc},
+{13200000, DIF_BPF_COEFF45,    0xffe00017},
+{13200000, DIF_BPF_COEFF67,    0x004cffb9},
+{13200000, DIF_BPF_COEFF89,    0xff7500a8},
+{13200000, DIF_BPF_COEFF1011,  0x00d1feb6},
+{13200000, DIF_BPF_COEFF1213,  0xfef90238},
+{13200000, DIF_BPF_COEFF1415,  0x0111fc91},
+{13200000, DIF_BPF_COEFF1617,  0xff3604df},
+{13200000, DIF_BPF_COEFF1819,  0x0012f99b},
+{13200000, DIF_BPF_COEFF2021,  0x012d07d2},
+{13200000, DIF_BPF_COEFF2223,  0xfd07f714},
+{13200000, DIF_BPF_COEFF2425,  0x0542097e},
+{13200000, DIF_BPF_COEFF2627,  0xf81ff6a4},
+{13200000, DIF_BPF_COEFF2829,  0x0a9a086e},
+{13200000, DIF_BPF_COEFF3031,  0xf2dbf94b},
+{13200000, DIF_BPF_COEFF3233,  0x0f380453},
+{13200000, DIF_BPF_COEFF3435,  0xef6cfe82},
+{13200000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 132_quant.dat*/
+
+
+/*case 13300000:*/
+/* BEGIN - DIF BPF register values from 133_quant.dat*/
+{13300000, DIF_BPF_COEFF01,    0xffffffff},
+{13300000, DIF_BPF_COEFF23,    0x00080003},
+{13300000, DIF_BPF_COEFF45,    0xffde0001},
+{13300000, DIF_BPF_COEFF67,    0x0056ffe3},
+{13300000, DIF_BPF_COEFF89,    0xff570064},
+{13300000, DIF_BPF_COEFF1011,  0x0113ff10},
+{13300000, DIF_BPF_COEFF1213,  0xfe8201d2},
+{13300000, DIF_BPF_COEFF1415,  0x01cafcf0},
+{13300000, DIF_BPF_COEFF1617,  0xfe35049e},
+{13300000, DIF_BPF_COEFF1819,  0x0155f9a6},
+{13300000, DIF_BPF_COEFF2021,  0xffba080e},
+{13300000, DIF_BPF_COEFF2223,  0xfe8cf689},
+{13300000, DIF_BPF_COEFF2425,  0x03ce0a4e},
+{13300000, DIF_BPF_COEFF2627,  0xf961f5a8},
+{13300000, DIF_BPF_COEFF2829,  0x09a50971},
+{13300000, DIF_BPF_COEFF3031,  0xf379f869},
+{13300000, DIF_BPF_COEFF3233,  0x0eeb04ec},
+{13300000, DIF_BPF_COEFF3435,  0xef80fe4b},
+{13300000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 133_quant.dat*/
+
+
+/*case 13400000:*/
+/* BEGIN - DIF BPF register values from 134_quant.dat*/
+{13400000, DIF_BPF_COEFF01,    0x0000fffe},
+{13400000, DIF_BPF_COEFF23,    0x0007000a},
+{13400000, DIF_BPF_COEFF45,    0xffe2ffec},
+{13400000, DIF_BPF_COEFF67,    0x00540012},
+{13400000, DIF_BPF_COEFF89,    0xff4e0015},
+{13400000, DIF_BPF_COEFF1011,  0x0137ff82},
+{13400000, DIF_BPF_COEFF1213,  0xfe2e0145},
+{13400000, DIF_BPF_COEFF1415,  0x0260fd86},
+{13400000, DIF_BPF_COEFF1617,  0xfd51041a},
+{13400000, DIF_BPF_COEFF1819,  0x0287f9fb},
+{13400000, DIF_BPF_COEFF2021,  0xfe4a0802},
+{13400000, DIF_BPF_COEFF2223,  0x001df63f},
+{13400000, DIF_BPF_COEFF2425,  0x02430aeb},
+{13400000, DIF_BPF_COEFF2627,  0xfabdf4ce},
+{13400000, DIF_BPF_COEFF2829,  0x08970a62},
+{13400000, DIF_BPF_COEFF3031,  0xf428f78f},
+{13400000, DIF_BPF_COEFF3233,  0x0e950584},
+{13400000, DIF_BPF_COEFF3435,  0xef97fe15},
+{13400000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 134_quant.dat*/
+
+
+/*case 13500000:*/
+/* BEGIN - DIF BPF register values from 135_quant.dat*/
+{13500000, DIF_BPF_COEFF01,    0x0000fffd},
+{13500000, DIF_BPF_COEFF23,    0x0004000f},
+{13500000, DIF_BPF_COEFF45,    0xffeaffda},
+{13500000, DIF_BPF_COEFF67,    0x0046003d},
+{13500000, DIF_BPF_COEFF89,    0xff5affc4},
+{13500000, DIF_BPF_COEFF1011,  0x013b0000},
+{13500000, DIF_BPF_COEFF1213,  0xfe04009d},
+{13500000, DIF_BPF_COEFF1415,  0x02c8fe48},
+{13500000, DIF_BPF_COEFF1617,  0xfc99035a},
+{13500000, DIF_BPF_COEFF1819,  0x0397fa96},
+{13500000, DIF_BPF_COEFF2021,  0xfcec07ad},
+{13500000, DIF_BPF_COEFF2223,  0x01adf637},
+{13500000, DIF_BPF_COEFF2425,  0x00ac0b53},
+{13500000, DIF_BPF_COEFF2627,  0xfc2ef419},
+{13500000, DIF_BPF_COEFF2829,  0x07730b3e},
+{13500000, DIF_BPF_COEFF3031,  0xf4e9f6bd},
+{13500000, DIF_BPF_COEFF3233,  0x0e35061a},
+{13500000, DIF_BPF_COEFF3435,  0xefb1fddf},
+{13500000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 135_quant.dat*/
+
+
+/*case 13600000:*/
+/* BEGIN - DIF BPF register values from 136_quant.dat*/
+{13600000, DIF_BPF_COEFF01,    0x0000fffd},
+{13600000, DIF_BPF_COEFF23,    0x00000012},
+{13600000, DIF_BPF_COEFF45,    0xfff6ffcd},
+{13600000, DIF_BPF_COEFF67,    0x002f0061},
+{13600000, DIF_BPF_COEFF89,    0xff7bff79},
+{13600000, DIF_BPF_COEFF1011,  0x011e007e},
+{13600000, DIF_BPF_COEFF1213,  0xfe08ffe8},
+{13600000, DIF_BPF_COEFF1415,  0x02f9ff28},
+{13600000, DIF_BPF_COEFF1617,  0xfc17026a},
+{13600000, DIF_BPF_COEFF1819,  0x0479fb70},
+{13600000, DIF_BPF_COEFF2021,  0xfbad0713},
+{13600000, DIF_BPF_COEFF2223,  0x032ff672},
+{13600000, DIF_BPF_COEFF2425,  0xff100b83},
+{13600000, DIF_BPF_COEFF2627,  0xfdaff38b},
+{13600000, DIF_BPF_COEFF2829,  0x063c0c04},
+{13600000, DIF_BPF_COEFF3031,  0xf5baf5f5},
+{13600000, DIF_BPF_COEFF3233,  0x0dcc06ae},
+{13600000, DIF_BPF_COEFF3435,  0xefcdfda8},
+{13600000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 136_quant.dat*/
+
+
+/*case 13700000:*/
+/* BEGIN - DIF BPF register values from 137_quant.dat*/
+{13700000, DIF_BPF_COEFF01,    0x0000fffd},
+{13700000, DIF_BPF_COEFF23,    0xfffd0012},
+{13700000, DIF_BPF_COEFF45,    0x0004ffc8},
+{13700000, DIF_BPF_COEFF67,    0x00100078},
+{13700000, DIF_BPF_COEFF89,    0xffacff3e},
+{13700000, DIF_BPF_COEFF1011,  0x00e200f0},
+{13700000, DIF_BPF_COEFF1213,  0xfe39ff35},
+{13700000, DIF_BPF_COEFF1415,  0x02f10017},
+{13700000, DIF_BPF_COEFF1617,  0xfbd30156},
+{13700000, DIF_BPF_COEFF1819,  0x0521fc7f},
+{13700000, DIF_BPF_COEFF2021,  0xfa9c0638},
+{13700000, DIF_BPF_COEFF2223,  0x0499f6ee},
+{13700000, DIF_BPF_COEFF2425,  0xfd7a0b7c},
+{13700000, DIF_BPF_COEFF2627,  0xff39f325},
+{13700000, DIF_BPF_COEFF2829,  0x04f40cb3},
+{13700000, DIF_BPF_COEFF3031,  0xf69af537},
+{13700000, DIF_BPF_COEFF3233,  0x0d5a073f},
+{13700000, DIF_BPF_COEFF3435,  0xefecfd72},
+{13700000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 137_quant.dat*/
+
+
+/*case 13800000:*/
+/* BEGIN - DIF BPF register values from 138_quant.dat*/
+{13800000, DIF_BPF_COEFF01,    0x0001fffe},
+{13800000, DIF_BPF_COEFF23,    0xfffa000e},
+{13800000, DIF_BPF_COEFF45,    0x0011ffcb},
+{13800000, DIF_BPF_COEFF67,    0xfff0007f},
+{13800000, DIF_BPF_COEFF89,    0xffe7ff19},
+{13800000, DIF_BPF_COEFF1011,  0x008f014a},
+{13800000, DIF_BPF_COEFF1213,  0xfe94fe93},
+{13800000, DIF_BPF_COEFF1415,  0x02b00105},
+{13800000, DIF_BPF_COEFF1617,  0xfbd3002f},
+{13800000, DIF_BPF_COEFF1819,  0x0585fdb7},
+{13800000, DIF_BPF_COEFF2021,  0xf9c10525},
+{13800000, DIF_BPF_COEFF2223,  0x05def7a8},
+{13800000, DIF_BPF_COEFF2425,  0xfbf20b3c},
+{13800000, DIF_BPF_COEFF2627,  0x00c7f2e9},
+{13800000, DIF_BPF_COEFF2829,  0x03a00d48},
+{13800000, DIF_BPF_COEFF3031,  0xf787f484},
+{13800000, DIF_BPF_COEFF3233,  0x0cdf07cd},
+{13800000, DIF_BPF_COEFF3435,  0xf00dfd3c},
+{13800000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 138_quant.dat*/
+
+
+/*case 13900000:*/
+/* BEGIN - DIF BPF register values from 139_quant.dat*/
+{13900000, DIF_BPF_COEFF01,    0x00010000},
+{13900000, DIF_BPF_COEFF23,    0xfff80008},
+{13900000, DIF_BPF_COEFF45,    0x001bffd7},
+{13900000, DIF_BPF_COEFF67,    0xffd10076},
+{13900000, DIF_BPF_COEFF89,    0x0026ff0e},
+{13900000, DIF_BPF_COEFF1011,  0x002c0184},
+{13900000, DIF_BPF_COEFF1213,  0xff0ffe10},
+{13900000, DIF_BPF_COEFF1415,  0x023b01e0},
+{13900000, DIF_BPF_COEFF1617,  0xfc17ff06},
+{13900000, DIF_BPF_COEFF1819,  0x05a2ff09},
+{13900000, DIF_BPF_COEFF2021,  0xf92703e4},
+{13900000, DIF_BPF_COEFF2223,  0x06f4f89b},
+{13900000, DIF_BPF_COEFF2425,  0xfa820ac5},
+{13900000, DIF_BPF_COEFF2627,  0x0251f2d9},
+{13900000, DIF_BPF_COEFF2829,  0x02430dc3},
+{13900000, DIF_BPF_COEFF3031,  0xf881f3dc},
+{13900000, DIF_BPF_COEFF3233,  0x0c5c0859},
+{13900000, DIF_BPF_COEFF3435,  0xf031fd06},
+{13900000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 139_quant.dat*/
+
+
+/*case 14000000:*/
+/* BEGIN - DIF BPF register values from 140_quant.dat*/
+{14000000, DIF_BPF_COEFF01,    0x00010001},
+{14000000, DIF_BPF_COEFF23,    0xfff80001},
+{14000000, DIF_BPF_COEFF45,    0x0021ffe8},
+{14000000, DIF_BPF_COEFF67,    0xffba005d},
+{14000000, DIF_BPF_COEFF89,    0x0060ff1f},
+{14000000, DIF_BPF_COEFF1011,  0xffc40198},
+{14000000, DIF_BPF_COEFF1213,  0xffa0fdb5},
+{14000000, DIF_BPF_COEFF1415,  0x019a029a},
+{14000000, DIF_BPF_COEFF1617,  0xfc99fdea},
+{14000000, DIF_BPF_COEFF1819,  0x05750067},
+{14000000, DIF_BPF_COEFF2021,  0xf8d4027f},
+{14000000, DIF_BPF_COEFF2223,  0x07d4f9c0},
+{14000000, DIF_BPF_COEFF2425,  0xf9320a1a},
+{14000000, DIF_BPF_COEFF2627,  0x03d2f2f3},
+{14000000, DIF_BPF_COEFF2829,  0x00df0e22},
+{14000000, DIF_BPF_COEFF3031,  0xf986f341},
+{14000000, DIF_BPF_COEFF3233,  0x0bd108e2},
+{14000000, DIF_BPF_COEFF3435,  0xf058fcd1},
+{14000000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 140_quant.dat*/
+
+
+/*case 14100000:*/
+/* BEGIN - DIF BPF register values from 141_quant.dat*/
+{14100000, DIF_BPF_COEFF01,    0x00000002},
+{14100000, DIF_BPF_COEFF23,    0xfff9fffa},
+{14100000, DIF_BPF_COEFF45,    0x0021fffd},
+{14100000, DIF_BPF_COEFF67,    0xffac0038},
+{14100000, DIF_BPF_COEFF89,    0x008eff4a},
+{14100000, DIF_BPF_COEFF1011,  0xff630184},
+{14100000, DIF_BPF_COEFF1213,  0x003afd8b},
+{14100000, DIF_BPF_COEFF1415,  0x00da0326},
+{14100000, DIF_BPF_COEFF1617,  0xfd51fced},
+{14100000, DIF_BPF_COEFF1819,  0x050101c0},
+{14100000, DIF_BPF_COEFF2021,  0xf8cb0103},
+{14100000, DIF_BPF_COEFF2223,  0x0876fb10},
+{14100000, DIF_BPF_COEFF2425,  0xf80a093e},
+{14100000, DIF_BPF_COEFF2627,  0x0543f338},
+{14100000, DIF_BPF_COEFF2829,  0xff7a0e66},
+{14100000, DIF_BPF_COEFF3031,  0xfa94f2b2},
+{14100000, DIF_BPF_COEFF3233,  0x0b3f0967},
+{14100000, DIF_BPF_COEFF3435,  0xf081fc9b},
+{14100000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 141_quant.dat*/
+
+
+/*case 14200000:*/
+/* BEGIN - DIF BPF register values from 142_quant.dat*/
+{14200000, DIF_BPF_COEFF01,    0x00000003},
+{14200000, DIF_BPF_COEFF23,    0xfffbfff3},
+{14200000, DIF_BPF_COEFF45,    0x001d0013},
+{14200000, DIF_BPF_COEFF67,    0xffaa000b},
+{14200000, DIF_BPF_COEFF89,    0x00aaff89},
+{14200000, DIF_BPF_COEFF1011,  0xff13014a},
+{14200000, DIF_BPF_COEFF1213,  0x00cefd95},
+{14200000, DIF_BPF_COEFF1415,  0x000a037b},
+{14200000, DIF_BPF_COEFF1617,  0xfe35fc1d},
+{14200000, DIF_BPF_COEFF1819,  0x044c0305},
+{14200000, DIF_BPF_COEFF2021,  0xf90cff7e},
+{14200000, DIF_BPF_COEFF2223,  0x08d5fc81},
+{14200000, DIF_BPF_COEFF2425,  0xf7100834},
+{14200000, DIF_BPF_COEFF2627,  0x069ff3a7},
+{14200000, DIF_BPF_COEFF2829,  0xfe160e8d},
+{14200000, DIF_BPF_COEFF3031,  0xfbaaf231},
+{14200000, DIF_BPF_COEFF3233,  0x0aa509e9},
+{14200000, DIF_BPF_COEFF3435,  0xf0adfc65},
+{14200000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 142_quant.dat*/
+
+
+/*case 14300000:*/
+/* BEGIN - DIF BPF register values from 143_quant.dat*/
+{14300000, DIF_BPF_COEFF01,    0x00000003},
+{14300000, DIF_BPF_COEFF23,    0xffffffef},
+{14300000, DIF_BPF_COEFF45,    0x00140025},
+{14300000, DIF_BPF_COEFF67,    0xffb4ffdd},
+{14300000, DIF_BPF_COEFF89,    0x00b2ffd6},
+{14300000, DIF_BPF_COEFF1011,  0xfedb00f0},
+{14300000, DIF_BPF_COEFF1213,  0x0150fdd3},
+{14300000, DIF_BPF_COEFF1415,  0xff380391},
+{14300000, DIF_BPF_COEFF1617,  0xff36fb85},
+{14300000, DIF_BPF_COEFF1819,  0x035e0426},
+{14300000, DIF_BPF_COEFF2021,  0xf994fdfe},
+{14300000, DIF_BPF_COEFF2223,  0x08eefe0b},
+{14300000, DIF_BPF_COEFF2425,  0xf6490702},
+{14300000, DIF_BPF_COEFF2627,  0x07e1f43e},
+{14300000, DIF_BPF_COEFF2829,  0xfcb60e97},
+{14300000, DIF_BPF_COEFF3031,  0xfcc6f1be},
+{14300000, DIF_BPF_COEFF3233,  0x0a040a67},
+{14300000, DIF_BPF_COEFF3435,  0xf0dbfc30},
+{14300000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 143_quant.dat*/
+
+
+/*case 14400000:*/
+/* BEGIN - DIF BPF register values from 144_quant.dat*/
+{14400000, DIF_BPF_COEFF01,    0x00000003},
+{14400000, DIF_BPF_COEFF23,    0x0002ffee},
+{14400000, DIF_BPF_COEFF45,    0x00070033},
+{14400000, DIF_BPF_COEFF67,    0xffc9ffb4},
+{14400000, DIF_BPF_COEFF89,    0x00a40027},
+{14400000, DIF_BPF_COEFF1011,  0xfec3007e},
+{14400000, DIF_BPF_COEFF1213,  0x01b4fe3f},
+{14400000, DIF_BPF_COEFF1415,  0xfe760369},
+{14400000, DIF_BPF_COEFF1617,  0x0044fb2e},
+{14400000, DIF_BPF_COEFF1819,  0x02450518},
+{14400000, DIF_BPF_COEFF2021,  0xfa5ffc90},
+{14400000, DIF_BPF_COEFF2223,  0x08c1ffa1},
+{14400000, DIF_BPF_COEFF2425,  0xf5bc05ae},
+{14400000, DIF_BPF_COEFF2627,  0x0902f4fc},
+{14400000, DIF_BPF_COEFF2829,  0xfb600e85},
+{14400000, DIF_BPF_COEFF3031,  0xfde7f15a},
+{14400000, DIF_BPF_COEFF3233,  0x095d0ae2},
+{14400000, DIF_BPF_COEFF3435,  0xf10cfbfb},
+{14400000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 144_quant.dat*/
+
+
+/*case 14500000:*/
+/* BEGIN - DIF BPF register values from 145_quant.dat*/
+{14500000, DIF_BPF_COEFF01,    0xffff0002},
+{14500000, DIF_BPF_COEFF23,    0x0005ffef},
+{14500000, DIF_BPF_COEFF45,    0xfffa0038},
+{14500000, DIF_BPF_COEFF67,    0xffe5ff95},
+{14500000, DIF_BPF_COEFF89,    0x00820074},
+{14500000, DIF_BPF_COEFF1011,  0xfecc0000},
+{14500000, DIF_BPF_COEFF1213,  0x01f0fed0},
+{14500000, DIF_BPF_COEFF1415,  0xfdd20304},
+{14500000, DIF_BPF_COEFF1617,  0x014dfb1d},
+{14500000, DIF_BPF_COEFF1819,  0x010e05ce},
+{14500000, DIF_BPF_COEFF2021,  0xfb64fb41},
+{14500000, DIF_BPF_COEFF2223,  0x084e013b},
+{14500000, DIF_BPF_COEFF2425,  0xf569043e},
+{14500000, DIF_BPF_COEFF2627,  0x0a00f5dd},
+{14500000, DIF_BPF_COEFF2829,  0xfa150e55},
+{14500000, DIF_BPF_COEFF3031,  0xff0bf104},
+{14500000, DIF_BPF_COEFF3233,  0x08b00b59},
+{14500000, DIF_BPF_COEFF3435,  0xf13ffbc6},
+{14500000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 145_quant.dat*/
+
+
+/*case 14600000:*/
+/* BEGIN - DIF BPF register values from 146_quant.dat*/
+{14600000, DIF_BPF_COEFF01,    0xffff0001},
+{14600000, DIF_BPF_COEFF23,    0x0008fff4},
+{14600000, DIF_BPF_COEFF45,    0xffed0035},
+{14600000, DIF_BPF_COEFF67,    0x0005ff83},
+{14600000, DIF_BPF_COEFF89,    0x005000b4},
+{14600000, DIF_BPF_COEFF1011,  0xfef6ff82},
+{14600000, DIF_BPF_COEFF1213,  0x01ffff7a},
+{14600000, DIF_BPF_COEFF1415,  0xfd580269},
+{14600000, DIF_BPF_COEFF1617,  0x0241fb53},
+{14600000, DIF_BPF_COEFF1819,  0xffca0640},
+{14600000, DIF_BPF_COEFF2021,  0xfc99fa1e},
+{14600000, DIF_BPF_COEFF2223,  0x079a02cb},
+{14600000, DIF_BPF_COEFF2425,  0xf55502ba},
+{14600000, DIF_BPF_COEFF2627,  0x0ad5f6e0},
+{14600000, DIF_BPF_COEFF2829,  0xf8d90e0a},
+{14600000, DIF_BPF_COEFF3031,  0x0031f0bd},
+{14600000, DIF_BPF_COEFF3233,  0x07fd0bcb},
+{14600000, DIF_BPF_COEFF3435,  0xf174fb91},
+{14600000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 146_quant.dat*/
+
+
+/*case 14700000:*/
+/* BEGIN - DIF BPF register values from 147_quant.dat*/
+{14700000, DIF_BPF_COEFF01,    0xffffffff},
+{14700000, DIF_BPF_COEFF23,    0x0009fffb},
+{14700000, DIF_BPF_COEFF45,    0xffe4002a},
+{14700000, DIF_BPF_COEFF67,    0x0025ff82},
+{14700000, DIF_BPF_COEFF89,    0x001400e0},
+{14700000, DIF_BPF_COEFF1011,  0xff3cff10},
+{14700000, DIF_BPF_COEFF1213,  0x01e10030},
+{14700000, DIF_BPF_COEFF1415,  0xfd1201a4},
+{14700000, DIF_BPF_COEFF1617,  0x0311fbcd},
+{14700000, DIF_BPF_COEFF1819,  0xfe88066a},
+{14700000, DIF_BPF_COEFF2021,  0xfdf1f92f},
+{14700000, DIF_BPF_COEFF2223,  0x06aa0449},
+{14700000, DIF_BPF_COEFF2425,  0xf57e0128},
+{14700000, DIF_BPF_COEFF2627,  0x0b7ef801},
+{14700000, DIF_BPF_COEFF2829,  0xf7b00da2},
+{14700000, DIF_BPF_COEFF3031,  0x0156f086},
+{14700000, DIF_BPF_COEFF3233,  0x07450c39},
+{14700000, DIF_BPF_COEFF3435,  0xf1acfb5c},
+{14700000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 147_quant.dat*/
+
+
+/*case 14800000:*/
+/* BEGIN - DIF BPF register values from 148_quant.dat*/
+{14800000, DIF_BPF_COEFF01,    0x0000fffe},
+{14800000, DIF_BPF_COEFF23,    0x00080002},
+{14800000, DIF_BPF_COEFF45,    0xffdf0019},
+{14800000, DIF_BPF_COEFF67,    0x003fff92},
+{14800000, DIF_BPF_COEFF89,    0xffd600f1},
+{14800000, DIF_BPF_COEFF1011,  0xff96feb6},
+{14800000, DIF_BPF_COEFF1213,  0x019700e1},
+{14800000, DIF_BPF_COEFF1415,  0xfd0500c2},
+{14800000, DIF_BPF_COEFF1617,  0x03b0fc84},
+{14800000, DIF_BPF_COEFF1819,  0xfd590649},
+{14800000, DIF_BPF_COEFF2021,  0xff5df87f},
+{14800000, DIF_BPF_COEFF2223,  0x058505aa},
+{14800000, DIF_BPF_COEFF2425,  0xf5e4ff91},
+{14800000, DIF_BPF_COEFF2627,  0x0bf9f93c},
+{14800000, DIF_BPF_COEFF2829,  0xf69d0d20},
+{14800000, DIF_BPF_COEFF3031,  0x0279f05e},
+{14800000, DIF_BPF_COEFF3233,  0x06880ca3},
+{14800000, DIF_BPF_COEFF3435,  0xf1e6fb28},
+{14800000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 148_quant.dat*/
+
+
+/*case 14900000:*/
+/* BEGIN - DIF BPF register values from 149_quant.dat*/
+{14900000, DIF_BPF_COEFF01,    0x0000fffd},
+{14900000, DIF_BPF_COEFF23,    0x00060009},
+{14900000, DIF_BPF_COEFF45,    0xffdf0004},
+{14900000, DIF_BPF_COEFF67,    0x0051ffb0},
+{14900000, DIF_BPF_COEFF89,    0xff9d00e8},
+{14900000, DIF_BPF_COEFF1011,  0xfffcfe7c},
+{14900000, DIF_BPF_COEFF1213,  0x01280180},
+{14900000, DIF_BPF_COEFF1415,  0xfd32ffd2},
+{14900000, DIF_BPF_COEFF1617,  0x0413fd6e},
+{14900000, DIF_BPF_COEFF1819,  0xfc4d05df},
+{14900000, DIF_BPF_COEFF2021,  0x00d1f812},
+{14900000, DIF_BPF_COEFF2223,  0x043506e4},
+{14900000, DIF_BPF_COEFF2425,  0xf685fdfb},
+{14900000, DIF_BPF_COEFF2627,  0x0c43fa8d},
+{14900000, DIF_BPF_COEFF2829,  0xf5a10c83},
+{14900000, DIF_BPF_COEFF3031,  0x0399f046},
+{14900000, DIF_BPF_COEFF3233,  0x05c70d08},
+{14900000, DIF_BPF_COEFF3435,  0xf222faf3},
+{14900000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 149_quant.dat*/
+
+
+/*case 15000000:*/
+/* BEGIN - DIF BPF register values from 150_quant.dat*/
+{15000000, DIF_BPF_COEFF01,    0x0000fffd},
+{15000000, DIF_BPF_COEFF23,    0x0003000f},
+{15000000, DIF_BPF_COEFF45,    0xffe5ffef},
+{15000000, DIF_BPF_COEFF67,    0x0057ffd9},
+{15000000, DIF_BPF_COEFF89,    0xff7000c4},
+{15000000, DIF_BPF_COEFF1011,  0x0062fe68},
+{15000000, DIF_BPF_COEFF1213,  0x009e01ff},
+{15000000, DIF_BPF_COEFF1415,  0xfd95fee6},
+{15000000, DIF_BPF_COEFF1617,  0x0435fe7d},
+{15000000, DIF_BPF_COEFF1819,  0xfb710530},
+{15000000, DIF_BPF_COEFF2021,  0x023cf7ee},
+{15000000, DIF_BPF_COEFF2223,  0x02c307ef},
+{15000000, DIF_BPF_COEFF2425,  0xf75efc70},
+{15000000, DIF_BPF_COEFF2627,  0x0c5cfbef},
+{15000000, DIF_BPF_COEFF2829,  0xf4c10bce},
+{15000000, DIF_BPF_COEFF3031,  0x04b3f03f},
+{15000000, DIF_BPF_COEFF3233,  0x05030d69},
+{15000000, DIF_BPF_COEFF3435,  0xf261fabf},
+{15000000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 150_quant.dat*/
+
+
+/*case 15100000:*/
+/* BEGIN - DIF BPF register values from 151_quant.dat*/
+{15100000, DIF_BPF_COEFF01,    0x0000fffd},
+{15100000, DIF_BPF_COEFF23,    0xffff0012},
+{15100000, DIF_BPF_COEFF45,    0xffefffdc},
+{15100000, DIF_BPF_COEFF67,    0x00510006},
+{15100000, DIF_BPF_COEFF89,    0xff540089},
+{15100000, DIF_BPF_COEFF1011,  0x00befe7c},
+{15100000, DIF_BPF_COEFF1213,  0x00060253},
+{15100000, DIF_BPF_COEFF1415,  0xfe27fe0d},
+{15100000, DIF_BPF_COEFF1617,  0x0413ffa2},
+{15100000, DIF_BPF_COEFF1819,  0xfad10446},
+{15100000, DIF_BPF_COEFF2021,  0x0390f812},
+{15100000, DIF_BPF_COEFF2223,  0x013b08c3},
+{15100000, DIF_BPF_COEFF2425,  0xf868faf6},
+{15100000, DIF_BPF_COEFF2627,  0x0c43fd5f},
+{15100000, DIF_BPF_COEFF2829,  0xf3fd0b02},
+{15100000, DIF_BPF_COEFF3031,  0x05c7f046},
+{15100000, DIF_BPF_COEFF3233,  0x043b0dc4},
+{15100000, DIF_BPF_COEFF3435,  0xf2a1fa8b},
+{15100000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 151_quant.dat*/
+
+
+/*case 15200000:*/
+/* BEGIN - DIF BPF register values from 152_quant.dat*/
+{15200000, DIF_BPF_COEFF01,    0x0001fffe},
+{15200000, DIF_BPF_COEFF23,    0xfffc0012},
+{15200000, DIF_BPF_COEFF45,    0xfffbffce},
+{15200000, DIF_BPF_COEFF67,    0x003f0033},
+{15200000, DIF_BPF_COEFF89,    0xff4e003f},
+{15200000, DIF_BPF_COEFF1011,  0x0106feb6},
+{15200000, DIF_BPF_COEFF1213,  0xff6e0276},
+{15200000, DIF_BPF_COEFF1415,  0xfeddfd56},
+{15200000, DIF_BPF_COEFF1617,  0x03b000cc},
+{15200000, DIF_BPF_COEFF1819,  0xfa740329},
+{15200000, DIF_BPF_COEFF2021,  0x04bff87f},
+{15200000, DIF_BPF_COEFF2223,  0xffaa095d},
+{15200000, DIF_BPF_COEFF2425,  0xf99ef995},
+{15200000, DIF_BPF_COEFF2627,  0x0bf9fed8},
+{15200000, DIF_BPF_COEFF2829,  0xf3590a1f},
+{15200000, DIF_BPF_COEFF3031,  0x06d2f05e},
+{15200000, DIF_BPF_COEFF3233,  0x03700e1b},
+{15200000, DIF_BPF_COEFF3435,  0xf2e4fa58},
+{15200000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 152_quant.dat*/
+
+
+/*case 115300000:*/
+/* BEGIN - DIF BPF register values from 153_quant.dat*/
+{15300000, DIF_BPF_COEFF01,    0x0001ffff},
+{15300000, DIF_BPF_COEFF23,    0xfff9000f},
+{15300000, DIF_BPF_COEFF45,    0x0009ffc8},
+{15300000, DIF_BPF_COEFF67,    0x00250059},
+{15300000, DIF_BPF_COEFF89,    0xff5effee},
+{15300000, DIF_BPF_COEFF1011,  0x0132ff10},
+{15300000, DIF_BPF_COEFF1213,  0xfee30265},
+{15300000, DIF_BPF_COEFF1415,  0xffaafccf},
+{15300000, DIF_BPF_COEFF1617,  0x031101eb},
+{15300000, DIF_BPF_COEFF1819,  0xfa6001e8},
+{15300000, DIF_BPF_COEFF2021,  0x05bdf92f},
+{15300000, DIF_BPF_COEFF2223,  0xfe1b09b6},
+{15300000, DIF_BPF_COEFF2425,  0xfafaf852},
+{15300000, DIF_BPF_COEFF2627,  0x0b7e0055},
+{15300000, DIF_BPF_COEFF2829,  0xf2d50929},
+{15300000, DIF_BPF_COEFF3031,  0x07d3f086},
+{15300000, DIF_BPF_COEFF3233,  0x02a30e6c},
+{15300000, DIF_BPF_COEFF3435,  0xf329fa24},
+{15300000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 153_quant.dat*/
+
+
+/*case 115400000:*/
+/* BEGIN - DIF BPF register values from 154_quant.dat*/
+{15400000, DIF_BPF_COEFF01,    0x00010001},
+{15400000, DIF_BPF_COEFF23,    0xfff80009},
+{15400000, DIF_BPF_COEFF45,    0x0015ffca},
+{15400000, DIF_BPF_COEFF67,    0x00050074},
+{15400000, DIF_BPF_COEFF89,    0xff81ff9f},
+{15400000, DIF_BPF_COEFF1011,  0x013dff82},
+{15400000, DIF_BPF_COEFF1213,  0xfe710221},
+{15400000, DIF_BPF_COEFF1415,  0x007cfc80},
+{15400000, DIF_BPF_COEFF1617,  0x024102ed},
+{15400000, DIF_BPF_COEFF1819,  0xfa940090},
+{15400000, DIF_BPF_COEFF2021,  0x0680fa1e},
+{15400000, DIF_BPF_COEFF2223,  0xfc9b09cd},
+{15400000, DIF_BPF_COEFF2425,  0xfc73f736},
+{15400000, DIF_BPF_COEFF2627,  0x0ad501d0},
+{15400000, DIF_BPF_COEFF2829,  0xf2740820},
+{15400000, DIF_BPF_COEFF3031,  0x08c9f0bd},
+{15400000, DIF_BPF_COEFF3233,  0x01d40eb9},
+{15400000, DIF_BPF_COEFF3435,  0xf371f9f1},
+{15400000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 154_quant.dat*/
+
+
+/*case 115500000:*/
+/* BEGIN - DIF BPF register values from 155_quant.dat*/
+{15500000, DIF_BPF_COEFF01,    0x00000002},
+{15500000, DIF_BPF_COEFF23,    0xfff80002},
+{15500000, DIF_BPF_COEFF45,    0x001effd5},
+{15500000, DIF_BPF_COEFF67,    0xffe5007f},
+{15500000, DIF_BPF_COEFF89,    0xffb4ff5b},
+{15500000, DIF_BPF_COEFF1011,  0x01280000},
+{15500000, DIF_BPF_COEFF1213,  0xfe2401b0},
+{15500000, DIF_BPF_COEFF1415,  0x0146fc70},
+{15500000, DIF_BPF_COEFF1617,  0x014d03c6},
+{15500000, DIF_BPF_COEFF1819,  0xfb10ff32},
+{15500000, DIF_BPF_COEFF2021,  0x0701fb41},
+{15500000, DIF_BPF_COEFF2223,  0xfb3709a1},
+{15500000, DIF_BPF_COEFF2425,  0xfe00f644},
+{15500000, DIF_BPF_COEFF2627,  0x0a000345},
+{15500000, DIF_BPF_COEFF2829,  0xf2350708},
+{15500000, DIF_BPF_COEFF3031,  0x09b2f104},
+{15500000, DIF_BPF_COEFF3233,  0x01050eff},
+{15500000, DIF_BPF_COEFF3435,  0xf3baf9be},
+{15500000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 155_quant.dat*/
+
+
+/*case 115600000:*/
+/* BEGIN - DIF BPF register values from 156_quant.dat*/
+{15600000, DIF_BPF_COEFF01,    0x00000003},
+{15600000, DIF_BPF_COEFF23,    0xfff9fffb},
+{15600000, DIF_BPF_COEFF45,    0x0022ffe6},
+{15600000, DIF_BPF_COEFF67,    0xffc9007a},
+{15600000, DIF_BPF_COEFF89,    0xfff0ff29},
+{15600000, DIF_BPF_COEFF1011,  0x00f2007e},
+{15600000, DIF_BPF_COEFF1213,  0xfe01011b},
+{15600000, DIF_BPF_COEFF1415,  0x01f6fc9e},
+{15600000, DIF_BPF_COEFF1617,  0x00440467},
+{15600000, DIF_BPF_COEFF1819,  0xfbccfdde},
+{15600000, DIF_BPF_COEFF2021,  0x0738fc90},
+{15600000, DIF_BPF_COEFF2223,  0xf9f70934},
+{15600000, DIF_BPF_COEFF2425,  0xff99f582},
+{15600000, DIF_BPF_COEFF2627,  0x090204b0},
+{15600000, DIF_BPF_COEFF2829,  0xf21a05e1},
+{15600000, DIF_BPF_COEFF3031,  0x0a8df15a},
+{15600000, DIF_BPF_COEFF3233,  0x00340f41},
+{15600000, DIF_BPF_COEFF3435,  0xf405f98b},
+{15600000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 156_quant.dat*/
+
+
+/*case 115700000:*/
+/* BEGIN - DIF BPF register values from 157_quant.dat*/
+{15700000, DIF_BPF_COEFF01,    0x00000003},
+{15700000, DIF_BPF_COEFF23,    0xfffcfff4},
+{15700000, DIF_BPF_COEFF45,    0x0020fffa},
+{15700000, DIF_BPF_COEFF67,    0xffb40064},
+{15700000, DIF_BPF_COEFF89,    0x002fff11},
+{15700000, DIF_BPF_COEFF1011,  0x00a400f0},
+{15700000, DIF_BPF_COEFF1213,  0xfe0d006e},
+{15700000, DIF_BPF_COEFF1415,  0x0281fd09},
+{15700000, DIF_BPF_COEFF1617,  0xff3604c9},
+{15700000, DIF_BPF_COEFF1819,  0xfcbffca2},
+{15700000, DIF_BPF_COEFF2021,  0x0726fdfe},
+{15700000, DIF_BPF_COEFF2223,  0xf8e80888},
+{15700000, DIF_BPF_COEFF2425,  0x0134f4f3},
+{15700000, DIF_BPF_COEFF2627,  0x07e1060c},
+{15700000, DIF_BPF_COEFF2829,  0xf22304af},
+{15700000, DIF_BPF_COEFF3031,  0x0b59f1be},
+{15700000, DIF_BPF_COEFF3233,  0xff640f7d},
+{15700000, DIF_BPF_COEFF3435,  0xf452f959},
+{15700000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 157_quant.dat*/
+
+
+/*case 115800000:*/
+/* BEGIN - DIF BPF register values from 158_quant.dat*/
+{15800000, DIF_BPF_COEFF01,    0x00000003},
+{15800000, DIF_BPF_COEFF23,    0x0000fff0},
+{15800000, DIF_BPF_COEFF45,    0x001a0010},
+{15800000, DIF_BPF_COEFF67,    0xffaa0041},
+{15800000, DIF_BPF_COEFF89,    0x0067ff13},
+{15800000, DIF_BPF_COEFF1011,  0x0043014a},
+{15800000, DIF_BPF_COEFF1213,  0xfe46ffb9},
+{15800000, DIF_BPF_COEFF1415,  0x02dbfda8},
+{15800000, DIF_BPF_COEFF1617,  0xfe3504e5},
+{15800000, DIF_BPF_COEFF1819,  0xfddcfb8d},
+{15800000, DIF_BPF_COEFF2021,  0x06c9ff7e},
+{15800000, DIF_BPF_COEFF2223,  0xf81107a2},
+{15800000, DIF_BPF_COEFF2425,  0x02c9f49a},
+{15800000, DIF_BPF_COEFF2627,  0x069f0753},
+{15800000, DIF_BPF_COEFF2829,  0xf2500373},
+{15800000, DIF_BPF_COEFF3031,  0x0c14f231},
+{15800000, DIF_BPF_COEFF3233,  0xfe930fb3},
+{15800000, DIF_BPF_COEFF3435,  0xf4a1f927},
+{15800000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 158_quant.dat*/
+
+
+/*case 115900000:*/
+/* BEGIN - DIF BPF register values from 159_quant.dat*/
+{15900000, DIF_BPF_COEFF01,    0xffff0002},
+{15900000, DIF_BPF_COEFF23,    0x0003ffee},
+{15900000, DIF_BPF_COEFF45,    0x000f0023},
+{15900000, DIF_BPF_COEFF67,    0xffac0016},
+{15900000, DIF_BPF_COEFF89,    0x0093ff31},
+{15900000, DIF_BPF_COEFF1011,  0xffdc0184},
+{15900000, DIF_BPF_COEFF1213,  0xfea6ff09},
+{15900000, DIF_BPF_COEFF1415,  0x02fdfe70},
+{15900000, DIF_BPF_COEFF1617,  0xfd5104ba},
+{15900000, DIF_BPF_COEFF1819,  0xff15faac},
+{15900000, DIF_BPF_COEFF2021,  0x06270103},
+{15900000, DIF_BPF_COEFF2223,  0xf7780688},
+{15900000, DIF_BPF_COEFF2425,  0x044df479},
+{15900000, DIF_BPF_COEFF2627,  0x05430883},
+{15900000, DIF_BPF_COEFF2829,  0xf2a00231},
+{15900000, DIF_BPF_COEFF3031,  0x0cbef2b2},
+{15900000, DIF_BPF_COEFF3233,  0xfdc40fe3},
+{15900000, DIF_BPF_COEFF3435,  0xf4f2f8f5},
+{15900000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 159_quant.dat*/
+
+
+/*case 116000000:*/
+/* BEGIN - DIF BPF register values from 160_quant.dat*/
+{16000000, DIF_BPF_COEFF01,    0xffff0001},
+{16000000, DIF_BPF_COEFF23,    0x0006ffef},
+{16000000, DIF_BPF_COEFF45,    0x00020031},
+{16000000, DIF_BPF_COEFF67,    0xffbaffe8},
+{16000000, DIF_BPF_COEFF89,    0x00adff66},
+{16000000, DIF_BPF_COEFF1011,  0xff790198},
+{16000000, DIF_BPF_COEFF1213,  0xff26fe6e},
+{16000000, DIF_BPF_COEFF1415,  0x02e5ff55},
+{16000000, DIF_BPF_COEFF1617,  0xfc99044a},
+{16000000, DIF_BPF_COEFF1819,  0x005bfa09},
+{16000000, DIF_BPF_COEFF2021,  0x0545027f},
+{16000000, DIF_BPF_COEFF2223,  0xf7230541},
+{16000000, DIF_BPF_COEFF2425,  0x05b8f490},
+{16000000, DIF_BPF_COEFF2627,  0x03d20997},
+{16000000, DIF_BPF_COEFF2829,  0xf31300eb},
+{16000000, DIF_BPF_COEFF3031,  0x0d55f341},
+{16000000, DIF_BPF_COEFF3233,  0xfcf6100e},
+{16000000, DIF_BPF_COEFF3435,  0xf544f8c3},
+{16000000, DIF_BPF_COEFF36,    0x110d0000},
+/* END - DIF BPF register values from 160_quant.dat*/
+};
+
+#endif
diff --git a/drivers/media/video/cx231xx/cx231xx-dvb.c b/drivers/media/video/cx231xx/cx231xx-dvb.c
index 4ea3776..5feb3ee 100644
--- a/drivers/media/video/cx231xx/cx231xx-dvb.c
+++ b/drivers/media/video/cx231xx/cx231xx-dvb.c
@@ -29,6 +29,10 @@
 
 #include "xc5000.h"
 #include "dvb_dummy_fe.h"
+#include "s5h1432.h"
+#include "tda18271.h"
+#include "s5h1411.h"
+#include "lgdt3305.h"
 
 MODULE_DESCRIPTION("driver for cx231xx based DVB cards");
 MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
@@ -65,6 +69,72 @@
 	struct dvb_net net;
 };
 
+static struct s5h1432_config dvico_s5h1432_config = {
+	.output_mode   = S5H1432_SERIAL_OUTPUT,
+	.gpio          = S5H1432_GPIO_ON,
+	.qam_if        = S5H1432_IF_4000,
+	.vsb_if        = S5H1432_IF_4000,
+	.inversion     = S5H1432_INVERSION_OFF,
+	.status_mode   = S5H1432_DEMODLOCKING,
+	.mpeg_timing   = S5H1432_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct tda18271_std_map cnxt_rde253s_tda18271_std_map = {
+	.dvbt_6   = { .if_freq = 4000, .agc_mode = 3, .std = 4,
+		      .if_lvl = 1, .rfagc_top = 0x37, },
+	.dvbt_7   = { .if_freq = 4000, .agc_mode = 3, .std = 5,
+		      .if_lvl = 1, .rfagc_top = 0x37, },
+	.dvbt_8   = { .if_freq = 4000, .agc_mode = 3, .std = 6,
+		      .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config cnxt_rde253s_tunerconfig = {
+	.std_map = &cnxt_rde253s_tda18271_std_map,
+	.gate    = TDA18271_GATE_ANALOG,
+};
+
+static struct s5h1411_config tda18271_s5h1411_config = {
+	.output_mode   = S5H1411_SERIAL_OUTPUT,
+	.gpio          = S5H1411_GPIO_OFF,
+	.vsb_if        = S5H1411_IF_3250,
+	.qam_if        = S5H1411_IF_4000,
+	.inversion     = S5H1411_INVERSION_ON,
+	.status_mode   = S5H1411_DEMODLOCKING,
+	.mpeg_timing   = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+static struct s5h1411_config xc5000_s5h1411_config = {
+	.output_mode   = S5H1411_SERIAL_OUTPUT,
+	.gpio          = S5H1411_GPIO_OFF,
+	.vsb_if        = S5H1411_IF_3250,
+	.qam_if        = S5H1411_IF_3250,
+	.inversion     = S5H1411_INVERSION_OFF,
+	.status_mode   = S5H1411_DEMODLOCKING,
+	.mpeg_timing   = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct lgdt3305_config hcw_lgdt3305_config = {
+	.i2c_addr           = 0x0e,
+	.mpeg_mode          = LGDT3305_MPEG_SERIAL,
+	.tpclk_edge         = LGDT3305_TPCLK_FALLING_EDGE,
+	.tpvalid_polarity   = LGDT3305_TP_VALID_HIGH,
+	.deny_i2c_rptr      = 1,
+	.spectral_inversion = 1,
+	.qam_if_khz         = 4000,
+	.vsb_if_khz         = 3250,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+	.atsc_6   = { .if_freq = 3250, .agc_mode = 3, .std = 4,
+		      .if_lvl = 1, .rfagc_top = 0x58, },
+	.qam_6    = { .if_freq = 4000, .agc_mode = 3, .std = 5,
+		      .if_lvl = 1, .rfagc_top = 0x58, },
+};
+
+static struct tda18271_config hcw_tda18271_config = {
+	.std_map = &hauppauge_tda18271_std_map,
+	.gate    = TDA18271_GATE_DIGITAL,
+};
+
 static inline void print_err_status(struct cx231xx *dev, int packet, int status)
 {
 	char *errmsg = "Unknown";
@@ -128,34 +198,79 @@
 				continue;
 		}
 
-		dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer +
-				 urb->iso_frame_desc[i].offset,
-				 urb->iso_frame_desc[i].actual_length);
+		dvb_dmx_swfilter(&dev->dvb->demux,
+				 urb->transfer_buffer +
+				urb->iso_frame_desc[i].offset,
+				urb->iso_frame_desc[i].actual_length);
 	}
 
 	return 0;
 }
 
+static inline int dvb_bulk_copy(struct cx231xx *dev, struct urb *urb)
+{
+	if (!dev)
+		return 0;
+
+	if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
+		return 0;
+
+	if (urb->status < 0) {
+		print_err_status(dev, -1, urb->status);
+		if (urb->status == -ENOENT)
+			return 0;
+	}
+
+	/* Feed the transport payload into the kernel demux */
+	dvb_dmx_swfilter(&dev->dvb->demux,
+		urb->transfer_buffer, urb->actual_length);
+
+	return 0;
+}
+
 static int start_streaming(struct cx231xx_dvb *dvb)
 {
 	int rc;
 	struct cx231xx *dev = dvb->adapter.priv;
 
-	usb_set_interface(dev->udev, 0, 1);
-	rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
-	if (rc < 0)
-		return rc;
+	if (dev->USE_ISO) {
+		cx231xx_info("DVB transfer mode is ISO.\n");
+		mutex_lock(&dev->i2c_lock);
+		cx231xx_enable_i2c_port_3(dev, false);
+		cx231xx_set_alt_setting(dev, INDEX_TS1, 4);
+		cx231xx_enable_i2c_port_3(dev, true);
+		mutex_unlock(&dev->i2c_lock);
+		rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+		if (rc < 0)
+			return rc;
+		dev->mode_tv = 1;
+		return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS,
+					CX231XX_DVB_NUM_BUFS,
+					dev->ts1_mode.max_pkt_size,
+					dvb_isoc_copy);
+	} else {
+		cx231xx_info("DVB transfer mode is BULK.\n");
+		cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
+		rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+		if (rc < 0)
+			return rc;
+		dev->mode_tv = 1;
+		return cx231xx_init_bulk(dev, CX231XX_DVB_MAX_PACKETS,
+					CX231XX_DVB_NUM_BUFS,
+					dev->ts1_mode.max_pkt_size,
+					dvb_bulk_copy);
+	}
 
-	return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS,
-				 CX231XX_DVB_NUM_BUFS,
-				 CX231XX_DVB_MAX_PACKETSIZE, dvb_isoc_copy);
 }
 
 static int stop_streaming(struct cx231xx_dvb *dvb)
 {
 	struct cx231xx *dev = dvb->adapter.priv;
 
-	cx231xx_uninit_isoc(dev);
+	if (dev->USE_ISO)
+		cx231xx_uninit_isoc(dev);
+	else
+		cx231xx_uninit_bulk(dev);
 
 	cx231xx_set_mode(dev, CX231XX_SUSPEND);
 
@@ -216,7 +331,11 @@
 
 static struct xc5000_config cnxt_rde250_tunerconfig = {
 	.i2c_address = 0x61,
-	.if_khz = 5380,
+	.if_khz = 4000,
+};
+static struct xc5000_config cnxt_rdu250_tunerconfig = {
+	.i2c_address = 0x61,
+	.if_khz = 3250,
 };
 
 /* ------------------------------------------------------------------ */
@@ -228,7 +347,7 @@
 	struct xc5000_config cfg;
 
 	memset(&cfg, 0, sizeof(cfg));
-	cfg.i2c_adap = &dev->i2c_bus[1].i2c_adap;
+	cfg.i2c_adap = &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap;
 	cfg.i2c_addr = addr;
 
 	if (!dev->dvb->frontend) {
@@ -268,7 +387,6 @@
 			/*params.audmode = ;       */
 
 			/* Set the analog parameters to set the frequency */
-			cx231xx_info("Setting Frequency for XC5000\n");
 			dops->set_analog_params(dev->dvb->frontend, &params);
 		}
 
@@ -445,19 +563,21 @@
 	dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq;
 	dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner;
 
+	mutex_lock(&dev->lock);
 	cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+	cx231xx_demod_reset(dev);
 	/* init frontend */
 	switch (dev->model) {
+	case CX231XX_BOARD_CNXT_CARRAERA:
 	case CX231XX_BOARD_CNXT_RDE_250:
 
-		/* dev->dvb->frontend = dvb_attach(s5h1411_attach,
-		   &dvico_s5h1411_config,
-		   &dev->i2c_bus[1].i2c_adap); */
-		dev->dvb->frontend = dvb_attach(dvb_dummy_fe_ofdm_attach);
+		dev->dvb->frontend = dvb_attach(s5h1432_attach,
+					&dvico_s5h1432_config,
+					&dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
 
 		if (dev->dvb->frontend == NULL) {
 			printk(DRIVER_NAME
-			       ": Failed to attach dummy front end\n");
+			       ": Failed to attach s5h1432 front end\n");
 			result = -EINVAL;
 			goto out_free;
 		}
@@ -466,16 +586,19 @@
 		dvb->frontend->callback = cx231xx_tuner_callback;
 
 		if (!dvb_attach(xc5000_attach, dev->dvb->frontend,
-			       &dev->i2c_bus[1].i2c_adap,
+			       &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
 			       &cnxt_rde250_tunerconfig)) {
 			result = -EINVAL;
 			goto out_free;
 		}
 
 		break;
+	case CX231XX_BOARD_CNXT_SHELBY:
 	case CX231XX_BOARD_CNXT_RDU_250:
 
-		dev->dvb->frontend = dvb_attach(dvb_dummy_fe_ofdm_attach);
+		dev->dvb->frontend = dvb_attach(s5h1411_attach,
+					       &xc5000_s5h1411_config,
+					       &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
 
 		if (dev->dvb->frontend == NULL) {
 			printk(DRIVER_NAME
@@ -488,12 +611,82 @@
 		dvb->frontend->callback = cx231xx_tuner_callback;
 
 		if (!dvb_attach(xc5000_attach, dev->dvb->frontend,
-			       &dev->i2c_bus[1].i2c_adap,
-			       &cnxt_rde250_tunerconfig)) {
+			       &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+			       &cnxt_rdu250_tunerconfig)) {
 			result = -EINVAL;
 			goto out_free;
 		}
 		break;
+	case CX231XX_BOARD_CNXT_RDE_253S:
+
+		dev->dvb->frontend = dvb_attach(s5h1432_attach,
+					&dvico_s5h1432_config,
+					&dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
+
+		if (dev->dvb->frontend == NULL) {
+			printk(DRIVER_NAME
+			       ": Failed to attach s5h1432 front end\n");
+			result = -EINVAL;
+			goto out_free;
+		}
+
+		/* define general-purpose callback pointer */
+		dvb->frontend->callback = cx231xx_tuner_callback;
+
+		if (!dvb_attach(tda18271_attach, dev->dvb->frontend,
+			       0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+			       &cnxt_rde253s_tunerconfig)) {
+			result = -EINVAL;
+			goto out_free;
+		}
+		break;
+	case CX231XX_BOARD_CNXT_RDU_253S:
+
+		dev->dvb->frontend = dvb_attach(s5h1411_attach,
+					       &tda18271_s5h1411_config,
+					       &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
+
+		if (dev->dvb->frontend == NULL) {
+			printk(DRIVER_NAME
+			       ": Failed to attach dummy front end\n");
+			result = -EINVAL;
+			goto out_free;
+		}
+
+		/* define general-purpose callback pointer */
+		dvb->frontend->callback = cx231xx_tuner_callback;
+
+		if (!dvb_attach(tda18271_attach, dev->dvb->frontend,
+			       0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+			       &cnxt_rde253s_tunerconfig)) {
+			result = -EINVAL;
+			goto out_free;
+		}
+		break;
+	case CX231XX_BOARD_HAUPPAUGE_EXETER:
+
+		printk(KERN_INFO "%s: looking for tuner / demod on i2c bus: %d\n",
+		       __func__, i2c_adapter_id(&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap));
+
+		dev->dvb->frontend = dvb_attach(lgdt3305_attach,
+						&hcw_lgdt3305_config,
+						&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap);
+
+		if (dev->dvb->frontend == NULL) {
+			printk(DRIVER_NAME
+			       ": Failed to attach LG3305 front end\n");
+			result = -EINVAL;
+			goto out_free;
+		}
+
+		/* define general-purpose callback pointer */
+		dvb->frontend->callback = cx231xx_tuner_callback;
+
+		dvb_attach(tda18271_attach, dev->dvb->frontend,
+			   0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+			   &hcw_tda18271_config);
+		break;
+
 
 	default:
 		printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
@@ -513,15 +706,18 @@
 	if (result < 0)
 		goto out_free;
 
-	cx231xx_set_mode(dev, CX231XX_SUSPEND);
+
 	printk(KERN_INFO "Successfully loaded cx231xx-dvb\n");
-	return 0;
+
+ret:
+	cx231xx_set_mode(dev, CX231XX_SUSPEND);
+	mutex_unlock(&dev->lock);
+	return result;
 
 out_free:
-	cx231xx_set_mode(dev, CX231XX_SUSPEND);
 	kfree(dvb);
 	dev->dvb = NULL;
-	return result;
+	goto ret;
 }
 
 static int dvb_fini(struct cx231xx *dev)
diff --git a/drivers/media/video/cx231xx/cx231xx-i2c.c b/drivers/media/video/cx231xx/cx231xx-i2c.c
index 58d9cc0..8356706 100644
--- a/drivers/media/video/cx231xx/cx231xx-i2c.c
+++ b/drivers/media/video/cx231xx/cx231xx-i2c.c
@@ -359,7 +359,7 @@
 
 	if (num <= 0)
 		return 0;
-
+	mutex_lock(&dev->i2c_lock);
 	for (i = 0; i < num; i++) {
 
 		addr = msgs[i].addr >> 1;
@@ -372,6 +372,7 @@
 			rc = cx231xx_i2c_check_for_device(i2c_adap, &msgs[i]);
 			if (rc < 0) {
 				dprintk2(2, " no device\n");
+				mutex_unlock(&dev->i2c_lock);
 				return rc;
 			}
 
@@ -384,7 +385,7 @@
 			}
 		} else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
 			   msgs[i].addr == msgs[i + 1].addr
-			   && (msgs[i].len <= 2) && (bus->nr < 2)) {
+			   && (msgs[i].len <= 2) && (bus->nr < 3)) {
 			/* read bytes */
 			rc = cx231xx_i2c_recv_bytes_with_saddr(i2c_adap,
 							       &msgs[i],
@@ -407,10 +408,11 @@
 		if (i2c_debug >= 2)
 			printk("\n");
 	}
-
+	mutex_unlock(&dev->i2c_lock);
 	return num;
 err:
 	dprintk2(2, " ERROR: %i\n", rc);
+	mutex_unlock(&dev->i2c_lock);
 	return rc;
 }
 
@@ -507,9 +509,6 @@
 	if (0 == bus->i2c_rc) {
 		if (i2c_scan)
 			cx231xx_do_i2c_scan(dev, &bus->i2c_client);
-
-		/* Instantiate the IR receiver device, if present */
-		cx231xx_register_i2c_ir(dev);
 	} else
 		cx231xx_warn("%s: i2c bus %d register FAILED\n",
 			     dev->name, bus->nr);
diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c
deleted file mode 100644
index fd09915..0000000
--- a/drivers/media/video/cx231xx/cx231xx-input.c
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
-  handle cx231xx IR remotes via linux kernel input layer.
-
-  Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
-		Based on em28xx driver
-
-		< This is a place holder for IR now.>
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with this program; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/input.h>
-#include <linux/usb.h>
-#include <linux/slab.h>
-
-#include "cx231xx.h"
-
-static unsigned int ir_debug;
-module_param(ir_debug, int, 0644);
-MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
-
-#define MODULE_NAME "cx231xx"
-
-#define i2cdprintk(fmt, arg...) \
-	if (ir_debug) { \
-		printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
-	}
-
-#define dprintk(fmt, arg...) \
-	if (ir_debug) { \
-		printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
-	}
-
-/**********************************************************
- Polling structure used by cx231xx IR's
- **********************************************************/
-
-struct cx231xx_ir_poll_result {
-	unsigned int toggle_bit:1;
-	unsigned int read_count:7;
-	u8 rc_address;
-	u8 rc_data[4];
-};
-
-struct cx231xx_IR {
-	struct cx231xx *dev;
-	struct input_dev *input;
-	char name[32];
-	char phys[32];
-
-	/* poll external decoder */
-	int polling;
-	struct work_struct work;
-	struct timer_list timer;
-	unsigned int last_readcount;
-
-	int (*get_key) (struct cx231xx_IR *, struct cx231xx_ir_poll_result *);
-};
-
-/**********************************************************
- Polling code for cx231xx
- **********************************************************/
-
-static void cx231xx_ir_handle_key(struct cx231xx_IR *ir)
-{
-	int result;
-	struct cx231xx_ir_poll_result poll_result;
-
-	/* read the registers containing the IR status */
-	result = ir->get_key(ir, &poll_result);
-	if (result < 0) {
-		dprintk("ir->get_key() failed %d\n", result);
-		return;
-	}
-
-	dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n",
-		poll_result.toggle_bit, poll_result.read_count,
-		ir->last_readcount, poll_result.rc_data[0]);
-
-	if (poll_result.read_count > 0 &&
-	    poll_result.read_count != ir->last_readcount)
-		ir_keydown(ir->input,
-			   poll_result.rc_data[0],
-			   poll_result.toggle_bit);
-
-	if (ir->dev->chip_id == CHIP_ID_EM2874)
-		/* The em2874 clears the readcount field every time the
-		   register is read.  The em2860/2880 datasheet says that it
-		   is supposed to clear the readcount, but it doesn't.  So with
-		   the em2874, we are looking for a non-zero read count as
-		   opposed to a readcount that is incrementing */
-		ir->last_readcount = 0;
-	else
-		ir->last_readcount = poll_result.read_count;
-
-	}
-}
-
-static void ir_timer(unsigned long data)
-{
-	struct cx231xx_IR *ir = (struct cx231xx_IR *)data;
-
-	schedule_work(&ir->work);
-}
-
-static void cx231xx_ir_work(struct work_struct *work)
-{
-	struct cx231xx_IR *ir = container_of(work, struct cx231xx_IR, work);
-
-	cx231xx_ir_handle_key(ir);
-	mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
-}
-
-void cx231xx_ir_start(struct cx231xx_IR *ir)
-{
-	setup_timer(&ir->timer, ir_timer, (unsigned long)ir);
-	INIT_WORK(&ir->work, cx231xx_ir_work);
-	schedule_work(&ir->work);
-}
-
-static void cx231xx_ir_stop(struct cx231xx_IR *ir)
-{
-	del_timer_sync(&ir->timer);
-	flush_scheduled_work();
-}
-
-int cx231xx_ir_init(struct cx231xx *dev)
-{
-	struct cx231xx_IR *ir;
-	struct input_dev *input_dev;
-	u8 ir_config;
-	int err = -ENOMEM;
-
-	if (dev->board.ir_codes == NULL) {
-		/* No remote control support */
-		return 0;
-	}
-
-	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
-	input_dev = input_allocate_device();
-	if (!ir || !input_dev)
-		goto err_out_free;
-
-	ir->input = input_dev;
-
-	/* Setup the proper handler based on the chip */
-	switch (dev->chip_id) {
-	default:
-		printk("Unrecognized cx231xx chip id: IR not supported\n");
-		goto err_out_free;
-	}
-
-	/* This is how often we ask the chip for IR information */
-	ir->polling = 100;	/* ms */
-
-	/* init input device */
-	snprintf(ir->name, sizeof(ir->name), "cx231xx IR (%s)", dev->name);
-
-	usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
-	strlcat(ir->phys, "/input0", sizeof(ir->phys));
-
-	input_dev->name = ir->name;
-	input_dev->phys = ir->phys;
-	input_dev->id.bustype = BUS_USB;
-	input_dev->id.version = 1;
-	input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
-	input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
-
-	input_dev->dev.parent = &dev->udev->dev;
-	/* record handles to ourself */
-	ir->dev = dev;
-	dev->ir = ir;
-
-	cx231xx_ir_start(ir);
-
-	/* all done */
-	err = __ir_input_register(ir->input, dev->board.ir_codes,
-				NULL, MODULE_NAME);
-	if (err)
-		goto err_out_stop;
-
-	return 0;
-err_out_stop:
-	cx231xx_ir_stop(ir);
-	dev->ir = NULL;
-err_out_free:
-	kfree(ir);
-	return err;
-}
-
-int cx231xx_ir_fini(struct cx231xx *dev)
-{
-	struct cx231xx_IR *ir = dev->ir;
-
-	/* skip detach on non attached boards */
-	if (!ir)
-		return 0;
-
-	cx231xx_ir_stop(ir);
-	ir_input_unregister(ir->input);
-	kfree(ir);
-
-	/* done */
-	dev->ir = NULL;
-	return 0;
-}
diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.c b/drivers/media/video/cx231xx/cx231xx-vbi.c
index 689c5e2..1d91448 100644
--- a/drivers/media/video/cx231xx/cx231xx-vbi.c
+++ b/drivers/media/video/cx231xx/cx231xx-vbi.c
@@ -102,7 +102,7 @@
 			return 0;
 	}
 
-	buf = dev->vbi_mode.isoc_ctl.buf;
+	buf = dev->vbi_mode.bulk_ctl.buf;
 
 	/* get buffer pointer and length */
 	p_buffer = urb->transfer_buffer;
@@ -180,7 +180,7 @@
 	height = ((dev->norm & V4L2_STD_625_50) ?
 		  PAL_VBI_LINES : NTSC_VBI_LINES);
 
-	*size = (dev->width * height * 2);
+	*size = (dev->width * height * 2 * 2);
 	if (0 == *count)
 		*count = CX231XX_DEF_VBI_BUF;
 
@@ -209,8 +209,8 @@
 	   VIDEOBUF_ACTIVE, it won't be, though.
 	 */
 	spin_lock_irqsave(&dev->vbi_mode.slock, flags);
-	if (dev->vbi_mode.isoc_ctl.buf == buf)
-		dev->vbi_mode.isoc_ctl.buf = NULL;
+	if (dev->vbi_mode.bulk_ctl.buf == buf)
+		dev->vbi_mode.bulk_ctl.buf = NULL;
 	spin_unlock_irqrestore(&dev->vbi_mode.slock, flags);
 
 	videobuf_vmalloc_free(&buf->vb);
@@ -230,7 +230,7 @@
 
 	height = ((dev->norm & V4L2_STD_625_50) ?
 		  PAL_VBI_LINES : NTSC_VBI_LINES);
-	buf->vb.size = ((dev->width << 1) * height);
+	buf->vb.size = ((dev->width << 1) * height * 2);
 
 	if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
 		return -EINVAL;
@@ -246,7 +246,7 @@
 			goto fail;
 	}
 
-	if (!dev->vbi_mode.isoc_ctl.num_bufs)
+	if (!dev->vbi_mode.bulk_ctl.num_bufs)
 		urb_init = 1;
 
 	if (urb_init) {
@@ -328,7 +328,7 @@
 
 	/* Copy data from URB */
 	spin_lock(&dev->vbi_mode.slock);
-	rc = dev->vbi_mode.isoc_ctl.isoc_copy(dev, urb);
+	rc = dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb);
 	spin_unlock(&dev->vbi_mode.slock);
 
 	/* Reset status */
@@ -351,34 +351,34 @@
 
 	cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_uninit_vbi_isoc\n");
 
-	dev->vbi_mode.isoc_ctl.nfields = -1;
-	for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
-		urb = dev->vbi_mode.isoc_ctl.urb[i];
+	dev->vbi_mode.bulk_ctl.nfields = -1;
+	for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
+		urb = dev->vbi_mode.bulk_ctl.urb[i];
 		if (urb) {
 			if (!irqs_disabled())
 				usb_kill_urb(urb);
 			else
 				usb_unlink_urb(urb);
 
-			if (dev->vbi_mode.isoc_ctl.transfer_buffer[i]) {
+			if (dev->vbi_mode.bulk_ctl.transfer_buffer[i]) {
 
-				kfree(dev->vbi_mode.isoc_ctl.
+				kfree(dev->vbi_mode.bulk_ctl.
 				      transfer_buffer[i]);
-				dev->vbi_mode.isoc_ctl.transfer_buffer[i] =
+				dev->vbi_mode.bulk_ctl.transfer_buffer[i] =
 				    NULL;
 			}
 			usb_free_urb(urb);
-			dev->vbi_mode.isoc_ctl.urb[i] = NULL;
+			dev->vbi_mode.bulk_ctl.urb[i] = NULL;
 		}
-		dev->vbi_mode.isoc_ctl.transfer_buffer[i] = NULL;
+		dev->vbi_mode.bulk_ctl.transfer_buffer[i] = NULL;
 	}
 
-	kfree(dev->vbi_mode.isoc_ctl.urb);
-	kfree(dev->vbi_mode.isoc_ctl.transfer_buffer);
+	kfree(dev->vbi_mode.bulk_ctl.urb);
+	kfree(dev->vbi_mode.bulk_ctl.transfer_buffer);
 
-	dev->vbi_mode.isoc_ctl.urb = NULL;
-	dev->vbi_mode.isoc_ctl.transfer_buffer = NULL;
-	dev->vbi_mode.isoc_ctl.num_bufs = 0;
+	dev->vbi_mode.bulk_ctl.urb = NULL;
+	dev->vbi_mode.bulk_ctl.transfer_buffer = NULL;
+	dev->vbi_mode.bulk_ctl.num_bufs = 0;
 
 	cx231xx_capture_start(dev, 0, Vbi);
 }
@@ -389,7 +389,7 @@
  */
 int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
 			  int num_bufs, int max_pkt_size,
-			  int (*isoc_copy) (struct cx231xx *dev,
+			  int (*bulk_copy) (struct cx231xx *dev,
 					    struct urb *urb))
 {
 	struct cx231xx_dmaqueue *dma_q = &dev->vbi_mode.vidq;
@@ -408,8 +408,8 @@
 		       usb_rcvbulkpipe(dev->udev,
 				       dev->vbi_mode.end_point_addr));
 
-	dev->vbi_mode.isoc_ctl.isoc_copy = isoc_copy;
-	dev->vbi_mode.isoc_ctl.num_bufs = num_bufs;
+	dev->vbi_mode.bulk_ctl.bulk_copy = bulk_copy;
+	dev->vbi_mode.bulk_ctl.num_bufs = num_bufs;
 	dma_q->pos = 0;
 	dma_q->is_partial_line = 0;
 	dma_q->last_sav = 0;
@@ -421,42 +421,42 @@
 	for (i = 0; i < 8; i++)
 		dma_q->partial_buf[i] = 0;
 
-	dev->vbi_mode.isoc_ctl.urb = kzalloc(sizeof(void *) * num_bufs,
+	dev->vbi_mode.bulk_ctl.urb = kzalloc(sizeof(void *) * num_bufs,
 					     GFP_KERNEL);
-	if (!dev->vbi_mode.isoc_ctl.urb) {
+	if (!dev->vbi_mode.bulk_ctl.urb) {
 		cx231xx_errdev("cannot alloc memory for usb buffers\n");
 		return -ENOMEM;
 	}
 
-	dev->vbi_mode.isoc_ctl.transfer_buffer =
+	dev->vbi_mode.bulk_ctl.transfer_buffer =
 	    kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
-	if (!dev->vbi_mode.isoc_ctl.transfer_buffer) {
+	if (!dev->vbi_mode.bulk_ctl.transfer_buffer) {
 		cx231xx_errdev("cannot allocate memory for usbtransfer\n");
-		kfree(dev->vbi_mode.isoc_ctl.urb);
+		kfree(dev->vbi_mode.bulk_ctl.urb);
 		return -ENOMEM;
 	}
 
-	dev->vbi_mode.isoc_ctl.max_pkt_size = max_pkt_size;
-	dev->vbi_mode.isoc_ctl.buf = NULL;
+	dev->vbi_mode.bulk_ctl.max_pkt_size = max_pkt_size;
+	dev->vbi_mode.bulk_ctl.buf = NULL;
 
-	sb_size = max_packets * dev->vbi_mode.isoc_ctl.max_pkt_size;
+	sb_size = max_packets * dev->vbi_mode.bulk_ctl.max_pkt_size;
 
 	/* allocate urbs and transfer buffers */
-	for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
+	for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
 
 		urb = usb_alloc_urb(0, GFP_KERNEL);
 		if (!urb) {
 			cx231xx_err(DRIVER_NAME
-				    ": cannot alloc isoc_ctl.urb %i\n", i);
+				    ": cannot alloc bulk_ctl.urb %i\n", i);
 			cx231xx_uninit_vbi_isoc(dev);
 			return -ENOMEM;
 		}
-		dev->vbi_mode.isoc_ctl.urb[i] = urb;
+		dev->vbi_mode.bulk_ctl.urb[i] = urb;
 		urb->transfer_flags = 0;
 
-		dev->vbi_mode.isoc_ctl.transfer_buffer[i] =
+		dev->vbi_mode.bulk_ctl.transfer_buffer[i] =
 		    kzalloc(sb_size, GFP_KERNEL);
-		if (!dev->vbi_mode.isoc_ctl.transfer_buffer[i]) {
+		if (!dev->vbi_mode.bulk_ctl.transfer_buffer[i]) {
 			cx231xx_err(DRIVER_NAME
 				    ": unable to allocate %i bytes for transfer"
 				    " buffer %i%s\n", sb_size, i,
@@ -467,15 +467,15 @@
 
 		pipe = usb_rcvbulkpipe(dev->udev, dev->vbi_mode.end_point_addr);
 		usb_fill_bulk_urb(urb, dev->udev, pipe,
-				  dev->vbi_mode.isoc_ctl.transfer_buffer[i],
+				  dev->vbi_mode.bulk_ctl.transfer_buffer[i],
 				  sb_size, cx231xx_irq_vbi_callback, dma_q);
 	}
 
 	init_waitqueue_head(&dma_q->wq);
 
 	/* submit urbs and enables IRQ */
-	for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
-		rc = usb_submit_urb(dev->vbi_mode.isoc_ctl.urb[i], GFP_ATOMIC);
+	for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
+		rc = usb_submit_urb(dev->vbi_mode.bulk_ctl.urb[i], GFP_ATOMIC);
 		if (rc) {
 			cx231xx_err(DRIVER_NAME
 				    ": submit of urb %i failed (error=%i)\n", i,
@@ -536,7 +536,7 @@
 	buf->vb.field_count++;
 	do_gettimeofday(&buf->vb.ts);
 
-	dev->vbi_mode.isoc_ctl.buf = NULL;
+	dev->vbi_mode.bulk_ctl.buf = NULL;
 
 	list_del(&buf->vb.queue);
 	wake_up(&buf->vb.done);
@@ -549,11 +549,16 @@
 	struct cx231xx_buffer *buf;
 	u32 _line_size = dev->width * 2;
 
-	if (dma_q->current_field != field_number)
+	if (dma_q->current_field == -1) {
+		/* Just starting up */
 		cx231xx_reset_vbi_buffer(dev, dma_q);
+	}
+
+	if (dma_q->current_field != field_number)
+		dma_q->lines_completed = 0;
 
 	/* get the buffer pointer */
-	buf = dev->vbi_mode.isoc_ctl.buf;
+	buf = dev->vbi_mode.bulk_ctl.buf;
 
 	/* Remember the field number for next time */
 	dma_q->current_field = field_number;
@@ -597,8 +602,8 @@
 			vbi_buffer_filled(dev, dma_q, buf);
 
 			dma_q->pos = 0;
-			buf = NULL;
 			dma_q->lines_completed = 0;
+			cx231xx_reset_vbi_buffer(dev, dma_q);
 		}
 	}
 
@@ -618,7 +623,7 @@
 
 	if (list_empty(&dma_q->active)) {
 		cx231xx_err(DRIVER_NAME ": No active queue to serve\n");
-		dev->vbi_mode.isoc_ctl.buf = NULL;
+		dev->vbi_mode.bulk_ctl.buf = NULL;
 		*buf = NULL;
 		return;
 	}
@@ -630,7 +635,7 @@
 	outp = videobuf_to_vmalloc(&(*buf)->vb);
 	memset(outp, 0, (*buf)->vb.size);
 
-	dev->vbi_mode.isoc_ctl.buf = *buf;
+	dev->vbi_mode.bulk_ctl.buf = *buf;
 
 	return;
 }
@@ -640,7 +645,7 @@
 {
 	struct cx231xx_buffer *buf;
 
-	buf = dev->vbi_mode.isoc_ctl.buf;
+	buf = dev->vbi_mode.bulk_ctl.buf;
 
 	if (buf == NULL) {
 		/* first try to get the buffer */
@@ -664,7 +669,7 @@
 	void *startwrite;
 	int offset, lencopy;
 
-	buf = dev->vbi_mode.isoc_ctl.buf;
+	buf = dev->vbi_mode.bulk_ctl.buf;
 
 	if (buf == NULL)
 		return -EINVAL;
@@ -679,6 +684,11 @@
 	offset = (dma_q->lines_completed * _line_size) +
 		 current_line_bytes_copied;
 
+	if (dma_q->current_field == 2) {
+		/* Populate the second half of the frame */
+		offset += (dev->width * 2 * dma_q->lines_per_field);
+	}
+
 	/* prepare destination address */
 	startwrite = p_out_buffer + offset;
 
@@ -697,5 +707,8 @@
 
 	height = ((dev->norm & V4L2_STD_625_50) ?
 		  PAL_VBI_LINES : NTSC_VBI_LINES);
-	return (dma_q->lines_completed == height) ? 1 : 0;
+	if (dma_q->lines_completed == height && dma_q->current_field == 2)
+		return 1;
+	else
+		return 0;
 }
diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.h b/drivers/media/video/cx231xx/cx231xx-vbi.h
index 89c7fe8..16c7d20 100644
--- a/drivers/media/video/cx231xx/cx231xx-vbi.h
+++ b/drivers/media/video/cx231xx/cx231xx-vbi.h
@@ -41,7 +41,7 @@
 /* stream functions */
 int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
 			  int num_bufs, int max_pkt_size,
-			  int (*isoc_copy) (struct cx231xx *dev,
+			  int (*bulk_copy) (struct cx231xx *dev,
 					    struct urb *urb));
 
 void cx231xx_uninit_vbi_isoc(struct cx231xx *dev);
diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c
index e760145..b13b69f 100644
--- a/drivers/media/video/cx231xx/cx231xx-video.c
+++ b/drivers/media/video/cx231xx/cx231xx-video.c
@@ -237,7 +237,10 @@
 	buf->vb.field_count++;
 	do_gettimeofday(&buf->vb.ts);
 
-	dev->video_mode.isoc_ctl.buf = NULL;
+	if (dev->USE_ISO)
+		dev->video_mode.isoc_ctl.buf = NULL;
+	else
+		dev->video_mode.bulk_ctl.buf = NULL;
 
 	list_del(&buf->vb.queue);
 	wake_up(&buf->vb.done);
@@ -295,7 +298,10 @@
 
 	if (list_empty(&dma_q->active)) {
 		cx231xx_isocdbg("No active queue to serve\n");
-		dev->video_mode.isoc_ctl.buf = NULL;
+		if (dev->USE_ISO)
+			dev->video_mode.isoc_ctl.buf = NULL;
+		else
+			dev->video_mode.bulk_ctl.buf = NULL;
 		*buf = NULL;
 		return;
 	}
@@ -307,7 +313,10 @@
 	outp = videobuf_to_vmalloc(&(*buf)->vb);
 	memset(outp, 0, (*buf)->vb.size);
 
-	dev->video_mode.isoc_ctl.buf = *buf;
+	if (dev->USE_ISO)
+		dev->video_mode.isoc_ctl.buf = *buf;
+	else
+		dev->video_mode.bulk_ctl.buf = *buf;
 
 	return;
 }
@@ -418,6 +427,93 @@
 	return rc;
 }
 
+static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb)
+{
+	struct cx231xx_buffer *buf;
+	struct cx231xx_dmaqueue *dma_q = urb->context;
+	unsigned char *outp = NULL;
+	int rc = 1;
+	unsigned char *p_buffer;
+	u32 bytes_parsed = 0, buffer_size = 0;
+	u8 sav_eav = 0;
+
+	if (!dev)
+		return 0;
+
+	if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
+		return 0;
+
+	if (urb->status < 0) {
+		print_err_status(dev, -1, urb->status);
+		if (urb->status == -ENOENT)
+			return 0;
+	}
+
+	buf = dev->video_mode.bulk_ctl.buf;
+	if (buf != NULL)
+		outp = videobuf_to_vmalloc(&buf->vb);
+
+	if (1) {
+
+		/*  get buffer pointer and length */
+		p_buffer = urb->transfer_buffer;
+		buffer_size = urb->actual_length;
+		bytes_parsed = 0;
+
+		if (dma_q->is_partial_line) {
+			/* Handle the case of a partial line */
+			sav_eav = dma_q->last_sav;
+		} else {
+			/* Check for a SAV/EAV overlapping
+				the buffer boundary */
+			sav_eav =
+			    cx231xx_find_boundary_SAV_EAV(p_buffer,
+							  dma_q->partial_buf,
+							  &bytes_parsed);
+		}
+
+		sav_eav &= 0xF0;
+		/* Get the first line if we have some portion of an SAV/EAV from
+		   the last buffer or a partial line  */
+		if (sav_eav) {
+			bytes_parsed += cx231xx_get_video_line(dev, dma_q,
+				sav_eav,	/* SAV/EAV */
+				p_buffer + bytes_parsed,	/* p_buffer */
+				buffer_size - bytes_parsed);/* buf size */
+		}
+
+		/* Now parse data that is completely in this buffer */
+		/* dma_q->is_partial_line = 0;  */
+
+		while (bytes_parsed < buffer_size) {
+			u32 bytes_used = 0;
+
+			sav_eav = cx231xx_find_next_SAV_EAV(
+				p_buffer + bytes_parsed,	/* p_buffer */
+				buffer_size - bytes_parsed,	/* buf size */
+				&bytes_used);/* bytes used to get SAV/EAV */
+
+			bytes_parsed += bytes_used;
+
+			sav_eav &= 0xF0;
+			if (sav_eav && (bytes_parsed < buffer_size)) {
+				bytes_parsed += cx231xx_get_video_line(dev,
+					dma_q, sav_eav,	/* SAV/EAV */
+					p_buffer + bytes_parsed,/* p_buffer */
+					buffer_size - bytes_parsed);/*buf size*/
+			}
+		}
+
+		/* Save the last four bytes of the buffer so we can check the
+		   buffer boundary condition next time */
+		memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
+		bytes_parsed = 0;
+
+	}
+	return rc;
+}
+
+
 u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
 				 u32 *p_bytes_used)
 {
@@ -533,7 +629,10 @@
 		cx231xx_reset_video_buffer(dev, dma_q);
 
 	/* get the buffer pointer */
-	buf = dev->video_mode.isoc_ctl.buf;
+	if (dev->USE_ISO)
+		buf = dev->video_mode.isoc_ctl.buf;
+	else
+		buf = dev->video_mode.bulk_ctl.buf;
 
 	/* Remember the field number for next time */
 	dma_q->current_field = field_number;
@@ -596,7 +695,10 @@
 			dma_q->field1_done = 0;
 	}
 
-	buf = dev->video_mode.isoc_ctl.buf;
+	if (dev->USE_ISO)
+		buf = dev->video_mode.isoc_ctl.buf;
+	else
+		buf = dev->video_mode.bulk_ctl.buf;
 
 	if (buf == NULL) {
 		u8 *outp = NULL;
@@ -626,7 +728,10 @@
 	void *startwrite;
 	int offset, lencopy;
 
-	buf = dev->video_mode.isoc_ctl.buf;
+	if (dev->USE_ISO)
+		buf = dev->video_mode.isoc_ctl.buf;
+	else
+		buf = dev->video_mode.bulk_ctl.buf;
 
 	if (buf == NULL)
 		return -1;
@@ -691,7 +796,6 @@
 {
 	struct cx231xx_fh *fh = vq->priv_data;
 	struct cx231xx *dev = fh->dev;
-	struct v4l2_frequency f;
 
 	*size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)>>3;
 	if (0 == *count)
@@ -700,13 +804,6 @@
 	if (*count < CX231XX_MIN_BUF)
 		*count = CX231XX_MIN_BUF;
 
-	/* Ask tuner to go to analog mode */
-	memset(&f, 0, sizeof(f));
-	f.frequency = dev->ctl_freq;
-	f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-
-	call_all(dev, tuner, s_frequency, &f);
-
 	return 0;
 }
 
@@ -730,8 +827,13 @@
 	   VIDEOBUF_ACTIVE, it won't be, though.
 	 */
 	spin_lock_irqsave(&dev->video_mode.slock, flags);
-	if (dev->video_mode.isoc_ctl.buf == buf)
-		dev->video_mode.isoc_ctl.buf = NULL;
+	if (dev->USE_ISO) {
+		if (dev->video_mode.isoc_ctl.buf == buf)
+			dev->video_mode.isoc_ctl.buf = NULL;
+	} else {
+		if (dev->video_mode.bulk_ctl.buf == buf)
+			dev->video_mode.bulk_ctl.buf = NULL;
+	}
 	spin_unlock_irqrestore(&dev->video_mode.slock, flags);
 
 	videobuf_vmalloc_free(&buf->vb);
@@ -764,14 +866,27 @@
 			goto fail;
 	}
 
-	if (!dev->video_mode.isoc_ctl.num_bufs)
-		urb_init = 1;
-
+	if (dev->USE_ISO) {
+		if (!dev->video_mode.isoc_ctl.num_bufs)
+			urb_init = 1;
+	} else {
+		if (!dev->video_mode.bulk_ctl.num_bufs)
+			urb_init = 1;
+	}
+	/*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n",
+		urb_init, dev->video_mode.max_pkt_size);*/
 	if (urb_init) {
-		rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
+		dev->mode_tv = 0;
+		if (dev->USE_ISO)
+			rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
 				       CX231XX_NUM_BUFS,
 				       dev->video_mode.max_pkt_size,
 				       cx231xx_isoc_copy);
+		else
+			rc = cx231xx_init_bulk(dev, CX231XX_NUM_PACKETS,
+				       CX231XX_NUM_BUFS,
+				       dev->video_mode.max_pkt_size,
+				       cx231xx_bulk_copy);
 		if (rc < 0)
 			goto fail;
 	}
@@ -894,22 +1009,6 @@
 	return 0;
 }
 
-static void get_scale(struct cx231xx *dev,
-		      unsigned int width, unsigned int height,
-		      unsigned int *hscale, unsigned int *vscale)
-{
-	unsigned int maxw = norm_maxw(dev);
-	unsigned int maxh = norm_maxh(dev);
-
-	*hscale = (((unsigned long)maxw) << 12) / width - 4096L;
-	if (*hscale >= 0x4000)
-		*hscale = 0x3fff;
-
-	*vscale = (((unsigned long)maxh) << 12) / height - 4096L;
-	if (*vscale >= 0x4000)
-		*vscale = 0x3fff;
-}
-
 /* ------------------------------------------------------------------
 	IOCTL vidioc handling
    ------------------------------------------------------------------*/
@@ -920,8 +1019,6 @@
 	struct cx231xx_fh *fh = priv;
 	struct cx231xx *dev = fh->dev;
 
-	mutex_lock(&dev->lock);
-
 	f->fmt.pix.width = dev->width;
 	f->fmt.pix.height = dev->height;
 	f->fmt.pix.pixelformat = dev->format->fourcc;
@@ -931,8 +1028,6 @@
 
 	f->fmt.pix.field = V4L2_FIELD_INTERLACED;
 
-	mutex_unlock(&dev->lock);
-
 	return 0;
 }
 
@@ -956,7 +1051,6 @@
 	unsigned int height = f->fmt.pix.height;
 	unsigned int maxw = norm_maxw(dev);
 	unsigned int maxh = norm_maxh(dev);
-	unsigned int hscale, vscale;
 	struct cx231xx_fmt *fmt;
 
 	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
@@ -970,11 +1064,6 @@
 	   height must be even because of interlacing */
 	v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0);
 
-	get_scale(dev, width, height, &hscale, &vscale);
-
-	width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
-	height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
-
 	f->fmt.pix.width = width;
 	f->fmt.pix.height = height;
 	f->fmt.pix.pixelformat = fmt->fourcc;
@@ -999,47 +1088,35 @@
 	if (rc < 0)
 		return rc;
 
-	mutex_lock(&dev->lock);
-
 	vidioc_try_fmt_vid_cap(file, priv, f);
 
 	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
-	if (!fmt) {
-		rc = -EINVAL;
-		goto out;
-	}
+	if (!fmt)
+		return -EINVAL;
 
 	if (videobuf_queue_is_busy(&fh->vb_vidq)) {
 		cx231xx_errdev("%s queue busy\n", __func__);
-		rc = -EBUSY;
-		goto out;
+		return -EBUSY;
 	}
 
 	if (dev->stream_on && !fh->stream_on) {
 		cx231xx_errdev("%s device in use by another fh\n", __func__);
-		rc = -EBUSY;
-		goto out;
+		return -EBUSY;
 	}
 
 	/* set new image size */
 	dev->width = f->fmt.pix.width;
 	dev->height = f->fmt.pix.height;
 	dev->format = fmt;
-	get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
 
 	v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
 	call_all(dev, video, s_mbus_fmt, &mbus_fmt);
 	v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
 
-	/* Set the correct alternate setting for this resolution */
-	cx231xx_resolution_set(dev);
-
-out:
-	mutex_unlock(&dev->lock);
 	return rc;
 }
 
-static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id * id)
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
 {
 	struct cx231xx_fh *fh = priv;
 	struct cx231xx *dev = fh->dev;
@@ -1052,6 +1129,7 @@
 {
 	struct cx231xx_fh *fh = priv;
 	struct cx231xx *dev = fh->dev;
+	struct v4l2_mbus_framefmt mbus_fmt;
 	struct v4l2_format f;
 	int rc;
 
@@ -1061,7 +1139,6 @@
 
 	cx231xx_info("vidioc_s_std : 0x%x\n", (unsigned int)*norm);
 
-	mutex_lock(&dev->lock);
 	dev->norm = *norm;
 
 	/* Adjusts width/height, if needed */
@@ -1069,16 +1146,18 @@
 	f.fmt.pix.height = dev->height;
 	vidioc_try_fmt_vid_cap(file, priv, &f);
 
+	call_all(dev, core, s_std, dev->norm);
+
+	/* We need to reset basic properties in the decoder related to
+	   resolution (since a standard change effects things like the number
+	   of lines in VACT, etc) */
+	v4l2_fill_mbus_format(&mbus_fmt, &f.fmt.pix, V4L2_MBUS_FMT_FIXED);
+	call_all(dev, video, s_mbus_fmt, &mbus_fmt);
+	v4l2_fill_pix_format(&f.fmt.pix, &mbus_fmt);
+
 	/* set new image size */
 	dev->width = f.fmt.pix.width;
 	dev->height = f.fmt.pix.height;
-	get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
-
-	call_all(dev, core, s_std, dev->norm);
-
-	mutex_unlock(&dev->lock);
-
-	cx231xx_resolution_set(dev);
 
 	/* do mode control overrides */
 	cx231xx_do_mode_ctrl_overrides(dev);
@@ -1138,6 +1217,7 @@
 	struct cx231xx *dev = fh->dev;
 	int rc;
 
+	dev->mode_tv = 0;
 	rc = check_dev(dev);
 	if (rc < 0)
 		return rc;
@@ -1147,11 +1227,16 @@
 	if (0 == INPUT(i)->type)
 		return -EINVAL;
 
-	mutex_lock(&dev->lock);
-
 	video_mux(dev, i);
 
-	mutex_unlock(&dev->lock);
+	if (INPUT(i)->type == CX231XX_VMUX_TELEVISION ||
+	    INPUT(i)->type == CX231XX_VMUX_CABLE) {
+		/* There's a tuner, so reset the standard and put it on the
+		   last known frequency (since it was probably powered down
+		   until now */
+		call_all(dev, core, s_std, dev->norm);
+	}
+
 	return 0;
 }
 
@@ -1227,9 +1312,7 @@
 	}
 	*qc = cx231xx_ctls[i].v;
 
-	mutex_lock(&dev->lock);
 	call_all(dev, core, queryctrl, qc);
-	mutex_unlock(&dev->lock);
 
 	if (qc->type)
 		return 0;
@@ -1248,9 +1331,7 @@
 	if (rc < 0)
 		return rc;
 
-	mutex_lock(&dev->lock);
 	call_all(dev, core, g_ctrl, ctrl);
-	mutex_unlock(&dev->lock);
 	return rc;
 }
 
@@ -1265,9 +1346,7 @@
 	if (rc < 0)
 		return rc;
 
-	mutex_lock(&dev->lock);
 	call_all(dev, core, s_ctrl, ctrl);
-	mutex_unlock(&dev->lock);
 	return rc;
 }
 
@@ -1307,9 +1386,7 @@
 	if (0 != t->index)
 		return -EINVAL;
 #if 0
-	mutex_lock(&dev->lock);
 	call_all(dev, tuner, s_tuner, t);
-	mutex_unlock(&dev->lock);
 #endif
 	return 0;
 }
@@ -1320,14 +1397,11 @@
 	struct cx231xx_fh *fh = priv;
 	struct cx231xx *dev = fh->dev;
 
-	mutex_lock(&dev->lock);
 	f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
 	f->frequency = dev->ctl_freq;
 
 	call_all(dev, tuner, g_frequency, f);
 
-	mutex_unlock(&dev->lock);
-
 	return 0;
 }
 
@@ -1337,6 +1411,11 @@
 	struct cx231xx_fh *fh = priv;
 	struct cx231xx *dev = fh->dev;
 	int rc;
+	u32 if_frequency = 5400000;
+
+	cx231xx_info("Enter vidioc_s_frequency()f->frequency=%d;f->type=%d\n",
+		 f->frequency, f->type);
+	/*cx231xx_info("f->type:  1-radio 2-analogTV 3-digitalTV\n");*/
 
 	rc = check_dev(dev);
 	if (rc < 0)
@@ -1353,21 +1432,34 @@
 	/* set pre channel change settings in DIF first */
 	rc = cx231xx_tuner_pre_channel_change(dev);
 
-	mutex_lock(&dev->lock);
-
 	dev->ctl_freq = f->frequency;
-
-	if (dev->tuner_type == TUNER_XC5000) {
-		if (dev->cx231xx_set_analog_freq != NULL)
-			dev->cx231xx_set_analog_freq(dev, f->frequency);
-	} else
-		call_all(dev, tuner, s_frequency, f);
-
-	mutex_unlock(&dev->lock);
+	call_all(dev, tuner, s_frequency, f);
 
 	/* set post channel change settings in DIF first */
 	rc = cx231xx_tuner_post_channel_change(dev);
 
+	if (dev->tuner_type == TUNER_NXP_TDA18271) {
+		if (dev->norm & (V4L2_STD_MN | V4L2_STD_NTSC_443))
+			if_frequency = 5400000;  /*5.4MHz	*/
+		else if (dev->norm & V4L2_STD_B)
+			if_frequency = 6000000;  /*6.0MHz	*/
+		else if (dev->norm & (V4L2_STD_PAL_DK | V4L2_STD_SECAM_DK))
+			if_frequency = 6900000;  /*6.9MHz	*/
+		else if (dev->norm & V4L2_STD_GH)
+			if_frequency = 7100000;  /*7.1MHz	*/
+		else if (dev->norm & V4L2_STD_PAL_I)
+			if_frequency = 7250000;  /*7.25MHz	*/
+		else if (dev->norm & V4L2_STD_SECAM_L)
+			if_frequency = 6900000;  /*6.9MHz	*/
+		else if (dev->norm & V4L2_STD_SECAM_LC)
+			if_frequency = 1250000;  /*1.25MHz	*/
+
+		cx231xx_info("if_frequency is set to %d\n", if_frequency);
+		cx231xx_set_Colibri_For_LowIF(dev, if_frequency, 1, 1);
+
+		update_HH_register_after_set_DIF(dev);
+	}
+
 	cx231xx_info("Set New FREQUENCY to %d\n", f->frequency);
 
 	return rc;
@@ -1445,17 +1537,92 @@
 	case V4L2_CHIP_MATCH_I2C_DRIVER:
 		call_all(dev, core, g_register, reg);
 		return 0;
-	case V4L2_CHIP_MATCH_I2C_ADDR:
-		/* Not supported yet */
-		return -EINVAL;
+	case V4L2_CHIP_MATCH_I2C_ADDR:/*for register debug*/
+		switch (reg->match.addr) {
+		case 0:	/* Cx231xx - internal registers */
+			ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
+						  (u16)reg->reg, value, 4);
+			reg->val = value[0] | value[1] << 8 |
+				   value[2] << 16 | value[3] << 24;
+
+			break;
+		case 0x600:/* AFE - read byte */
+			ret = cx231xx_read_i2c_master(dev, AFE_DEVICE_ADDRESS,
+						 (u16)reg->reg, 2,
+						 &data, 1 , 0);
+			reg->val = le32_to_cpu(data & 0xff);
+			break;
+
+		case 0x880:/* Video Block - read byte */
+			if (reg->reg < 0x0b) {
+				ret = cx231xx_read_i2c_master(dev,
+						VID_BLK_I2C_ADDRESS,
+						 (u16)reg->reg, 2,
+						 &data, 1 , 0);
+				reg->val = le32_to_cpu(data & 0xff);
+			} else {
+				ret = cx231xx_read_i2c_master(dev,
+						VID_BLK_I2C_ADDRESS,
+						 (u16)reg->reg, 2,
+						 &data, 4 , 0);
+				reg->val = le32_to_cpu(data);
+			}
+			break;
+		case 0x980:
+			ret = cx231xx_read_i2c_master(dev,
+						I2S_BLK_DEVICE_ADDRESS,
+						(u16)reg->reg, 1,
+						&data, 1 , 0);
+			reg->val = le32_to_cpu(data & 0xff);
+			break;
+		case 0x400:
+			ret =
+			    cx231xx_read_i2c_master(dev, 0x40,
+						  (u16)reg->reg, 1,
+						 &data, 1 , 0);
+			reg->val = le32_to_cpu(data & 0xff);
+			break;
+		case 0xc01:
+			ret =
+				cx231xx_read_i2c_master(dev, 0xc0,
+						(u16)reg->reg, 2,
+						 &data, 38, 1);
+			reg->val = le32_to_cpu(data);
+			break;
+		case 0x022:
+			ret =
+				cx231xx_read_i2c_master(dev, 0x02,
+						(u16)reg->reg, 1,
+						 &data, 1, 2);
+			reg->val = le32_to_cpu(data & 0xff);
+			break;
+		case 0x322:
+			ret = cx231xx_read_i2c_master(dev,
+						0x32,
+						 (u16)reg->reg, 1,
+						 &data, 4 , 2);
+				reg->val = le32_to_cpu(data);
+			break;
+		case 0x342:
+			ret = cx231xx_read_i2c_master(dev,
+						0x34,
+						 (u16)reg->reg, 1,
+						 &data, 4 , 2);
+				reg->val = le32_to_cpu(data);
+			break;
+
+		default:
+			cx231xx_info("no match device address!!\n");
+			break;
+			}
+		return ret < 0 ? ret : 0;
+		/*return -EINVAL;*/
 	default:
 		if (!v4l2_chip_match_host(&reg->match))
 			return -EINVAL;
 	}
 
-	mutex_lock(&dev->lock);
 	call_all(dev, core, g_register, reg);
-	mutex_unlock(&dev->lock);
 
 	return ret;
 }
@@ -1531,14 +1698,96 @@
 			}
 		}
 		return ret < 0 ? ret : 0;
+	case V4L2_CHIP_MATCH_I2C_ADDR:
+		{
+			value = (u32) buf & 0xffffffff;
 
+			switch (reg->match.addr) {
+			case 0:/*cx231xx internal registers*/
+					data[0] = (u8) value;
+					data[1] = (u8) (value >> 8);
+					data[2] = (u8) (value >> 16);
+					data[3] = (u8) (value >> 24);
+					ret = cx231xx_write_ctrl_reg(dev,
+							   VRT_SET_REGISTER,
+							   (u16)reg->reg, data,
+							   4);
+					break;
+			case 0x600:/* AFE - read byte */
+					ret = cx231xx_write_i2c_master(dev,
+							AFE_DEVICE_ADDRESS,
+							(u16)reg->reg, 2,
+							value, 1 , 0);
+					break;
+
+			case 0x880:/* Video Block - read byte */
+					if (reg->reg < 0x0b)
+						cx231xx_write_i2c_master(dev,
+							VID_BLK_I2C_ADDRESS,
+							(u16)reg->reg, 2,
+							value, 1, 0);
+					else
+						cx231xx_write_i2c_master(dev,
+							VID_BLK_I2C_ADDRESS,
+							(u16)reg->reg, 2,
+							value, 4, 0);
+					break;
+			case 0x980:
+					ret =
+						cx231xx_write_i2c_master(dev,
+							I2S_BLK_DEVICE_ADDRESS,
+							(u16)reg->reg, 1,
+							value, 1, 0);
+					break;
+			case 0x400:
+					ret =
+						cx231xx_write_i2c_master(dev,
+							0x40,
+							(u16)reg->reg, 1,
+							value, 1, 0);
+					break;
+			case 0xc01:
+					ret =
+						cx231xx_write_i2c_master(dev,
+							 0xc0,
+							 (u16)reg->reg, 1,
+							 value, 1, 1);
+					break;
+
+			case 0x022:
+					ret =
+						cx231xx_write_i2c_master(dev,
+							0x02,
+							(u16)reg->reg, 1,
+							value, 1, 2);
+			case 0x322:
+					ret =
+						cx231xx_write_i2c_master(dev,
+							0x32,
+							(u16)reg->reg, 1,
+							value, 4, 2);
+					break;
+
+			case 0x342:
+					ret =
+						cx231xx_write_i2c_master(dev,
+							0x34,
+							(u16)reg->reg, 1,
+							value, 4, 2);
+					break;
+			default:
+				cx231xx_info("no match device address, "
+					"the value is %x\n", reg->match.addr);
+					break;
+
+					}
+
+		}
 	default:
 		break;
 	}
 
-	mutex_lock(&dev->lock);
 	call_all(dev, core, s_register, reg);
-	mutex_unlock(&dev->lock);
 
 	return ret;
 }
@@ -1575,7 +1824,6 @@
 	if (rc < 0)
 		return rc;
 
-	mutex_lock(&dev->lock);
 	rc = res_get(fh);
 
 	if (likely(rc >= 0))
@@ -1583,8 +1831,6 @@
 
 	call_all(dev, video, s_stream, 1);
 
-	mutex_unlock(&dev->lock);
-
 	return rc;
 }
 
@@ -1605,15 +1851,11 @@
 	if (type != fh->type)
 		return -EINVAL;
 
-	mutex_lock(&dev->lock);
-
 	cx25840_call(dev, video, s_stream, 0);
 
 	videobuf_streamoff(&fh->vb_vidq);
 	res_free(fh);
 
-	mutex_unlock(&dev->lock);
-
 	return 0;
 }
 
@@ -1668,8 +1910,6 @@
 	if (rc < 0)
 		return rc;
 
-	mutex_lock(&dev->lock);
-
 	f->fmt.sliced.service_set = 0;
 
 	call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced);
@@ -1677,7 +1917,6 @@
 	if (f->fmt.sliced.service_set == 0)
 		rc = -EINVAL;
 
-	mutex_unlock(&dev->lock);
 	return rc;
 }
 
@@ -1692,9 +1931,7 @@
 	if (rc < 0)
 		return rc;
 
-	mutex_lock(&dev->lock);
 	call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced);
-	mutex_unlock(&dev->lock);
 
 	if (f->fmt.sliced.service_set == 0)
 		return -EINVAL;
@@ -1709,12 +1946,10 @@
 {
 	struct cx231xx_fh *fh = priv;
 	struct cx231xx *dev = fh->dev;
-
-	f->fmt.vbi.sampling_rate = (dev->norm & V4L2_STD_625_50) ?
-	    35468950 : 28636363;
+	f->fmt.vbi.sampling_rate = 6750000 * 4;
 	f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
 	f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
-	f->fmt.vbi.offset = 64 * 4;
+	f->fmt.vbi.offset = 0;
 	f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
 	    PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
 	f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
@@ -1739,11 +1974,10 @@
 	}
 
 	f->type = V4L2_BUF_TYPE_VBI_CAPTURE;
-	f->fmt.vbi.sampling_rate = (dev->norm & V4L2_STD_625_50) ?
-	    35468950 : 28636363;
+	f->fmt.vbi.sampling_rate = 6750000 * 4;
 	f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
 	f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
-	f->fmt.vbi.offset = 244;
+	f->fmt.vbi.offset = 0;
 	f->fmt.vbi.flags = 0;
 	f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
 	    PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
@@ -1847,9 +2081,7 @@
 	strcpy(t->name, "Radio");
 	t->type = V4L2_TUNER_RADIO;
 
-	mutex_lock(&dev->lock);
 	call_all(dev, tuner, s_tuner, t);
-	mutex_unlock(&dev->lock);
 
 	return 0;
 }
@@ -1880,9 +2112,7 @@
 	if (0 != t->index)
 		return -EINVAL;
 
-	mutex_lock(&dev->lock);
 	call_all(dev, tuner, s_tuner, t);
-	mutex_unlock(&dev->lock);
 
 	return 0;
 }
@@ -1941,8 +2171,6 @@
 		break;
 	}
 
-	mutex_lock(&dev->lock);
-
 	cx231xx_videodbg("open dev=%s type=%s users=%d\n",
 			 video_device_node_name(vdev), v4l2_type_names[fh_type],
 			 dev->users);
@@ -1952,7 +2180,6 @@
 	if (errCode < 0) {
 		cx231xx_errdev
 		    ("Device locked on digital mode. Can't open analog\n");
-		mutex_unlock(&dev->lock);
 		return -EBUSY;
 	}
 #endif
@@ -1960,7 +2187,6 @@
 	fh = kzalloc(sizeof(struct cx231xx_fh), GFP_KERNEL);
 	if (!fh) {
 		cx231xx_errdev("cx231xx-video.c: Out of memory?!\n");
-		mutex_unlock(&dev->lock);
 		return -ENOMEM;
 	}
 	fh->dev = dev;
@@ -1971,16 +2197,18 @@
 	if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
 		dev->width = norm_maxw(dev);
 		dev->height = norm_maxh(dev);
-		dev->hscale = 0;
-		dev->vscale = 0;
 
 		/* Power up in Analog TV mode */
-		cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);
+		if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER ||
+		    dev->model == CX231XX_BOARD_HAUPPAUGE_USBLIVE2)
+			cx231xx_set_power_mode(dev,
+				 POLARIS_AVMODE_ENXTERNAL_AV);
+		else
+			cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);
 
 #if 0
 		cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
 #endif
-		cx231xx_resolution_set(dev);
 
 		/* set video alternate setting */
 		cx231xx_set_video_alternate(dev);
@@ -1991,7 +2219,6 @@
 
 		/* device needs to be initialized before isoc transfer */
 		dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
-		video_mux(dev, dev->video_input);
 
 	}
 	if (fh->radio) {
@@ -2008,20 +2235,22 @@
 		videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_video_qops,
 					    NULL, &dev->video_mode.slock,
 					    fh->type, V4L2_FIELD_INTERLACED,
-					    sizeof(struct cx231xx_buffer), fh);
+					    sizeof(struct cx231xx_buffer),
+					    fh, &dev->lock);
 	if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
 		/* Set the required alternate setting  VBI interface works in
 		   Bulk mode only */
-		cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
+		if (dev->model != CX231XX_BOARD_CNXT_VIDEO_GRABBER &&
+		    dev->model != CX231XX_BOARD_HAUPPAUGE_USBLIVE2)
+			cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
 
 		videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_vbi_qops,
 					    NULL, &dev->vbi_mode.slock,
 					    fh->type, V4L2_FIELD_SEQ_TB,
-					    sizeof(struct cx231xx_buffer), fh);
+					    sizeof(struct cx231xx_buffer),
+					    fh, &dev->lock);
 	}
 
-	mutex_unlock(&dev->lock);
-
 	return errCode;
 }
 
@@ -2054,6 +2283,10 @@
 	if (dev->vdev) {
 		cx231xx_info("V4L2 device %s deregistered\n",
 			     video_device_node_name(dev->vdev));
+
+		if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER)
+			cx231xx_417_unregister(dev);
+
 		if (video_is_registered(dev->vdev))
 			video_unregister_device(dev->vdev);
 		else
@@ -2074,40 +2307,45 @@
 
 	cx231xx_videodbg("users=%d\n", dev->users);
 
-	mutex_lock(&dev->lock);
-
+	cx231xx_videodbg("users=%d\n", dev->users);
 	if (res_check(fh))
 		res_free(fh);
 
-	if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
-		videobuf_stop(&fh->vb_vidq);
-		videobuf_mmap_free(&fh->vb_vidq);
+	/*To workaround error number=-71 on EP0 for VideoGrabber,
+		 need exclude following.*/
+	if (dev->model != CX231XX_BOARD_CNXT_VIDEO_GRABBER &&
+	    dev->model != CX231XX_BOARD_HAUPPAUGE_USBLIVE2)
+		if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+			videobuf_stop(&fh->vb_vidq);
+			videobuf_mmap_free(&fh->vb_vidq);
 
-		/* the device is already disconnect,
-		   free the remaining resources */
-		if (dev->state & DEV_DISCONNECTED) {
-			cx231xx_release_resources(dev);
-			mutex_unlock(&dev->lock);
-			kfree(dev);
+			/* the device is already disconnect,
+			   free the remaining resources */
+			if (dev->state & DEV_DISCONNECTED) {
+				if (atomic_read(&dev->devlist_count) > 0) {
+					cx231xx_release_resources(dev);
+					kfree(dev);
+					dev = NULL;
+					return 0;
+				}
+				return 0;
+			}
+
+			/* do this before setting alternate! */
+			cx231xx_uninit_vbi_isoc(dev);
+
+			/* set alternate 0 */
+			if (!dev->vbi_or_sliced_cc_mode)
+				cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
+			else
+				cx231xx_set_alt_setting(dev, INDEX_HANC, 0);
+
+			kfree(fh);
+			dev->users--;
+			wake_up_interruptible_nr(&dev->open, 1);
 			return 0;
 		}
 
-		/* do this before setting alternate! */
-		cx231xx_uninit_vbi_isoc(dev);
-
-		/* set alternate 0 */
-		if (!dev->vbi_or_sliced_cc_mode)
-			cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
-		else
-			cx231xx_set_alt_setting(dev, INDEX_HANC, 0);
-
-		kfree(fh);
-		dev->users--;
-		wake_up_interruptible_nr(&dev->open, 1);
-		mutex_unlock(&dev->lock);
-		return 0;
-	}
-
 	if (dev->users == 1) {
 		videobuf_stop(&fh->vb_vidq);
 		videobuf_mmap_free(&fh->vb_vidq);
@@ -2116,8 +2354,8 @@
 		   free the remaining resources */
 		if (dev->state & DEV_DISCONNECTED) {
 			cx231xx_release_resources(dev);
-			mutex_unlock(&dev->lock);
 			kfree(dev);
+			dev = NULL;
 			return 0;
 		}
 
@@ -2125,7 +2363,10 @@
 		call_all(dev, core, s_power, 0);
 
 		/* do this before setting alternate! */
-		cx231xx_uninit_isoc(dev);
+		if (dev->USE_ISO)
+			cx231xx_uninit_isoc(dev);
+		else
+			cx231xx_uninit_bulk(dev);
 		cx231xx_set_mode(dev, CX231XX_SUSPEND);
 
 		/* set alternate 0 */
@@ -2134,7 +2375,6 @@
 	kfree(fh);
 	dev->users--;
 	wake_up_interruptible_nr(&dev->open, 1);
-	mutex_unlock(&dev->lock);
 	return 0;
 }
 
@@ -2156,9 +2396,7 @@
 
 	if ((fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
 	    (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)) {
-		mutex_lock(&dev->lock);
 		rc = res_get(fh);
-		mutex_unlock(&dev->lock);
 
 		if (unlikely(rc < 0))
 			return rc;
@@ -2173,7 +2411,7 @@
  * cx231xx_v4l2_poll()
  * will allocate buffers when called for the first time
  */
-static unsigned int cx231xx_v4l2_poll(struct file *filp, poll_table * wait)
+static unsigned int cx231xx_v4l2_poll(struct file *filp, poll_table *wait)
 {
 	struct cx231xx_fh *fh = filp->private_data;
 	struct cx231xx *dev = fh->dev;
@@ -2183,9 +2421,7 @@
 	if (rc < 0)
 		return rc;
 
-	mutex_lock(&dev->lock);
 	rc = res_get(fh);
-	mutex_unlock(&dev->lock);
 
 	if (unlikely(rc < 0))
 		return POLLERR;
@@ -2210,9 +2446,7 @@
 	if (rc < 0)
 		return rc;
 
-	mutex_lock(&dev->lock);
 	rc = res_get(fh);
-	mutex_unlock(&dev->lock);
 
 	if (unlikely(rc < 0))
 		return rc;
@@ -2234,7 +2468,7 @@
 	.read    = cx231xx_v4l2_read,
 	.poll    = cx231xx_v4l2_poll,
 	.mmap    = cx231xx_v4l2_mmap,
-	.ioctl   = video_ioctl2,
+	.unlocked_ioctl   = video_ioctl2,
 };
 
 static const struct v4l2_ioctl_ops video_ioctl_ops = {
@@ -2336,6 +2570,7 @@
 	vfd->v4l2_dev = &dev->v4l2_dev;
 	vfd->release = video_device_release;
 	vfd->debug = video_debug;
+	vfd->lock = &dev->lock;
 
 	snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
 
@@ -2358,12 +2593,12 @@
 	dev->width = norm_maxw(dev);
 	dev->height = norm_maxh(dev);
 	dev->interlaced = 0;
-	dev->hscale = 0;
-	dev->vscale = 0;
 
 	/* Analog specific initialization */
 	dev->format = &format[0];
-	/* video_mux(dev, dev->video_input); */
+
+	/* Set the initial input */
+	video_mux(dev, dev->video_input);
 
 	/* Audio defaults */
 	dev->mute = 1;
diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h
index 38d4171..d067df9 100644
--- a/drivers/media/video/cx231xx/cx231xx.h
+++ b/drivers/media/video/cx231xx/cx231xx.h
@@ -27,16 +27,15 @@
 #include <linux/ioctl.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
+#include <linux/workqueue.h>
 #include <linux/mutex.h>
 
+#include <media/cx2341x.h>
 
 #include <media/videobuf-vmalloc.h>
 #include <media/v4l2-device.h>
 #include <media/ir-core.h>
-#if defined(CONFIG_VIDEO_CX231XX_DVB) || \
-	defined(CONFIG_VIDEO_CX231XX_DVB_MODULE)
 #include <media/videobuf-dvb.h>
-#endif
 
 #include "cx231xx-reg.h"
 #include "cx231xx-pcb-cfg.h"
@@ -49,12 +48,20 @@
 #define     AFE_DEVICE_ADDRESS		0x60
 #define     I2S_BLK_DEVICE_ADDRESS	0x98
 #define     VID_BLK_I2C_ADDRESS		0x88
+#define     VERVE_I2C_ADDRESS           0x40
 #define     DIF_USE_BASEBAND            0xFFFFFFFF
 
 /* Boards supported by driver */
 #define CX231XX_BOARD_UNKNOWN		    0
-#define CX231XX_BOARD_CNXT_RDE_250     	1
-#define CX231XX_BOARD_CNXT_RDU_250     	2
+#define CX231XX_BOARD_CNXT_CARRAERA	1
+#define CX231XX_BOARD_CNXT_SHELBY	2
+#define CX231XX_BOARD_CNXT_RDE_253S	3
+#define CX231XX_BOARD_CNXT_RDU_253S	4
+#define CX231XX_BOARD_CNXT_VIDEO_GRABBER	5
+#define CX231XX_BOARD_CNXT_RDE_250	6
+#define CX231XX_BOARD_CNXT_RDU_250	7
+#define CX231XX_BOARD_HAUPPAUGE_EXETER  8
+#define CX231XX_BOARD_HAUPPAUGE_USBLIVE2 9
 
 /* Limits minimum and default number of buffers */
 #define CX231XX_MIN_BUF                 4
@@ -95,6 +102,24 @@
 #define CX231XX_URB_TIMEOUT		\
 		msecs_to_jiffies(CX231XX_NUM_BUFS * CX231XX_NUM_PACKETS)
 
+#define CX231xx_NORMS (\
+	V4L2_STD_NTSC_M |  V4L2_STD_NTSC_M_JP |  V4L2_STD_NTSC_443 | \
+	V4L2_STD_PAL_BG |  V4L2_STD_PAL_DK    |  V4L2_STD_PAL_I    | \
+	V4L2_STD_PAL_M  |  V4L2_STD_PAL_N     |  V4L2_STD_PAL_Nc   | \
+	V4L2_STD_PAL_60 |  V4L2_STD_SECAM_L   |  V4L2_STD_SECAM_DK)
+#define CX231xx_VERSION_CODE KERNEL_VERSION(0, 0, 2)
+
+#define SLEEP_S5H1432    30
+#define CX23417_OSC_EN   8
+#define CX23417_RESET    9
+
+struct cx23417_fmt {
+	char  *name;
+	u32   fourcc;          /* v4l2 format id */
+	int   depth;
+	int   flags;
+	u32   cxformat;
+};
 enum cx231xx_mode {
 	CX231XX_SUSPEND,
 	CX231XX_ANALOG_MODE,
@@ -114,7 +139,7 @@
 
 struct cx231xx;
 
-struct cx231xx_usb_isoc_ctl {
+struct cx231xx_isoc_ctl {
 	/* max packet size of isoc transaction */
 	int max_pkt_size;
 
@@ -148,6 +173,40 @@
 	int (*isoc_copy) (struct cx231xx *dev, struct urb *urb);
 };
 
+struct cx231xx_bulk_ctl {
+	/* max packet size of bulk transaction */
+	int max_pkt_size;
+
+	/* number of allocated urbs */
+	int num_bufs;
+
+	/* urb for bulk transfers */
+	struct urb **urb;
+
+	/* transfer buffers for bulk transfer */
+	char **transfer_buffer;
+
+	/* Last buffer command and region */
+	u8 cmd;
+	int pos, size, pktsize;
+
+	/* Last field: ODD or EVEN? */
+	int field;
+
+	/* Stores incomplete commands */
+	u32 tmp_buf;
+	int tmp_buf_len;
+
+	/* Stores already requested buffers */
+	struct cx231xx_buffer *buf;
+
+	/* Stores the number of received fields */
+	int nfields;
+
+	/* bulk urb callback */
+	int (*bulk_copy) (struct cx231xx *dev, struct urb *urb);
+};
+
 struct cx231xx_fmt {
 	char *name;
 	u32 fourcc;		/* v4l2 format id */
@@ -165,6 +224,11 @@
 	int receiving;
 };
 
+enum ps_package_head {
+	CX231XX_NEED_ADD_PS_PACKAGE_HEAD = 0,
+	CX231XX_NONEED_PS_PACKAGE_HEAD
+};
+
 struct cx231xx_dmaqueue {
 	struct list_head active;
 	struct list_head queued;
@@ -181,6 +245,14 @@
 	u32 lines_completed;
 	u8 field1_done;
 	u32 lines_per_field;
+
+	/*Mpeg2 control buffer*/
+	u8 *p_left_data;
+	u32 left_data_count;
+	u8 mpeg_buffer_done;
+	u32 mpeg_buffer_completed;
+	enum ps_package_head add_ps_package_head;
+	char ps_head[10];
 };
 
 /* inputs */
@@ -259,9 +331,10 @@
 	struct cx231xx_reg_seq *dvb_gpio;
 	struct cx231xx_reg_seq *suspend_gpio;
 	struct cx231xx_reg_seq *tuner_gpio;
-	u8 tuner_sif_gpio;
-	u8 tuner_scl_gpio;
-	u8 tuner_sda_gpio;
+		/* Negative means don't use it */
+	s8 tuner_sif_gpio;
+	s8 tuner_scl_gpio;
+	s8 tuner_sda_gpio;
 
 	/* PIN ctrl */
 	u32 ctl_pin_status_mask;
@@ -279,6 +352,7 @@
 	unsigned char xclk, i2c_speed;
 
 	enum cx231xx_decoder decoder;
+	int output_mode;
 
 	struct cx231xx_input input[MAX_CX231XX_INPUT];
 	struct cx231xx_input radio;
@@ -309,10 +383,8 @@
 };
 
 #define CX231XX_AUDIO_BUFS              5
-#define CX231XX_NUM_AUDIO_PACKETS       64
-#define CX231XX_CAPTURE_STREAM_EN       1
-#define CX231XX_STOP_AUDIO              0
-#define CX231XX_START_AUDIO             1
+#define CX231XX_NUM_AUDIO_PACKETS       16
+#define CX231XX_ISO_NUM_AUDIO_PACKETS	64
 
 /* cx231xx extensions */
 #define CX231XX_AUDIO                   0x10
@@ -330,7 +402,7 @@
 	struct snd_card *sndcard;
 
 	int users, shutdown;
-	enum cx231xx_stream_state capture_stream;
+	/* locks */
 	spinlock_t slock;
 
 	int alt;		/* alternate */
@@ -350,6 +422,28 @@
 	struct videobuf_queue vb_vidq;
 
 	enum v4l2_buf_type type;
+
+
+
+/*following is copyed from cx23885.h*/
+	u32                        resources;
+
+	/* video overlay */
+	struct v4l2_window         win;
+	struct v4l2_clip           *clips;
+	unsigned int               nclips;
+
+	/* video capture */
+	struct cx23417_fmt         *fmt;
+	unsigned int               width, height;
+
+	/* vbi capture */
+	struct videobuf_queue      vidq;
+	struct videobuf_queue      vbiq;
+
+	/* MPEG Encoder specifics ONLY */
+
+	atomic_t                   v4l_reading;
 };
 
 /*****************************************************************/
@@ -403,6 +497,13 @@
 	u8 *pBuff;
 };
 
+struct cx231xx_tvnorm {
+	char		*name;
+	v4l2_std_id	id;
+	u32		cxiformat;
+	u32		cxoformat;
+};
+
 struct cx231xx_ctrl {
 	struct v4l2_queryctrl v;
 	u32 off;
@@ -424,7 +525,9 @@
 struct cx231xx_video_mode {
 	/* Isoc control struct */
 	struct cx231xx_dmaqueue vidq;
-	struct cx231xx_usb_isoc_ctl isoc_ctl;
+	struct cx231xx_isoc_ctl isoc_ctl;
+	struct cx231xx_bulk_ctl bulk_ctl;
+	/* locks */
 	spinlock_t slock;
 
 	/* usb transfer */
@@ -434,6 +537,64 @@
 	unsigned int *alt_max_pkt_size;	/* array of wMaxPacketSize */
 	u16 end_point_addr;
 };
+/*
+struct cx23885_dmaqueue {
+	struct list_head       active;
+	struct list_head       queued;
+	struct timer_list      timeout;
+	struct btcx_riscmem    stopper;
+	u32                    count;
+};
+*/
+struct cx231xx_tsport {
+	struct cx231xx *dev;
+
+	int                        nr;
+	int                        sram_chno;
+
+	struct videobuf_dvb_frontends frontends;
+
+	/* dma queues */
+
+	u32                        ts_packet_size;
+	u32                        ts_packet_count;
+
+	int                        width;
+	int                        height;
+
+	/* locks */
+	spinlock_t                 slock;
+
+	/* registers */
+	u32                        reg_gpcnt;
+	u32                        reg_gpcnt_ctl;
+	u32                        reg_dma_ctl;
+	u32                        reg_lngth;
+	u32                        reg_hw_sop_ctrl;
+	u32                        reg_gen_ctrl;
+	u32                        reg_bd_pkt_status;
+	u32                        reg_sop_status;
+	u32                        reg_fifo_ovfl_stat;
+	u32                        reg_vld_misc;
+	u32                        reg_ts_clk_en;
+	u32                        reg_ts_int_msk;
+	u32                        reg_ts_int_stat;
+	u32                        reg_src_sel;
+
+	/* Default register vals */
+	int                        pci_irqmask;
+	u32                        dma_ctl_val;
+	u32                        ts_int_msk_val;
+	u32                        gen_ctrl_val;
+	u32                        ts_clk_en_val;
+	u32                        src_sel_val;
+	u32                        vld_misc_val;
+	u32                        hw_sop_ctrl_val;
+
+	/* Allow a single tsport to have multiple frontends */
+	u32                        num_frontends;
+	void                       *port_priv;
+};
 
 /* main device struct */
 struct cx231xx {
@@ -457,6 +618,9 @@
 
 	struct cx231xx_IR *ir;
 
+	struct work_struct wq_trigger;		/* Trigger to start/stop audio for alsa module */
+	atomic_t	   stream_started;	/* stream should be running if true */
+
 	struct list_head devlist;
 
 	int tuner_type;		/* type of the tuner */
@@ -465,7 +629,9 @@
 	/* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
 	struct cx231xx_i2c i2c_bus[3];
 	unsigned int xc_fw_load_done:1;
+	/* locks */
 	struct mutex gpio_i2c_lock;
+	struct mutex i2c_lock;
 
 	/* video for linux */
 	int users;		/* user count for exclusive use */
@@ -479,8 +645,6 @@
 	/* frame properties */
 	int width;		/* current frame width */
 	int height;		/* current frame height */
-	unsigned hscale;	/* horizontal scale factor (see datasheet) */
-	unsigned vscale;	/* vertical scale factor (see datasheet) */
 	int interlaced;		/* 1=interlace fileds, 0=just top fileds */
 
 	struct cx231xx_audio adev;
@@ -505,6 +669,8 @@
 	struct cx231xx_video_mode sliced_cc_mode;
 	struct cx231xx_video_mode ts1_mode;
 
+	atomic_t devlist_count;
+
 	struct usb_device *udev;	/* the usb device */
 	char urb_buf[URB_MAX_CTRL_SIZE];	/* urb control msg buffer */
 
@@ -550,8 +716,24 @@
 	u8 vbi_or_sliced_cc_mode;	/* 0 - vbi ; 1 - sliced cc mode */
 	enum cx231xx_std_mode std_mode;	/* 0 - Air; 1 - cable */
 
+	/*mode: digital=1 or analog=0*/
+	u8 mode_tv;
+
+	u8 USE_ISO;
+	struct cx231xx_tvnorm      encodernorm;
+	struct cx231xx_tsport      ts1, ts2;
+	struct cx2341x_mpeg_params mpeg_params;
+	struct video_device        *v4l_device;
+	atomic_t                   v4l_reader_count;
+	u32                        freq;
+	unsigned int               input;
+	u32                        cx23417_mailbox;
+	u32                        __iomem *lmmio;
+	u8                         __iomem *bmmio;
 };
 
+extern struct list_head cx231xx_devlist;
+
 #define cx25840_call(cx231xx, o, f, args...) \
 	v4l2_subdev_call(cx231xx->sd_cx25840, o, f, ##args)
 #define tuner_call(cx231xx, o, f, args...) \
@@ -577,6 +759,10 @@
 int cx231xx_i2c_unregister(struct cx231xx_i2c *bus);
 
 /* Internal block control functions */
+int cx231xx_read_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr,
+		 u8 saddr_len, u32 *data, u8 data_len, int master);
+int cx231xx_write_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr,
+		 u8 saddr_len, u32 data, u8 data_len, int master);
 int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr,
 			  u16 saddr, u8 saddr_len, u32 *data, u8 data_len);
 int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr,
@@ -588,6 +774,9 @@
 					u16 saddr, u32 mask, u32 value);
 u32 cx231xx_set_field(u32 field_mask, u32 data);
 
+/*verve r/w*/
+void initGPIO(struct cx231xx *dev);
+void uninitGPIO(struct cx231xx *dev);
 /* afe related functions */
 int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count);
 int cx231xx_afe_init_channels(struct cx231xx *dev);
@@ -607,6 +796,19 @@
 /* DIF related functions */
 int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode,
 					  u32 function_mode, u32 standard);
+void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq,
+					 u8 spectral_invert, u32 mode);
+u32 cx231xx_Get_Colibri_CarrierOffset(u32 mode, u32 standerd);
+void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq,
+					 u8 spectral_invert, u32 mode);
+void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev);
+void reset_s5h1432_demod(struct cx231xx *dev);
+void cx231xx_dump_HH_reg(struct cx231xx *dev);
+void update_HH_register_after_set_DIF(struct cx231xx *dev);
+void cx231xx_dump_SC_reg(struct cx231xx *dev);
+
+
+
 int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard);
 int cx231xx_tuner_pre_channel_change(struct cx231xx *dev);
 int cx231xx_tuner_post_channel_change(struct cx231xx *dev);
@@ -672,15 +874,28 @@
 				    enum AUDIO_INPUT audio_input);
 
 int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type);
-int cx231xx_resolution_set(struct cx231xx *dev);
 int cx231xx_set_video_alternate(struct cx231xx *dev);
 int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt);
+int is_fw_load(struct cx231xx *dev);
+int cx231xx_check_fw(struct cx231xx *dev);
 int cx231xx_init_isoc(struct cx231xx *dev, int max_packets,
 		      int num_bufs, int max_pkt_size,
 		      int (*isoc_copy) (struct cx231xx *dev,
 					struct urb *urb));
+int cx231xx_init_bulk(struct cx231xx *dev, int max_packets,
+		      int num_bufs, int max_pkt_size,
+		      int (*bulk_copy) (struct cx231xx *dev,
+					struct urb *urb));
+void cx231xx_stop_TS1(struct cx231xx *dev);
+void cx231xx_start_TS1(struct cx231xx *dev);
 void cx231xx_uninit_isoc(struct cx231xx *dev);
+void cx231xx_uninit_bulk(struct cx231xx *dev);
 int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode);
+int cx231xx_unmute_audio(struct cx231xx *dev);
+int cx231xx_ep5_bulkout(struct cx231xx *dev, u8 *firmware, u16 size);
+void cx231xx_disable656(struct cx231xx *dev);
+void cx231xx_enable656(struct cx231xx *dev);
+int cx231xx_demod_reset(struct cx231xx *dev);
 int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio);
 
 /* Device list functions */
@@ -712,7 +927,7 @@
 int cx231xx_init_ctrl_pin_status(struct cx231xx *dev);
 int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev,
 					      u8 analog_or_digital);
-int cx231xx_enable_i2c_for_tuner(struct cx231xx *dev, u8 I2CIndex);
+int cx231xx_enable_i2c_port_3(struct cx231xx *dev, bool is_port_3);
 
 /* video audio decoder related functions */
 void video_mux(struct cx231xx *dev, int index);
@@ -733,12 +948,11 @@
 extern struct cx231xx_board cx231xx_boards[];
 extern struct usb_device_id cx231xx_id_table[];
 extern const unsigned int cx231xx_bcount;
-void cx231xx_register_i2c_ir(struct cx231xx *dev);
 int cx231xx_tuner_callback(void *ptr, int component, int command, int arg);
 
-/* Provided by cx231xx-input.c */
-int cx231xx_ir_init(struct cx231xx *dev);
-int cx231xx_ir_fini(struct cx231xx *dev);
+/* cx23885-417.c                                               */
+extern int cx231xx_417_register(struct cx231xx *dev);
+extern void cx231xx_417_unregister(struct cx231xx *dev);
 
 /* printk macros */
 
diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c
index 53a6782..a6cc12f 100644
--- a/drivers/media/video/cx23885/cx23885-417.c
+++ b/drivers/media/video/cx23885/cx23885-417.c
@@ -1591,7 +1591,7 @@
 			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
 			    V4L2_FIELD_INTERLACED,
 			    sizeof(struct cx23885_buffer),
-			    fh);
+			    fh, NULL);
 	unlock_kernel();
 
 	return 0;
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index e76ce87..db05400 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -1247,7 +1247,7 @@
 	case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
 		dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
 				&dev->i2c_bus[2].i2c_adap,
-				"cx25840", "cx25840", 0x88 >> 1, NULL);
+				NULL, "cx25840", 0x88 >> 1, NULL);
 		if (dev->sd_cx25840) {
 			dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE;
 			v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index f6b62e7..3598824 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -815,6 +815,7 @@
 	case 0x0e:
 		/* CX23887-15Z */
 		dev->hwrevision = 0xc0;
+		break;
 	case 0x0f:
 		/* CX23887-14Z */
 		dev->hwrevision = 0xb1;
@@ -1221,7 +1222,7 @@
 	struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
 
 	BUG_ON(in_interrupt());
-	videobuf_waiton(&buf->vb, 0, 0);
+	videobuf_waiton(q, &buf->vb, 0, 0);
 	videobuf_dma_unmap(q->dev, dma);
 	videobuf_dma_free(dma);
 	btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index 3d70af2..5958cb8 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -1017,10 +1017,7 @@
 		/* Read entire EEPROM */
 		dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
 		tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom));
-		printk(KERN_INFO "TeVii S470 MAC= "
-				"%02X:%02X:%02X:%02X:%02X:%02X\n",
-				eeprom[0xa0], eeprom[0xa1], eeprom[0xa2],
-				eeprom[0xa3], eeprom[0xa4], eeprom[0xa5]);
+		printk(KERN_INFO "TeVii S470 MAC= %pM\n", eeprom + 0xa0);
 		memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6);
 		break;
 		}
@@ -1074,7 +1071,7 @@
 		videobuf_queue_sg_init(&fe0->dvb.dvbq, &dvb_qops,
 			    &dev->pci->dev, &port->slock,
 			    V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP,
-			    sizeof(struct cx23885_buffer), port);
+			    sizeof(struct cx23885_buffer), port, NULL);
 	}
 	err = dvb_register(port);
 	if (err != 0)
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
index da66e5f..93af9c6 100644
--- a/drivers/media/video/cx23885/cx23885-video.c
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -758,7 +758,7 @@
 			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
 			    V4L2_FIELD_INTERLACED,
 			    sizeof(struct cx23885_buffer),
-			    fh);
+			    fh, NULL);
 
 	dprintk(1, "post videobuf_queue_init()\n");
 
@@ -1165,9 +1165,10 @@
 	i->type  = V4L2_INPUT_TYPE_CAMERA;
 	strcpy(i->name, iname[INPUT(n)->type]);
 	if ((CX23885_VMUX_TELEVISION == INPUT(n)->type) ||
-		(CX23885_VMUX_CABLE == INPUT(n)->type))
+		(CX23885_VMUX_CABLE == INPUT(n)->type)) {
 		i->type = V4L2_INPUT_TYPE_TUNER;
 		i->std = CX23885_NORMS;
+	}
 	return 0;
 }
 
@@ -1511,11 +1512,11 @@
 		if (dev->tuner_addr)
 			sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
 				&dev->i2c_bus[1].i2c_adap,
-				"tuner", "tuner", dev->tuner_addr, NULL);
+				NULL, "tuner", dev->tuner_addr, NULL);
 		else
 			sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
-				&dev->i2c_bus[1].i2c_adap,
-				"tuner", "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_TV));
+				&dev->i2c_bus[1].i2c_adap, NULL,
+				"tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_TV));
 		if (sd) {
 			struct tuner_setup tun_setup;
 
diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c
index 2502a0a..e78e3e4 100644
--- a/drivers/media/video/cx23885/cx23888-ir.c
+++ b/drivers/media/video/cx23885/cx23888-ir.c
@@ -704,6 +704,7 @@
 		if (v > IR_MAX_DURATION)
 			v = IR_MAX_DURATION;
 
+		init_ir_raw_event(&p->ir_core_data);
 		p->ir_core_data.pulse = u;
 		p->ir_core_data.duration = v;
 
diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
index 6faad34..34b96c7 100644
--- a/drivers/media/video/cx25840/cx25840-audio.c
+++ b/drivers/media/video/cx25840/cx25840-audio.c
@@ -437,41 +437,45 @@
 {
 	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
 
-	/* assert soft reset */
-	cx25840_and_or(client, 0x810, ~0x1, 0x01);
+	if (!is_cx2583x(state)) {
+		/* assert soft reset */
+		cx25840_and_or(client, 0x810, ~0x1, 0x01);
 
-	/* stop microcontroller */
-	cx25840_and_or(client, 0x803, ~0x10, 0);
+		/* stop microcontroller */
+		cx25840_and_or(client, 0x803, ~0x10, 0);
 
-	/* Mute everything to prevent the PFFT! */
-	cx25840_write(client, 0x8d3, 0x1f);
+		/* Mute everything to prevent the PFFT! */
+		cx25840_write(client, 0x8d3, 0x1f);
 
-	if (state->aud_input == CX25840_AUDIO_SERIAL) {
-		/* Set Path1 to Serial Audio Input */
-		cx25840_write4(client, 0x8d0, 0x01011012);
+		if (state->aud_input == CX25840_AUDIO_SERIAL) {
+			/* Set Path1 to Serial Audio Input */
+			cx25840_write4(client, 0x8d0, 0x01011012);
 
-		/* The microcontroller should not be started for the
-		 * non-tuner inputs: autodetection is specific for
-		 * TV audio. */
-	} else {
-		/* Set Path1 to Analog Demod Main Channel */
-		cx25840_write4(client, 0x8d0, 0x1f063870);
+			/* The microcontroller should not be started for the
+			 * non-tuner inputs: autodetection is specific for
+			 * TV audio. */
+		} else {
+			/* Set Path1 to Analog Demod Main Channel */
+			cx25840_write4(client, 0x8d0, 0x1f063870);
+		}
 	}
 
 	set_audclk_freq(client, state->audclk_freq);
 
-	if (state->aud_input != CX25840_AUDIO_SERIAL) {
-		/* When the microcontroller detects the
-		 * audio format, it will unmute the lines */
-		cx25840_and_or(client, 0x803, ~0x10, 0x10);
+	if (!is_cx2583x(state)) {
+		if (state->aud_input != CX25840_AUDIO_SERIAL) {
+			/* When the microcontroller detects the
+			 * audio format, it will unmute the lines */
+			cx25840_and_or(client, 0x803, ~0x10, 0x10);
+		}
+
+		/* deassert soft reset */
+		cx25840_and_or(client, 0x810, ~0x1, 0x00);
+
+		/* Ensure the controller is running when we exit */
+		if (is_cx2388x(state) || is_cx231xx(state))
+			cx25840_and_or(client, 0x803, ~0x10, 0x10);
 	}
-
-	/* deassert soft reset */
-	cx25840_and_or(client, 0x810, ~0x1, 0x00);
-
-	/* Ensure the controller is running when we exit */
-	if (is_cx2388x(state) || is_cx231xx(state))
-		cx25840_and_or(client, 0x803, ~0x10, 0x10);
 }
 
 static void set_volume(struct i2c_client *client, int volume)
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index f5a3e74..dfb198d 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -42,7 +42,6 @@
 #include <linux/delay.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 #include <media/cx25840.h>
 
 #include "cx25840-core.h"
@@ -871,6 +870,11 @@
 	}
 	cx25840_and_or(client, 0x401, ~0x60, 0);
 	cx25840_and_or(client, 0x401, ~0x60, 0x60);
+
+	/* Don't write into audio registers on cx2583x chips */
+	if (is_cx2583x(state))
+		return;
+
 	cx25840_and_or(client, 0x810, ~0x01, 1);
 
 	if (state->radio) {
@@ -1029,10 +1033,8 @@
 
 	state->vid_input = vid_input;
 	state->aud_input = aud_input;
-	if (!is_cx2583x(state)) {
-		cx25840_audio_set_path(client);
-		input_change(client);
-	}
+	cx25840_audio_set_path(client);
+	input_change(client);
 
 	if (is_cx2388x(state)) {
 		/* Audio channel 1 src : Parallel 1 */
@@ -1553,18 +1555,14 @@
 	struct cx25840_state *state = to_state(sd);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-	if (is_cx2583x(state))
-		return -EINVAL;
 	return set_input(client, state->vid_input, input);
 }
 
 static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
 {
-	struct cx25840_state *state = to_state(sd);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-	if (!is_cx2583x(state))
-		input_change(client);
+	input_change(client);
 	return 0;
 }
 
@@ -2043,9 +2041,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, cx25840_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "cx25840",
-	.probe = cx25840_probe,
-	.remove = cx25840_remove,
-	.id_table = cx25840_id,
+static struct i2c_driver cx25840_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "cx25840",
+	},
+	.probe		= cx25840_probe,
+	.remove		= cx25840_remove,
+	.id_table	= cx25840_id,
 };
+
+static __init int init_cx25840(void)
+{
+	return i2c_add_driver(&cx25840_driver);
+}
+
+static __exit void exit_cx25840(void)
+{
+	i2c_del_driver(&cx25840_driver);
+}
+
+module_init(init_cx25840);
+module_exit(exit_cx25840);
diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c
index c2b4c14..97a4e9b 100644
--- a/drivers/media/video/cx25840/cx25840-ir.c
+++ b/drivers/media/video/cx25840/cx25840-ir.c
@@ -706,6 +706,7 @@
 		if (v > IR_MAX_DURATION)
 			v = IR_MAX_DURATION;
 
+		init_ir_raw_event(&p->ir_core_data);
 		p->ir_core_data.pulse = u;
 		p->ir_core_data.duration = v;
 
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
index 4f383cd..4aaa47c 100644
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -40,6 +40,7 @@
 #include <sound/control.h>
 #include <sound/initval.h>
 #include <sound/tlv.h>
+#include <media/wm8775.h>
 
 #include "cx88.h"
 #include "cx88-reg.h"
@@ -94,7 +95,7 @@
  ****************************************************************************/
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static const char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
 static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
 
 module_param_array(enable, bool, NULL, 0444);
@@ -131,7 +132,7 @@
 {
 	struct cx88_audio_buffer *buf = chip->buf;
 	struct cx88_core *core=chip->core;
-	struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25];
+	const struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25];
 
 	/* Make sure RISC/FIFO are off before changing FIFO/RISC settings */
 	cx_clear(MO_AUD_DMACNTRL, 0x11);
@@ -197,7 +198,7 @@
 /*
  * BOARD Specific: IRQ dma bits
  */
-static char *cx88_aud_irqs[32] = {
+static const char *cx88_aud_irqs[32] = {
 	"dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */
 	NULL,					  /* reserved */
 	"dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */
@@ -308,7 +309,7 @@
  * Digital hardware definition
  */
 #define DEFAULT_FIFO_SIZE	4096
-static struct snd_pcm_hardware snd_cx88_digital_hw = {
+static const struct snd_pcm_hardware snd_cx88_digital_hw = {
 	.info = SNDRV_PCM_INFO_MMAP |
 		SNDRV_PCM_INFO_INTERLEAVED |
 		SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -533,7 +534,7 @@
 /*
  * create a PCM device
  */
-static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, char *name)
+static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, const char *name)
 {
 	int err;
 	struct snd_pcm *pcm;
@@ -586,26 +587,47 @@
 	int left, right, v, b;
 	int changed = 0;
 	u32 old;
+	struct v4l2_control client_ctl;
+
+	/* Pass volume & balance onto any WM8775 */
+	if (value->value.integer.value[0] >= value->value.integer.value[1]) {
+		v = value->value.integer.value[0] << 10;
+		b = value->value.integer.value[0] ?
+			(0x8000 * value->value.integer.value[1]) / value->value.integer.value[0] :
+			0x8000;
+	} else {
+		v = value->value.integer.value[1] << 10;
+		b = value->value.integer.value[1] ?
+		0xffff - (0x8000 * value->value.integer.value[0]) / value->value.integer.value[1] :
+		0x8000;
+	}
+	client_ctl.value = v;
+	client_ctl.id = V4L2_CID_AUDIO_VOLUME;
+	call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+
+	client_ctl.value = b;
+	client_ctl.id = V4L2_CID_AUDIO_BALANCE;
+	call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
 
 	left = value->value.integer.value[0] & 0x3f;
 	right = value->value.integer.value[1] & 0x3f;
 	b = right - left;
 	if (b < 0) {
-	    v = 0x3f - left;
-	    b = (-b) | 0x40;
+		v = 0x3f - left;
+		b = (-b) | 0x40;
 	} else {
-	    v = 0x3f - right;
+		v = 0x3f - right;
 	}
 	/* Do we really know this will always be called with IRQs on? */
 	spin_lock_irq(&chip->reg_lock);
 	old = cx_read(AUD_VOL_CTL);
 	if (v != (old & 0x3f)) {
-	    cx_write(AUD_VOL_CTL, (old & ~0x3f) | v);
-	    changed = 1;
+		cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v);
+		changed = 1;
 	}
-	if (cx_read(AUD_BAL_CTL) != b) {
-	    cx_write(AUD_BAL_CTL, b);
-	    changed = 1;
+	if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) {
+		cx_write(AUD_BAL_CTL, b);
+		changed = 1;
 	}
 	spin_unlock_irq(&chip->reg_lock);
 
@@ -614,11 +636,11 @@
 
 static const DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0);
 
-static struct snd_kcontrol_new snd_cx88_volume = {
+static const struct snd_kcontrol_new snd_cx88_volume = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 		  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
-	.name = "Playback Volume",
+	.name = "Analog-TV Volume",
 	.info = snd_cx88_volume_info,
 	.get = snd_cx88_volume_get,
 	.put = snd_cx88_volume_put,
@@ -649,31 +671,74 @@
 	vol = cx_read(AUD_VOL_CTL);
 	if (value->value.integer.value[0] != !(vol & bit)) {
 		vol ^= bit;
-		cx_write(AUD_VOL_CTL, vol);
+		cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol);
+		/* Pass mute onto any WM8775 */
+		if ((1<<6) == bit) {
+			struct v4l2_control client_ctl;
+			client_ctl.value = 0 != (vol & bit);
+			client_ctl.id = V4L2_CID_AUDIO_MUTE;
+			call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+		}
 		ret = 1;
 	}
 	spin_unlock_irq(&chip->reg_lock);
 	return ret;
 }
 
-static struct snd_kcontrol_new snd_cx88_dac_switch = {
+static const struct snd_kcontrol_new snd_cx88_dac_switch = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Playback Switch",
+	.name = "Audio-Out Switch",
 	.info = snd_ctl_boolean_mono_info,
 	.get = snd_cx88_switch_get,
 	.put = snd_cx88_switch_put,
 	.private_value = (1<<8),
 };
 
-static struct snd_kcontrol_new snd_cx88_source_switch = {
+static const struct snd_kcontrol_new snd_cx88_source_switch = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Capture Switch",
+	.name = "Analog-TV Switch",
 	.info = snd_ctl_boolean_mono_info,
 	.get = snd_cx88_switch_get,
 	.put = snd_cx88_switch_put,
 	.private_value = (1<<6),
 };
 
+static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *value)
+{
+	snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+	struct cx88_core *core = chip->core;
+	struct v4l2_control client_ctl;
+
+	client_ctl.id = V4L2_CID_AUDIO_LOUDNESS;
+	call_hw(core, WM8775_GID, core, g_ctrl, &client_ctl);
+	value->value.integer.value[0] = client_ctl.value ? 1 : 0;
+
+	return 0;
+}
+
+static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *value)
+{
+	snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+	struct cx88_core *core = chip->core;
+	struct v4l2_control client_ctl;
+
+	client_ctl.value = 0 != value->value.integer.value[0];
+	client_ctl.id = V4L2_CID_AUDIO_LOUDNESS;
+	call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+
+	return 0;
+}
+
+static struct snd_kcontrol_new snd_cx88_alc_switch = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Line-In ALC Switch",
+	.info = snd_ctl_boolean_mono_info,
+	.get = snd_cx88_alc_get,
+	.put = snd_cx88_alc_put,
+};
+
 /****************************************************************************
 			Basic Flow for Sound Devices
  ****************************************************************************/
@@ -683,7 +748,7 @@
  * Only boards with eeprom and byte 1 at eeprom=1 have it
  */
 
-static struct pci_device_id cx88_audio_pci_tbl[] __devinitdata = {
+static const struct pci_device_id const cx88_audio_pci_tbl[] __devinitdata = {
 	{0x14f1,0x8801,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
 	{0x14f1,0x8811,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
 	{0, }
@@ -795,6 +860,7 @@
 {
 	struct snd_card  *card;
 	snd_cx88_card_t  *chip;
+	struct v4l2_subdev *sd;
 	int              err;
 
 	if (devno >= SNDRV_CARDS)
@@ -830,6 +896,15 @@
 	if (err < 0)
 		goto error;
 
+	/* If there's a wm8775 then add a Line-In ALC switch */
+	list_for_each_entry(sd, &chip->core->v4l2_dev.subdevs, list) {
+		if (WM8775_GID == sd->grp_id) {
+			snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch,
+						       chip));
+			break;
+		}
+	}
+
 	strcpy (card->driver, "CX88x");
 	sprintf(card->shortname, "Conexant CX%x", pci->device);
 	sprintf(card->longname, "%s at %#llx",
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index 660b2a9..417d1d5 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -1057,7 +1057,7 @@
 
 	dprintk( 1, "%s\n", __func__);
 
-	lock_kernel();
+	mutex_lock(&dev->core->lock);
 
 	/* Make sure we can acquire the hardware */
 	drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
@@ -1065,7 +1065,7 @@
 		err = drv->request_acquire(drv);
 		if(err != 0) {
 			dprintk(1,"%s: Unable to acquire hardware, %d\n", __func__, err);
-			unlock_kernel();
+			mutex_unlock(&dev->core->lock);;
 			return err;
 		}
 	}
@@ -1073,7 +1073,7 @@
 	if (!atomic_read(&dev->core->mpeg_users) && blackbird_initialize_codec(dev) < 0) {
 		if (drv)
 			drv->request_release(drv);
-		unlock_kernel();
+		mutex_unlock(&dev->core->lock);
 		return -EINVAL;
 	}
 	dprintk(1, "open dev=%s\n", video_device_node_name(vdev));
@@ -1083,7 +1083,7 @@
 	if (NULL == fh) {
 		if (drv)
 			drv->request_release(drv);
-		unlock_kernel();
+		mutex_unlock(&dev->core->lock);
 		return -ENOMEM;
 	}
 	file->private_data = fh;
@@ -1094,15 +1094,14 @@
 			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
 			    V4L2_FIELD_INTERLACED,
 			    sizeof(struct cx88_buffer),
-			    fh);
+			    fh, NULL);
 
 	/* FIXME: locking against other video device */
 	cx88_set_scale(dev->core, dev->width, dev->height,
 			fh->mpegq.field);
-	unlock_kernel();
 
 	atomic_inc(&dev->core->mpeg_users);
-
+	mutex_unlock(&dev->core->lock);
 	return 0;
 }
 
@@ -1120,8 +1119,11 @@
 	videobuf_stop(&fh->mpegq);
 
 	videobuf_mmap_free(&fh->mpegq);
+
+	mutex_lock(&dev->core->lock);
 	file->private_data = NULL;
 	kfree(fh);
+	mutex_unlock(&dev->core->lock);
 
 	/* Make sure we release the hardware */
 	drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index e8416b7..b26fcba 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -970,15 +970,22 @@
 		.radio_type	= UNSET,
 		.tuner_addr	= ADDR_UNSET,
 		.radio_addr	= ADDR_UNSET,
+		.audio_chip = V4L2_IDENT_WM8775,
 		.input		= {{
 			.type	= CX88_VMUX_DVB,
 			.vmux	= 0,
+			/* 2: Line-In */
+			.audioroute = 2,
 		},{
 			.type	= CX88_VMUX_COMPOSITE1,
 			.vmux	= 1,
+			/* 2: Line-In */
+			.audioroute = 2,
 		},{
 			.type	= CX88_VMUX_SVIDEO,
 			.vmux	= 2,
+			/* 2: Line-In */
+			.audioroute = 2,
 		}},
 		.mpeg           = CX88_MPEG_DVB,
 	},
@@ -2104,6 +2111,18 @@
 		} },
 		.mpeg           = CX88_MPEG_DVB,
 	},
+	[CX88_BOARD_TWINHAN_VP1027_DVBS] = {
+		.name		= "Twinhan VP-1027 DVB-S",
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+		       .type   = CX88_VMUX_DVB,
+		       .vmux   = 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
 };
 
 /* ------------------------------------------------------------------ */
@@ -2576,6 +2595,10 @@
 		.subvendor = 0xb034,
 		.subdevice = 0x3034,
 		.card      = CX88_BOARD_PROF_7301,
+	}, {
+		.subvendor = 0x1822,
+		.subdevice = 0x0023,
+		.card      = CX88_BOARD_TWINHAN_VP1027_DVBS,
 	},
 };
 
@@ -2673,10 +2696,10 @@
 /* ----------------------------------------------------------------------- */
 /* some GDI (was: Modular Technology) specific stuff                       */
 
-static struct {
+static const struct {
 	int  id;
 	int  fm;
-	char *name;
+	const char *name;
 } gdi_tuner[] = {
 	[ 0x01 ] = { .id   = TUNER_ABSENT,
 		     .name = "NTSC_M" },
@@ -2710,7 +2733,7 @@
 
 static void gdi_eeprom(struct cx88_core *core, u8 *eeprom_data)
 {
-	char *name = (eeprom_data[0x0d] < ARRAY_SIZE(gdi_tuner))
+	const char *name = (eeprom_data[0x0d] < ARRAY_SIZE(gdi_tuner))
 		? gdi_tuner[eeprom_data[0x0d]].name : NULL;
 
 	info_printk(core, "GDI: tuner=%s\n", name ? name : "unknown");
@@ -3070,6 +3093,13 @@
 		cx_set(MO_GP1_IO, 0x10);
 		mdelay(50);
 		break;
+
+	case CX88_BOARD_TWINHAN_VP1027_DVBS:
+		cx_write(MO_GP0_IO, 0x00003230);
+		cx_write(MO_GP0_IO, 0x00003210);
+		msleep(1);
+		cx_write(MO_GP0_IO, 0x00001230);
+		break;
 	}
 }
 
@@ -3485,19 +3515,19 @@
 		   later code configures a tea5767.
 		 */
 		v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
-				"tuner", "tuner",
+				NULL, "tuner",
 				0, v4l2_i2c_tuner_addrs(ADDRS_RADIO));
 		if (has_demod)
 			v4l2_i2c_new_subdev(&core->v4l2_dev,
-				&core->i2c_adap, "tuner", "tuner",
+				&core->i2c_adap, NULL, "tuner",
 				0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
 		if (core->board.tuner_addr == ADDR_UNSET) {
 			v4l2_i2c_new_subdev(&core->v4l2_dev,
-				&core->i2c_adap, "tuner", "tuner",
+				&core->i2c_adap, NULL, "tuner",
 				0, has_demod ? tv_addrs + 4 : tv_addrs);
 		} else {
 			v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
-				"tuner", "tuner", core->board.tuner_addr, NULL);
+				NULL, "tuner", core->board.tuner_addr, NULL);
 		}
 	}
 
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index 85eb266f..2e145f0a 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -217,7 +217,7 @@
 	struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
 
 	BUG_ON(in_interrupt());
-	videobuf_waiton(&buf->vb,0,0);
+	videobuf_waiton(q, &buf->vb, 0, 0);
 	videobuf_dma_unmap(q->dev, dma);
 	videobuf_dma_free(dma);
 	btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
@@ -253,7 +253,7 @@
  *    0x0c00 -           FIFOs
  */
 
-struct sram_channel cx88_sram_channels[] = {
+const struct sram_channel const cx88_sram_channels[] = {
 	[SRAM_CH21] = {
 		.name       = "video y / packed",
 		.cmds_start = 0x180040,
@@ -353,7 +353,7 @@
 };
 
 int cx88_sram_channel_setup(struct cx88_core *core,
-			    struct sram_channel *ch,
+			    const struct sram_channel *ch,
 			    unsigned int bpl, u32 risc)
 {
 	unsigned int i,lines;
@@ -394,7 +394,7 @@
 
 static int cx88_risc_decode(u32 risc)
 {
-	static char *instr[16] = {
+	static const char * const instr[16] = {
 		[ RISC_SYNC    >> 28 ] = "sync",
 		[ RISC_WRITE   >> 28 ] = "write",
 		[ RISC_WRITEC  >> 28 ] = "writec",
@@ -406,14 +406,14 @@
 		[ RISC_WRITECM >> 28 ] = "writecm",
 		[ RISC_WRITECR >> 28 ] = "writecr",
 	};
-	static int incr[16] = {
+	static int const incr[16] = {
 		[ RISC_WRITE   >> 28 ] = 2,
 		[ RISC_JUMP    >> 28 ] = 2,
 		[ RISC_WRITERM >> 28 ] = 3,
 		[ RISC_WRITECM >> 28 ] = 3,
 		[ RISC_WRITECR >> 28 ] = 4,
 	};
-	static char *bits[] = {
+	static const char * const bits[] = {
 		"12",   "13",   "14",   "resync",
 		"cnt0", "cnt1", "18",   "19",
 		"20",   "21",   "22",   "23",
@@ -432,9 +432,9 @@
 
 
 void cx88_sram_channel_dump(struct cx88_core *core,
-			    struct sram_channel *ch)
+			    const struct sram_channel *ch)
 {
-	static char *name[] = {
+	static const char * const name[] = {
 		"initial risc",
 		"cdt base",
 		"cdt size",
@@ -489,14 +489,14 @@
 	       core->name,cx_read(ch->cnt2_reg));
 }
 
-static char *cx88_pci_irqs[32] = {
+static const char *cx88_pci_irqs[32] = {
 	"vid", "aud", "ts", "vip", "hst", "5", "6", "tm1",
 	"src_dma", "dst_dma", "risc_rd_err", "risc_wr_err",
 	"brdg_err", "src_dma_err", "dst_dma_err", "ipb_dma_err",
 	"i2c", "i2c_rack", "ir_smp", "gpio0", "gpio1"
 };
 
-void cx88_print_irqbits(char *name, char *tag, char **strings,
+void cx88_print_irqbits(const char *name, const char *tag, const char *strings[],
 			int len, u32 bits, u32 mask)
 {
 	unsigned int i;
@@ -770,7 +770,7 @@
 
 static int set_pll(struct cx88_core *core, int prescale, u32 ofreq)
 {
-	static u32 pre[] = { 0, 0, 0, 3, 2, 1 };
+	static const u32 pre[] = { 0, 0, 0, 3, 2, 1 };
 	u64 pll;
 	u32 reg;
 	int i;
@@ -879,7 +879,7 @@
 	} else {
 		printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n",
 		       core->name, v4l2_norm_to_name(core->tvnorm));
-		core->tvaudio = 0;
+		core->tvaudio = WW_NONE;
 		return 0;
 	}
 
@@ -1020,15 +1020,15 @@
 
 struct video_device *cx88_vdev_init(struct cx88_core *core,
 				    struct pci_dev *pci,
-				    struct video_device *template,
-				    char *type)
+				    const struct video_device *template_,
+				    const char *type)
 {
 	struct video_device *vfd;
 
 	vfd = video_device_alloc();
 	if (NULL == vfd)
 		return NULL;
-	*vfd = *template;
+	*vfd = *template_;
 	vfd->v4l2_dev = &core->v4l2_dev;
 	vfd->parent = &pci->dev;
 	vfd->release = video_device_release;
diff --git a/drivers/media/video/cx88/cx88-dsp.c b/drivers/media/video/cx88/cx88-dsp.c
index a94e00a..a990726 100644
--- a/drivers/media/video/cx88/cx88-dsp.c
+++ b/drivers/media/video/cx88/cx88-dsp.c
@@ -230,7 +230,7 @@
 
 static s16 *read_rds_samples(struct cx88_core *core, u32 *N)
 {
-	struct sram_channel *srch = &cx88_sram_channels[SRAM_CH27];
+	const struct sram_channel *srch = &cx88_sram_channels[SRAM_CH27];
 	s16 *samples;
 
 	unsigned int i;
@@ -292,11 +292,20 @@
 	switch (core->tvaudio) {
 	case WW_BG:
 	case WW_DK:
+	case WW_EIAJ:
+	case WW_M:
 		ret = detect_a2_a2m_eiaj(core, samples, N);
 		break;
 	case WW_BTSC:
 		ret = detect_btsc(core, samples, N);
 		break;
+	case WW_NONE:
+	case WW_I:
+	case WW_L:
+	case WW_I2SPT:
+	case WW_FM:
+	case WW_I2SADC:
+		break;
 	}
 
 	kfree(samples);
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index faa8e81..367a653f 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -56,6 +56,7 @@
 #include "stv0900.h"
 #include "stb6100.h"
 #include "stb6100_proc.h"
+#include "mb86a16.h"
 
 MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
 MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
@@ -105,7 +106,7 @@
 	cx88_free_buffer(q, (struct cx88_buffer*)vb);
 }
 
-static struct videobuf_queue_ops dvb_qops = {
+static const struct videobuf_queue_ops dvb_qops = {
 	.buf_setup    = dvb_buf_setup,
 	.buf_prepare  = dvb_buf_prepare,
 	.buf_queue    = dvb_buf_queue,
@@ -167,12 +168,12 @@
 
 static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe)
 {
-	static u8 clock_config []  = { CLOCK_CTL,  0x38, 0x39 };
-	static u8 reset []         = { RESET,      0x80 };
-	static u8 adc_ctl_1_cfg [] = { ADC_CTL_1,  0x40 };
-	static u8 agc_cfg []       = { AGC_TARGET, 0x24, 0x20 };
-	static u8 gpp_ctl_cfg []   = { GPP_CTL,    0x33 };
-	static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+	static const u8 clock_config []  = { CLOCK_CTL,  0x38, 0x39 };
+	static const u8 reset []         = { RESET,      0x80 };
+	static const u8 adc_ctl_1_cfg [] = { ADC_CTL_1,  0x40 };
+	static const u8 agc_cfg []       = { AGC_TARGET, 0x24, 0x20 };
+	static const u8 gpp_ctl_cfg []   = { GPP_CTL,    0x33 };
+	static const u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
 
 	mt352_write(fe, clock_config,   sizeof(clock_config));
 	udelay(200);
@@ -187,12 +188,12 @@
 
 static int dvico_dual_demod_init(struct dvb_frontend *fe)
 {
-	static u8 clock_config []  = { CLOCK_CTL,  0x38, 0x38 };
-	static u8 reset []         = { RESET,      0x80 };
-	static u8 adc_ctl_1_cfg [] = { ADC_CTL_1,  0x40 };
-	static u8 agc_cfg []       = { AGC_TARGET, 0x28, 0x20 };
-	static u8 gpp_ctl_cfg []   = { GPP_CTL,    0x33 };
-	static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+	static const u8 clock_config []  = { CLOCK_CTL,  0x38, 0x38 };
+	static const u8 reset []         = { RESET,      0x80 };
+	static const u8 adc_ctl_1_cfg [] = { ADC_CTL_1,  0x40 };
+	static const u8 agc_cfg []       = { AGC_TARGET, 0x28, 0x20 };
+	static const u8 gpp_ctl_cfg []   = { GPP_CTL,    0x33 };
+	static const u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
 
 	mt352_write(fe, clock_config,   sizeof(clock_config));
 	udelay(200);
@@ -208,13 +209,13 @@
 
 static int dntv_live_dvbt_demod_init(struct dvb_frontend* fe)
 {
-	static u8 clock_config []  = { 0x89, 0x38, 0x39 };
-	static u8 reset []         = { 0x50, 0x80 };
-	static u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 };
-	static u8 agc_cfg []       = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF,
+	static const u8 clock_config []  = { 0x89, 0x38, 0x39 };
+	static const u8 reset []         = { 0x50, 0x80 };
+	static const u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+	static const u8 agc_cfg []       = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF,
 				       0x00, 0xFF, 0x00, 0x40, 0x40 };
-	static u8 dntv_extra[]     = { 0xB5, 0x7A };
-	static u8 capt_range_cfg[] = { 0x75, 0x32 };
+	static const u8 dntv_extra[]     = { 0xB5, 0x7A };
+	static const u8 capt_range_cfg[] = { 0x75, 0x32 };
 
 	mt352_write(fe, clock_config,   sizeof(clock_config));
 	udelay(2000);
@@ -229,37 +230,41 @@
 	return 0;
 }
 
-static struct mt352_config dvico_fusionhdtv = {
+static const struct mt352_config dvico_fusionhdtv = {
 	.demod_address = 0x0f,
 	.demod_init    = dvico_fusionhdtv_demod_init,
 };
 
-static struct mt352_config dntv_live_dvbt_config = {
+static const struct mt352_config dntv_live_dvbt_config = {
 	.demod_address = 0x0f,
 	.demod_init    = dntv_live_dvbt_demod_init,
 };
 
-static struct mt352_config dvico_fusionhdtv_dual = {
+static const struct mt352_config dvico_fusionhdtv_dual = {
 	.demod_address = 0x0f,
 	.demod_init    = dvico_dual_demod_init,
 };
 
-static struct zl10353_config cx88_terratec_cinergy_ht_pci_mkii_config = {
+static const struct zl10353_config cx88_terratec_cinergy_ht_pci_mkii_config = {
 	.demod_address = (0x1e >> 1),
 	.no_tuner      = 1,
 	.if2           = 45600,
 };
 
+static struct mb86a16_config twinhan_vp1027 = {
+	.demod_address  = 0x08,
+};
+
 #if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE))
 static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe)
 {
-	static u8 clock_config []  = { 0x89, 0x38, 0x38 };
-	static u8 reset []         = { 0x50, 0x80 };
-	static u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 };
-	static u8 agc_cfg []       = { 0x67, 0x10, 0x20, 0x00, 0xFF, 0xFF,
+	static const u8 clock_config []  = { 0x89, 0x38, 0x38 };
+	static const u8 reset []         = { 0x50, 0x80 };
+	static const u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+	static const u8 agc_cfg []       = { 0x67, 0x10, 0x20, 0x00, 0xFF, 0xFF,
 				       0x00, 0xFF, 0x00, 0x40, 0x40 };
-	static u8 dntv_extra[]     = { 0xB5, 0x7A };
-	static u8 capt_range_cfg[] = { 0x75, 0x32 };
+	static const u8 dntv_extra[]     = { 0xB5, 0x7A };
+	static const u8 capt_range_cfg[] = { 0x75, 0x32 };
 
 	mt352_write(fe, clock_config,   sizeof(clock_config));
 	udelay(2000);
@@ -274,41 +279,41 @@
 	return 0;
 }
 
-static struct mt352_config dntv_live_dvbt_pro_config = {
+static const struct mt352_config dntv_live_dvbt_pro_config = {
 	.demod_address = 0x0f,
 	.no_tuner      = 1,
 	.demod_init    = dntv_live_dvbt_pro_demod_init,
 };
 #endif
 
-static struct zl10353_config dvico_fusionhdtv_hybrid = {
+static const struct zl10353_config dvico_fusionhdtv_hybrid = {
 	.demod_address = 0x0f,
 	.no_tuner      = 1,
 };
 
-static struct zl10353_config dvico_fusionhdtv_xc3028 = {
+static const struct zl10353_config dvico_fusionhdtv_xc3028 = {
 	.demod_address = 0x0f,
 	.if2           = 45600,
 	.no_tuner      = 1,
 };
 
-static struct mt352_config dvico_fusionhdtv_mt352_xc3028 = {
+static const struct mt352_config dvico_fusionhdtv_mt352_xc3028 = {
 	.demod_address = 0x0f,
 	.if2 = 4560,
 	.no_tuner = 1,
 	.demod_init = dvico_fusionhdtv_demod_init,
 };
 
-static struct zl10353_config dvico_fusionhdtv_plus_v1_1 = {
+static const struct zl10353_config dvico_fusionhdtv_plus_v1_1 = {
 	.demod_address = 0x0f,
 };
 
-static struct cx22702_config connexant_refboard_config = {
+static const struct cx22702_config connexant_refboard_config = {
 	.demod_address = 0x43,
 	.output_mode   = CX22702_SERIAL_OUTPUT,
 };
 
-static struct cx22702_config hauppauge_hvr_config = {
+static const struct cx22702_config hauppauge_hvr_config = {
 	.demod_address = 0x63,
 	.output_mode   = CX22702_SERIAL_OUTPUT,
 };
@@ -320,7 +325,7 @@
 	return 0;
 }
 
-static struct or51132_config pchdtv_hd3000 = {
+static const struct or51132_config pchdtv_hd3000 = {
 	.demod_address = 0x15,
 	.set_ts_params = or51132_set_ts_param,
 };
@@ -355,14 +360,14 @@
 	.set_ts_params = lgdt330x_set_ts_param,
 };
 
-static struct lgdt330x_config fusionhdtv_5_gold = {
+static const struct lgdt330x_config fusionhdtv_5_gold = {
 	.demod_address = 0x0e,
 	.demod_chip    = LGDT3303,
 	.serial_mpeg   = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
 	.set_ts_params = lgdt330x_set_ts_param,
 };
 
-static struct lgdt330x_config pchdtv_hd5500 = {
+static const struct lgdt330x_config pchdtv_hd5500 = {
 	.demod_address = 0x59,
 	.demod_chip    = LGDT3303,
 	.serial_mpeg   = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
@@ -376,7 +381,7 @@
 	return 0;
 }
 
-static struct nxt200x_config ati_hdtvwonder = {
+static const struct nxt200x_config ati_hdtvwonder = {
 	.demod_address = 0x0a,
 	.set_ts_params = nxt200x_set_ts_param,
 };
@@ -429,15 +434,15 @@
 
 	cx_set(MO_GP0_IO, 0x6040);
 	switch (voltage) {
-		case SEC_VOLTAGE_13:
-			cx_clear(MO_GP0_IO, 0x20);
-			break;
-		case SEC_VOLTAGE_18:
-			cx_set(MO_GP0_IO, 0x20);
-			break;
-		case SEC_VOLTAGE_OFF:
-			cx_clear(MO_GP0_IO, 0x20);
-			break;
+	case SEC_VOLTAGE_13:
+		cx_clear(MO_GP0_IO, 0x20);
+		break;
+	case SEC_VOLTAGE_18:
+		cx_set(MO_GP0_IO, 0x20);
+		break;
+	case SEC_VOLTAGE_OFF:
+		cx_clear(MO_GP0_IO, 0x20);
+		break;
 	}
 
 	if (core->prev_set_voltage)
@@ -445,23 +450,49 @@
 	return 0;
 }
 
-static struct cx24123_config geniatech_dvbs_config = {
+static int vp1027_set_voltage(struct dvb_frontend *fe,
+				    fe_sec_voltage_t voltage)
+{
+	struct cx8802_dev *dev = fe->dvb->priv;
+	struct cx88_core *core = dev->core;
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		dprintk(1, "LNB SEC Voltage=13\n");
+		cx_write(MO_GP0_IO, 0x00001220);
+		break;
+	case SEC_VOLTAGE_18:
+		dprintk(1, "LNB SEC Voltage=18\n");
+		cx_write(MO_GP0_IO, 0x00001222);
+		break;
+	case SEC_VOLTAGE_OFF:
+		dprintk(1, "LNB Voltage OFF\n");
+		cx_write(MO_GP0_IO, 0x00001230);
+		break;
+	}
+
+	if (core->prev_set_voltage)
+		return core->prev_set_voltage(fe, voltage);
+	return 0;
+}
+
+static const struct cx24123_config geniatech_dvbs_config = {
 	.demod_address = 0x55,
 	.set_ts_params = cx24123_set_ts_param,
 };
 
-static struct cx24123_config hauppauge_novas_config = {
+static const struct cx24123_config hauppauge_novas_config = {
 	.demod_address = 0x55,
 	.set_ts_params = cx24123_set_ts_param,
 };
 
-static struct cx24123_config kworld_dvbs_100_config = {
+static const struct cx24123_config kworld_dvbs_100_config = {
 	.demod_address = 0x15,
 	.set_ts_params = cx24123_set_ts_param,
 	.lnb_polarity  = 1,
 };
 
-static struct s5h1409_config pinnacle_pctv_hd_800i_config = {
+static const struct s5h1409_config pinnacle_pctv_hd_800i_config = {
 	.demod_address = 0x32 >> 1,
 	.output_mode   = S5H1409_PARALLEL_OUTPUT,
 	.gpio	       = S5H1409_GPIO_ON,
@@ -471,7 +502,7 @@
 	.mpeg_timing   = S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK,
 };
 
-static struct s5h1409_config dvico_hdtv5_pci_nano_config = {
+static const struct s5h1409_config dvico_hdtv5_pci_nano_config = {
 	.demod_address = 0x32 >> 1,
 	.output_mode   = S5H1409_SERIAL_OUTPUT,
 	.gpio          = S5H1409_GPIO_OFF,
@@ -480,7 +511,7 @@
 	.mpeg_timing   = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
 };
 
-static struct s5h1409_config kworld_atsc_120_config = {
+static const struct s5h1409_config kworld_atsc_120_config = {
 	.demod_address = 0x32 >> 1,
 	.output_mode   = S5H1409_SERIAL_OUTPUT,
 	.gpio	       = S5H1409_GPIO_OFF,
@@ -489,24 +520,24 @@
 	.mpeg_timing   = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
 };
 
-static struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = {
+static const struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = {
 	.i2c_address	= 0x64,
 	.if_khz		= 5380,
 };
 
-static struct zl10353_config cx88_pinnacle_hybrid_pctv = {
+static const struct zl10353_config cx88_pinnacle_hybrid_pctv = {
 	.demod_address = (0x1e >> 1),
 	.no_tuner      = 1,
 	.if2           = 45600,
 };
 
-static struct zl10353_config cx88_geniatech_x8000_mt = {
+static const struct zl10353_config cx88_geniatech_x8000_mt = {
 	.demod_address = (0x1e >> 1),
 	.no_tuner = 1,
 	.disable_i2c_gate_ctrl = 1,
 };
 
-static struct s5h1411_config dvico_fusionhdtv7_config = {
+static const struct s5h1411_config dvico_fusionhdtv7_config = {
 	.output_mode   = S5H1411_SERIAL_OUTPUT,
 	.gpio          = S5H1411_GPIO_ON,
 	.mpeg_timing   = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
@@ -516,7 +547,7 @@
 	.status_mode   = S5H1411_DEMODLOCKING
 };
 
-static struct xc5000_config dvico_fusionhdtv7_tuner_config = {
+static const struct xc5000_config dvico_fusionhdtv7_tuner_config = {
 	.i2c_address    = 0xc2 >> 1,
 	.if_khz         = 5380,
 };
@@ -601,19 +632,19 @@
 	return 0;
 }
 
-static struct cx24116_config hauppauge_hvr4000_config = {
+static const struct cx24116_config hauppauge_hvr4000_config = {
 	.demod_address          = 0x05,
 	.set_ts_params          = cx24116_set_ts_param,
 	.reset_device           = cx24116_reset_device,
 };
 
-static struct cx24116_config tevii_s460_config = {
+static const struct cx24116_config tevii_s460_config = {
 	.demod_address = 0x55,
 	.set_ts_params = cx24116_set_ts_param,
 	.reset_device  = cx24116_reset_device,
 };
 
-static struct stv0900_config prof_7301_stv0900_config = {
+static const struct stv0900_config prof_7301_stv0900_config = {
 	.demod_address = 0x6a,
 /*	demod_mode = 0,*/
 	.xtal = 27000000,
@@ -625,12 +656,12 @@
 	.set_ts_params = stv0900_set_ts_param,
 };
 
-static struct stb6100_config prof_7301_stb6100_config = {
+static const struct stb6100_config prof_7301_stb6100_config = {
 	.tuner_address = 0x60,
 	.refclock = 27000000,
 };
 
-static struct stv0299_config tevii_tuner_sharp_config = {
+static const struct stv0299_config tevii_tuner_sharp_config = {
 	.demod_address = 0x68,
 	.inittab = sharp_z0194a_inittab,
 	.mclk = 88000000UL,
@@ -643,7 +674,7 @@
 	.set_ts_params = cx24116_set_ts_param,
 };
 
-static struct stv0288_config tevii_tuner_earda_config = {
+static const struct stv0288_config tevii_tuner_earda_config = {
 	.demod_address = 0x68,
 	.min_delay_ms = 100,
 	.set_ts_params = cx24116_set_ts_param,
@@ -676,7 +707,7 @@
 
 
 
-static u8 samsung_smt_7020_inittab[] = {
+static const u8 samsung_smt_7020_inittab[] = {
 	     0x01, 0x15,
 	     0x02, 0x00,
 	     0x03, 0x00,
@@ -850,7 +881,7 @@
 }
 
 
-static struct stv0299_config samsung_stv0299_config = {
+static const struct stv0299_config samsung_stv0299_config = {
 	.demod_address = 0x68,
 	.inittab = samsung_smt_7020_inittab,
 	.mclk = 88000000UL,
@@ -1416,6 +1447,18 @@
 		}
 
 		break;
+	case CX88_BOARD_TWINHAN_VP1027_DVBS:
+		dev->ts_gen_cntrl = 0x00;
+		fe0->dvb.frontend = dvb_attach(mb86a16_attach,
+						&twinhan_vp1027,
+						&core->i2c_adap);
+		if (fe0->dvb.frontend) {
+			core->prev_set_voltage =
+					fe0->dvb.frontend->ops.set_voltage;
+			fe0->dvb.frontend->ops.set_voltage =
+					vp1027_set_voltage;
+		}
+		break;
 
 	default:
 		printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
@@ -1576,7 +1619,7 @@
 				    V4L2_BUF_TYPE_VIDEO_CAPTURE,
 				    V4L2_FIELD_TOP,
 				    sizeof(struct cx88_buffer),
-				    dev);
+				    dev, NULL);
 		/* init struct videobuf_dvb */
 		fe->dvb.name = dev->core->name;
 	}
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
index 82db555..f53836b 100644
--- a/drivers/media/video/cx88/cx88-i2c.c
+++ b/drivers/media/video/cx88/cx88-i2c.c
@@ -108,7 +108,7 @@
 
 /* ----------------------------------------------------------------------- */
 
-static char *i2c_devs[128] = {
+static const char * const i2c_devs[128] = {
 	[ 0x1c >> 1 ] = "lgdt330x",
 	[ 0x86 >> 1 ] = "tda9887/cx22702",
 	[ 0xa0 >> 1 ] = "eeprom",
@@ -117,7 +117,7 @@
 	[ 0xc8 >> 1 ] = "xc5000",
 };
 
-static void do_i2c_scan(char *name, struct i2c_client *c)
+static void do_i2c_scan(const char *name, struct i2c_client *c)
 {
 	unsigned char buf;
 	int i,rc;
@@ -183,30 +183,3 @@
 
 	return core->i2c_rc;
 }
-
-void cx88_i2c_init_ir(struct cx88_core *core)
-{
-	/* Instantiate the IR receiver device, if present */
-	if (0 == core->i2c_rc) {
-		struct i2c_board_info info;
-		const unsigned short addr_list[] = {
-			0x18, 0x6b, 0x71,
-			I2C_CLIENT_END
-		};
-
-		memset(&info, 0, sizeof(struct i2c_board_info));
-		strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
-		/* Use quick read command for probe, some IR chips don't
-		 * support writes */
-		i2c_new_probed_device(&core->i2c_adap, &info, addr_list,
-				      i2c_probe_func_quick_read);
-	}
-}
-
-/* ----------------------------------------------------------------------- */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index eccc5e4..fc777bc 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -405,6 +405,11 @@
 		ir->mask_keycode = 0x7e;
 		ir->polling      = 100; /* ms */
 		break;
+	case CX88_BOARD_TWINHAN_VP1027_DVBS:
+		ir_codes         = RC_MAP_TWINHAN_VP1027_DVBS;
+		ir_type          = IR_TYPE_NEC;
+		ir->sampling     = 0xff00; /* address */
+		break;
 	}
 
 	if (NULL == ir_codes) {
@@ -530,6 +535,7 @@
 	case CX88_BOARD_PROF_7300:
 	case CX88_BOARD_PROF_7301:
 	case CX88_BOARD_PROF_6200:
+	case CX88_BOARD_TWINHAN_VP1027_DVBS:
 		ircode = ir_decode_pulsedistance(ir->samples, ir->scount, 1, 4);
 
 		if (ircode == 0xffffffff) { /* decoding error */
@@ -609,13 +615,54 @@
 	return;
 }
 
+
+void cx88_i2c_init_ir(struct cx88_core *core)
+{
+	struct i2c_board_info info;
+	const unsigned short addr_list[] = {
+		0x18, 0x6b, 0x71,
+		I2C_CLIENT_END
+	};
+	const unsigned short *addrp;
+	/* Instantiate the IR receiver device, if present */
+	if (0 != core->i2c_rc)
+		return;
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+
+	/*
+	 * We can't call i2c_new_probed_device() because it uses
+	 * quick writes for probing and at least some RC receiver
+	 * devices only reply to reads.
+	 * Also, Hauppauge XVR needs to be specified, as address 0x71
+	 * conflicts with another remote type used with saa7134
+	 */
+	for (addrp = addr_list; *addrp != I2C_CLIENT_END; addrp++) {
+		info.platform_data = NULL;
+		memset(&core->init_data, 0, sizeof(core->init_data));
+
+		if (*addrp == 0x71) {
+			/* Hauppauge XVR */
+			core->init_data.name = "cx88 Hauppauge XVR remote";
+			core->init_data.ir_codes = RC_MAP_HAUPPAUGE_NEW;
+			core->init_data.type = IR_TYPE_RC5;
+			core->init_data.internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
+
+			info.platform_data = &core->init_data;
+		}
+		if (i2c_smbus_xfer(&core->i2c_adap, *addrp, 0,
+					I2C_SMBUS_READ, 0,
+					I2C_SMBUS_QUICK, NULL) >= 0) {
+			info.addr = *addrp;
+			i2c_new_device(&core->i2c_adap, &info);
+			break;
+		}
+	}
+}
+
 /* ---------------------------------------------------------------------- */
 
 MODULE_AUTHOR("Gerd Knorr, Pavel Machek, Chris Pascoe");
 MODULE_DESCRIPTION("input driver for cx88 GPIO-based IR remote controls");
 MODULE_LICENSE("GPL");
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index 499f8d5..f7d71ac 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -313,7 +313,7 @@
 
 /* ----------------------------------------------------------- */
 
-static void do_cancel_buffers(struct cx8802_dev *dev, char *reason, int restart)
+static void do_cancel_buffers(struct cx8802_dev *dev, const char *reason, int restart)
 {
 	struct cx88_dmaqueue *q = &dev->mpegq;
 	struct cx88_buffer *buf;
@@ -358,7 +358,7 @@
 	do_cancel_buffers(dev,"timeout",1);
 }
 
-static char *cx88_mpeg_irqs[32] = {
+static const char * cx88_mpeg_irqs[32] = {
 	"ts_risci1", NULL, NULL, NULL,
 	"ts_risci2", NULL, NULL, NULL,
 	"ts_oflow",  NULL, NULL, NULL,
@@ -849,7 +849,7 @@
 	kfree(dev);
 }
 
-static struct pci_device_id cx8802_pci_tbl[] = {
+static const struct pci_device_id cx8802_pci_tbl[] = {
 	{
 		.vendor       = 0x14f1,
 		.device       = 0x8802,
diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c
index 2396315..08220de 100644
--- a/drivers/media/video/cx88/cx88-tvaudio.c
+++ b/drivers/media/video/cx88/cx88-tvaudio.c
@@ -70,7 +70,7 @@
 
 /* ----------------------------------------------------------- */
 
-static char *aud_ctl_names[64] = {
+static const char * const aud_ctl_names[64] = {
 	[EN_BTSC_FORCE_MONO] = "BTSC_FORCE_MONO",
 	[EN_BTSC_FORCE_STEREO] = "BTSC_FORCE_STEREO",
 	[EN_BTSC_FORCE_SAP] = "BTSC_FORCE_SAP",
@@ -360,7 +360,15 @@
 		set_audio_registers(core, nicam_bgdki_common);
 		set_audio_registers(core, nicam_i);
 		break;
-	default:
+	case WW_NONE:
+	case WW_BTSC:
+	case WW_BG:
+	case WW_DK:
+	case WW_EIAJ:
+	case WW_I2SPT:
+	case WW_FM:
+	case WW_I2SADC:
+	case WW_M:
 		dprintk("%s PAL-BGDK NICAM (status: known-good)\n", __func__);
 		set_audio_registers(core, nicam_bgdki_common);
 		set_audio_registers(core, nicam_default);
@@ -621,7 +629,13 @@
 		dprintk("%s AM-L (status: devel)\n", __func__);
 		set_audio_registers(core, am_l);
 		break;
-	default:
+	case WW_NONE:
+	case WW_BTSC:
+	case WW_EIAJ:
+	case WW_I2SPT:
+	case WW_FM:
+	case WW_I2SADC:
+	case WW_M:
 		dprintk("%s Warning: wrong value\n", __func__);
 		return;
 		break;
@@ -779,7 +793,7 @@
 		set_audio_finish(core, EN_I2SIN_ENABLE);
 		break;
 	case WW_NONE:
-	default:
+	case WW_I2SPT:
 		printk("%s/0: unknown tv audio mode [%d]\n",
 		       core->name, core->tvaudio);
 		break;
@@ -795,8 +809,8 @@
 
 void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t)
 {
-	static char *m[] = { "stereo", "dual mono", "mono", "sap" };
-	static char *p[] = { "no pilot", "pilot c1", "pilot c2", "?" };
+	static const char * const m[] = { "stereo", "dual mono", "mono", "sap" };
+	static const char * const p[] = { "no pilot", "pilot c1", "pilot c2", "?" };
 	u32 reg, mode, pilot;
 
 	reg = cx_read(AUD_STATUS);
@@ -840,7 +854,12 @@
 			break;
 		}
 		break;
-	default:
+	case WW_NONE:
+	case WW_I:
+	case WW_L:
+	case WW_I2SPT:
+	case WW_FM:
+	case WW_I2SADC:
 		/* nothing */
 		break;
 	}
@@ -945,6 +964,9 @@
 		}
 		break;
 	case WW_I2SADC:
+	case WW_NONE:
+	case WW_EIAJ:
+	case WW_I2SPT:
 		/* DO NOTHING */
 		break;
 	}
@@ -1000,7 +1022,12 @@
 			/* automatically switch to best available mode */
 			cx88_set_stereo(core, mode, 0);
 			break;
-		default:
+		case WW_NONE:
+		case WW_BTSC:
+		case WW_EIAJ:
+		case WW_I2SPT:
+		case WW_FM:
+		case WW_I2SADC:
 hw_autodetect:
 			/* stereo autodetection is supported by hardware so
 			   we don't need to do it manually. Do nothing. */
diff --git a/drivers/media/video/cx88/cx88-vbi.c b/drivers/media/video/cx88/cx88-vbi.c
index d9445b0..f8f8389 100644
--- a/drivers/media/video/cx88/cx88-vbi.c
+++ b/drivers/media/video/cx88/cx88-vbi.c
@@ -230,7 +230,7 @@
 	cx88_free_buffer(q,buf);
 }
 
-struct videobuf_queue_ops cx8800_vbi_qops = {
+const struct videobuf_queue_ops cx8800_vbi_qops = {
 	.buf_setup    = vbi_setup,
 	.buf_prepare  = vbi_prepare,
 	.buf_queue    = vbi_queue,
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index 0fab65c..d2f159d 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -41,6 +41,7 @@
 #include "cx88.h"
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
+#include <media/wm8775.h>
 
 MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
 MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
@@ -78,7 +79,7 @@
 /* ------------------------------------------------------------------- */
 /* static data                                                         */
 
-static struct cx8800_fmt formats[] = {
+static const struct cx8800_fmt formats[] = {
 	{
 		.name     = "8 bpp, gray",
 		.fourcc   = V4L2_PIX_FMT_GREY,
@@ -142,7 +143,7 @@
 	},
 };
 
-static struct cx8800_fmt* format_by_fourcc(unsigned int fourcc)
+static const struct cx8800_fmt* format_by_fourcc(unsigned int fourcc)
 {
 	unsigned int i;
 
@@ -159,7 +160,7 @@
 	.flags = V4L2_CTRL_FLAG_DISABLED,
 };
 
-static struct cx88_ctrl cx8800_ctls[] = {
+static const struct cx88_ctrl cx8800_ctls[] = {
 	/* --- video --- */
 	{
 		.v = {
@@ -288,7 +289,7 @@
 		.shift                 = 0,
 	}
 };
-static const int CX8800_CTLS = ARRAY_SIZE(cx8800_ctls);
+enum { CX8800_CTLS = ARRAY_SIZE(cx8800_ctls) };
 
 /* Must be sorted from low to high control ID! */
 const u32 cx88_user_ctrls[] = {
@@ -306,7 +307,7 @@
 };
 EXPORT_SYMBOL(cx88_user_ctrls);
 
-static const u32 *ctrl_classes[] = {
+static const u32 * const ctrl_classes[] = {
 	cx88_user_ctrls,
 	NULL
 };
@@ -710,7 +711,7 @@
 	cx88_free_buffer(q,buf);
 }
 
-static struct videobuf_queue_ops cx8800_video_qops = {
+static const struct videobuf_queue_ops cx8800_video_qops = {
 	.buf_setup    = buffer_setup,
 	.buf_prepare  = buffer_prepare,
 	.buf_queue    = buffer_queue,
@@ -752,7 +753,7 @@
 {
 	struct video_device *vdev = video_devdata(file);
 	struct cx8800_dev *dev = video_drvdata(file);
-	struct cx88_core *core;
+	struct cx88_core *core = dev->core;
 	struct cx8800_fh *fh;
 	enum v4l2_buf_type type = 0;
 	int radio = 0;
@@ -769,19 +770,14 @@
 		break;
 	}
 
-	lock_kernel();
-
-	core = dev->core;
-
 	dprintk(1, "open dev=%s radio=%d type=%s\n",
 		video_device_node_name(vdev), radio, v4l2_type_names[type]);
 
 	/* allocate + initialize per filehandle data */
 	fh = kzalloc(sizeof(*fh),GFP_KERNEL);
-	if (NULL == fh) {
-		unlock_kernel();
+	if (unlikely(!fh))
 		return -ENOMEM;
-	}
+
 	file->private_data = fh;
 	fh->dev      = dev;
 	fh->radio    = radio;
@@ -790,18 +786,20 @@
 	fh->height   = 240;
 	fh->fmt      = format_by_fourcc(V4L2_PIX_FMT_BGR24);
 
+	mutex_lock(&core->lock);
+
 	videobuf_queue_sg_init(&fh->vidq, &cx8800_video_qops,
 			    &dev->pci->dev, &dev->slock,
 			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
 			    V4L2_FIELD_INTERLACED,
 			    sizeof(struct cx88_buffer),
-			    fh);
+			    fh, NULL);
 	videobuf_queue_sg_init(&fh->vbiq, &cx8800_vbi_qops,
 			    &dev->pci->dev, &dev->slock,
 			    V4L2_BUF_TYPE_VBI_CAPTURE,
 			    V4L2_FIELD_SEQ_TB,
 			    sizeof(struct cx88_buffer),
-			    fh);
+			    fh, NULL);
 
 	if (fh->radio) {
 		dprintk(1,"video_open: setting radio device\n");
@@ -826,9 +824,9 @@
 		}
 		call_all(core, tuner, s_radio);
 	}
-	unlock_kernel();
 
 	atomic_inc(&core->users);
+	mutex_unlock(&core->lock);
 
 	return 0;
 }
@@ -920,10 +918,11 @@
 
 	videobuf_mmap_free(&fh->vidq);
 	videobuf_mmap_free(&fh->vbiq);
+
+	mutex_lock(&dev->core->lock);
 	file->private_data = NULL;
 	kfree(fh);
 
-	mutex_lock(&dev->core->lock);
 	if(atomic_dec_and_test(&dev->core->users))
 		call_all(dev->core, core, s_power, 0);
 	mutex_unlock(&dev->core->lock);
@@ -944,7 +943,7 @@
 
 int cx88_get_control (struct cx88_core  *core, struct v4l2_control *ctl)
 {
-	struct cx88_ctrl  *c    = NULL;
+	const struct cx88_ctrl  *c    = NULL;
 	u32 value;
 	int i;
 
@@ -976,9 +975,10 @@
 
 int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl)
 {
-	struct cx88_ctrl *c = NULL;
+	const struct cx88_ctrl *c = NULL;
 	u32 value,mask;
 	int i;
+	struct v4l2_control client_ctl;
 
 	for (i = 0; i < CX8800_CTLS; i++) {
 		if (cx8800_ctls[i].v.id == ctl->id) {
@@ -992,6 +992,27 @@
 		ctl->value = c->v.minimum;
 	if (ctl->value > c->v.maximum)
 		ctl->value = c->v.maximum;
+
+	/* Pass changes onto any WM8775 */
+	client_ctl.id = ctl->id;
+	switch (ctl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		client_ctl.value = ctl->value;
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+		client_ctl.value = (ctl->value) ?
+			(0x90 + ctl->value) << 8 : 0;
+		break;
+	case V4L2_CID_AUDIO_BALANCE:
+		client_ctl.value = ctl->value << 9;
+		break;
+	default:
+		client_ctl.id = 0;
+		break;
+	}
+	if (client_ctl.id)
+		call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+
 	mask=c->mask;
 	switch (ctl->id) {
 	case V4L2_CID_AUDIO_BALANCE:
@@ -1072,7 +1093,7 @@
 			struct v4l2_format *f)
 {
 	struct cx88_core  *core = ((struct cx8800_fh *)priv)->dev->core;
-	struct cx8800_fmt *fmt;
+	const struct cx8800_fmt *fmt;
 	enum v4l2_field   field;
 	unsigned int      maxw, maxh;
 
@@ -1247,7 +1268,7 @@
 /* only one input in this sample driver */
 int cx88_enum_input (struct cx88_core  *core,struct v4l2_input *i)
 {
-	static const char *iname[] = {
+	static const char * const iname[] = {
 		[ CX88_VMUX_COMPOSITE1 ] = "Composite1",
 		[ CX88_VMUX_COMPOSITE2 ] = "Composite2",
 		[ CX88_VMUX_COMPOSITE3 ] = "Composite3",
@@ -1267,9 +1288,10 @@
 	i->type  = V4L2_INPUT_TYPE_CAMERA;
 	strcpy(i->name,iname[INPUT(n).type]);
 	if ((CX88_VMUX_TELEVISION == INPUT(n).type) ||
-	    (CX88_VMUX_CABLE      == INPUT(n).type))
+	    (CX88_VMUX_CABLE      == INPUT(n).type)) {
 		i->type = V4L2_INPUT_TYPE_TUNER;
 		i->std = CX88_NORMS;
+	}
 	return 0;
 }
 EXPORT_SYMBOL(cx88_enum_input);
@@ -1537,7 +1559,9 @@
 	if (c->id <  V4L2_CID_BASE ||
 		c->id >= V4L2_CID_LASTP1)
 		return -EINVAL;
-	if (c->id == V4L2_CID_AUDIO_MUTE) {
+	if (c->id == V4L2_CID_AUDIO_MUTE ||
+		c->id == V4L2_CID_AUDIO_VOLUME ||
+		c->id == V4L2_CID_AUDIO_BALANCE) {
 		for (i = 0; i < CX8800_CTLS; i++) {
 			if (cx8800_ctls[i].v.id == c->id)
 				break;
@@ -1578,7 +1602,7 @@
 	spin_unlock_irqrestore(&dev->slock,flags);
 }
 
-static char *cx88_vid_irqs[32] = {
+static const char *cx88_vid_irqs[32] = {
 	"y_risci1", "u_risci1", "v_risci1", "vbi_risc1",
 	"y_risci2", "u_risci2", "v_risci2", "vbi_risc2",
 	"y_oflow",  "u_oflow",  "v_oflow",  "vbi_oflow",
@@ -1723,7 +1747,7 @@
 
 static struct video_device cx8800_vbi_template;
 
-static struct video_device cx8800_video_template = {
+static const struct video_device cx8800_video_template = {
 	.name                 = "cx8800-video",
 	.fops                 = &video_fops,
 	.ioctl_ops 	      = &video_ioctl_ops,
@@ -1758,7 +1782,7 @@
 #endif
 };
 
-static struct video_device cx8800_radio_template = {
+static const struct video_device cx8800_radio_template = {
 	.name                 = "cx8800-radio",
 	.fops                 = &radio_fops,
 	.ioctl_ops 	      = &radio_ioctl_ops,
@@ -1872,20 +1896,20 @@
 
 	if (core->board.audio_chip == V4L2_IDENT_WM8775)
 		v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
-				"wm8775", "wm8775", 0x36 >> 1, NULL);
+				NULL, "wm8775", 0x36 >> 1, NULL);
 
 	if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) {
 		/* This probes for a tda9874 as is used on some
 		   Pixelview Ultra boards. */
 		v4l2_i2c_new_subdev(&core->v4l2_dev,
 				&core->i2c_adap,
-				"tvaudio", "tvaudio", 0, I2C_ADDRS(0xb0 >> 1));
+				NULL, "tvaudio", 0, I2C_ADDRS(0xb0 >> 1));
 	}
 
 	switch (core->boardnr) {
 	case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
 	case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: {
-		static struct i2c_board_info rtc_info = {
+		static const struct i2c_board_info rtc_info = {
 			I2C_BOARD_INFO("isl1208", 0x6f)
 		};
 
@@ -2082,7 +2106,7 @@
 
 /* ----------------------------------------------------------- */
 
-static struct pci_device_id cx8800_pci_tbl[] = {
+static const struct pci_device_id cx8800_pci_tbl[] = {
 	{
 		.vendor       = 0x14f1,
 		.device       = 0x8800,
diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.c b/drivers/media/video/cx88/cx88-vp3054-i2c.c
index 794f293..ec5476d 100644
--- a/drivers/media/video/cx88/cx88-vp3054-i2c.c
+++ b/drivers/media/video/cx88/cx88-vp3054-i2c.c
@@ -121,8 +121,6 @@
 	memcpy(&vp3054_i2c->algo, &vp3054_i2c_algo_template,
 	       sizeof(vp3054_i2c->algo));
 
-	vp3054_i2c->adap.class |= I2C_CLASS_TV_DIGITAL;
-
 	vp3054_i2c->adap.dev.parent = &dev->pci->dev;
 	strlcpy(vp3054_i2c->adap.name, core->name,
 		sizeof(vp3054_i2c->adap.name));
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index 33d161a..e8c732e 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -31,9 +31,8 @@
 #include <media/videobuf-dma-sg.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/cx2341x.h>
-#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
 #include <media/videobuf-dvb.h>
-#endif
+#include <media/ir-kbd-i2c.h>
 
 #include "btcx-risc.h"
 #include "cx88-reg.h"
@@ -108,7 +107,7 @@
 /* static data                                                 */
 
 struct cx8800_fmt {
-	char  *name;
+	const char  *name;
 	u32   fourcc;          /* v4l2 format id */
 	int   depth;
 	int   flags;
@@ -138,7 +137,7 @@
 /* more */
 
 struct sram_channel {
-	char *name;
+	const char *name;
 	u32  cmds_start;
 	u32  ctrl_start;
 	u32  cdt;
@@ -149,7 +148,7 @@
 	u32  cnt1_reg;
 	u32  cnt2_reg;
 };
-extern struct sram_channel cx88_sram_channels[];
+extern const struct sram_channel const cx88_sram_channels[];
 
 /* ----------------------------------------------------------- */
 /* card configuration                                          */
@@ -240,6 +239,7 @@
 #define CX88_BOARD_WINFAST_DTV2000H_J      82
 #define CX88_BOARD_PROF_7301               83
 #define CX88_BOARD_SAMSUNG_SMT_7020        84
+#define CX88_BOARD_TWINHAN_VP1027_DVBS     85
 
 enum cx88_itype {
 	CX88_VMUX_COMPOSITE1 = 1,
@@ -262,7 +262,7 @@
 };
 
 struct cx88_board {
-	char                    *name;
+	const char              *name;
 	unsigned int            tuner_type;
 	unsigned int		radio_type;
 	unsigned char		tuner_addr;
@@ -281,6 +281,20 @@
 	u32     card;
 };
 
+enum cx88_tvaudio {
+	WW_NONE = 1,
+	WW_BTSC,
+	WW_BG,
+	WW_DK,
+	WW_I,
+	WW_L,
+	WW_EIAJ,
+	WW_I2SPT,
+	WW_FM,
+	WW_I2SADC,
+	WW_M
+};
+
 #define INPUT(nr) (core->board.input[nr])
 
 /* ----------------------------------------------------------- */
@@ -300,7 +314,7 @@
 	/* cx88 specific */
 	unsigned int           bpl;
 	struct btcx_riscmem    risc;
-	struct cx8800_fmt      *fmt;
+	const struct cx8800_fmt *fmt;
 	u32                    count;
 };
 
@@ -352,7 +366,7 @@
 	/* state info */
 	struct task_struct         *kthread;
 	v4l2_std_id                tvnorm;
-	u32                        tvaudio;
+	enum cx88_tvaudio          tvaudio;
 	u32                        audiomode_manual;
 	u32                        audiomode_current;
 	u32                        input;
@@ -363,6 +377,9 @@
 	/* IR remote control state */
 	struct cx88_IR             *ir;
 
+	/* I2C remote data */
+	struct IR_i2c_init_data    init_data;
+
 	struct mutex               lock;
 	/* various v4l controls */
 	u32                        freq;
@@ -381,17 +398,19 @@
 	return container_of(v4l2_dev, struct cx88_core, v4l2_dev);
 }
 
-#define call_all(core, o, f, args...) 				\
+#define call_hw(core, grpid, o, f, args...) \
 	do {							\
 		if (!core->i2c_rc) {				\
 			if (core->gate_ctrl)			\
 				core->gate_ctrl(core, 1);	\
-			v4l2_device_call_all(&core->v4l2_dev, 0, o, f, ##args); \
+			v4l2_device_call_all(&core->v4l2_dev, grpid, o, f, ##args); \
 			if (core->gate_ctrl)			\
 				core->gate_ctrl(core, 0);	\
 		}						\
 	} while (0)
 
+#define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args)
+
 struct cx8800_dev;
 struct cx8802_dev;
 
@@ -410,7 +429,7 @@
 	unsigned int               nclips;
 
 	/* video capture */
-	struct cx8800_fmt          *fmt;
+	const struct cx8800_fmt    *fmt;
 	unsigned int               width,height;
 	struct videobuf_queue      vidq;
 
@@ -565,7 +584,7 @@
 /* ----------------------------------------------------------- */
 /* cx88-core.c                                                 */
 
-extern void cx88_print_irqbits(char *name, char *tag, char **strings,
+extern void cx88_print_irqbits(const char *name, const char *tag, const char *strings[],
 			       int len, u32 bits, u32 mask);
 
 extern int cx88_core_irq(struct cx88_core *core, u32 status);
@@ -592,10 +611,10 @@
 extern void cx88_risc_disasm(struct cx88_core *core,
 			     struct btcx_riscmem *risc);
 extern int cx88_sram_channel_setup(struct cx88_core *core,
-				   struct sram_channel *ch,
+				   const struct sram_channel *ch,
 				   unsigned int bpl, u32 risc);
 extern void cx88_sram_channel_dump(struct cx88_core *core,
-				   struct sram_channel *ch);
+				   const struct sram_channel *ch);
 
 extern int cx88_set_scale(struct cx88_core *core, unsigned int width,
 			  unsigned int height, enum v4l2_field field);
@@ -603,8 +622,8 @@
 
 extern struct video_device *cx88_vdev_init(struct cx88_core *core,
 					   struct pci_dev *pci,
-					   struct video_device *template,
-					   char *type);
+					   const struct video_device *template_,
+					   const char *type);
 extern struct cx88_core* cx88_core_get(struct pci_dev *pci);
 extern void cx88_core_put(struct cx88_core *core,
 			  struct pci_dev *pci);
@@ -630,13 +649,12 @@
 			     struct cx88_dmaqueue *q);
 void cx8800_vbi_timeout(unsigned long data);
 
-extern struct videobuf_queue_ops cx8800_vbi_qops;
+extern const struct videobuf_queue_ops cx8800_vbi_qops;
 
 /* ----------------------------------------------------------- */
 /* cx88-i2c.c                                                  */
 
 extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci);
-extern void cx88_i2c_init_ir(struct cx88_core *core);
 
 
 /* ----------------------------------------------------------- */
@@ -651,18 +669,6 @@
 /* ----------------------------------------------------------- */
 /* cx88-tvaudio.c                                              */
 
-#define WW_NONE		 1
-#define WW_BTSC		 2
-#define WW_BG		 3
-#define WW_DK		 4
-#define WW_I		 5
-#define WW_L		 6
-#define WW_EIAJ		 7
-#define WW_I2SPT	 8
-#define WW_FM		 9
-#define WW_I2SADC	 10
-#define WW_M		 11
-
 void cx88_set_tvaudio(struct cx88_core *core);
 void cx88_newstation(struct cx88_core *core);
 void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t);
@@ -686,6 +692,7 @@
 void cx88_ir_irq(struct cx88_core *core);
 int cx88_ir_start(struct cx88_core *core);
 void cx88_ir_stop(struct cx88_core *core);
+extern void cx88_i2c_init_ir(struct cx88_core *core);
 
 /* ----------------------------------------------------------- */
 /* cx88-mpeg.c                                                 */
@@ -705,10 +712,3 @@
 int cx88_get_control(struct cx88_core *core, struct v4l2_control *ctl);
 int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl);
 int cx88_video_mux(struct cx88_core *core, unsigned int input);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
- */
diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c
index 1c25882..d8e38cc 100644
--- a/drivers/media/video/davinci/vpfe_capture.c
+++ b/drivers/media/video/davinci/vpfe_capture.c
@@ -370,7 +370,7 @@
  * For a given standard, this functions sets up the default
  * pix format & crop values in the vpfe device and ccdc.  It first
  * starts with defaults based values from the standard table.
- * It then checks if sub device support g_fmt and then override the
+ * It then checks if sub device support g_mbus_fmt and then override the
  * values based on that.Sets crop values to match with scan resolution
  * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the
  * values in ccdc
@@ -379,6 +379,8 @@
 				    const v4l2_std_id *std_id)
 {
 	struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev;
+	struct v4l2_mbus_framefmt mbus_fmt;
+	struct v4l2_pix_format *pix = &vpfe_dev->fmt.fmt.pix;
 	int i, ret = 0;
 
 	for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) {
@@ -403,29 +405,36 @@
 	vpfe_dev->crop.left = 0;
 	vpfe_dev->crop.width = vpfe_dev->std_info.active_pixels;
 	vpfe_dev->crop.height = vpfe_dev->std_info.active_lines;
-	vpfe_dev->fmt.fmt.pix.width = vpfe_dev->crop.width;
-	vpfe_dev->fmt.fmt.pix.height = vpfe_dev->crop.height;
+	pix->width = vpfe_dev->crop.width;
+	pix->height = vpfe_dev->crop.height;
 
 	/* first field and frame format based on standard frame format */
 	if (vpfe_dev->std_info.frame_format) {
-		vpfe_dev->fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
+		pix->field = V4L2_FIELD_INTERLACED;
 		/* assume V4L2_PIX_FMT_UYVY as default */
-		vpfe_dev->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+		pix->pixelformat = V4L2_PIX_FMT_UYVY;
+		v4l2_fill_mbus_format(&mbus_fmt, pix,
+				V4L2_MBUS_FMT_YUYV10_2X10);
 	} else {
-		vpfe_dev->fmt.fmt.pix.field = V4L2_FIELD_NONE;
+		pix->field = V4L2_FIELD_NONE;
 		/* assume V4L2_PIX_FMT_SBGGR8 */
-		vpfe_dev->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
+		pix->pixelformat = V4L2_PIX_FMT_SBGGR8;
+		v4l2_fill_mbus_format(&mbus_fmt, pix,
+				V4L2_MBUS_FMT_SBGGR8_1X8);
 	}
 
-	/* if sub device supports g_fmt, override the defaults */
+	/* if sub device supports g_mbus_fmt, override the defaults */
 	ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev,
-			sdinfo->grp_id, video, g_fmt, &vpfe_dev->fmt);
+			sdinfo->grp_id, video, g_mbus_fmt, &mbus_fmt);
 
 	if (ret && ret != -ENOIOCTLCMD) {
 		v4l2_err(&vpfe_dev->v4l2_dev,
-			"error in getting g_fmt from sub device\n");
+			"error in getting g_mbus_fmt from sub device\n");
 		return ret;
 	}
+	v4l2_fill_pix_format(pix, &mbus_fmt);
+	pix->bytesperline = pix->width * 2;
+	pix->sizeimage = pix->bytesperline * pix->height;
 
 	/* Sets the values in CCDC */
 	ret = vpfe_config_ccdc_image_format(vpfe_dev);
@@ -434,11 +443,8 @@
 
 	/* Update the values of sizeimage and bytesperline */
 	if (!ret) {
-		vpfe_dev->fmt.fmt.pix.bytesperline =
-			ccdc_dev->hw_ops.get_line_length();
-		vpfe_dev->fmt.fmt.pix.sizeimage =
-			vpfe_dev->fmt.fmt.pix.bytesperline *
-			vpfe_dev->fmt.fmt.pix.height;
+		pix->bytesperline = ccdc_dev->hw_ops.get_line_length();
+		pix->sizeimage = pix->bytesperline * pix->height;
 	}
 	return ret;
 }
@@ -1366,7 +1372,7 @@
 				req_buf->type,
 				vpfe_dev->fmt.fmt.pix.field,
 				sizeof(struct videobuf_buffer),
-				fh);
+				fh, NULL);
 
 	fh->io_allowed = 1;
 	vpfe_dev->io_usrs = 1;
@@ -1980,7 +1986,7 @@
 		vpfe_dev->sd[i] =
 			v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev,
 						  i2c_adap,
-						  sdinfo->name,
+						  NULL,
 						  &sdinfo->board_info,
 						  NULL);
 		if (vpfe_dev->sd[i]) {
diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c
index a7f48b5..6ac6acd 100644
--- a/drivers/media/video/davinci/vpif_capture.c
+++ b/drivers/media/video/davinci/vpif_capture.c
@@ -731,7 +731,6 @@
  */
 static unsigned int vpif_poll(struct file *filep, poll_table * wait)
 {
-	int err = 0;
 	struct vpif_fh *fh = filep->private_data;
 	struct channel_obj *channel = fh->channel;
 	struct common_obj *common = &(channel->common[VPIF_VIDEO_INDEX]);
@@ -739,8 +738,7 @@
 	vpif_dbg(2, debug, "vpif_poll\n");
 
 	if (common->started)
-		err = videobuf_poll_stream(filep, &common->buffer_queue, wait);
-
+		return videobuf_poll_stream(filep, &common->buffer_queue, wait);
 	return 0;
 }
 
@@ -793,7 +791,7 @@
 	}
 
 	/* Allocate memory for the file handle object */
-	fh = kmalloc(sizeof(struct vpif_fh), GFP_KERNEL);
+	fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL);
 	if (NULL == fh) {
 		vpif_err("unable to allocate memory for file handle object\n");
 		ret = -ENOMEM;
@@ -929,7 +927,8 @@
 					    &common->irqlock,
 					    reqbuf->type,
 					    common->fmt.fmt.pix.field,
-					    sizeof(struct videobuf_buffer), fh);
+					    sizeof(struct videobuf_buffer), fh,
+					    NULL);
 
 	/* Set io allowed member of file handle to TRUE */
 	fh->io_allowed[index] = 1;
@@ -1030,9 +1029,10 @@
 			goto qbuf_exit;
 
 		if ((VIDEOBUF_NEEDS_INIT != buf1->state)
-			    && (buf1->baddr != tbuf.m.userptr))
+			    && (buf1->baddr != tbuf.m.userptr)) {
 			vpif_buffer_release(&common->buffer_queue, buf1);
 			buf1->baddr = tbuf.m.userptr;
+		}
 		break;
 
 	default:
@@ -1994,7 +1994,7 @@
 	config = pdev->dev.platform_data;
 
 	subdev_count = config->subdev_count;
-	vpif_obj.sd = kmalloc(sizeof(struct v4l2_subdev *) * subdev_count,
+	vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count,
 				GFP_KERNEL);
 	if (vpif_obj.sd == NULL) {
 		vpif_err("unable to allocate memory for subdevice pointers\n");
@@ -2013,7 +2013,7 @@
 		vpif_obj.sd[i] =
 			v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
 						  i2c_adap,
-						  subdevdata->name,
+						  NULL,
 						  &subdevdata->board_info,
 						  NULL);
 
@@ -2113,7 +2113,7 @@
 	.resume = vpif_resume,
 };
 
-static struct platform_driver vpif_driver = {
+static __refdata struct platform_driver vpif_driver = {
 	.driver	= {
 		.name	= "vpif_capture",
 		.owner	= THIS_MODULE,
diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c
index da07607..685f6a6 100644
--- a/drivers/media/video/davinci/vpif_display.c
+++ b/drivers/media/video/davinci/vpif_display.c
@@ -600,7 +600,7 @@
 
 	ch = video_get_drvdata(vdev);
 	/* Allocate memory for the file handle object */
-	fh = kmalloc(sizeof(struct vpif_fh), GFP_KERNEL);
+	fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL);
 	if (fh == NULL) {
 		vpif_err("unable to allocate memory for file handle object\n");
 		return -ENOMEM;
@@ -853,7 +853,8 @@
 					    &video_qops, NULL,
 					    &common->irqlock,
 					    reqbuf->type, field,
-					    sizeof(struct videobuf_buffer), fh);
+					    sizeof(struct videobuf_buffer), fh,
+					    NULL);
 
 	/* Set io allowed member of file handle to TRUE */
 	fh->io_allowed[index] = 1;
@@ -935,9 +936,10 @@
 			goto qbuf_exit;
 
 		if ((VIDEOBUF_NEEDS_INIT != buf1->state)
-			    && (buf1->baddr != tbuf.m.userptr))
+			    && (buf1->baddr != tbuf.m.userptr)) {
 			vpif_buffer_release(&common->buffer_queue, buf1);
 			buf1->baddr = tbuf.m.userptr;
+		}
 		break;
 
 	default:
@@ -1395,7 +1397,7 @@
 	/* Allocate memory for six channel objects */
 	for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
 		vpif_obj.dev[i] =
-		    kmalloc(sizeof(struct channel_obj), GFP_KERNEL);
+		    kzalloc(sizeof(struct channel_obj), GFP_KERNEL);
 		/* If memory allocation fails, return error */
 		if (!vpif_obj.dev[i]) {
 			free_channel_objects_index = i;
@@ -1541,7 +1543,7 @@
 	config = pdev->dev.platform_data;
 	subdev_count = config->subdev_count;
 	subdevdata = config->subdevinfo;
-	vpif_obj.sd = kmalloc(sizeof(struct v4l2_subdev *) * subdev_count,
+	vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count,
 								GFP_KERNEL);
 	if (vpif_obj.sd == NULL) {
 		vpif_err("unable to allocate memory for subdevice pointers\n");
@@ -1551,7 +1553,7 @@
 
 	for (i = 0; i < subdev_count; i++) {
 		vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
-						i2c_adap, subdevdata[i].name,
+						i2c_adap, NULL,
 						&subdevdata[i].board_info,
 						NULL);
 		if (!vpif_obj.sd[i]) {
@@ -1610,7 +1612,7 @@
 	return 0;
 }
 
-static struct platform_driver vpif_driver = {
+static __refdata struct platform_driver vpif_driver = {
 	.driver	= {
 			.name	= "vpif_display",
 			.owner	= THIS_MODULE,
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
index e182abf..3c48a72 100644
--- a/drivers/media/video/em28xx/em28xx-audio.c
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -102,6 +102,9 @@
 		break;
 	}
 
+	if (atomic_read(&dev->stream_started) == 0)
+		return;
+
 	if (dev->adev.capture_pcm_substream) {
 		substream = dev->adev.capture_pcm_substream;
 		runtime = substream->runtime;
@@ -217,31 +220,6 @@
 	return 0;
 }
 
-static int em28xx_cmd(struct em28xx *dev, int cmd, int arg)
-{
-	dprintk("%s transfer\n", (dev->adev.capture_stream == STREAM_ON) ?
-				 "stop" : "start");
-
-	switch (cmd) {
-	case EM28XX_CAPTURE_STREAM_EN:
-		if (dev->adev.capture_stream == STREAM_OFF &&
-		    arg == EM28XX_START_AUDIO) {
-			dev->adev.capture_stream = STREAM_ON;
-			em28xx_init_audio_isoc(dev);
-		} else if (dev->adev.capture_stream == STREAM_ON &&
-			   arg == EM28XX_STOP_AUDIO) {
-			dev->adev.capture_stream = STREAM_OFF;
-			em28xx_deinit_isoc_audio(dev);
-		} else {
-			em28xx_errdev("An underrun very likely occurred. "
-					"Ignoring it.\n");
-		}
-		return 0;
-	default:
-		return -EINVAL;
-	}
-}
-
 static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
 					size_t size)
 {
@@ -303,7 +281,6 @@
 	dev->mute = 0;
 	mutex_lock(&dev->lock);
 	ret = em28xx_audio_analog_set(dev);
-	mutex_unlock(&dev->lock);
 	if (ret < 0)
 		goto err;
 
@@ -311,11 +288,10 @@
 	if (dev->alt == 0 && dev->adev.users == 0) {
 		int errCode;
 		dev->alt = 7;
-		errCode = usb_set_interface(dev->udev, 0, 7);
 		dprintk("changing alternate number to 7\n");
+		errCode = usb_set_interface(dev->udev, 0, 7);
 	}
 
-	mutex_lock(&dev->lock);
 	dev->adev.users++;
 	mutex_unlock(&dev->lock);
 
@@ -325,6 +301,8 @@
 
 	return 0;
 err:
+	mutex_unlock(&dev->lock);
+
 	em28xx_err("Error while configuring em28xx mixer\n");
 	return ret;
 }
@@ -338,6 +316,11 @@
 	dev->mute = 1;
 	mutex_lock(&dev->lock);
 	dev->adev.users--;
+	if (atomic_read(&dev->stream_started) > 0) {
+		atomic_set(&dev->stream_started, 0);
+		schedule_work(&dev->wq_trigger);
+	}
+
 	em28xx_audio_analog_set(dev);
 	if (substream->runtime->dma_area) {
 		dprintk("freeing\n");
@@ -375,8 +358,10 @@
 
 	dprintk("Stop capture, if needed\n");
 
-	if (dev->adev.capture_stream == STREAM_ON)
-		em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, EM28XX_STOP_AUDIO);
+	if (atomic_read(&dev->stream_started) > 0) {
+		atomic_set(&dev->stream_started, 0);
+		schedule_work(&dev->wq_trigger);
+	}
 
 	return 0;
 }
@@ -391,31 +376,37 @@
 	return 0;
 }
 
+static void audio_trigger(struct work_struct *work)
+{
+	struct em28xx *dev = container_of(work, struct em28xx, wq_trigger);
+
+	if (atomic_read(&dev->stream_started)) {
+		dprintk("starting capture");
+		em28xx_init_audio_isoc(dev);
+	} else {
+		dprintk("stopping capture");
+		em28xx_deinit_isoc_audio(dev);
+	}
+}
+
 static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
 				      int cmd)
 {
 	struct em28xx *dev = snd_pcm_substream_chip(substream);
 	int retval;
 
-	dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START) ?
-				       "start" : "stop");
-
-	spin_lock(&dev->adev.slock);
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
-		em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, EM28XX_START_AUDIO);
-		retval = 0;
+		atomic_set(&dev->stream_started, 1);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
-		em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, EM28XX_STOP_AUDIO);
-		retval = 0;
+		atomic_set(&dev->stream_started, 1);
 		break;
 	default:
 		retval = -EINVAL;
 	}
-
-	spin_unlock(&dev->adev.slock);
-	return retval;
+	schedule_work(&dev->wq_trigger);
+	return 0;
 }
 
 static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream
@@ -495,6 +486,8 @@
 	strcpy(card->shortname, "Em28xx Audio");
 	strcpy(card->longname, "Empia Em28xx Audio");
 
+	INIT_WORK(&dev->wq_trigger, audio_trigger);
+
 	err = snd_card_register(card);
 	if (err < 0) {
 		snd_card_free(card);
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index e7efb4b..5485923 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -187,6 +187,18 @@
 	{	-1,		-1,	-1,		-1},
 };
 
+static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_analog[] = {
+	{EM28XX_R08_GPIO,	0x6d,	~EM_GPIO_4,	10},
+	{EM2880_R04_GPO,	0x00,	0xff,		10},
+	{ -1,			-1,	-1,		-1},
+};
+
+static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = {
+	{EM28XX_R08_GPIO,	0x6e,	~EM_GPIO_4,	10},
+	{EM2880_R04_GPO,	0x08,	0xff,		10},
+	{ -1,			-1,	-1,		-1},
+};
+
 /* eb1a:2868 Reddo DVB-C USB TV Box
    GPIO4 - CU1216L NIM
    Other GPIOs seems to be don't care. */
@@ -781,22 +793,22 @@
 		.tuner_gpio   = default_tuner_gpio,
 		.decoder      = EM28XX_TVP5150,
 		.has_dvb      = 1,
-		.dvb_gpio     = default_digital,
+		.dvb_gpio     = terratec_cinergy_USB_XS_FR_digital,
 		.input        = { {
 			.type     = EM28XX_VMUX_TELEVISION,
 			.vmux     = TVP5150_COMPOSITE0,
 			.amux     = EM28XX_AMUX_VIDEO,
-			.gpio     = default_analog,
+			.gpio     = terratec_cinergy_USB_XS_FR_analog,
 		}, {
 			.type     = EM28XX_VMUX_COMPOSITE1,
 			.vmux     = TVP5150_COMPOSITE1,
 			.amux     = EM28XX_AMUX_LINE_IN,
-			.gpio     = default_analog,
+			.gpio     = terratec_cinergy_USB_XS_FR_analog,
 		}, {
 			.type     = EM28XX_VMUX_SVIDEO,
 			.vmux     = TVP5150_SVIDEO,
 			.amux     = EM28XX_AMUX_LINE_IN,
-			.gpio     = default_analog,
+			.gpio     = terratec_cinergy_USB_XS_FR_analog,
 		} },
 	},
 	[EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = {
@@ -1648,6 +1660,22 @@
 			.gpio     = terratec_av350_unmute_gpio,
 		} },
 	},
+
+	[EM2860_BOARD_ELGATO_VIDEO_CAPTURE] = {
+		.name         = "Elgato Video Capture",
+		.decoder      = EM28XX_SAA711X,
+		.tuner_type   = TUNER_ABSENT,   /* Capture only device */
+		.input        = { {
+			.type  = EM28XX_VMUX_COMPOSITE1,
+			.vmux  = SAA7115_COMPOSITE0,
+			.amux  = EM28XX_AMUX_LINE_IN,
+		}, {
+			.type  = EM28XX_VMUX_SVIDEO,
+			.vmux  = SAA7115_SVIDEO3,
+			.amux  = EM28XX_AMUX_LINE_IN,
+		} },
+	},
+
 	[EM2882_BOARD_EVGA_INDTUBE] = {
 		.name         = "Evga inDtube",
 		.tuner_type   = TUNER_XC2028,
@@ -1772,6 +1800,8 @@
 			.driver_info = EM2860_BOARD_TERRATEC_AV350 },
 	{ USB_DEVICE(0x0ccd, 0x0096),
 			.driver_info = EM2860_BOARD_TERRATEC_GRABBY },
+	{ USB_DEVICE(0x0fd9, 0x0033),
+			.driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE},
 	{ USB_DEVICE(0x185b, 0x2870),
 			.driver_info = EM2870_BOARD_COMPRO_VIDEOMATE },
 	{ USB_DEVICE(0x185b, 0x2041),
@@ -2168,6 +2198,7 @@
 		ctl->demod = XC3028_FE_ZARLINK456;
 		break;
 	case EM2880_BOARD_TERRATEC_HYBRID_XS:
+	case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
 	case EM2881_BOARD_PINNACLE_HYBRID_PRO:
 		ctl->demod = XC3028_FE_ZARLINK456;
 		break;
@@ -2523,39 +2554,39 @@
 	/* request some modules */
 	if (dev->board.has_msp34xx)
 		v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
-			"msp3400", "msp3400", 0, msp3400_addrs);
+			NULL, "msp3400", 0, msp3400_addrs);
 
 	if (dev->board.decoder == EM28XX_SAA711X)
 		v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
-			"saa7115", "saa7115_auto", 0, saa711x_addrs);
+			NULL, "saa7115_auto", 0, saa711x_addrs);
 
 	if (dev->board.decoder == EM28XX_TVP5150)
 		v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
-			"tvp5150", "tvp5150", 0, tvp5150_addrs);
+			NULL, "tvp5150", 0, tvp5150_addrs);
 
 	if (dev->em28xx_sensor == EM28XX_MT9V011) {
 		struct v4l2_subdev *sd;
 
 		sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
-			 &dev->i2c_adap, "mt9v011", "mt9v011", 0, mt9v011_addrs);
+			 &dev->i2c_adap, NULL, "mt9v011", 0, mt9v011_addrs);
 		v4l2_subdev_call(sd, core, s_config, 0, &dev->sensor_xtal);
 	}
 
 
 	if (dev->board.adecoder == EM28XX_TVAUDIO)
 		v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
-			"tvaudio", "tvaudio", dev->board.tvaudio_addr, NULL);
+			NULL, "tvaudio", dev->board.tvaudio_addr, NULL);
 
 	if (dev->board.tuner_type != TUNER_ABSENT) {
 		int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
 
 		if (dev->board.radio.type)
 			v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
-				"tuner", "tuner", dev->board.radio_addr, NULL);
+				NULL, "tuner", dev->board.radio_addr, NULL);
 
 		if (has_demod)
 			v4l2_i2c_new_subdev(&dev->v4l2_dev,
-				&dev->i2c_adap, "tuner", "tuner",
+				&dev->i2c_adap, NULL, "tuner",
 				0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
 		if (dev->tuner_addr == 0) {
 			enum v4l2_i2c_tuner_type type =
@@ -2563,14 +2594,14 @@
 			struct v4l2_subdev *sd;
 
 			sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
-				&dev->i2c_adap, "tuner", "tuner",
+				&dev->i2c_adap, NULL, "tuner",
 				0, v4l2_i2c_tuner_addrs(type));
 
 			if (sd)
 				dev->tuner_addr = v4l2_i2c_subdev_addr(sd);
 		} else {
 			v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
-				"tuner", "tuner", dev->tuner_addr, NULL);
+				NULL, "tuner", dev->tuner_addr, NULL);
 		}
 	}
 
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 7b9ec6e..908e3bc 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -277,12 +277,13 @@
 {
 	void *startwrite, *startread;
 	int  offset;
-	int bytesperline = dev->vbi_width;
+	int bytesperline;
 
 	if (dev == NULL) {
 		em28xx_isocdbg("dev is null\n");
 		return;
 	}
+	bytesperline = dev->vbi_width;
 
 	if (dma_q == NULL) {
 		em28xx_isocdbg("dma_q is null\n");
@@ -862,17 +863,14 @@
 		return 1;
 
 	/* is it free? */
-	mutex_lock(&dev->lock);
 	if (dev->resources & bit) {
 		/* no, someone else uses it */
-		mutex_unlock(&dev->lock);
 		return 0;
 	}
 	/* it's free, grab it */
 	fh->resources  |= bit;
 	dev->resources |= bit;
 	em28xx_videodbg("res: get %d\n", bit);
-	mutex_unlock(&dev->lock);
 	return 1;
 }
 
@@ -892,11 +890,9 @@
 
 	BUG_ON((fh->resources & bits) != bits);
 
-	mutex_lock(&dev->lock);
 	fh->resources  &= ~bits;
 	dev->resources &= ~bits;
 	em28xx_videodbg("res: put %d\n", bits);
-	mutex_unlock(&dev->lock);
 }
 
 static int get_ressource(struct em28xx_fh *fh)
@@ -1023,8 +1019,6 @@
 	struct em28xx_fh      *fh  = priv;
 	struct em28xx         *dev = fh->dev;
 
-	mutex_lock(&dev->lock);
-
 	f->fmt.pix.width = dev->width;
 	f->fmt.pix.height = dev->height;
 	f->fmt.pix.pixelformat = dev->format->fourcc;
@@ -1038,8 +1032,6 @@
 	else
 		f->fmt.pix.field = dev->interlaced ?
 			   V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
-
-	mutex_unlock(&dev->lock);
 	return 0;
 }
 
@@ -1137,22 +1129,15 @@
 	if (rc < 0)
 		return rc;
 
-	mutex_lock(&dev->lock);
-
 	vidioc_try_fmt_vid_cap(file, priv, f);
 
 	if (videobuf_queue_is_busy(&fh->vb_vidq)) {
 		em28xx_errdev("%s queue busy\n", __func__);
-		rc = -EBUSY;
-		goto out;
+		return -EBUSY;
 	}
 
-	rc = em28xx_set_video_format(dev, f->fmt.pix.pixelformat,
+	return em28xx_set_video_format(dev, f->fmt.pix.pixelformat,
 				f->fmt.pix.width, f->fmt.pix.height);
-
-out:
-	mutex_unlock(&dev->lock);
-	return rc;
 }
 
 static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
@@ -1181,7 +1166,6 @@
 	if (rc < 0)
 		return rc;
 
-	mutex_lock(&dev->lock);
 	dev->norm = *norm;
 
 	/* Adjusts width/height, if needed */
@@ -1197,7 +1181,6 @@
 	em28xx_resolution_set(dev);
 	v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
 
-	mutex_unlock(&dev->lock);
 	return 0;
 }
 
@@ -1302,9 +1285,7 @@
 
 	dev->ctl_input = i;
 
-	mutex_lock(&dev->lock);
 	video_mux(dev, dev->ctl_input);
-	mutex_unlock(&dev->lock);
 	return 0;
 }
 
@@ -1365,15 +1346,12 @@
 	if (0 == INPUT(a->index)->type)
 		return -EINVAL;
 
-	mutex_lock(&dev->lock);
-
 	dev->ctl_ainput = INPUT(a->index)->amux;
 	dev->ctl_aoutput = INPUT(a->index)->aout;
 
 	if (!dev->ctl_aoutput)
 		dev->ctl_aoutput = EM28XX_AOUT_MASTER;
 
-	mutex_unlock(&dev->lock);
 	return 0;
 }
 
@@ -1393,17 +1371,15 @@
 
 	qc->id = id;
 
-	/* enumberate AC97 controls */
+	/* enumerate AC97 controls */
 	if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
 		rc = ac97_queryctrl(qc);
 		if (!rc)
 			return 0;
 	}
 
-	/* enumberate V4L2 device controls */
-	mutex_lock(&dev->lock);
+	/* enumerate V4L2 device controls */
 	v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc);
-	mutex_unlock(&dev->lock);
 
 	if (qc->type)
 		return 0;
@@ -1423,7 +1399,6 @@
 		return rc;
 	rc = 0;
 
-	mutex_lock(&dev->lock);
 
 	/* Set an AC97 control */
 	if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
@@ -1437,7 +1412,6 @@
 		rc = 0;
 	}
 
-	mutex_unlock(&dev->lock);
 	return rc;
 }
 
@@ -1452,8 +1426,6 @@
 	if (rc < 0)
 		return rc;
 
-	mutex_lock(&dev->lock);
-
 	/* Set an AC97 control */
 	if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
 		rc = ac97_set_ctrl(dev, ctrl);
@@ -1480,8 +1452,6 @@
 			rc = em28xx_audio_analog_set(dev);
 		}
 	}
-
-	mutex_unlock(&dev->lock);
 	return rc;
 }
 
@@ -1502,10 +1472,7 @@
 	strcpy(t->name, "Tuner");
 	t->type = V4L2_TUNER_ANALOG_TV;
 
-	mutex_lock(&dev->lock);
 	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
-	mutex_unlock(&dev->lock);
-
 	return 0;
 }
 
@@ -1523,10 +1490,7 @@
 	if (0 != t->index)
 		return -EINVAL;
 
-	mutex_lock(&dev->lock);
 	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
-	mutex_unlock(&dev->lock);
-
 	return 0;
 }
 
@@ -1536,11 +1500,8 @@
 	struct em28xx_fh      *fh  = priv;
 	struct em28xx         *dev = fh->dev;
 
-	mutex_lock(&dev->lock);
 	f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
 	f->frequency = dev->ctl_freq;
-	mutex_unlock(&dev->lock);
-
 	return 0;
 }
 
@@ -1563,13 +1524,9 @@
 	if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
 		return -EINVAL;
 
-	mutex_lock(&dev->lock);
-
 	dev->ctl_freq = f->frequency;
 	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f);
 
-	mutex_unlock(&dev->lock);
-
 	return 0;
 }
 
@@ -1610,9 +1567,7 @@
 
 	switch (reg->match.type) {
 	case V4L2_CHIP_MATCH_AC97:
-		mutex_lock(&dev->lock);
 		ret = em28xx_read_ac97(dev, reg->reg);
-		mutex_unlock(&dev->lock);
 		if (ret < 0)
 			return ret;
 
@@ -1634,9 +1589,7 @@
 	/* Match host */
 	reg->size = em28xx_reg_len(reg->reg);
 	if (reg->size == 1) {
-		mutex_lock(&dev->lock);
 		ret = em28xx_read_reg(dev, reg->reg);
-		mutex_unlock(&dev->lock);
 
 		if (ret < 0)
 			return ret;
@@ -1644,10 +1597,8 @@
 		reg->val = ret;
 	} else {
 		__le16 val = 0;
-		mutex_lock(&dev->lock);
 		ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS,
 						   reg->reg, (char *)&val, 2);
-		mutex_unlock(&dev->lock);
 		if (ret < 0)
 			return ret;
 
@@ -1663,15 +1614,10 @@
 	struct em28xx_fh      *fh  = priv;
 	struct em28xx         *dev = fh->dev;
 	__le16 buf;
-	int    rc;
 
 	switch (reg->match.type) {
 	case V4L2_CHIP_MATCH_AC97:
-		mutex_lock(&dev->lock);
-		rc = em28xx_write_ac97(dev, reg->reg, reg->val);
-		mutex_unlock(&dev->lock);
-
-		return rc;
+		return em28xx_write_ac97(dev, reg->reg, reg->val);
 	case V4L2_CHIP_MATCH_I2C_DRIVER:
 		v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
 		return 0;
@@ -1687,12 +1633,8 @@
 	/* Match host */
 	buf = cpu_to_le16(reg->val);
 
-	mutex_lock(&dev->lock);
-	rc = em28xx_write_regs(dev, reg->reg, (char *)&buf,
+	return em28xx_write_regs(dev, reg->reg, (char *)&buf,
 			       em28xx_reg_len(reg->reg));
-	mutex_unlock(&dev->lock);
-
-	return rc;
 }
 #endif
 
@@ -1829,16 +1771,12 @@
 	if (rc < 0)
 		return rc;
 
-	mutex_lock(&dev->lock);
-
 	f->fmt.sliced.service_set = 0;
 	v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced);
 
 	if (f->fmt.sliced.service_set == 0)
 		rc = -EINVAL;
 
-	mutex_unlock(&dev->lock);
-
 	return rc;
 }
 
@@ -1853,9 +1791,7 @@
 	if (rc < 0)
 		return rc;
 
-	mutex_lock(&dev->lock);
 	v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced);
-	mutex_unlock(&dev->lock);
 
 	if (f->fmt.sliced.service_set == 0)
 		return -EINVAL;
@@ -2040,9 +1976,7 @@
 	strcpy(t->name, "Radio");
 	t->type = V4L2_TUNER_RADIO;
 
-	mutex_lock(&dev->lock);
 	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
-	mutex_unlock(&dev->lock);
 
 	return 0;
 }
@@ -2075,9 +2009,7 @@
 	if (0 != t->index)
 		return -EINVAL;
 
-	mutex_lock(&dev->lock);
 	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
-	mutex_unlock(&dev->lock);
 
 	return 0;
 }
@@ -2137,8 +2069,6 @@
 		break;
 	}
 
-	mutex_lock(&dev->lock);
-
 	em28xx_videodbg("open dev=%s type=%s users=%d\n",
 			video_device_node_name(vdev), v4l2_type_names[fh_type],
 			dev->users);
@@ -2147,7 +2077,6 @@
 	fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
 	if (!fh) {
 		em28xx_errdev("em28xx-video.c: Out of memory?!\n");
-		mutex_unlock(&dev->lock);
 		return -ENOMEM;
 	}
 	fh->dev = dev;
@@ -2181,15 +2110,13 @@
 	videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops,
 				    NULL, &dev->slock,
 				    V4L2_BUF_TYPE_VIDEO_CAPTURE, field,
-				    sizeof(struct em28xx_buffer), fh);
+				    sizeof(struct em28xx_buffer), fh, &dev->lock);
 
 	videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops,
 				    NULL, &dev->slock,
 				    V4L2_BUF_TYPE_VBI_CAPTURE,
 				    V4L2_FIELD_SEQ_TB,
-				    sizeof(struct em28xx_buffer), fh);
-
-	mutex_unlock(&dev->lock);
+				    sizeof(struct em28xx_buffer), fh, &dev->lock);
 
 	return errCode;
 }
@@ -2388,7 +2315,7 @@
 	.read          = em28xx_v4l2_read,
 	.poll          = em28xx_v4l2_poll,
 	.mmap          = em28xx_v4l2_mmap,
-	.ioctl	       = video_ioctl2,
+	.unlocked_ioctl = video_ioctl2,
 };
 
 static const struct v4l2_ioctl_ops video_ioctl_ops = {
@@ -2496,6 +2423,7 @@
 	vfd->v4l2_dev	= &dev->v4l2_dev;
 	vfd->release	= video_device_release;
 	vfd->debug	= video_debug;
+	vfd->lock	= &dev->lock;
 
 	snprintf(vfd->name, sizeof(vfd->name), "%s %s",
 		 dev->name, type_name);
@@ -2516,6 +2444,7 @@
 
 	/* set default norm */
 	dev->norm = em28xx_video_template.current_norm;
+	v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
 	dev->interlaced = EM28XX_INTERLACED_DEFAULT;
 	dev->ctl_input = 0;
 
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 1c61a6b..6a75e6a 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -25,12 +25,13 @@
 #ifndef _EM28XX_H
 #define _EM28XX_H
 
-#include <linux/videodev2.h>
-#include <media/videobuf-vmalloc.h>
-#include <media/v4l2-device.h>
-
+#include <linux/workqueue.h>
 #include <linux/i2c.h>
 #include <linux/mutex.h>
+#include <linux/videodev2.h>
+
+#include <media/videobuf-vmalloc.h>
+#include <media/v4l2-device.h>
 #include <media/ir-kbd-i2c.h>
 #include <media/ir-core.h>
 #if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE)
@@ -73,6 +74,7 @@
 #define EM2820_BOARD_VIDEOLOGY_20K14XUSB	  30
 #define EM2821_BOARD_USBGEAR_VD204		  31
 #define EM2821_BOARD_SUPERCOMP_USB_2		  32
+#define EM2860_BOARD_ELGATO_VIDEO_CAPTURE	  33
 #define EM2860_BOARD_TERRATEC_HYBRID_XS		  34
 #define EM2860_BOARD_TYPHOON_DVD_MAKER		  35
 #define EM2860_BOARD_NETGMBH_CAM		  36
@@ -184,11 +186,6 @@
 	EM28XX_DIGITAL_MODE,
 };
 
-enum em28xx_stream_state {
-	STREAM_OFF,
-	STREAM_INTERRUPT,
-	STREAM_ON,
-};
 
 struct em28xx;
 
@@ -463,7 +460,6 @@
 	struct snd_card            *sndcard;
 
 	int users;
-	enum em28xx_stream_state capture_stream;
 	spinlock_t slock;
 };
 
@@ -505,6 +501,10 @@
 	unsigned int has_audio_class:1;
 	unsigned int has_alsa_audio:1;
 
+	/* Controls audio streaming */
+	struct work_struct wq_trigger;              /* Trigger to start/stop audio for alsa module */
+	 atomic_t       stream_started;      /* stream should be running if true */
+
 	struct em28xx_fmt *format;
 
 	struct em28xx_IR *ir;
diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c
index 43d208f..9a075d8 100644
--- a/drivers/media/video/fsl-viu.c
+++ b/drivers/media/video/fsl-viu.c
@@ -22,6 +22,7 @@
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/of_platform.h>
+#include <linux/slab.h>
 #include <linux/version.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
@@ -425,7 +426,7 @@
 
 	BUG_ON(in_interrupt());
 
-	videobuf_waiton(&buf->vb, 0, 0);
+	videobuf_waiton(vq, &buf->vb, 0, 0);
 
 	if (vq->int_ops && vq->int_ops->vaddr)
 		vaddr = vq->int_ops->vaddr(vb);
@@ -1287,7 +1288,7 @@
 	videobuf_queue_dma_contig_init(&fh->vb_vidq, &viu_video_qops,
 				       dev->dev, &fh->vbq_lock,
 				       fh->type, V4L2_FIELD_INTERLACED,
-				       sizeof(struct viu_buf), fh);
+				       sizeof(struct viu_buf), fh, NULL);
 	return 0;
 }
 
@@ -1485,7 +1486,7 @@
 
 	ad = i2c_get_adapter(0);
 	viu_dev->decoder = v4l2_i2c_new_subdev(&viu_dev->v4l2_dev, ad,
-			"saa7115", "saa7113", VIU_VIDEO_DECODER_ADDR, NULL);
+			NULL, "saa7113", VIU_VIDEO_DECODER_ADDR, NULL);
 
 	viu_dev->vidq.timeout.function = viu_vid_timeout;
 	viu_dev->vidq.timeout.data     = (unsigned long)viu_dev;
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
index 23db0c2..dda56ff 100644
--- a/drivers/media/video/gspca/Kconfig
+++ b/drivers/media/video/gspca/Kconfig
@@ -77,6 +77,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called gspca_jeilinj.
 
+config USB_GSPCA_KONICA
+	tristate "Konica USB Camera V4L2 driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for cameras based on the Konica chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_konica.
+
 config USB_GSPCA_MARS
 	tristate "Mars USB Camera Driver"
 	depends on VIDEO_V4L2 && USB_GSPCA
@@ -337,6 +346,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called gspca_vc032x.
 
+config USB_GSPCA_XIRLINK_CIT
+	tristate "Xirlink C-It USB Camera Driver"
+	depends on VIDEO_V4L2 && USB_GSPCA
+	help
+	  Say Y here if you want support for Xirlink C-It bases cameras.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gspca_xirlink_cit.
+
 config USB_GSPCA_ZC3XX
 	tristate "ZC3XX USB Camera Driver"
 	depends on VIDEO_V4L2 && USB_GSPCA
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
index f6616db..24e695b 100644
--- a/drivers/media/video/gspca/Makefile
+++ b/drivers/media/video/gspca/Makefile
@@ -5,6 +5,7 @@
 obj-$(CONFIG_USB_GSPCA_ETOMS)    += gspca_etoms.o
 obj-$(CONFIG_USB_GSPCA_FINEPIX)  += gspca_finepix.o
 obj-$(CONFIG_USB_GSPCA_JEILINJ)  += gspca_jeilinj.o
+obj-$(CONFIG_USB_GSPCA_KONICA)   += gspca_konica.o
 obj-$(CONFIG_USB_GSPCA_MARS)     += gspca_mars.o
 obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o
 obj-$(CONFIG_USB_GSPCA_OV519)    += gspca_ov519.o
@@ -33,6 +34,7 @@
 obj-$(CONFIG_USB_GSPCA_T613)     += gspca_t613.o
 obj-$(CONFIG_USB_GSPCA_TV8532)   += gspca_tv8532.o
 obj-$(CONFIG_USB_GSPCA_VC032X)   += gspca_vc032x.o
+obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o
 obj-$(CONFIG_USB_GSPCA_ZC3XX)    += gspca_zc3xx.o
 
 gspca_main-objs     := gspca.o
@@ -42,6 +44,7 @@
 gspca_etoms-objs    := etoms.o
 gspca_finepix-objs  := finepix.o
 gspca_jeilinj-objs  := jeilinj.o
+gspca_konica-objs   := konica.o
 gspca_mars-objs     := mars.o
 gspca_mr97310a-objs := mr97310a.o
 gspca_ov519-objs    := ov519.o
@@ -70,6 +73,7 @@
 gspca_t613-objs     := t613.o
 gspca_tv8532-objs   := tv8532.o
 gspca_vc032x-objs   := vc032x.o
+gspca_xirlink_cit-objs := xirlink_cit.o
 gspca_zc3xx-objs    := zc3xx.o
 
 obj-$(CONFIG_USB_M5602)   += m5602/
diff --git a/drivers/media/video/gspca/benq.c b/drivers/media/video/gspca/benq.c
index fce8d94..6290439 100644
--- a/drivers/media/video/gspca/benq.c
+++ b/drivers/media/video/gspca/benq.c
@@ -62,7 +62,7 @@
 			0,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_w err %d", ret);
+		err("reg_w err %d", ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -152,7 +152,8 @@
 	reg_w(gspca_dev, 0x003c, 0x0005);
 	reg_w(gspca_dev, 0x003c, 0x0006);
 	reg_w(gspca_dev, 0x003c, 0x0007);
-	usb_set_interface(gspca_dev->dev, gspca_dev->iface, gspca_dev->nbalt - 1);
+	usb_set_interface(gspca_dev->dev, gspca_dev->iface,
+					gspca_dev->nbalt - 1);
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
@@ -180,7 +181,7 @@
 		if (gspca_dev->frozen)
 			return;
 #endif
-		PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+		err("urb status: %d", urb->status);
 		return;
 	}
 
@@ -208,8 +209,7 @@
 		if (st == 0)
 			st = urb->iso_frame_desc[i].status;
 		if (st) {
-			PDEBUG(D_ERR,
-				"ISOC data error: [%d] status=%d",
+			err("ISOC data error: [%d] status=%d",
 				i, st);
 			gspca_dev->last_packet_type = DISCARD_PACKET;
 			continue;
@@ -256,10 +256,10 @@
 	/* resubmit the URBs */
 	st = usb_submit_urb(urb0, GFP_ATOMIC);
 	if (st < 0)
-		PDEBUG(D_ERR|D_PACK, "usb_submit_urb(0) ret %d", st);
+		err("usb_submit_urb(0) ret %d", st);
 	st = usb_submit_urb(urb, GFP_ATOMIC);
 	if (st < 0)
-		PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st);
+		err("usb_submit_urb() ret %d", st);
 }
 
 /* sub-driver description */
@@ -304,18 +304,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	info("registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	info("deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
index d6a7577..1eacb6c 100644
--- a/drivers/media/video/gspca/conex.c
+++ b/drivers/media/video/gspca/conex.c
@@ -687,7 +687,7 @@
 	reg_w_val(gspca_dev, 0x00c0, 0x00);
 	reg_r(gspca_dev, 0x0001, 1);
 	length = 8;
-	switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+	switch (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv) {
 	case 0:
 		for (i = 0; i < 27; i++) {
 			if (i == 26)
@@ -901,7 +901,7 @@
 	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
-static void setbrightness(struct gspca_dev*gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 	__u8 regE5cbx[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 };
@@ -924,7 +924,7 @@
 	reg_w_val(gspca_dev, 0x0070, reg70);
 }
 
-static void setcontrast(struct gspca_dev*gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 	__u8 regE5acx[] = { 0x88, 0x0a, 0x0c, 0x01 };	/* seem MSB */
@@ -1068,17 +1068,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c
index 3747a1d..9b12168 100644
--- a/drivers/media/video/gspca/cpia1.c
+++ b/drivers/media/video/gspca/cpia1.c
@@ -1,7 +1,7 @@
 /*
  * cpia CPiA (1) gspca driver
  *
- * Copyright (C) 2010 Hans de Goede <hdgoede@redhat.com>
+ * Copyright (C) 2010 Hans de Goede <hdegoede@redhat.com>
  *
  * This module is adapted from the in kernel v4l1 cpia driver which is :
  *
@@ -30,7 +30,7 @@
 
 #include "gspca.h"
 
-MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
 MODULE_DESCRIPTION("Vision CPiA");
 MODULE_LICENSE("GPL");
 
@@ -373,9 +373,14 @@
 static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
 static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getilluminator1(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getilluminator2(struct gspca_dev *gspca_dev, __s32 *val);
 
 static const struct ctrl sd_ctrls[] = {
 	{
+#define BRIGHTNESS_IDX 0
 	    {
 		.id      = V4L2_CID_BRIGHTNESS,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -390,6 +395,7 @@
 	    .set = sd_setbrightness,
 	    .get = sd_getbrightness,
 	},
+#define CONTRAST_IDX 1
 	{
 	    {
 		.id      = V4L2_CID_CONTRAST,
@@ -404,6 +410,7 @@
 	    .set = sd_setcontrast,
 	    .get = sd_getcontrast,
 	},
+#define SATURATION_IDX 2
 	{
 	    {
 		.id      = V4L2_CID_SATURATION,
@@ -418,6 +425,7 @@
 	    .set = sd_setsaturation,
 	    .get = sd_getsaturation,
 	},
+#define POWER_LINE_FREQUENCY_IDX 3
 	{
 		{
 			.id	 = V4L2_CID_POWER_LINE_FREQUENCY,
@@ -432,6 +440,37 @@
 		.set = sd_setfreq,
 		.get = sd_getfreq,
 	},
+#define ILLUMINATORS_1_IDX 4
+	{
+		{
+			.id	 = V4L2_CID_ILLUMINATORS_1,
+			.type    = V4L2_CTRL_TYPE_BOOLEAN,
+			.name    = "Illuminator 1",
+			.minimum = 0,
+			.maximum = 1,
+			.step    = 1,
+#define ILLUMINATORS_1_DEF 0
+			.default_value = ILLUMINATORS_1_DEF,
+		},
+		.set = sd_setilluminator1,
+		.get = sd_getilluminator1,
+	},
+#define ILLUMINATORS_2_IDX 5
+	{
+		{
+			.id	 = V4L2_CID_ILLUMINATORS_2,
+			.type    = V4L2_CTRL_TYPE_BOOLEAN,
+			.name    = "Illuminator 2",
+			.minimum = 0,
+			.maximum = 1,
+			.step    = 1,
+#define ILLUMINATORS_2_DEF 0
+			.default_value = ILLUMINATORS_2_DEF,
+		},
+		.set = sd_setilluminator2,
+		.get = sd_getilluminator2,
+	},
+#define COMP_TARGET_IDX 6
 	{
 		{
 #define V4L2_CID_COMP_TARGET V4L2_CID_PRIVATE_BASE
@@ -510,7 +549,7 @@
 			      gspca_dev->usb_buf, databytes, 1000);
 
 	if (ret < 0)
-		PDEBUG(D_ERR, "usb_control_msg %02x, error %d", command[1],
+		err("usb_control_msg %02x, error %d", command[1],
 		       ret);
 
 	if (ret == -EPIPE && retries > 0) {
@@ -1059,7 +1098,6 @@
 			  0, sd->params.streamStartLine, 0, 0);
 }
 
-#if 0 /* Currently unused */
 static int command_setlights(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
@@ -1079,7 +1117,6 @@
 	return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0,
 			  p1 | p2 | 0xE0, 0);
 }
-#endif
 
 static int set_flicker(struct gspca_dev *gspca_dev, int on, int apply)
 {
@@ -1236,7 +1273,7 @@
 	cmd[7] = 0;
 	ret = cpia_usb_transferCmd(gspca_dev, cmd);
 	if (ret) {
-		PDEBUG(D_ERR, "ReadVPRegs(30,4,9,8) - failed: %d", ret);
+		err("ReadVPRegs(30,4,9,8) - failed: %d", ret);
 		return;
 	}
 	exp_acc = gspca_dev->usb_buf[0];
@@ -1716,7 +1753,9 @@
 /* this function is called at probe and resume time */
 static int sd_init(struct gspca_dev *gspca_dev)
 {
+#ifdef GSPCA_DEBUG
 	struct sd *sd = (struct sd *) gspca_dev;
+#endif
 	int ret;
 
 	/* Start / Stop the camera to make sure we are talking to
@@ -1726,6 +1765,14 @@
 	if (ret)
 		return ret;
 
+	/* Ensure the QX3 illuminators' states are restored upon resume,
+	   or disable the illuminator controls, if this isn't a QX3 */
+	if (sd->params.qx3.qx3_detected)
+		command_setlights(gspca_dev);
+	else
+		gspca_dev->ctrl_dis |=
+			((1 << ILLUMINATORS_1_IDX) | (1 << ILLUMINATORS_2_IDX));
+
 	sd_stopN(gspca_dev);
 
 	PDEBUG(D_PROBE, "CPIA Version:             %d.%02d (%d.%d)",
@@ -1929,6 +1976,72 @@
 	return 0;
 }
 
+static int sd_setilluminator(struct gspca_dev *gspca_dev, __s32 val, int n)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int ret;
+
+	if (!sd->params.qx3.qx3_detected)
+		return -EINVAL;
+
+	switch (n) {
+	case 1:
+		sd->params.qx3.bottomlight = val ? 1 : 0;
+		break;
+	case 2:
+		sd->params.qx3.toplight = val ? 1 : 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = command_setlights(gspca_dev);
+	if (ret && ret != -EINVAL)
+		ret = -EBUSY;
+
+	return ret;
+}
+
+static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val)
+{
+	return sd_setilluminator(gspca_dev, val, 1);
+}
+
+static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val)
+{
+	return sd_setilluminator(gspca_dev, val, 2);
+}
+
+static int sd_getilluminator(struct gspca_dev *gspca_dev, __s32 *val, int n)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (!sd->params.qx3.qx3_detected)
+		return -EINVAL;
+
+	switch (n) {
+	case 1:
+		*val = sd->params.qx3.bottomlight;
+		break;
+	case 2:
+		*val = sd->params.qx3.toplight;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int sd_getilluminator1(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	return sd_getilluminator(gspca_dev, val, 1);
+}
+
+static int sd_getilluminator2(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	return sd_getilluminator(gspca_dev, val, 2);
+}
+
 static int sd_querymenu(struct gspca_dev *gspca_dev,
 			struct v4l2_querymenu *menu)
 {
@@ -2004,17 +2117,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c
index ecd4d74..a594b36 100644
--- a/drivers/media/video/gspca/etoms.c
+++ b/drivers/media/video/gspca/etoms.c
@@ -710,9 +710,9 @@
 }
 
 #define BLIMIT(bright) \
-	(__u8)((bright > 0x1f)?0x1f:((bright < 4)?3:bright))
+	(u8)((bright > 0x1f) ? 0x1f : ((bright < 4) ? 3 : bright))
 #define LIMIT(color) \
-	(unsigned char)((color > 0xff)?0xff:((color < 0)?0:color))
+	(u8)((color > 0xff) ? 0xff : ((color < 0) ? 0 : color))
 
 static void do_autogain(struct gspca_dev *gspca_dev)
 {
@@ -896,18 +896,12 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c
index 5d90e74..d782264 100644
--- a/drivers/media/video/gspca/finepix.c
+++ b/drivers/media/video/gspca/finepix.c
@@ -182,7 +182,7 @@
 	/* Init the device */
 	ret = command(gspca_dev, 0);
 	if (ret < 0) {
-		PDEBUG(D_STREAM, "init failed %d", ret);
+		err("init failed %d", ret);
 		return ret;
 	}
 
@@ -194,14 +194,14 @@
 			FPIX_MAX_TRANSFER, &len,
 			FPIX_TIMEOUT);
 	if (ret < 0) {
-		PDEBUG(D_STREAM, "usb_bulk_msg failed %d", ret);
+		err("usb_bulk_msg failed %d", ret);
 		return ret;
 	}
 
 	/* Request a frame, but don't read it */
 	ret = command(gspca_dev, 1);
 	if (ret < 0) {
-		PDEBUG(D_STREAM, "frame request failed %d", ret);
+		err("frame request failed %d", ret);
 		return ret;
 	}
 
@@ -291,19 +291,12 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/gl860/gl860-mi2020.c b/drivers/media/video/gspca/gl860/gl860-mi2020.c
index 57782e0..2edda6b 100644
--- a/drivers/media/video/gspca/gl860/gl860-mi2020.c
+++ b/drivers/media/video/gspca/gl860/gl860-mi2020.c
@@ -69,15 +69,15 @@
 static u8 dat_multi6[] = { 0x90, 0x00, 0x05 };
 
 static struct validx tbl_init_at_startup[] = {
-	{0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001,0x00c1},
+	{0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1},
 	{0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d},
 	{53, 0xffff},
 	{0x0040, 0x0000}, {0x0063, 0x0006},
 };
 
 static struct validx tbl_common_0B[] = {
-	{0x0002, 0x0004}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a,0x000d},
-	{0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042,0x00c2},
+	{0x0002, 0x0004}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d},
+	{0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2},
 	{0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000},
 };
 
diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c
index e86eb8b..b05bec7 100644
--- a/drivers/media/video/gspca/gl860/gl860.c
+++ b/drivers/media/video/gspca/gl860/gl860.c
@@ -540,15 +540,12 @@
 
 	if (usb_register(&sd_driver) < 0)
 		return -1;
-	PDEBUG(D_PROBE, "driver registered");
-
 	return 0;
 }
 
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "driver deregistered");
 }
 
 module_init(sd_mod_init);
@@ -588,8 +585,7 @@
 	}
 
 	if (r < 0)
-		PDEBUG(D_ERR,
-			"ctrl transfer failed %4d "
+		err("ctrl transfer failed %4d "
 			"[p%02x r%d v%04x i%04x len%d]",
 			r, pref, req, val, index, len);
 	else if (len > 1 && r < len)
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index 78abc1c..8fe8fb4 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -148,7 +148,7 @@
 	if (ret == 0) {
 		ret = usb_submit_urb(urb, GFP_ATOMIC);
 		if (ret < 0)
-			PDEBUG(D_ERR, "Resubmit URB failed with error %i", ret);
+			err("Resubmit URB failed with error %i", ret);
 	}
 }
 
@@ -177,8 +177,8 @@
 
 		err = input_register_device(input_dev);
 		if (err) {
-			PDEBUG(D_ERR, "Input device registration failed "
-				"with error %i", err);
+			err("Input device registration failed with error %i",
+				err);
 			input_dev->dev.parent = NULL;
 			input_free_device(input_dev);
 		} else {
@@ -328,8 +328,7 @@
 		}
 		st = urb->iso_frame_desc[i].status;
 		if (st) {
-			PDEBUG(D_ERR,
-				"ISOC data error: [%d] len=%d, status=%d",
+			err("ISOC data error: [%d] len=%d, status=%d",
 				i, len, st);
 			gspca_dev->last_packet_type = DISCARD_PACKET;
 			continue;
@@ -347,7 +346,7 @@
 	/* resubmit the URB */
 	st = usb_submit_urb(urb, GFP_ATOMIC);
 	if (st < 0)
-		PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st);
+		err("usb_submit_urb() ret %d", st);
 }
 
 /*
@@ -401,7 +400,7 @@
 	if (gspca_dev->cam.bulk_nurbs != 0) {
 		st = usb_submit_urb(urb, GFP_ATOMIC);
 		if (st < 0)
-			PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st);
+			err("usb_submit_urb() ret %d", st);
 	}
 }
 
@@ -433,12 +432,13 @@
 		/* if there are no queued buffer, discard the whole frame */
 		if (i == atomic_read(&gspca_dev->fr_q)) {
 			gspca_dev->last_packet_type = DISCARD_PACKET;
+			gspca_dev->sequence++;
 			return;
 		}
 		j = gspca_dev->fr_queue[i];
 		frame = &gspca_dev->frame[j];
 		frame->v4l2_buf.timestamp = ktime_to_timeval(ktime_get());
-		frame->v4l2_buf.sequence = ++gspca_dev->sequence;
+		frame->v4l2_buf.sequence = gspca_dev->sequence++;
 		gspca_dev->image = frame->data;
 		gspca_dev->image_len = 0;
 	} else {
@@ -590,7 +590,7 @@
 		return 0;
 	ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0);
 	if (ret < 0)
-		PDEBUG(D_ERR|D_STREAM, "set alt 0 err %d", ret);
+		err("set alt 0 err %d", ret);
 	return ret;
 }
 
@@ -652,7 +652,7 @@
 				   : USB_ENDPOINT_XFER_ISOC;
 	i = gspca_dev->alt;			/* previous alt setting */
 	if (gspca_dev->cam.reverse_alts) {
-		if (gspca_dev->audio)
+		if (gspca_dev->audio && i < gspca_dev->nbalt - 2)
 			i++;
 		while (++i < gspca_dev->nbalt) {
 			ep = alt_xfer(&intf->altsetting[i], xfer);
@@ -660,7 +660,7 @@
 				break;
 		}
 	} else {
-		if (gspca_dev->audio)
+		if (gspca_dev->audio && i > 1)
 			i--;
 		while (--i >= 0) {
 			ep = alt_xfer(&intf->altsetting[i], xfer);
@@ -850,8 +850,7 @@
 			break;
 		gspca_stream_off(gspca_dev);
 		if (ret != -ENOSPC) {
-			PDEBUG(D_ERR|D_STREAM,
-				"usb_submit_urb alt %d err %d",
+			err("usb_submit_urb alt %d err %d",
 				gspca_dev->alt, ret);
 			goto out;
 		}
@@ -880,6 +879,7 @@
 
 static void gspca_set_default_mode(struct gspca_dev *gspca_dev)
 {
+	struct gspca_ctrl *ctrl;
 	int i;
 
 	i = gspca_dev->cam.nmodes - 1;	/* take the highest mode */
@@ -887,6 +887,16 @@
 	gspca_dev->width = gspca_dev->cam.cam_mode[i].width;
 	gspca_dev->height = gspca_dev->cam.cam_mode[i].height;
 	gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i].pixelformat;
+
+	/* set the current control values to their default values
+	 * which may have changed in sd_init() */
+	ctrl = gspca_dev->cam.ctrls;
+	if (ctrl != NULL) {
+		for (i = 0;
+		     i < gspca_dev->sd_desc->nctrls;
+		     i++, ctrl++)
+			ctrl->val = ctrl->def;
+	}
 }
 
 static int wxh_to_mode(struct gspca_dev *gspca_dev,
@@ -1310,7 +1320,7 @@
 	return ret;
 }
 
-static const struct ctrl *get_ctrl(struct gspca_dev *gspca_dev,
+static int get_ctrl(struct gspca_dev *gspca_dev,
 				   int id)
 {
 	const struct ctrl *ctrls;
@@ -1322,9 +1332,9 @@
 		if (gspca_dev->ctrl_dis & (1 << i))
 			continue;
 		if (id == ctrls->qctrl.id)
-			return ctrls;
+			return i;
 	}
-	return NULL;
+	return -1;
 }
 
 static int vidioc_queryctrl(struct file *file, void *priv,
@@ -1332,34 +1342,40 @@
 {
 	struct gspca_dev *gspca_dev = priv;
 	const struct ctrl *ctrls;
-	int i;
+	struct gspca_ctrl *gspca_ctrl;
+	int i, idx;
 	u32 id;
 
-	ctrls = NULL;
 	id = q_ctrl->id;
 	if (id & V4L2_CTRL_FLAG_NEXT_CTRL) {
 		id &= V4L2_CTRL_ID_MASK;
 		id++;
+		idx = -1;
 		for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
 			if (gspca_dev->ctrl_dis & (1 << i))
 				continue;
 			if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id)
 				continue;
-			if (ctrls && gspca_dev->sd_desc->ctrls[i].qctrl.id
-					    > ctrls->qctrl.id)
+			if (idx >= 0
+			 && gspca_dev->sd_desc->ctrls[i].qctrl.id
+				    > gspca_dev->sd_desc->ctrls[idx].qctrl.id)
 				continue;
-			ctrls = &gspca_dev->sd_desc->ctrls[i];
+			idx = i;
 		}
-		if (ctrls == NULL)
-			return -EINVAL;
 	} else {
-		ctrls = get_ctrl(gspca_dev, id);
-		if (ctrls == NULL)
-			return -EINVAL;
-		i = ctrls - gspca_dev->sd_desc->ctrls;
+		idx = get_ctrl(gspca_dev, id);
 	}
-	memcpy(q_ctrl, ctrls, sizeof *q_ctrl);
-	if (gspca_dev->ctrl_inac & (1 << i))
+	if (idx < 0)
+		return -EINVAL;
+	ctrls = &gspca_dev->sd_desc->ctrls[idx];
+	memcpy(q_ctrl, &ctrls->qctrl, sizeof *q_ctrl);
+	if (gspca_dev->cam.ctrls != NULL) {
+		gspca_ctrl = &gspca_dev->cam.ctrls[idx];
+		q_ctrl->default_value = gspca_ctrl->def;
+		q_ctrl->minimum = gspca_ctrl->min;
+		q_ctrl->maximum = gspca_ctrl->max;
+	}
+	if (gspca_dev->ctrl_inac & (1 << idx))
 		q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
 	return 0;
 }
@@ -1369,23 +1385,46 @@
 {
 	struct gspca_dev *gspca_dev = priv;
 	const struct ctrl *ctrls;
-	int ret;
+	struct gspca_ctrl *gspca_ctrl;
+	int idx, ret;
 
-	ctrls = get_ctrl(gspca_dev, ctrl->id);
-	if (ctrls == NULL)
+	idx = get_ctrl(gspca_dev, ctrl->id);
+	if (idx < 0)
 		return -EINVAL;
-
-	if (ctrl->value < ctrls->qctrl.minimum
-	    || ctrl->value > ctrls->qctrl.maximum)
-		return -ERANGE;
+	if (gspca_dev->ctrl_inac & (1 << idx))
+		return -EINVAL;
+	ctrls = &gspca_dev->sd_desc->ctrls[idx];
+	if (gspca_dev->cam.ctrls != NULL) {
+		gspca_ctrl = &gspca_dev->cam.ctrls[idx];
+		if (ctrl->value < gspca_ctrl->min
+		    || ctrl->value > gspca_ctrl->max)
+			return -ERANGE;
+	} else {
+		gspca_ctrl = NULL;
+		if (ctrl->value < ctrls->qctrl.minimum
+		    || ctrl->value > ctrls->qctrl.maximum)
+			return -ERANGE;
+	}
 	PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value);
 	if (mutex_lock_interruptible(&gspca_dev->usb_lock))
 		return -ERESTARTSYS;
-	gspca_dev->usb_err = 0;
-	if (gspca_dev->present)
-		ret = ctrls->set(gspca_dev, ctrl->value);
-	else
+	if (!gspca_dev->present) {
 		ret = -ENODEV;
+		goto out;
+	}
+	gspca_dev->usb_err = 0;
+	if (ctrls->set != NULL) {
+		ret = ctrls->set(gspca_dev, ctrl->value);
+		goto out;
+	}
+	if (gspca_ctrl != NULL) {
+		gspca_ctrl->val = ctrl->value;
+		if (ctrls->set_control != NULL
+		 && gspca_dev->streaming)
+			ctrls->set_control(gspca_dev);
+	}
+	ret = gspca_dev->usb_err;
+out:
 	mutex_unlock(&gspca_dev->usb_lock);
 	return ret;
 }
@@ -1395,19 +1434,28 @@
 {
 	struct gspca_dev *gspca_dev = priv;
 	const struct ctrl *ctrls;
-	int ret;
+	int idx, ret;
 
-	ctrls = get_ctrl(gspca_dev, ctrl->id);
-	if (ctrls == NULL)
+	idx = get_ctrl(gspca_dev, ctrl->id);
+	if (idx < 0)
 		return -EINVAL;
+	ctrls = &gspca_dev->sd_desc->ctrls[idx];
 
 	if (mutex_lock_interruptible(&gspca_dev->usb_lock))
 		return -ERESTARTSYS;
-	gspca_dev->usb_err = 0;
-	if (gspca_dev->present)
-		ret = ctrls->get(gspca_dev, &ctrl->value);
-	else
+	if (!gspca_dev->present) {
 		ret = -ENODEV;
+		goto out;
+	}
+	gspca_dev->usb_err = 0;
+	if (ctrls->get != NULL) {
+		ret = ctrls->get(gspca_dev, &ctrl->value);
+		goto out;
+	}
+	if (gspca_dev->cam.ctrls != NULL)
+		ctrl->value = gspca_dev->cam.ctrls[idx].val;
+	ret = 0;
+out:
 	mutex_unlock(&gspca_dev->usb_lock);
 	return ret;
 }
@@ -2127,6 +2175,22 @@
 	.release = gspca_release,
 };
 
+/* initialize the controls */
+static void ctrls_init(struct gspca_dev *gspca_dev)
+{
+	struct gspca_ctrl *ctrl;
+	int i;
+
+	for (i = 0, ctrl = gspca_dev->cam.ctrls;
+	     i < gspca_dev->sd_desc->nctrls;
+	     i++, ctrl++) {
+		ctrl->def = gspca_dev->sd_desc->ctrls[i].qctrl.default_value;
+		ctrl->val = ctrl->def;
+		ctrl->min = gspca_dev->sd_desc->ctrls[i].qctrl.minimum;
+		ctrl->max = gspca_dev->sd_desc->ctrls[i].qctrl.maximum;
+	}
+}
+
 /*
  * probe and create a new gspca device
  *
@@ -2188,6 +2252,8 @@
 	ret = sd_desc->config(gspca_dev, id);
 	if (ret < 0)
 		goto out;
+	if (gspca_dev->cam.ctrls != NULL)
+		ctrls_init(gspca_dev);
 	ret = sd_desc->init(gspca_dev);
 	if (ret < 0)
 		goto out;
@@ -2243,7 +2309,7 @@
 
 	/* we don't handle multi-config cameras */
 	if (dev->descriptor.bNumConfigurations != 1) {
-		PDEBUG(D_ERR, "%04x:%04x too many config",
+		err("%04x:%04x too many config",
 				id->idVendor, id->idProduct);
 		return -ENODEV;
 	}
@@ -2428,7 +2494,7 @@
 /* -- module insert / remove -- */
 static int __init gspca_init(void)
 {
-	info("main v%d.%d.%d registered",
+	info("v%d.%d.%d registered",
 		(DRIVER_VERSION_NUMBER >> 16) & 0xff,
 		(DRIVER_VERSION_NUMBER >> 8) & 0xff,
 		DRIVER_VERSION_NUMBER & 0xff);
@@ -2436,7 +2502,6 @@
 }
 static void __exit gspca_exit(void)
 {
-	info("main deregistered");
 }
 
 module_init(gspca_init);
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
index b749c36..d4d210b 100644
--- a/drivers/media/video/gspca/gspca.h
+++ b/drivers/media/video/gspca/gspca.h
@@ -52,11 +52,20 @@
 	int nrates;
 };
 
+/* control definition */
+struct gspca_ctrl {
+	s16 val;	/* current value */
+	s16 def;	/* default value */
+	s16 min, max;	/* minimum and maximum values */
+};
+
 /* device information - set at probe time */
 struct cam {
 	const struct v4l2_pix_format *cam_mode;	/* size nmodes */
 	const struct framerates *mode_framerates; /* must have size nmode,
 						   * just like cam_mode */
+	struct gspca_ctrl *ctrls;	/* control table - size nctrls */
+					/* may be NULL */
 	u32 bulk_size;		/* buffer size when image transfer by bulk */
 	u32 input_flags;	/* value for ENUM_INPUT status flags */
 	u8 nmodes;		/* size of cam_mode */
@@ -99,6 +108,7 @@
 	struct v4l2_queryctrl qctrl;
 	int (*set)(struct gspca_dev *, __s32);
 	int (*get)(struct gspca_dev *, __s32 *);
+	cam_v_op set_control;
 };
 
 /* subdriver description */
@@ -106,7 +116,7 @@
 /* information */
 	const char *name;	/* sub-driver name */
 /* controls */
-	const struct ctrl *ctrls;
+	const struct ctrl *ctrls;	/* static control definition */
 	int nctrls;
 /* mandatory operations */
 	cam_cf_op config;	/* called on probe */
diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c
index 12d9cf4..a35e87b 100644
--- a/drivers/media/video/gspca/jeilinj.c
+++ b/drivers/media/video/gspca/jeilinj.c
@@ -82,7 +82,7 @@
 			usb_sndbulkpipe(gspca_dev->dev, 3),
 			gspca_dev->usb_buf, 2, NULL, 500);
 	if (retval < 0)
-		PDEBUG(D_ERR, "command write [%02x] error %d",
+		err("command write [%02x] error %d",
 				gspca_dev->usb_buf[0], retval);
 	return retval;
 }
@@ -97,7 +97,7 @@
 				gspca_dev->usb_buf, 1, NULL, 500);
 	response = gspca_dev->usb_buf[0];
 	if (retval < 0)
-		PDEBUG(D_ERR, "read command [%02x] error %d",
+		err("read command [%02x] error %d",
 				gspca_dev->usb_buf[0], retval);
 	return retval;
 }
@@ -191,7 +191,7 @@
 
 	buffer = kmalloc(JEILINJ_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
 	if (!buffer) {
-		PDEBUG(D_ERR, "Couldn't allocate USB buffer");
+		err("Couldn't allocate USB buffer");
 		goto quit_stream;
 	}
 	while (gspca_dev->present && gspca_dev->streaming) {
@@ -354,19 +354,12 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/konica.c b/drivers/media/video/gspca/konica.c
new file mode 100644
index 0000000..d2ce65d
--- /dev/null
+++ b/drivers/media/video/gspca/konica.c
@@ -0,0 +1,646 @@
+/*
+ * Driver for USB webcams based on Konica chipset. This
+ * chipset is used in Intel YC76 camera.
+ *
+ * Copyright (C) 2010 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the usbvideo v4l1 konicawc driver which is:
+ *
+ * Copyright (C) 2002 Simon Evans <spse@secret.org.uk>
+ *
+ * The code for making gspca work with a webcam with 2 isoc endpoints was
+ * taken from the benq gspca subdriver which is:
+ *
+ * Copyright (C) 2009 Jean-Francois Moine (http://moinejf.free.fr)
+ *
+ * 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
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define MODULE_NAME "konica"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Konica chipset USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+#define WHITEBAL_REG   0x01
+#define BRIGHTNESS_REG 0x02
+#define SHARPNESS_REG  0x03
+#define CONTRAST_REG   0x04
+#define SATURATION_REG 0x05
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;	/* !! must be the first item */
+	struct urb *last_data_urb;
+	u8 snapshot_pressed;
+	u8 brightness;
+	u8 contrast;
+	u8 saturation;
+	u8 whitebal;
+	u8 sharpness;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setwhitebal(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getwhitebal(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+	{
+	    {
+		.id      = V4L2_CID_BRIGHTNESS,
+		.type    = V4L2_CTRL_TYPE_INTEGER,
+		.name    = "Brightness",
+		.minimum = 0,
+		.maximum = 9,
+		.step = 1,
+#define BRIGHTNESS_DEFAULT 4
+		.default_value = BRIGHTNESS_DEFAULT,
+		.flags = 0,
+	    },
+	    .set = sd_setbrightness,
+	    .get = sd_getbrightness,
+	},
+#define SD_CONTRAST 1
+	{
+	    {
+		.id = V4L2_CID_CONTRAST,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Contrast",
+		.minimum = 0,
+		.maximum = 9,
+		.step = 4,
+#define CONTRAST_DEFAULT 10
+		.default_value = CONTRAST_DEFAULT,
+		.flags = 0,
+	    },
+	    .set = sd_setcontrast,
+	    .get = sd_getcontrast,
+	},
+#define SD_SATURATION 2
+	{
+	    {
+		.id	= V4L2_CID_SATURATION,
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.name	= "Saturation",
+		.minimum = 0,
+		.maximum = 9,
+		.step	= 1,
+#define SATURATION_DEFAULT 4
+		.default_value = SATURATION_DEFAULT,
+		.flags = 0,
+	    },
+	    .set = sd_setsaturation,
+	    .get = sd_getsaturation,
+	},
+#define SD_WHITEBAL 3
+	{
+	    {
+		.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "White Balance",
+		.minimum = 0,
+		.maximum = 33,
+		.step = 1,
+#define WHITEBAL_DEFAULT 25
+		.default_value = WHITEBAL_DEFAULT,
+		.flags = 0,
+	    },
+	    .set = sd_setwhitebal,
+	    .get = sd_getwhitebal,
+	},
+#define SD_SHARPNESS 4
+	{
+	    {
+		.id = V4L2_CID_SHARPNESS,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Sharpness",
+		.minimum = 0,
+		.maximum = 9,
+		.step = 1,
+#define SHARPNESS_DEFAULT 4
+		.default_value = SHARPNESS_DEFAULT,
+		.flags = 0,
+	    },
+	    .set = sd_setsharpness,
+	    .get = sd_getsharpness,
+	},
+};
+
+/* .priv is what goes to register 8 for this mode, known working values:
+   0x00 -> 176x144, cropped
+   0x01 -> 176x144, cropped
+   0x02 -> 176x144, cropped
+   0x03 -> 176x144, cropped
+   0x04 -> 176x144, binned
+   0x05 -> 320x240
+   0x06 -> 320x240
+   0x07 -> 160x120, cropped
+   0x08 -> 160x120, cropped
+   0x09 -> 160x120, binned (note has 136 lines)
+   0x0a -> 160x120, binned (note has 136 lines)
+   0x0b -> 160x120, cropped
+*/
+static const struct v4l2_pix_format vga_mode[] = {
+	{160, 120, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 136 * 3 / 2 + 960,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0x0a},
+	{176, 144, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 3 / 2 + 960,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0x04},
+	{320, 240, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 2 + 960,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.priv = 0x05},
+};
+
+static void sd_isoc_irq(struct urb *urb);
+
+static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			0x02,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value,
+			index,
+			NULL,
+			0,
+			1000);
+	if (ret < 0) {
+		err("reg_w err %d", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void reg_r(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+	struct usb_device *dev = gspca_dev->dev;
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			0x03,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			value,
+			index,
+			gspca_dev->usb_buf,
+			2,
+			1000);
+	if (ret < 0) {
+		err("reg_w err %d", ret);
+		gspca_dev->usb_err = ret;
+	}
+}
+
+static void konica_stream_on(struct gspca_dev *gspca_dev)
+{
+	reg_w(gspca_dev, 1, 0x0b);
+}
+
+static void konica_stream_off(struct gspca_dev *gspca_dev)
+{
+	reg_w(gspca_dev, 0, 0x0b);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+			const struct usb_device_id *id)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	gspca_dev->cam.cam_mode = vga_mode;
+	gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+	gspca_dev->cam.no_urb_create = 1;
+	/* The highest alt setting has an isoc packetsize of 0, so we
+	   don't want to use it */
+	gspca_dev->nbalt--;
+
+	sd->brightness  = BRIGHTNESS_DEFAULT;
+	sd->contrast    = CONTRAST_DEFAULT;
+	sd->saturation  = SATURATION_DEFAULT;
+	sd->whitebal    = WHITEBAL_DEFAULT;
+	sd->sharpness   = SHARPNESS_DEFAULT;
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	/* HDG not sure if these 2 reads are needed */
+	reg_r(gspca_dev, 0, 0x10);
+	PDEBUG(D_PROBE, "Reg 0x10 reads: %02x %02x",
+	       gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+	reg_r(gspca_dev, 0, 0x10);
+	PDEBUG(D_PROBE, "Reg 0x10 reads: %02x %02x",
+	       gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+	reg_w(gspca_dev, 0, 0x0d);
+
+	return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct urb *urb;
+	int i, n, packet_size;
+	struct usb_host_interface *alt;
+	struct usb_interface *intf;
+
+	intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+	alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+	if (!alt) {
+		err("Couldn't get altsetting");
+		return -EIO;
+	}
+
+	packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+
+	reg_w(gspca_dev, sd->brightness, BRIGHTNESS_REG);
+	reg_w(gspca_dev, sd->whitebal, WHITEBAL_REG);
+	reg_w(gspca_dev, sd->contrast, CONTRAST_REG);
+	reg_w(gspca_dev, sd->saturation, SATURATION_REG);
+	reg_w(gspca_dev, sd->sharpness, SHARPNESS_REG);
+
+	n = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+	reg_w(gspca_dev, n, 0x08);
+
+	konica_stream_on(gspca_dev);
+
+	if (gspca_dev->usb_err)
+		return gspca_dev->usb_err;
+
+	/* create 4 URBs - 2 on endpoint 0x83 and 2 on 0x082 */
+#if MAX_NURBS < 4
+#error "Not enough URBs in the gspca table"
+#endif
+#define SD_NPKT 32
+	for (n = 0; n < 4; n++) {
+		i = n & 1 ? 0 : 1;
+		packet_size =
+			le16_to_cpu(alt->endpoint[i].desc.wMaxPacketSize);
+		urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL);
+		if (!urb) {
+			err("usb_alloc_urb failed");
+			return -ENOMEM;
+		}
+		gspca_dev->urb[n] = urb;
+		urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev,
+						packet_size * SD_NPKT,
+						GFP_KERNEL,
+						&urb->transfer_dma);
+		if (urb->transfer_buffer == NULL) {
+			err("usb_buffer_alloc failed");
+			return -ENOMEM;
+		}
+
+		urb->dev = gspca_dev->dev;
+		urb->context = gspca_dev;
+		urb->transfer_buffer_length = packet_size * SD_NPKT;
+		urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
+					n & 1 ? 0x81 : 0x82);
+		urb->transfer_flags = URB_ISO_ASAP
+					| URB_NO_TRANSFER_DMA_MAP;
+		urb->interval = 1;
+		urb->complete = sd_isoc_irq;
+		urb->number_of_packets = SD_NPKT;
+		for (i = 0; i < SD_NPKT; i++) {
+			urb->iso_frame_desc[i].length = packet_size;
+			urb->iso_frame_desc[i].offset = packet_size * i;
+		}
+	}
+
+	return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	konica_stream_off(gspca_dev);
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+	/* Don't keep the button in the pressed state "forever" if it was
+	   pressed when streaming is stopped */
+	if (sd->snapshot_pressed) {
+		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+		input_sync(gspca_dev->input_dev);
+		sd->snapshot_pressed = 0;
+	}
+#endif
+}
+
+/* reception of an URB */
+static void sd_isoc_irq(struct urb *urb)
+{
+	struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct urb *data_urb, *status_urb;
+	u8 *data;
+	int i, st;
+
+	PDEBUG(D_PACK, "sd isoc irq");
+	if (!gspca_dev->streaming)
+		return;
+
+	if (urb->status != 0) {
+		if (urb->status == -ESHUTDOWN)
+			return;		/* disconnection */
+#ifdef CONFIG_PM
+		if (gspca_dev->frozen)
+			return;
+#endif
+		PDEBUG(D_ERR, "urb status: %d", urb->status);
+		st = usb_submit_urb(urb, GFP_ATOMIC);
+		if (st < 0)
+			err("resubmit urb error %d", st);
+		return;
+	}
+
+	/* if this is a data URB (ep 0x82), wait */
+	if (urb->transfer_buffer_length > 32) {
+		sd->last_data_urb = urb;
+		return;
+	}
+
+	status_urb = urb;
+	data_urb   = sd->last_data_urb;
+	sd->last_data_urb = NULL;
+
+	if (!data_urb || data_urb->start_frame != status_urb->start_frame) {
+		PDEBUG(D_ERR|D_PACK, "lost sync on frames");
+		goto resubmit;
+	}
+
+	if (data_urb->number_of_packets != status_urb->number_of_packets) {
+		PDEBUG(D_ERR|D_PACK,
+		       "no packets does not match, data: %d, status: %d",
+		       data_urb->number_of_packets,
+		       status_urb->number_of_packets);
+		goto resubmit;
+	}
+
+	for (i = 0; i < status_urb->number_of_packets; i++) {
+		if (data_urb->iso_frame_desc[i].status ||
+		    status_urb->iso_frame_desc[i].status) {
+			PDEBUG(D_ERR|D_PACK,
+			       "pkt %d data-status %d, status-status %d", i,
+			       data_urb->iso_frame_desc[i].status,
+			       status_urb->iso_frame_desc[i].status);
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			continue;
+		}
+
+		if (status_urb->iso_frame_desc[i].actual_length != 1) {
+			PDEBUG(D_ERR|D_PACK,
+			       "bad status packet length %d",
+			       status_urb->iso_frame_desc[i].actual_length);
+			gspca_dev->last_packet_type = DISCARD_PACKET;
+			continue;
+		}
+
+		st = *((u8 *)status_urb->transfer_buffer
+				+ status_urb->iso_frame_desc[i].offset);
+
+		data = (u8 *)data_urb->transfer_buffer
+				+ data_urb->iso_frame_desc[i].offset;
+
+		/* st: 0x80-0xff: frame start with frame number (ie 0-7f)
+		 * otherwise:
+		 * bit 0 0: keep packet
+		 *	 1: drop packet (padding data)
+		 *
+		 * bit 4 0 button not clicked
+		 *       1 button clicked
+		 * button is used to `take a picture' (in software)
+		 */
+		if (st & 0x80) {
+			gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+			gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+		} else {
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+			u8 button_state = st & 0x40 ? 1 : 0;
+			if (sd->snapshot_pressed != button_state) {
+				input_report_key(gspca_dev->input_dev,
+						 KEY_CAMERA,
+						 button_state);
+				input_sync(gspca_dev->input_dev);
+				sd->snapshot_pressed = button_state;
+			}
+#endif
+			if (st & 0x01)
+				continue;
+		}
+		gspca_frame_add(gspca_dev, INTER_PACKET, data,
+				data_urb->iso_frame_desc[i].actual_length);
+	}
+
+resubmit:
+	if (data_urb) {
+		st = usb_submit_urb(data_urb, GFP_ATOMIC);
+		if (st < 0)
+			PDEBUG(D_ERR|D_PACK,
+			       "usb_submit_urb(data_urb) ret %d", st);
+	}
+	st = usb_submit_urb(status_urb, GFP_ATOMIC);
+	if (st < 0)
+		err("usb_submit_urb(status_urb) ret %d", st);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->brightness = val;
+	if (gspca_dev->streaming) {
+		konica_stream_off(gspca_dev);
+		reg_w(gspca_dev, sd->brightness, BRIGHTNESS_REG);
+		konica_stream_on(gspca_dev);
+	}
+
+	return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->brightness;
+
+	return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->contrast = val;
+	if (gspca_dev->streaming) {
+		konica_stream_off(gspca_dev);
+		reg_w(gspca_dev, sd->contrast, CONTRAST_REG);
+		konica_stream_on(gspca_dev);
+	}
+
+	return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->contrast;
+
+	return 0;
+}
+
+static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->saturation = val;
+	if (gspca_dev->streaming) {
+		konica_stream_off(gspca_dev);
+		reg_w(gspca_dev, sd->saturation, SATURATION_REG);
+		konica_stream_on(gspca_dev);
+	}
+	return 0;
+}
+
+static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->saturation;
+
+	return 0;
+}
+
+static int sd_setwhitebal(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->whitebal = val;
+	if (gspca_dev->streaming) {
+		konica_stream_off(gspca_dev);
+		reg_w(gspca_dev, sd->whitebal, WHITEBAL_REG);
+		konica_stream_on(gspca_dev);
+	}
+	return 0;
+}
+
+static int sd_getwhitebal(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->whitebal;
+
+	return 0;
+}
+
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->sharpness = val;
+	if (gspca_dev->streaming) {
+		konica_stream_off(gspca_dev);
+		reg_w(gspca_dev, sd->sharpness, SHARPNESS_REG);
+		konica_stream_on(gspca_dev);
+	}
+	return 0;
+}
+
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->sharpness;
+
+	return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.ctrls = sd_ctrls,
+	.nctrls = ARRAY_SIZE(sd_ctrls),
+	.config = sd_config,
+	.init = sd_init,
+	.start = sd_start,
+	.stopN = sd_stopN,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+	.other_input = 1,
+#endif
+};
+
+/* -- module initialisation -- */
+static const __devinitdata struct usb_device_id device_table[] = {
+	{USB_DEVICE(0x04c8, 0x0720)}, /* Intel YC 76 */
+	{}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+				THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+	.name = MODULE_NAME,
+	.id_table = device_table,
+	.probe = sd_probe,
+	.disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+	return usb_register(&sd_driver);
+}
+static void __exit sd_mod_exit(void)
+{
+	usb_deregister(&sd_driver);
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c
index b073d66..c872b93 100644
--- a/drivers/media/video/gspca/m5602/m5602_core.c
+++ b/drivers/media/video/gspca/m5602/m5602_core.c
@@ -406,18 +406,12 @@
 /* -- module insert / remove -- */
 static int __init mod_m5602_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 
 static void __exit mod_m5602_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(mod_m5602_init);
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
index c0722fa..0d605a5 100644
--- a/drivers/media/video/gspca/m5602/m5602_mt9m111.c
+++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
@@ -109,14 +109,14 @@
 #define GREEN_BALANCE_IDX 4
 	{
 		{
-			.id 		= M5602_V4L2_CID_GREEN_BALANCE,
-			.type 		= V4L2_CTRL_TYPE_INTEGER,
-			.name 		= "green balance",
-			.minimum 	= 0x00,
-			.maximum 	= 0x7ff,
-			.step 		= 0x1,
-			.default_value 	= MT9M111_GREEN_GAIN_DEFAULT,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.id		= M5602_V4L2_CID_GREEN_BALANCE,
+			.type		= V4L2_CTRL_TYPE_INTEGER,
+			.name		= "green balance",
+			.minimum	= 0x00,
+			.maximum	= 0x7ff,
+			.step		= 0x1,
+			.default_value	= MT9M111_GREEN_GAIN_DEFAULT,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = mt9m111_set_green_balance,
 		.get = mt9m111_get_green_balance
@@ -124,14 +124,14 @@
 #define BLUE_BALANCE_IDX 5
 	{
 		{
-			.id 		= V4L2_CID_BLUE_BALANCE,
-			.type 		= V4L2_CTRL_TYPE_INTEGER,
-			.name 		= "blue balance",
-			.minimum 	= 0x00,
-			.maximum 	= 0x7ff,
-			.step 		= 0x1,
-			.default_value 	= MT9M111_BLUE_GAIN_DEFAULT,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.id		= V4L2_CID_BLUE_BALANCE,
+			.type		= V4L2_CTRL_TYPE_INTEGER,
+			.name		= "blue balance",
+			.minimum	= 0x00,
+			.maximum	= 0x7ff,
+			.step		= 0x1,
+			.default_value	= MT9M111_BLUE_GAIN_DEFAULT,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = mt9m111_set_blue_balance,
 		.get = mt9m111_get_blue_balance
@@ -139,14 +139,14 @@
 #define RED_BALANCE_IDX 5
 	{
 		{
-			.id 		= V4L2_CID_RED_BALANCE,
-			.type 		= V4L2_CTRL_TYPE_INTEGER,
-			.name 		= "red balance",
-			.minimum 	= 0x00,
-			.maximum 	= 0x7ff,
-			.step 		= 0x1,
-			.default_value 	= MT9M111_RED_GAIN_DEFAULT,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.id		= V4L2_CID_RED_BALANCE,
+			.type		= V4L2_CTRL_TYPE_INTEGER,
+			.name		= "red balance",
+			.minimum	= 0x00,
+			.maximum	= 0x7ff,
+			.step		= 0x1,
+			.default_value	= MT9M111_RED_GAIN_DEFAULT,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = mt9m111_set_red_balance,
 		.get = mt9m111_get_red_balance
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.h b/drivers/media/video/gspca/m5602/m5602_mt9m111.h
index b3de778..b1f0c49 100644
--- a/drivers/media/video/gspca/m5602/m5602_mt9m111.h
+++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.h
@@ -70,7 +70,7 @@
 #define MT9M111_COLORPIPE			0x01
 #define MT9M111_CAMERA_CONTROL			0x02
 
-#define MT9M111_RESET 				(1 << 0)
+#define MT9M111_RESET				(1 << 0)
 #define MT9M111_RESTART				(1 << 1)
 #define MT9M111_ANALOG_STANDBY			(1 << 2)
 #define MT9M111_CHIP_ENABLE			(1 << 3)
@@ -97,7 +97,7 @@
 #define MT9M111_2D_DEFECT_CORRECTION_ENABLE	(1 << 0)
 
 #define INITIAL_MAX_GAIN			64
-#define MT9M111_DEFAULT_GAIN 			283
+#define MT9M111_DEFAULT_GAIN			283
 #define MT9M111_GREEN_GAIN_DEFAULT		0x20
 #define MT9M111_BLUE_GAIN_DEFAULT		0x20
 #define MT9M111_RED_GAIN_DEFAULT		0x20
@@ -125,8 +125,7 @@
 	.start = mt9m111_start,
 };
 
-static const unsigned char preinit_mt9m111[][4] =
-{
+static const unsigned char preinit_mt9m111[][4] = {
 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
@@ -165,8 +164,7 @@
 	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}
 };
 
-static const unsigned char init_mt9m111[][4] =
-{
+static const unsigned char init_mt9m111[][4] = {
 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
@@ -257,8 +255,7 @@
 	{SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90},
 };
 
-static const unsigned char start_mt9m111[][4] =
-{
+static const unsigned char start_mt9m111[][4] = {
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
@@ -271,5 +268,4 @@
 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
 };
-
 #endif
diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.c b/drivers/media/video/gspca/m5602/m5602_ov7660.c
index 62c1cbf..b12f604 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov7660.c
+++ b/drivers/media/video/gspca/m5602/m5602_ov7660.c
@@ -54,13 +54,13 @@
 #define AUTO_WHITE_BALANCE_IDX 4
 	{
 		{
-			.id 		= V4L2_CID_AUTO_WHITE_BALANCE,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "auto white balance",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 1
+			.id		= V4L2_CID_AUTO_WHITE_BALANCE,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "auto white balance",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 1
 		},
 		.set = ov7660_set_auto_white_balance,
 		.get = ov7660_get_auto_white_balance
@@ -68,13 +68,13 @@
 #define AUTO_GAIN_CTRL_IDX 5
 	{
 		{
-			.id 		= V4L2_CID_AUTOGAIN,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "auto gain control",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 1
+			.id		= V4L2_CID_AUTOGAIN,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "auto gain control",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 1
 		},
 		.set = ov7660_set_auto_gain,
 		.get = ov7660_get_auto_gain
@@ -82,13 +82,13 @@
 #define AUTO_EXPOSURE_IDX 6
 	{
 		{
-			.id 		= V4L2_CID_EXPOSURE_AUTO,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "auto exposure",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 1
+			.id		= V4L2_CID_EXPOSURE_AUTO,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "auto exposure",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 1
 		},
 		.set = ov7660_set_auto_exposure,
 		.get = ov7660_get_auto_exposure
@@ -96,13 +96,13 @@
 #define HFLIP_IDX 7
 	{
 		{
-			.id 		= V4L2_CID_HFLIP,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "horizontal flip",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 0
+			.id		= V4L2_CID_HFLIP,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "horizontal flip",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 0
 		},
 		.set = ov7660_set_hflip,
 		.get = ov7660_get_hflip
@@ -110,13 +110,13 @@
 #define VFLIP_IDX 8
 	{
 		{
-			.id 		= V4L2_CID_VFLIP,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "vertical flip",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 0
+			.id		= V4L2_CID_VFLIP,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "vertical flip",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 0
 		},
 		.set = ov7660_set_vflip,
 		.get = ov7660_get_vflip
diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.h b/drivers/media/video/gspca/m5602/m5602_ov7660.h
index 4d9dcf2..2efd607 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov7660.h
+++ b/drivers/media/video/gspca/m5602/m5602_ov7660.h
@@ -80,7 +80,7 @@
 
 #define OV7660_DEFAULT_GAIN		0x0e
 #define OV7660_DEFAULT_RED_GAIN		0x80
-#define OV7660_DEFAULT_BLUE_GAIN 	0x80
+#define OV7660_DEFAULT_BLUE_GAIN	0x80
 #define OV7660_DEFAULT_SATURATION	0x00
 #define OV7660_DEFAULT_EXPOSURE		0x20
 
@@ -105,8 +105,7 @@
 	.disconnect = ov7660_disconnect,
 };
 
-static const unsigned char preinit_ov7660[][4] =
-{
+static const unsigned char preinit_ov7660[][4] = {
 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
@@ -140,8 +139,7 @@
 	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00}
 };
 
-static const unsigned char init_ov7660[][4] =
-{
+static const unsigned char init_ov7660[][4] = {
 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
@@ -259,5 +257,4 @@
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
 };
-
 #endif
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c
index 069ba00..8ded8b1 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov9650.c
+++ b/drivers/media/video/gspca/m5602/m5602_ov9650.c
@@ -121,8 +121,8 @@
 			.minimum	= 0x00,
 			.maximum	= 0x1ff,
 			.step		= 0x4,
-			.default_value 	= EXPOSURE_DEFAULT,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.default_value	= EXPOSURE_DEFAULT,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = ov9650_set_exposure,
 		.get = ov9650_get_exposure
@@ -146,13 +146,13 @@
 	{
 		{
 			.id		= V4L2_CID_RED_BALANCE,
-			.type 		= V4L2_CTRL_TYPE_INTEGER,
-			.name 		= "red balance",
-			.minimum 	= 0x00,
-			.maximum 	= 0xff,
-			.step 		= 0x1,
-			.default_value 	= RED_GAIN_DEFAULT,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.type		= V4L2_CTRL_TYPE_INTEGER,
+			.name		= "red balance",
+			.minimum	= 0x00,
+			.maximum	= 0xff,
+			.step		= 0x1,
+			.default_value	= RED_GAIN_DEFAULT,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = ov9650_set_red_balance,
 		.get = ov9650_get_red_balance
@@ -161,13 +161,13 @@
 	{
 		{
 			.id		= V4L2_CID_BLUE_BALANCE,
-			.type 		= V4L2_CTRL_TYPE_INTEGER,
-			.name 		= "blue balance",
-			.minimum 	= 0x00,
-			.maximum 	= 0xff,
-			.step 		= 0x1,
-			.default_value 	= BLUE_GAIN_DEFAULT,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.type		= V4L2_CTRL_TYPE_INTEGER,
+			.name		= "blue balance",
+			.minimum	= 0x00,
+			.maximum	= 0xff,
+			.step		= 0x1,
+			.default_value	= BLUE_GAIN_DEFAULT,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = ov9650_set_blue_balance,
 		.get = ov9650_get_blue_balance
@@ -175,13 +175,13 @@
 #define HFLIP_IDX 4
 	{
 		{
-			.id 		= V4L2_CID_HFLIP,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "horizontal flip",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 0
+			.id		= V4L2_CID_HFLIP,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "horizontal flip",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 0
 		},
 		.set = ov9650_set_hflip,
 		.get = ov9650_get_hflip
@@ -189,13 +189,13 @@
 #define VFLIP_IDX 5
 	{
 		{
-			.id 		= V4L2_CID_VFLIP,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "vertical flip",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 0
+			.id		= V4L2_CID_VFLIP,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "vertical flip",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 0
 		},
 		.set = ov9650_set_vflip,
 		.get = ov9650_get_vflip
@@ -203,13 +203,13 @@
 #define AUTO_WHITE_BALANCE_IDX 6
 	{
 		{
-			.id 		= V4L2_CID_AUTO_WHITE_BALANCE,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "auto white balance",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 1
+			.id		= V4L2_CID_AUTO_WHITE_BALANCE,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "auto white balance",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 1
 		},
 		.set = ov9650_set_auto_white_balance,
 		.get = ov9650_get_auto_white_balance
@@ -217,13 +217,13 @@
 #define AUTO_GAIN_CTRL_IDX 7
 	{
 		{
-			.id 		= V4L2_CID_AUTOGAIN,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "auto gain control",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 1
+			.id		= V4L2_CID_AUTOGAIN,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "auto gain control",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 1
 		},
 		.set = ov9650_set_auto_gain,
 		.get = ov9650_get_auto_gain
@@ -231,13 +231,13 @@
 #define AUTO_EXPOSURE_IDX 8
 	{
 		{
-			.id 		= V4L2_CID_EXPOSURE_AUTO,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "auto exposure",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 1
+			.id		= V4L2_CID_EXPOSURE_AUTO,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "auto exposure",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 1
 		},
 		.set = ov9650_set_auto_exposure,
 		.get = ov9650_get_auto_exposure
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.h b/drivers/media/video/gspca/m5602/m5602_ov9650.h
index c98c40d..da9a129 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov9650.h
+++ b/drivers/media/video/gspca/m5602/m5602_ov9650.h
@@ -110,7 +110,7 @@
 
 #define OV9650_VARIOPIXEL		(1 << 2)
 #define OV9650_SYSTEM_CLK_SEL		(1 << 7)
-#define OV9650_SLAM_MODE 		(1 << 4)
+#define OV9650_SLAM_MODE		(1 << 4)
 
 #define OV9650_QVGA_VARIOPIXEL		(1 << 7)
 
@@ -154,8 +154,7 @@
 	.disconnect = ov9650_disconnect,
 };
 
-static const unsigned char preinit_ov9650[][3] =
-{
+static const unsigned char preinit_ov9650[][3] = {
 	/* [INITCAM] */
 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
@@ -180,8 +179,7 @@
 	{SENSOR, OV9650_OFON, 0x40}
 };
 
-static const unsigned char init_ov9650[][3] =
-{
+static const unsigned char init_ov9650[][3] = {
 	/* [INITCAM] */
 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
@@ -297,8 +295,7 @@
 	{SENSOR, OV9650_COM2, OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X},
 };
 
-static const unsigned char res_init_ov9650[][3] =
-{
+static const unsigned char res_init_ov9650[][3] = {
 	{SENSOR, OV9650_COM2, OV9650_OUTPUT_DRIVE_2X},
 
 	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82},
@@ -307,5 +304,4 @@
 	{BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00},
 	{BRIDGE, M5602_XB_SIG_INI, 0x01}
 };
-
 #endif
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/video/gspca/m5602/m5602_po1030.c
index 925b87d..1febd34 100644
--- a/drivers/media/video/gspca/m5602/m5602_po1030.c
+++ b/drivers/media/video/gspca/m5602/m5602_po1030.c
@@ -58,14 +58,14 @@
 #define GAIN_IDX 0
 	{
 		{
-			.id 		= V4L2_CID_GAIN,
-			.type 		= V4L2_CTRL_TYPE_INTEGER,
-			.name 		= "gain",
-			.minimum 	= 0x00,
-			.maximum 	= 0x4f,
-			.step 		= 0x1,
-			.default_value 	= PO1030_GLOBAL_GAIN_DEFAULT,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.id		= V4L2_CID_GAIN,
+			.type		= V4L2_CTRL_TYPE_INTEGER,
+			.name		= "gain",
+			.minimum	= 0x00,
+			.maximum	= 0x4f,
+			.step		= 0x1,
+			.default_value	= PO1030_GLOBAL_GAIN_DEFAULT,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = po1030_set_gain,
 		.get = po1030_get_gain
@@ -73,14 +73,14 @@
 #define EXPOSURE_IDX 1
 	{
 		{
-			.id 		= V4L2_CID_EXPOSURE,
-			.type 		= V4L2_CTRL_TYPE_INTEGER,
-			.name 		= "exposure",
-			.minimum 	= 0x00,
-			.maximum 	= 0x02ff,
-			.step 		= 0x1,
-			.default_value 	= PO1030_EXPOSURE_DEFAULT,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.id		= V4L2_CID_EXPOSURE,
+			.type		= V4L2_CTRL_TYPE_INTEGER,
+			.name		= "exposure",
+			.minimum	= 0x00,
+			.maximum	= 0x02ff,
+			.step		= 0x1,
+			.default_value	= PO1030_EXPOSURE_DEFAULT,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = po1030_set_exposure,
 		.get = po1030_get_exposure
@@ -88,14 +88,14 @@
 #define RED_BALANCE_IDX 2
 	{
 		{
-			.id 		= V4L2_CID_RED_BALANCE,
-			.type 		= V4L2_CTRL_TYPE_INTEGER,
-			.name 		= "red balance",
-			.minimum 	= 0x00,
-			.maximum 	= 0xff,
-			.step 		= 0x1,
-			.default_value 	= PO1030_RED_GAIN_DEFAULT,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.id		= V4L2_CID_RED_BALANCE,
+			.type		= V4L2_CTRL_TYPE_INTEGER,
+			.name		= "red balance",
+			.minimum	= 0x00,
+			.maximum	= 0xff,
+			.step		= 0x1,
+			.default_value	= PO1030_RED_GAIN_DEFAULT,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = po1030_set_red_balance,
 		.get = po1030_get_red_balance
@@ -103,14 +103,14 @@
 #define BLUE_BALANCE_IDX 3
 	{
 		{
-			.id 		= V4L2_CID_BLUE_BALANCE,
-			.type 		= V4L2_CTRL_TYPE_INTEGER,
-			.name 		= "blue balance",
-			.minimum 	= 0x00,
-			.maximum 	= 0xff,
-			.step 		= 0x1,
-			.default_value 	= PO1030_BLUE_GAIN_DEFAULT,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.id		= V4L2_CID_BLUE_BALANCE,
+			.type		= V4L2_CTRL_TYPE_INTEGER,
+			.name		= "blue balance",
+			.minimum	= 0x00,
+			.maximum	= 0xff,
+			.step		= 0x1,
+			.default_value	= PO1030_BLUE_GAIN_DEFAULT,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = po1030_set_blue_balance,
 		.get = po1030_get_blue_balance
@@ -118,13 +118,13 @@
 #define HFLIP_IDX 4
 	{
 		{
-			.id 		= V4L2_CID_HFLIP,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "horizontal flip",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 0,
+			.id		= V4L2_CID_HFLIP,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "horizontal flip",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 0,
 		},
 		.set = po1030_set_hflip,
 		.get = po1030_get_hflip
@@ -132,13 +132,13 @@
 #define VFLIP_IDX 5
 	{
 		{
-			.id 		= V4L2_CID_VFLIP,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "vertical flip",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 0,
+			.id		= V4L2_CID_VFLIP,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "vertical flip",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 0,
 		},
 		.set = po1030_set_vflip,
 		.get = po1030_get_vflip
@@ -146,13 +146,13 @@
 #define AUTO_WHITE_BALANCE_IDX 6
 	{
 		{
-			.id 		= V4L2_CID_AUTO_WHITE_BALANCE,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "auto white balance",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 0,
+			.id		= V4L2_CID_AUTO_WHITE_BALANCE,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "auto white balance",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 0,
 		},
 		.set = po1030_set_auto_white_balance,
 		.get = po1030_get_auto_white_balance
@@ -160,13 +160,13 @@
 #define AUTO_EXPOSURE_IDX 7
 	{
 		{
-			.id 		= V4L2_CID_EXPOSURE_AUTO,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "auto exposure",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 0,
+			.id		= V4L2_CID_EXPOSURE_AUTO,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "auto exposure",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 0,
 		},
 		.set = po1030_set_auto_exposure,
 		.get = po1030_get_auto_exposure
@@ -174,14 +174,14 @@
 #define GREEN_BALANCE_IDX 8
 	{
 		{
-			.id 		= M5602_V4L2_CID_GREEN_BALANCE,
-			.type 		= V4L2_CTRL_TYPE_INTEGER,
-			.name 		= "green balance",
-			.minimum 	= 0x00,
-			.maximum 	= 0xff,
-			.step 		= 0x1,
-			.default_value 	= PO1030_GREEN_GAIN_DEFAULT,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.id		= M5602_V4L2_CID_GREEN_BALANCE,
+			.type		= V4L2_CTRL_TYPE_INTEGER,
+			.name		= "green balance",
+			.minimum	= 0x00,
+			.maximum	= 0xff,
+			.step		= 0x1,
+			.default_value	= PO1030_GREEN_GAIN_DEFAULT,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = po1030_set_green_balance,
 		.get = po1030_get_green_balance
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.h b/drivers/media/video/gspca/m5602/m5602_po1030.h
index 1ea380b..3383595 100644
--- a/drivers/media/video/gspca/m5602/m5602_po1030.h
+++ b/drivers/media/video/gspca/m5602/m5602_po1030.h
@@ -139,9 +139,9 @@
 
 #define PO1030_GLOBAL_GAIN_DEFAULT	0x12
 #define PO1030_EXPOSURE_DEFAULT		0x0085
-#define PO1030_BLUE_GAIN_DEFAULT 	0x36
-#define PO1030_RED_GAIN_DEFAULT 	0x36
-#define PO1030_GREEN_GAIN_DEFAULT 	0x40
+#define PO1030_BLUE_GAIN_DEFAULT	0x36
+#define PO1030_RED_GAIN_DEFAULT		0x36
+#define PO1030_GREEN_GAIN_DEFAULT	0x40
 
 /*****************************************************************************/
 
@@ -166,8 +166,7 @@
 	.disconnect = po1030_disconnect,
 };
 
-static const unsigned char preinit_po1030[][3] =
-{
+static const unsigned char preinit_po1030[][3] = {
 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
@@ -193,8 +192,7 @@
 	{BRIDGE, M5602_XB_GPIO_DAT, 0x00}
 };
 
-static const unsigned char init_po1030[][3] =
-{
+static const unsigned char init_po1030[][3] = {
 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
@@ -271,5 +269,4 @@
 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
 	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
 };
-
 #endif
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
index da0a38c..d27280b 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
+++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
@@ -143,13 +143,13 @@
 #define VFLIP_IDX 0
 	{
 		{
-			.id 		= V4L2_CID_VFLIP,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "vertical flip",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 0
+			.id		= V4L2_CID_VFLIP,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "vertical flip",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 0
 		},
 		.set = s5k4aa_set_vflip,
 		.get = s5k4aa_get_vflip
@@ -157,13 +157,13 @@
 #define HFLIP_IDX 1
 	{
 		{
-			.id 		= V4L2_CID_HFLIP,
-			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
-			.name 		= "horizontal flip",
-			.minimum 	= 0,
-			.maximum 	= 1,
-			.step 		= 1,
-			.default_value 	= 0
+			.id		= V4L2_CID_HFLIP,
+			.type		= V4L2_CTRL_TYPE_BOOLEAN,
+			.name		= "horizontal flip",
+			.minimum	= 0,
+			.maximum	= 1,
+			.step		= 1,
+			.default_value	= 0
 		},
 		.set = s5k4aa_set_hflip,
 		.get = s5k4aa_get_hflip
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h b/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
index 4440da4..8cc7a3f 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
+++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
@@ -83,8 +83,7 @@
 	.disconnect = s5k4aa_disconnect,
 };
 
-static const unsigned char preinit_s5k4aa[][4] =
-{
+static const unsigned char preinit_s5k4aa[][4] = {
 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
@@ -127,8 +126,7 @@
 	{SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}
 };
 
-static const unsigned char init_s5k4aa[][4] =
-{
+static const unsigned char init_s5k4aa[][4] = {
 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
@@ -179,8 +177,7 @@
 	{SENSOR, 0x37, 0x00, 0x00},
 };
 
-static const unsigned char VGA_s5k4aa[][4] =
-{
+static const unsigned char VGA_s5k4aa[][4] = {
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
@@ -235,8 +232,7 @@
 	{SENSOR, 0x02, 0x0e, 0x00},
 };
 
-static const unsigned char SXGA_s5k4aa[][4] =
-{
+static const unsigned char SXGA_s5k4aa[][4] = {
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
@@ -284,6 +280,4 @@
 	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
 	{SENSOR, 0x02, 0x0e, 0x00},
 };
-
-
 #endif
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.h b/drivers/media/video/gspca/m5602/m5602_s5k83a.h
index 7814b07..80a63a2 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k83a.h
+++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.h
@@ -35,7 +35,7 @@
 #define S5K83A_MAXIMUM_EXPOSURE		0x3c
 #define S5K83A_FLIP_MASK		0x10
 #define S5K83A_GPIO_LED_MASK		0x10
-#define S5K83A_GPIO_ROTATION_MASK 	0x40
+#define S5K83A_GPIO_ROTATION_MASK	0x40
 
 /*****************************************************************************/
 
@@ -67,8 +67,7 @@
 	s32 *settings;
 };
 
-static const unsigned char preinit_s5k83a[][4] =
-{
+static const unsigned char preinit_s5k83a[][4] = {
 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
@@ -108,8 +107,7 @@
 /* This could probably be considerably shortened.
    I don't have the hardware to experiment with it, patches welcome
 */
-static const unsigned char init_s5k83a[][4] =
-{
+static const unsigned char init_s5k83a[][4] = {
 	/* The following sequence is useless after a clean boot
 	   but is necessary after resume from suspend */
 	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
@@ -166,8 +164,7 @@
 	{SENSOR, 0x00, 0x06, 0x00},
 };
 
-static const unsigned char start_s5k83a[][4] =
-{
+static const unsigned char start_s5k83a[][4] = {
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
@@ -193,5 +190,4 @@
 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
 };
-
 #endif
diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c
index 031f719..a81536e 100644
--- a/drivers/media/video/gspca/mars.c
+++ b/drivers/media/video/gspca/mars.c
@@ -28,14 +28,23 @@
 MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver");
 MODULE_LICENSE("GPL");
 
+/* controls */
+enum e_ctrl {
+	BRIGHTNESS,
+	COLORS,
+	GAMMA,
+	SHARPNESS,
+	ILLUM_TOP,
+	ILLUM_BOT,
+	NCTRLS		/* number of controls */
+};
+
 /* specific webcam descriptor */
 struct sd {
 	struct gspca_dev gspca_dev;	/* !! must be the first item */
 
-	u8 brightness;
-	u8 colors;
-	u8 gamma;
-	u8 sharpness;
+	struct gspca_ctrl ctrls[NCTRLS];
+
 	u8 quality;
 #define QUALITY_MIN 40
 #define QUALITY_MAX 70
@@ -45,17 +54,15 @@
 };
 
 /* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setcolors(struct gspca_dev *gspca_dev);
+static void setgamma(struct gspca_dev *gspca_dev);
+static void setsharpness(struct gspca_dev *gspca_dev);
+static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val);
 
-static const struct ctrl sd_ctrls[] = {
-	{
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[BRIGHTNESS] = {
 	    {
 		.id      = V4L2_CID_BRIGHTNESS,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -63,13 +70,11 @@
 		.minimum = 0,
 		.maximum = 30,
 		.step    = 1,
-#define BRIGHTNESS_DEF 15
-		.default_value = BRIGHTNESS_DEF,
+		.default_value = 15,
 	    },
-	    .set = sd_setbrightness,
-	    .get = sd_getbrightness,
+	    .set_control = setbrightness
 	},
-	{
+[COLORS] = {
 	    {
 		.id      = V4L2_CID_SATURATION,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -77,13 +82,11 @@
 		.minimum = 1,
 		.maximum = 255,
 		.step    = 1,
-#define COLOR_DEF 200
-		.default_value = COLOR_DEF,
+		.default_value = 200,
 	    },
-	    .set = sd_setcolors,
-	    .get = sd_getcolors,
+	    .set_control = setcolors
 	},
-	{
+[GAMMA] = {
 	    {
 		.id      = V4L2_CID_GAMMA,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -91,13 +94,11 @@
 		.minimum = 0,
 		.maximum = 3,
 		.step    = 1,
-#define GAMMA_DEF 1
-		.default_value = GAMMA_DEF,
+		.default_value = 1,
 	    },
-	    .set = sd_setgamma,
-	    .get = sd_getgamma,
+	    .set_control = setgamma
 	},
-	{
+[SHARPNESS] = {
 	    {
 		.id	 = V4L2_CID_SHARPNESS,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -105,11 +106,35 @@
 		.minimum = 0,
 		.maximum = 2,
 		.step    = 1,
-#define SHARPNESS_DEF 1
-		.default_value = SHARPNESS_DEF,
+		.default_value = 1,
 	    },
-	    .set = sd_setsharpness,
-	    .get = sd_getsharpness,
+	    .set_control = setsharpness
+	},
+[ILLUM_TOP] = {
+	    {
+		.id	 = V4L2_CID_ILLUMINATORS_1,
+		.type    = V4L2_CTRL_TYPE_BOOLEAN,
+		.name    = "Top illuminator",
+		.minimum = 0,
+		.maximum = 1,
+		.step    = 1,
+		.default_value = 0,
+		.flags = V4L2_CTRL_FLAG_UPDATE,
+	    },
+	    .set = sd_setilluminator1
+	},
+[ILLUM_BOT] = {
+	    {
+		.id	 = V4L2_CID_ILLUMINATORS_2,
+		.type    = V4L2_CTRL_TYPE_BOOLEAN,
+		.name    = "Bottom illuminator",
+		.minimum = 0,
+		.maximum = 1,
+		.step    = 1,
+		.default_value = 0,
+		.flags = V4L2_CTRL_FLAG_UPDATE,
+	    },
+	    .set = sd_setilluminator2
 	},
 };
 
@@ -138,21 +163,25 @@
 };
 
 /* write <len> bytes from gspca_dev->usb_buf */
-static int reg_w(struct gspca_dev *gspca_dev,
+static void reg_w(struct gspca_dev *gspca_dev,
 		 int len)
 {
 	int alen, ret;
 
+	if (gspca_dev->usb_err < 0)
+		return;
+
 	ret = usb_bulk_msg(gspca_dev->dev,
 			usb_sndbulkpipe(gspca_dev->dev, 4),
 			gspca_dev->usb_buf,
 			len,
 			&alen,
 			500);	/* timeout in milliseconds */
-	if (ret < 0)
-		PDEBUG(D_ERR, "reg write [%02x] error %d",
+	if (ret < 0) {
+		err("reg write [%02x] error %d",
 			gspca_dev->usb_buf[0], ret);
-	return ret;
+		gspca_dev->usb_err = ret;
+	}
 }
 
 static void mi_w(struct gspca_dev *gspca_dev,
@@ -167,6 +196,59 @@
 	reg_w(gspca_dev, 4);
 }
 
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	gspca_dev->usb_buf[0] = 0x61;
+	gspca_dev->usb_buf[1] = sd->ctrls[BRIGHTNESS].val;
+	reg_w(gspca_dev, 2);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	s16 val;
+
+	val = sd->ctrls[COLORS].val;
+	gspca_dev->usb_buf[0] = 0x5f;
+	gspca_dev->usb_buf[1] = val << 3;
+	gspca_dev->usb_buf[2] = ((val >> 2) & 0xf8) | 0x04;
+	reg_w(gspca_dev, 3);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	gspca_dev->usb_buf[0] = 0x06;
+	gspca_dev->usb_buf[1] = sd->ctrls[GAMMA].val * 0x40;
+	reg_w(gspca_dev, 2);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	gspca_dev->usb_buf[0] = 0x67;
+	gspca_dev->usb_buf[1] = sd->ctrls[SHARPNESS].val * 4 + 3;
+	reg_w(gspca_dev, 2);
+}
+
+static void setilluminators(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	gspca_dev->usb_buf[0] = 0x22;
+	if (sd->ctrls[ILLUM_TOP].val)
+		gspca_dev->usb_buf[1] = 0x76;
+	else if (sd->ctrls[ILLUM_BOT].val)
+		gspca_dev->usb_buf[1] = 0x7a;
+	else
+		gspca_dev->usb_buf[1] = 0x7e;
+	reg_w(gspca_dev, 2);
+}
+
 /* this function is called at probe time */
 static int sd_config(struct gspca_dev *gspca_dev,
 			const struct usb_device_id *id)
@@ -177,10 +259,7 @@
 	cam = &gspca_dev->cam;
 	cam->cam_mode = vga_mode;
 	cam->nmodes = ARRAY_SIZE(vga_mode);
-	sd->brightness = BRIGHTNESS_DEF;
-	sd->colors = COLOR_DEF;
-	sd->gamma = GAMMA_DEF;
-	sd->sharpness = SHARPNESS_DEF;
+	cam->ctrls = sd->ctrls;
 	sd->quality = QUALITY_DEF;
 	gspca_dev->nbalt = 9;		/* use the altsetting 08 */
 	return 0;
@@ -189,13 +268,13 @@
 /* this function is called at probe and resume time */
 static int sd_init(struct gspca_dev *gspca_dev)
 {
+	gspca_dev->ctrl_inac = (1 << ILLUM_TOP) | (1 << ILLUM_BOT);
 	return 0;
 }
 
 static int sd_start(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
-	int err_code;
 	u8 *data;
 	int i;
 
@@ -208,9 +287,7 @@
 
 	data[0] = 0x01;		/* address */
 	data[1] = 0x01;
-	err_code = reg_w(gspca_dev, 2);
-	if (err_code < 0)
-		return err_code;
+	reg_w(gspca_dev, 2);
 
 	/*
 	   Initialize the MR97113 chip register
@@ -223,7 +300,7 @@
 	data[5] = 0x30;		/* reg 4, MI, PAS5101 :
 				 *	0x30 for 24mhz , 0x28 for 12mhz */
 	data[6] = 0x02;		/* reg 5, H start - was 0x04 */
-	data[7] = sd->gamma * 0x40;	/* reg 0x06: gamma */
+	data[7] = sd->ctrls[GAMMA].val * 0x40;	/* reg 0x06: gamma */
 	data[8] = 0x01;		/* reg 7, V start - was 0x03 */
 /*	if (h_size == 320 ) */
 /*		data[9]= 0x56;	 * reg 8, 24MHz, 2:1 scale down */
@@ -232,16 +309,12 @@
 /*jfm: from win trace*/
 	data[10] = 0x18;
 
-	err_code = reg_w(gspca_dev, 11);
-	if (err_code < 0)
-		return err_code;
+	reg_w(gspca_dev, 11);
 
 	data[0] = 0x23;		/* address */
 	data[1] = 0x09;		/* reg 35, append frame header */
 
-	err_code = reg_w(gspca_dev, 2);
-	if (err_code < 0)
-		return err_code;
+	reg_w(gspca_dev, 2);
 
 	data[0] = 0x3c;		/* address */
 /*	if (gspca_dev->width == 1280) */
@@ -250,9 +323,7 @@
 /*	else */
 	data[1] = 50;		/* 50 reg 60, pc-cam frame size
 				 *	(unit: 4KB) 200KB */
-	err_code = reg_w(gspca_dev, 2);
-	if (err_code < 0)
-		return err_code;
+	reg_w(gspca_dev, 2);
 
 	/* auto dark-gain */
 	data[0] = 0x5e;		/* address */
@@ -261,37 +332,29 @@
 				/* reg 0x5f/0x60 (LE) = saturation */
 				/* h (60): xxxx x100
 				 * l (5f): xxxx x000 */
-	data[2] = sd->colors << 3;
-	data[3] = ((sd->colors >> 2) & 0xf8) | 0x04;
-	data[4] = sd->brightness; /* reg 0x61 = brightness */
+	data[2] = sd->ctrls[COLORS].val << 3;
+	data[3] = ((sd->ctrls[COLORS].val >> 2) & 0xf8) | 0x04;
+	data[4] = sd->ctrls[BRIGHTNESS].val; /* reg 0x61 = brightness */
 	data[5] = 0x00;
 
-	err_code = reg_w(gspca_dev, 6);
-	if (err_code < 0)
-		return err_code;
+	reg_w(gspca_dev, 6);
 
 	data[0] = 0x67;
 /*jfm: from win trace*/
-	data[1] = sd->sharpness * 4 + 3;
+	data[1] = sd->ctrls[SHARPNESS].val * 4 + 3;
 	data[2] = 0x14;
-	err_code = reg_w(gspca_dev, 3);
-	if (err_code < 0)
-		return err_code;
+	reg_w(gspca_dev, 3);
 
 	data[0] = 0x69;
 	data[1] = 0x2f;
 	data[2] = 0x28;
 	data[3] = 0x42;
-	err_code = reg_w(gspca_dev, 4);
-	if (err_code < 0)
-		return err_code;
+	reg_w(gspca_dev, 4);
 
 	data[0] = 0x63;
 	data[1] = 0x07;
-	err_code = reg_w(gspca_dev, 2);
+	reg_w(gspca_dev, 2);
 /*jfm: win trace - many writes here to reg 0x64*/
-	if (err_code < 0)
-		return err_code;
 
 	/* initialize the MI sensor */
 	for (i = 0; i < sizeof mi_data; i++)
@@ -300,18 +363,26 @@
 	data[0] = 0x00;
 	data[1] = 0x4d;		/* ISOC transfering enable... */
 	reg_w(gspca_dev, 2);
-	return 0;
+
+	gspca_dev->ctrl_inac = 0; /* activate the illuminator controls */
+	return gspca_dev->usb_err;
 }
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
 {
-	int result;
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	gspca_dev->ctrl_inac = (1 << ILLUM_TOP) | (1 << ILLUM_BOT);
+	if (sd->ctrls[ILLUM_TOP].val || sd->ctrls[ILLUM_BOT].val) {
+		sd->ctrls[ILLUM_TOP].val = 0;
+		sd->ctrls[ILLUM_BOT].val = 0;
+		setilluminators(gspca_dev);
+		msleep(20);
+	}
 
 	gspca_dev->usb_buf[0] = 1;
 	gspca_dev->usb_buf[1] = 0;
-	result = reg_w(gspca_dev, 2);
-	if (result < 0)
-		PDEBUG(D_ERR, "Camera Stop failed");
+	reg_w(gspca_dev, 2);
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
@@ -352,91 +423,28 @@
 	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 
-	sd->brightness = val;
-	if (gspca_dev->streaming) {
-		gspca_dev->usb_buf[0] = 0x61;
-		gspca_dev->usb_buf[1] = val;
-		reg_w(gspca_dev, 2);
-	}
-	return 0;
+	/* only one illuminator may be on */
+	sd->ctrls[ILLUM_TOP].val = val;
+	if (val)
+		sd->ctrls[ILLUM_BOT].val = 0;
+	setilluminators(gspca_dev);
+	return gspca_dev->usb_err;
 }
 
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 
-	*val = sd->brightness;
-	return 0;
-}
-
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->colors = val;
-	if (gspca_dev->streaming) {
-
-		/* see sd_start */
-		gspca_dev->usb_buf[0] = 0x5f;
-		gspca_dev->usb_buf[1] = sd->colors << 3;
-		gspca_dev->usb_buf[2] = ((sd->colors >> 2) & 0xf8) | 0x04;
-		reg_w(gspca_dev, 3);
-	}
-	return 0;
-}
-
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->colors;
-	return 0;
-}
-
-static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->gamma = val;
-	if (gspca_dev->streaming) {
-		gspca_dev->usb_buf[0] = 0x06;
-		gspca_dev->usb_buf[1] = val * 0x40;
-		reg_w(gspca_dev, 2);
-	}
-	return 0;
-}
-
-static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->gamma;
-	return 0;
-}
-
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->sharpness = val;
-	if (gspca_dev->streaming) {
-		gspca_dev->usb_buf[0] = 0x67;
-		gspca_dev->usb_buf[1] = val * 4 + 3;
-		reg_w(gspca_dev, 2);
-	}
-	return 0;
-}
-
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->sharpness;
-	return 0;
+	/* only one illuminator may be on */
+	sd->ctrls[ILLUM_BOT].val = val;
+	if (val)
+		sd->ctrls[ILLUM_TOP].val = 0;
+	setilluminators(gspca_dev);
+	return gspca_dev->usb_err;
 }
 
 static int sd_set_jcomp(struct gspca_dev *gspca_dev,
@@ -471,7 +479,7 @@
 static const struct sd_desc sd_desc = {
 	.name = MODULE_NAME,
 	.ctrls = sd_ctrls,
-	.nctrls = ARRAY_SIZE(sd_ctrls),
+	.nctrls = NCTRLS,
 	.config = sd_config,
 	.init = sd_init,
 	.start = sd_start,
@@ -510,18 +518,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c
index 33744e7..7607a28 100644
--- a/drivers/media/video/gspca/mr97310a.c
+++ b/drivers/media/video/gspca/mr97310a.c
@@ -9,14 +9,14 @@
  * is Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
  *
  * Support for the control settings for the CIF cameras is
- * Copyright (C) 2009 Hans de Goede <hdgoede@redhat.com> and
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com> and
  * Thomas Kaiser <thomas@kaiser-linux.li>
  *
  * Support for the control settings for the VGA cameras is
  * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
  *
  * Several previously unsupported cameras are owned and have been tested by
- * Hans de Goede <hdgoede@redhat.com> and
+ * Hans de Goede <hdegoede@redhat.com> and
  * Thomas Kaiser <thomas@kaiser-linux.li> and
  * Theodore Kilgore <kilgota@auburn.edu> and
  * Edmond Rodriguez <erodrig_97@yahoo.com> and
@@ -267,7 +267,7 @@
 			  usb_sndbulkpipe(gspca_dev->dev, 4),
 			  gspca_dev->usb_buf, len, NULL, 500);
 	if (rc < 0)
-		PDEBUG(D_ERR, "reg write [%02x] error %d",
+		err("reg write [%02x] error %d",
 		       gspca_dev->usb_buf[0], rc);
 	return rc;
 }
@@ -281,7 +281,7 @@
 			  usb_rcvbulkpipe(gspca_dev->dev, 3),
 			  gspca_dev->usb_buf, len, NULL, 500);
 	if (rc < 0)
-		PDEBUG(D_ERR, "reg read [%02x] error %d",
+		err("reg read [%02x] error %d",
 		       gspca_dev->usb_buf[0], rc);
 	return rc;
 }
@@ -540,7 +540,7 @@
 			sd->sensor_type = 1;
 			break;
 		default:
-			PDEBUG(D_ERR, "Unknown CIF Sensor id : %02x",
+			err("Unknown CIF Sensor id : %02x",
 			       gspca_dev->usb_buf[1]);
 			return -ENODEV;
 		}
@@ -575,10 +575,10 @@
 			sd->sensor_type = 2;
 		} else if ((gspca_dev->usb_buf[0] != 0x03) &&
 					(gspca_dev->usb_buf[0] != 0x04)) {
-			PDEBUG(D_ERR, "Unknown VGA Sensor id Byte 0: %02x",
+			err("Unknown VGA Sensor id Byte 0: %02x",
 					gspca_dev->usb_buf[0]);
-			PDEBUG(D_ERR, "Defaults assumed, may not work");
-			PDEBUG(D_ERR, "Please report this");
+			err("Defaults assumed, may not work");
+			err("Please report this");
 		}
 		/* Sakar Digital color needs to be adjusted. */
 		if ((gspca_dev->usb_buf[0] == 0x03) &&
@@ -595,12 +595,10 @@
 				/* Nothing to do here. */
 				break;
 			default:
-				PDEBUG(D_ERR,
-					"Unknown VGA Sensor id Byte 1: %02x",
+				err("Unknown VGA Sensor id Byte 1: %02x",
 					gspca_dev->usb_buf[1]);
-				PDEBUG(D_ERR,
-					"Defaults assumed, may not work");
-				PDEBUG(D_ERR, "Please report this");
+				err("Defaults assumed, may not work");
+				err("Please report this");
 			}
 		}
 		PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d",
@@ -675,7 +673,7 @@
 	struct sd *sd = (struct sd *) gspca_dev;
 	__u8 *data = gspca_dev->usb_buf;
 	int err_code;
-	const __u8 startup_string[] = {
+	static const __u8 startup_string[] = {
 		0x00,
 		0x0d,
 		0x01,
@@ -721,7 +719,7 @@
 		return err_code;
 
 	if (!sd->sensor_type) {
-		const struct sensor_w_data cif_sensor0_init_data[] = {
+		static const struct sensor_w_data cif_sensor0_init_data[] = {
 			{0x02, 0x00, {0x03, 0x5a, 0xb5, 0x01,
 				      0x0f, 0x14, 0x0f, 0x10}, 8},
 			{0x0c, 0x00, {0x04, 0x01, 0x01, 0x00, 0x1f}, 5},
@@ -742,7 +740,7 @@
 		err_code = sensor_write_regs(gspca_dev, cif_sensor0_init_data,
 					 ARRAY_SIZE(cif_sensor0_init_data));
 	} else {	/* sd->sensor_type = 1 */
-		const struct sensor_w_data cif_sensor1_init_data[] = {
+		static const struct sensor_w_data cif_sensor1_init_data[] = {
 			/* Reg 3,4, 7,8 get set by the controls */
 			{0x02, 0x00, {0x10}, 1},
 			{0x05, 0x01, {0x22}, 1}, /* 5/6 also seen as 65h/32h */
@@ -777,8 +775,9 @@
 	struct sd *sd = (struct sd *) gspca_dev;
 	__u8 *data = gspca_dev->usb_buf;
 	int err_code;
-	const __u8 startup_string[] = {0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b,
-				       0x00, 0x00, 0x00, 0x50, 0xc0};
+	static const __u8 startup_string[] =
+		{0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b, 0x00, 0x00,
+		 0x00, 0x50, 0xc0};
 	/* What some of these mean is explained in start_cif_cam(), above */
 
 	memcpy(data, startup_string, 11);
@@ -830,7 +829,7 @@
 		return err_code;
 
 	if (!sd->sensor_type) {
-		const struct sensor_w_data vga_sensor0_init_data[] = {
+		static const struct sensor_w_data vga_sensor0_init_data[] = {
 			{0x01, 0x00, {0x0c, 0x00, 0x04}, 3},
 			{0x14, 0x00, {0x01, 0xe4, 0x02, 0x84}, 4},
 			{0x20, 0x00, {0x00, 0x80, 0x00, 0x08}, 4},
@@ -841,20 +840,20 @@
 		err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data,
 					 ARRAY_SIZE(vga_sensor0_init_data));
 	} else if (sd->sensor_type == 1) {
-		const struct sensor_w_data color_adj[] = {
+		static const struct sensor_w_data color_adj[] = {
 			{0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
 				/* adjusted blue, green, red gain correct
 				   too much blue from the Sakar Digital */
 				0x05, 0x01, 0x04}, 8}
 		};
 
-		const struct sensor_w_data color_no_adj[] = {
+		static const struct sensor_w_data color_no_adj[] = {
 			{0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
 				/* default blue, green, red gain settings */
 				0x07, 0x00, 0x01}, 8}
 		};
 
-		const struct sensor_w_data vga_sensor1_init_data[] = {
+		static const struct sensor_w_data vga_sensor1_init_data[] = {
 			{0x11, 0x04, {0x01}, 1},
 			{0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01,
 			/* These settings may be better for some cameras */
@@ -879,7 +878,7 @@
 		err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data,
 					 ARRAY_SIZE(vga_sensor1_init_data));
 	} else {	/* sensor type == 2 */
-		const struct sensor_w_data vga_sensor2_init_data[] = {
+		static const struct sensor_w_data vga_sensor2_init_data[] = {
 
 			{0x01, 0x00, {0x48}, 1},
 			{0x02, 0x00, {0x22}, 1},
@@ -976,7 +975,7 @@
 	u8 val;
 	u8 sign_reg = 7;  /* This reg and the next one used on CIF cams. */
 	u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */
-	const u8 quick_clix_table[] =
+	static const u8 quick_clix_table[] =
 	/*	  0  1  2   3  4  5  6  7  8  9  10  11  12  13  14  15 */
 		{ 0, 4, 8, 12, 1, 2, 3, 5, 6, 9,  7, 10, 13, 11, 14, 15};
 	/*
@@ -1261,18 +1260,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
index 2b2cbdb..6cf6855 100644
--- a/drivers/media/video/gspca/ov519.c
+++ b/drivers/media/video/gspca/ov519.c
@@ -57,10 +57,24 @@
  * are getting "Failed to read sensor ID..." */
 static int i2c_detect_tries = 10;
 
+/* controls */
+enum e_ctrl {
+	BRIGHTNESS,
+	CONTRAST,
+	COLORS,
+	HFLIP,
+	VFLIP,
+	AUTOBRIGHT,
+	FREQ,
+	NCTRL		/* number of controls */
+};
+
 /* ov519 device descriptor */
 struct sd {
 	struct gspca_dev gspca_dev;		/* !! must be the first item */
 
+	struct gspca_ctrl ctrls[NCTRL];
+
 	__u8 packet_nr;
 
 	char bridge;
@@ -82,13 +96,6 @@
 	/* Determined by sensor type */
 	__u8 sif;
 
-	__u8 brightness;
-	__u8 contrast;
-	__u8 colors;
-	__u8 hflip;
-	__u8 vflip;
-	__u8 autobrightness;
-	__u8 freq;
 	__u8 quality;
 #define QUALITY_MIN 50
 #define QUALITY_MAX 70
@@ -130,29 +137,16 @@
 #include "w996Xcf.c"
 
 /* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setautobrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getautobrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
 static void setbrightness(struct gspca_dev *gspca_dev);
 static void setcontrast(struct gspca_dev *gspca_dev);
 static void setcolors(struct gspca_dev *gspca_dev);
-static void setautobrightness(struct sd *sd);
-static void setfreq(struct sd *sd);
+static void sethvflip(struct gspca_dev *gspca_dev);
+static void setautobright(struct gspca_dev *gspca_dev);
+static void setfreq(struct gspca_dev *gspca_dev);
+static void setfreq_i(struct sd *sd);
 
 static const struct ctrl sd_ctrls[] = {
-#define BRIGHTNESS_IDX 0
-	{
+[BRIGHTNESS] = {
 	    {
 		.id      = V4L2_CID_BRIGHTNESS,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -160,14 +154,11 @@
 		.minimum = 0,
 		.maximum = 255,
 		.step    = 1,
-#define BRIGHTNESS_DEF 127
-		.default_value = BRIGHTNESS_DEF,
+		.default_value = 127,
 	    },
-	    .set = sd_setbrightness,
-	    .get = sd_getbrightness,
+	    .set_control = setbrightness,
 	},
-#define CONTRAST_IDX 1
-	{
+[CONTRAST] = {
 	    {
 		.id      = V4L2_CID_CONTRAST,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -175,14 +166,11 @@
 		.minimum = 0,
 		.maximum = 255,
 		.step    = 1,
-#define CONTRAST_DEF 127
-		.default_value = CONTRAST_DEF,
+		.default_value = 127,
 	    },
-	    .set = sd_setcontrast,
-	    .get = sd_getcontrast,
+	    .set_control = setcontrast,
 	},
-#define COLOR_IDX 2
-	{
+[COLORS] = {
 	    {
 		.id      = V4L2_CID_SATURATION,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -190,15 +178,12 @@
 		.minimum = 0,
 		.maximum = 255,
 		.step    = 1,
-#define COLOR_DEF 127
-		.default_value = COLOR_DEF,
+		.default_value = 127,
 	    },
-	    .set = sd_setcolors,
-	    .get = sd_getcolors,
+	    .set_control = setcolors,
 	},
 /* The flip controls work with ov7670 only */
-#define HFLIP_IDX 3
-	{
+[HFLIP] = {
 	    {
 		.id      = V4L2_CID_HFLIP,
 		.type    = V4L2_CTRL_TYPE_BOOLEAN,
@@ -206,14 +191,11 @@
 		.minimum = 0,
 		.maximum = 1,
 		.step    = 1,
-#define HFLIP_DEF 0
-		.default_value = HFLIP_DEF,
+		.default_value = 0,
 	    },
-	    .set = sd_sethflip,
-	    .get = sd_gethflip,
+	    .set_control = sethvflip,
 	},
-#define VFLIP_IDX 4
-	{
+[VFLIP] = {
 	    {
 		.id      = V4L2_CID_VFLIP,
 		.type    = V4L2_CTRL_TYPE_BOOLEAN,
@@ -221,14 +203,11 @@
 		.minimum = 0,
 		.maximum = 1,
 		.step    = 1,
-#define VFLIP_DEF 0
-		.default_value = VFLIP_DEF,
+		.default_value = 0,
 	    },
-	    .set = sd_setvflip,
-	    .get = sd_getvflip,
+	    .set_control = sethvflip,
 	},
-#define AUTOBRIGHT_IDX 5
-	{
+[AUTOBRIGHT] = {
 	    {
 		.id      = V4L2_CID_AUTOBRIGHTNESS,
 		.type    = V4L2_CTRL_TYPE_BOOLEAN,
@@ -236,14 +215,11 @@
 		.minimum = 0,
 		.maximum = 1,
 		.step    = 1,
-#define AUTOBRIGHT_DEF 1
-		.default_value = AUTOBRIGHT_DEF,
+		.default_value = 1,
 	    },
-	    .set = sd_setautobrightness,
-	    .get = sd_getautobrightness,
+	    .set_control = setautobright,
 	},
-#define FREQ_IDX 6
-	{
+[FREQ] = {
 	    {
 		.id	 = V4L2_CID_POWER_LINE_FREQUENCY,
 		.type    = V4L2_CTRL_TYPE_MENU,
@@ -251,26 +227,9 @@
 		.minimum = 0,
 		.maximum = 2,	/* 0: 0, 1: 50Hz, 2:60Hz */
 		.step    = 1,
-#define FREQ_DEF 0
-		.default_value = FREQ_DEF,
+		.default_value = 0,
 	    },
-	    .set = sd_setfreq,
-	    .get = sd_getfreq,
-	},
-#define OV7670_FREQ_IDX 7
-	{
-	    {
-		.id	 = V4L2_CID_POWER_LINE_FREQUENCY,
-		.type    = V4L2_CTRL_TYPE_MENU,
-		.name    = "Light frequency filter",
-		.minimum = 0,
-		.maximum = 3,	/* 0: 0, 1: 50Hz, 2:60Hz 3: Auto Hz */
-		.step    = 1,
-#define OV7670_FREQ_DEF 3
-		.default_value = OV7670_FREQ_DEF,
-	    },
-	    .set = sd_setfreq,
-	    .get = sd_getfreq,
+	    .set_control = setfreq,
 	},
 };
 
@@ -456,10 +415,10 @@
 
 /* Registers common to OV511 / OV518 */
 #define R51x_FIFO_PSIZE			0x30	/* 2 bytes wide w/ OV518(+) */
-#define R51x_SYS_RESET          	0x50
+#define R51x_SYS_RESET			0x50
 	/* Reset type flags */
 	#define	OV511_RESET_OMNICE	0x08
-#define R51x_SYS_INIT         		0x53
+#define R51x_SYS_INIT			0x53
 #define R51x_SYS_SNAP			0x52
 #define R51x_SYS_CUST_ID		0x5F
 #define R51x_COMP_LUT_BEGIN		0x80
@@ -644,13 +603,11 @@
 };
 
 /* Settings for OV2610 camera chip */
-static const struct ov_i2c_regvals norm_2610[] =
-{
+static const struct ov_i2c_regvals norm_2610[] = {
 	{ 0x12, 0x80 },	/* reset */
 };
 
-static const struct ov_i2c_regvals norm_3620b[] =
-{
+static const struct ov_i2c_regvals norm_3620b[] = {
 	/*
 	 * From the datasheet: "Note that after writing to register COMH
 	 * (0x12) to change the sensor mode, registers related to the
@@ -1900,7 +1857,7 @@
 			sd->gspca_dev.usb_buf, 1, 500);
 leave:
 	if (ret < 0) {
-		PDEBUG(D_ERR, "Write reg 0x%04x -> [0x%02x] failed",
+		err("Write reg 0x%04x -> [0x%02x] failed",
 		       value, index);
 		return ret;
 	}
@@ -1938,7 +1895,7 @@
 		ret = sd->gspca_dev.usb_buf[0];
 		PDEBUG(D_USBI, "Read reg [0x%02X] -> 0x%04X", index, ret);
 	} else
-		PDEBUG(D_ERR, "Read reg [0x%02x] failed", index);
+		err("Read reg [0x%02x] failed", index);
 
 	return ret;
 }
@@ -1958,7 +1915,7 @@
 	if (ret >= 0)
 		ret = sd->gspca_dev.usb_buf[0];
 	else
-		PDEBUG(D_ERR, "Read reg 8 [0x%02x] failed", index);
+		err("Read reg 8 [0x%02x] failed", index);
 
 	return ret;
 }
@@ -2006,7 +1963,7 @@
 			0, index,
 			sd->gspca_dev.usb_buf, n, 500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "Write reg32 [%02x] %08x failed", index, value);
+		err("Write reg32 [%02x] %08x failed", index, value);
 		return ret;
 	}
 
@@ -2203,7 +2160,7 @@
 			(__u16)value, (__u16)reg, NULL, 0, 500);
 
 	if (ret < 0) {
-		PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg);
+		err("i2c 0x%02x -> [0x%02x] failed", value, reg);
 		return ret;
 	}
 
@@ -2225,7 +2182,7 @@
 		ret = sd->gspca_dev.usb_buf[0];
 		PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, ret);
 	} else
-		PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg);
+		err("i2c read [0x%02x] failed", reg);
 
 	return ret;
 }
@@ -2481,7 +2438,7 @@
 	int high, low;
 
 	if (sd->bridge != BRIDGE_OVFX2) {
-		PDEBUG(D_ERR, "error hires sensors only supported with ovfx2");
+		err("error hires sensors only supported with ovfx2");
 		return -1;
 	}
 
@@ -2498,7 +2455,7 @@
 		PDEBUG(D_PROBE, "Sensor is an OV3610");
 		sd->sensor = SEN_OV3610;
 	} else {
-		PDEBUG(D_ERR, "Error unknown sensor type: 0x%02x%02x",
+		err("Error unknown sensor type: 0x%02x%02x",
 		       high, low);
 		return -1;
 	}
@@ -2526,7 +2483,7 @@
 	if ((rc & 3) == 1) {
 		sd->sensor = SEN_OV8610;
 	} else {
-		PDEBUG(D_ERR, "Unknown image sensor version: %d", rc & 3);
+		err("Unknown image sensor version: %d", rc & 3);
 		return -1;
 	}
 
@@ -2589,9 +2546,8 @@
 		if (high == 0x76) {
 			switch (low) {
 			case 0x30:
-				PDEBUG(D_PROBE, "Sensor is an OV7630/OV7635");
-				PDEBUG(D_ERR,
-				      "7630 is not supported by this driver");
+				err("Sensor is an OV7630/OV7635");
+				err("7630 is not supported by this driver");
 				return -1;
 			case 0x40:
 				PDEBUG(D_PROBE, "Sensor is an OV7645");
@@ -2614,7 +2570,7 @@
 			sd->sensor = SEN_OV7620;
 		}
 	} else {
-		PDEBUG(D_ERR, "Unknown image sensor version: %d", rc & 3);
+		err("Unknown image sensor version: %d", rc & 3);
 		return -1;
 	}
 
@@ -2641,9 +2597,8 @@
 	switch (rc) {
 	case 0x00:
 		sd->sensor = SEN_OV6630;
-		PDEBUG(D_ERR,
-			"WARNING: Sensor is an OV66308. Your camera may have");
-		PDEBUG(D_ERR, "been misdetected in previous driver versions.");
+		warn("WARNING: Sensor is an OV66308. Your camera may have");
+		warn("been misdetected in previous driver versions.");
 		break;
 	case 0x01:
 		sd->sensor = SEN_OV6620;
@@ -2659,12 +2614,11 @@
 		break;
 	case 0x90:
 		sd->sensor = SEN_OV6630;
-		PDEBUG(D_ERR,
-			"WARNING: Sensor is an OV66307. Your camera may have");
-		PDEBUG(D_ERR, "been misdetected in previous driver versions.");
+		warn("WARNING: Sensor is an OV66307. Your camera may have");
+		warn("been misdetected in previous driver versions.");
 		break;
 	default:
-		PDEBUG(D_ERR, "FATAL: Unknown sensor version: 0x%02x", rc);
+		err("FATAL: Unknown sensor version: 0x%02x", rc);
 		return -1;
 	}
 
@@ -2823,7 +2777,7 @@
 	};
 
 	const struct ov_regvals norm_511[] = {
-		{ R511_DRAM_FLOW_CTL, 	0x01 },
+		{ R511_DRAM_FLOW_CTL,	0x01 },
 		{ R51x_SYS_SNAP,	0x00 },
 		{ R51x_SYS_SNAP,	0x02 },
 		{ R51x_SYS_SNAP,	0x00 },
@@ -2907,7 +2861,7 @@
 	const struct ov_regvals norm_518[] = {
 		{ R51x_SYS_SNAP,	0x02 }, /* Reset */
 		{ R51x_SYS_SNAP,	0x01 }, /* Enable */
-		{ 0x31, 		0x0f },
+		{ 0x31,			0x0f },
 		{ 0x5d,			0x03 },
 		{ 0x24,			0x9f },
 		{ 0x25,			0x90 },
@@ -2920,7 +2874,7 @@
 	const struct ov_regvals norm_518_p[] = {
 		{ R51x_SYS_SNAP,	0x02 }, /* Reset */
 		{ R51x_SYS_SNAP,	0x01 }, /* Enable */
-		{ 0x31, 		0x0f },
+		{ 0x31,			0x0f },
 		{ 0x5d,			0x03 },
 		{ 0x24,			0x9f },
 		{ 0x25,			0x90 },
@@ -3082,7 +3036,7 @@
 			goto error;
 		}
 	} else {
-		PDEBUG(D_ERR, "Can't determine sensor slave IDs");
+		err("Can't determine sensor slave IDs");
 		goto error;
 	}
 
@@ -3142,36 +3096,23 @@
 			goto error;
 		break;
 	}
-	sd->brightness = BRIGHTNESS_DEF;
-	if (sd->sensor == SEN_OV6630 || sd->sensor == SEN_OV66308AF)
-		sd->contrast = 200; /* The default is too low for the ov6630 */
+	gspca_dev->cam.ctrls = sd->ctrls;
+	if (sd->sensor == SEN_OV7670)
+		gspca_dev->ctrl_dis = 1 << COLORS;
 	else
-		sd->contrast = CONTRAST_DEF;
-	sd->colors = COLOR_DEF;
-	sd->hflip = HFLIP_DEF;
-	sd->vflip = VFLIP_DEF;
-	sd->autobrightness = AUTOBRIGHT_DEF;
-	if (sd->sensor == SEN_OV7670) {
-		sd->freq = OV7670_FREQ_DEF;
-		gspca_dev->ctrl_dis = (1 << FREQ_IDX) | (1 << COLOR_IDX);
-	} else {
-		sd->freq = FREQ_DEF;
-		gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) |
-				      (1 << OV7670_FREQ_IDX);
-	}
+		gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
 	sd->quality = QUALITY_DEF;
 	if (sd->sensor == SEN_OV7640 ||
 	    sd->sensor == SEN_OV7648)
-		gspca_dev->ctrl_dis |= (1 << AUTOBRIGHT_IDX) |
-				       (1 << CONTRAST_IDX);
+		gspca_dev->ctrl_dis |= (1 << AUTOBRIGHT) | (1 << CONTRAST);
 	if (sd->sensor == SEN_OV7670)
-		gspca_dev->ctrl_dis |= 1 << AUTOBRIGHT_IDX;
+		gspca_dev->ctrl_dis |= 1 << AUTOBRIGHT;
 	/* OV8610 Frequency filter control should work but needs testing */
 	if (sd->sensor == SEN_OV8610)
-		gspca_dev->ctrl_dis |= 1 << FREQ_IDX;
+		gspca_dev->ctrl_dis |= 1 << FREQ;
 	/* No controls for the OV2610/OV3610 */
 	if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610)
-		gspca_dev->ctrl_dis |= 0xFF;
+		gspca_dev->ctrl_dis |= (1 << NCTRL) - 1;
 
 	return 0;
 error:
@@ -3206,6 +3147,8 @@
 		break;
 	case SEN_OV6630:
 	case SEN_OV66308AF:
+		sd->ctrls[CONTRAST].def = 200;
+				 /* The default is too low for the ov6630 */
 		if (write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30)))
 			return -EIO;
 		break;
@@ -3228,6 +3171,8 @@
 			return -EIO;
 		break;
 	case SEN_OV7670:
+		sd->ctrls[FREQ].max = 3;	/* auto */
+		sd->ctrls[FREQ].def = 3;
 		if (write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670)))
 			return -EIO;
 		break;
@@ -3253,7 +3198,7 @@
 	intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
 	alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
 	if (!alt) {
-		PDEBUG(D_ERR, "Couldn't get altsetting");
+		err("Couldn't get altsetting");
 		return -EIO;
 	}
 
@@ -3377,7 +3322,7 @@
 	intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
 	alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
 	if (!alt) {
-		PDEBUG(D_ERR, "Couldn't get altsetting");
+		err("Couldn't get altsetting");
 		return -EIO;
 	}
 
@@ -3706,7 +3651,7 @@
 		break;
 	case SEN_OV7610:
 		i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
-		i2c_w(sd, 0x35, qvga?0x1e:0x9e);
+		i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e);
 		i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
 		i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
 		break;
@@ -3798,15 +3743,17 @@
 	return 0;
 }
 
-static void sethvflip(struct sd *sd)
+static void sethvflip(struct gspca_dev *gspca_dev)
 {
+	struct sd *sd = (struct sd *) gspca_dev;
+
 	if (sd->sensor != SEN_OV7670)
 		return;
 	if (sd->gspca_dev.streaming)
 		ov51x_stop(sd);
 	i2c_w_mask(sd, OV7670_REG_MVFP,
-		OV7670_MVFP_MIRROR * sd->hflip
-			| OV7670_MVFP_VFLIP * sd->vflip,
+		OV7670_MVFP_MIRROR * sd->ctrls[HFLIP].val
+			| OV7670_MVFP_VFLIP * sd->ctrls[VFLIP].val,
 		OV7670_MVFP_MIRROR | OV7670_MVFP_VFLIP);
 	if (sd->gspca_dev.streaming)
 		ov51x_restart(sd);
@@ -3957,9 +3904,9 @@
 	setcontrast(gspca_dev);
 	setbrightness(gspca_dev);
 	setcolors(gspca_dev);
-	sethvflip(sd);
-	setautobrightness(sd);
-	setfreq(sd);
+	sethvflip(gspca_dev);
+	setautobright(gspca_dev);
+	setfreq_i(sd);
 
 	/* Force clear snapshot state in case the snapshot button was
 	   pressed while we weren't streaming */
@@ -4000,7 +3947,7 @@
 	struct sd *sd = (struct sd *) gspca_dev;
 
 	if (sd->snapshot_pressed != state) {
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 		input_report_key(gspca_dev->input_dev, KEY_CAMERA, state);
 		input_sync(gspca_dev->input_dev);
 #endif
@@ -4214,7 +4161,7 @@
 	struct sd *sd = (struct sd *) gspca_dev;
 	int val;
 
-	val = sd->brightness;
+	val = sd->ctrls[BRIGHTNESS].val;
 	switch (sd->sensor) {
 	case SEN_OV8610:
 	case SEN_OV7610:
@@ -4229,7 +4176,7 @@
 	case SEN_OV7620:
 	case SEN_OV7620AE:
 		/* 7620 doesn't like manual changes when in auto mode */
-		if (!sd->autobrightness)
+		if (!sd->ctrls[AUTOBRIGHT].val)
 			i2c_w(sd, OV7610_REG_BRT, val);
 		break;
 	case SEN_OV7670:
@@ -4245,7 +4192,7 @@
 	struct sd *sd = (struct sd *) gspca_dev;
 	int val;
 
-	val = sd->contrast;
+	val = sd->ctrls[CONTRAST].val;
 	switch (sd->sensor) {
 	case SEN_OV7610:
 	case SEN_OV6620:
@@ -4287,7 +4234,7 @@
 	struct sd *sd = (struct sd *) gspca_dev;
 	int val;
 
-	val = sd->colors;
+	val = sd->ctrls[COLORS].val;
 	switch (sd->sensor) {
 	case SEN_OV8610:
 	case SEN_OV7610:
@@ -4317,23 +4264,25 @@
 	}
 }
 
-static void setautobrightness(struct sd *sd)
+static void setautobright(struct gspca_dev *gspca_dev)
 {
+	struct sd *sd = (struct sd *) gspca_dev;
+
 	if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7648 ||
 	    sd->sensor == SEN_OV7670 ||
 	    sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610)
 		return;
 
-	i2c_w_mask(sd, 0x2d, sd->autobrightness ? 0x10 : 0x00, 0x10);
+	i2c_w_mask(sd, 0x2d, sd->ctrls[AUTOBRIGHT].val ? 0x10 : 0x00, 0x10);
 }
 
-static void setfreq(struct sd *sd)
+static void setfreq_i(struct sd *sd)
 {
 	if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610)
 		return;
 
 	if (sd->sensor == SEN_OV7670) {
-		switch (sd->freq) {
+		switch (sd->ctrls[FREQ].val) {
 		case 0: /* Banding filter disabled */
 			i2c_w_mask(sd, OV7670_REG_COM8, 0, OV7670_COM8_BFILT);
 			break;
@@ -4355,7 +4304,7 @@
 			break;
 		}
 	} else {
-		switch (sd->freq) {
+		switch (sd->ctrls[FREQ].val) {
 		case 0: /* Banding filter disabled */
 			i2c_w_mask(sd, 0x2d, 0x00, 0x04);
 			i2c_w_mask(sd, 0x2a, 0x00, 0x80);
@@ -4387,135 +4336,15 @@
 		}
 	}
 }
-
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+static void setfreq(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 
-	sd->brightness = val;
-	if (gspca_dev->streaming)
-		setbrightness(gspca_dev);
-	return 0;
-}
+	setfreq_i(sd);
 
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->brightness;
-	return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->contrast = val;
-	if (gspca_dev->streaming)
-		setcontrast(gspca_dev);
-	return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->contrast;
-	return 0;
-}
-
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->colors = val;
-	if (gspca_dev->streaming)
-		setcolors(gspca_dev);
-	return 0;
-}
-
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->colors;
-	return 0;
-}
-
-static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->hflip = val;
-	if (gspca_dev->streaming)
-		sethvflip(sd);
-	return 0;
-}
-
-static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->hflip;
-	return 0;
-}
-
-static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->vflip = val;
-	if (gspca_dev->streaming)
-		sethvflip(sd);
-	return 0;
-}
-
-static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->vflip;
-	return 0;
-}
-
-static int sd_setautobrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->autobrightness = val;
-	if (gspca_dev->streaming)
-		setautobrightness(sd);
-	return 0;
-}
-
-static int sd_getautobrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->autobrightness;
-	return 0;
-}
-
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->freq = val;
-	if (gspca_dev->streaming) {
-		setfreq(sd);
-		/* Ugly but necessary */
-		if (sd->bridge == BRIDGE_W9968CF)
-			w9968cf_set_crop_window(sd);
-	}
-	return 0;
-}
-
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->freq;
-	return 0;
+	/* Ugly but necessary */
+	if (sd->bridge == BRIDGE_W9968CF)
+		w9968cf_set_crop_window(sd);
 }
 
 static int sd_querymenu(struct gspca_dev *gspca_dev,
@@ -4601,7 +4430,7 @@
 	.querymenu = sd_querymenu,
 	.get_jcomp = sd_get_jcomp,
 	.set_jcomp = sd_set_jcomp,
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 	.other_input = 1,
 #endif
 };
@@ -4663,17 +4492,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c
index 96cb3a9..88ef03f 100644
--- a/drivers/media/video/gspca/ov534.c
+++ b/drivers/media/video/gspca/ov534.c
@@ -487,7 +487,7 @@
 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 			      0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
 	if (ret < 0)
-		PDEBUG(D_ERR, "write failed");
+		err("write failed %d", ret);
 }
 
 static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg)
@@ -502,7 +502,7 @@
 			      0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
 	PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, gspca_dev->usb_buf[0]);
 	if (ret < 0)
-		PDEBUG(D_ERR, "read failed");
+		err("read failed %d", ret);
 	return gspca_dev->usb_buf[0];
 }
 
@@ -564,7 +564,7 @@
 	ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
 
 	if (!sccb_check_status(gspca_dev))
-		PDEBUG(D_ERR, "sccb_reg_write failed");
+		err("sccb_reg_write failed");
 }
 
 static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg)
@@ -572,11 +572,11 @@
 	ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
 	ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
 	if (!sccb_check_status(gspca_dev))
-		PDEBUG(D_ERR, "sccb_reg_read failed 1");
+		err("sccb_reg_read failed 1");
 
 	ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
 	if (!sccb_check_status(gspca_dev))
-		PDEBUG(D_ERR, "sccb_reg_read failed 2");
+		err("sccb_reg_read failed 2");
 
 	return ov534_reg_read(gspca_dev, OV534_REG_READ);
 }
@@ -1327,19 +1327,12 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c
index bbe5a03..e831f0d2 100644
--- a/drivers/media/video/gspca/ov534_9.c
+++ b/drivers/media/video/gspca/ov534_9.c
@@ -785,7 +785,7 @@
 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 			      0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_w failed %d", ret);
+		err("reg_w failed %d", ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -810,7 +810,7 @@
 			      0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
 	PDEBUG(D_USBI, "reg_r [%04x] -> %02x", reg, gspca_dev->usb_buf[0]);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_r err %d", ret);
+		err("reg_r err %d", ret);
 		gspca_dev->usb_err = ret;
 	}
 	return gspca_dev->usb_buf[0];
@@ -848,7 +848,7 @@
 	reg_w_i(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
 
 	if (!sccb_check_status(gspca_dev))
-		PDEBUG(D_ERR, "sccb_write failed");
+		err("sccb_write failed");
 }
 
 static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg)
@@ -856,11 +856,11 @@
 	reg_w(gspca_dev, OV534_REG_SUBADDR, reg);
 	reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
 	if (!sccb_check_status(gspca_dev))
-		PDEBUG(D_ERR, "sccb_read failed 1");
+		err("sccb_read failed 1");
 
 	reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
 	if (!sccb_check_status(gspca_dev))
-		PDEBUG(D_ERR, "sccb_read failed 2");
+		err("sccb_read failed 2");
 
 	return reg_r(gspca_dev, OV534_REG_READ);
 }
@@ -1458,19 +1458,12 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c
index a40f8893..15e97fa 100644
--- a/drivers/media/video/gspca/pac207.c
+++ b/drivers/media/video/gspca/pac207.c
@@ -1,7 +1,7 @@
 /*
  * Pixart PAC207BCA library
  *
- * Copyright (C) 2008 Hans de Goede <hdgoede@redhat.com>
+ * Copyright (C) 2008 Hans de Goede <hdegoede@redhat.com>
  * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
  * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr
  *
@@ -28,7 +28,7 @@
 #include <linux/input.h>
 #include "gspca.h"
 
-MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
 MODULE_DESCRIPTION("Pixart PAC207");
 MODULE_LICENSE("GPL");
 
@@ -45,7 +45,7 @@
 
 #define PAC207_GAIN_MIN			0
 #define PAC207_GAIN_MAX			31
-#define PAC207_GAIN_DEFAULT         	9 /* power on default: 9 */
+#define PAC207_GAIN_DEFAULT		9 /* power on default: 9 */
 #define PAC207_GAIN_KNEE		31
 
 #define PAC207_AUTOGAIN_DEADZONE	30
@@ -178,8 +178,7 @@
 			0x00, index,
 			gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT);
 	if (err < 0)
-		PDEBUG(D_ERR,
-			"Failed to write registers to index 0x%04X, error %d)",
+		err("Failed to write registers to index 0x%04X, error %d)",
 			index, err);
 
 	return err;
@@ -195,7 +194,7 @@
 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
 			value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
 	if (err)
-		PDEBUG(D_ERR, "Failed to write a register (index 0x%04X,"
+		err("Failed to write a register (index 0x%04X,"
 			" value 0x%02X, error %d)", index, value, err);
 
 	return err;
@@ -211,8 +210,7 @@
 			0x00, index,
 			gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT);
 	if (res < 0) {
-		PDEBUG(D_ERR,
-			"Failed to read a register (index 0x%04X, error %d)",
+		err("Failed to read a register (index 0x%04X, error %d)",
 			index, res);
 		return res;
 	}
@@ -496,7 +494,7 @@
 	return 0;
 }
 
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
 			u8 *data,		/* interrupt packet data */
 			int len)		/* interrput packet length */
@@ -526,7 +524,7 @@
 	.stopN = sd_stopN,
 	.dq_callback = pac207_do_auto_gain,
 	.pkt_scan = sd_pkt_scan,
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 	.int_pkt_scan = sd_int_pkt_scan,
 #endif
 };
@@ -572,17 +570,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c
index a66df07..55fbea7 100644
--- a/drivers/media/video/gspca/pac7302.c
+++ b/drivers/media/video/gspca/pac7302.c
@@ -408,9 +408,8 @@
 			index, gspca_dev->usb_buf, len,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_w_buf(): "
-		"Failed to write registers to index 0x%x, error %i",
-		index, ret);
+		err("reg_w_buf failed index 0x%02x, error %d",
+			index, ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -432,9 +431,8 @@
 			0, index, gspca_dev->usb_buf, 1,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_w(): "
-		"Failed to write register to index 0x%x, value 0x%x, error %i",
-		index, value, ret);
+		err("reg_w() failed index 0x%02x, value 0x%02x, error %d",
+			index, value, ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -468,10 +466,9 @@
 				0, index, gspca_dev->usb_buf, 1,
 				500);
 		if (ret < 0) {
-			PDEBUG(D_ERR, "reg_w_page(): "
-			"Failed to write register to index 0x%x, "
-			"value 0x%x, error %i",
-			index, page[index], ret);
+			err("reg_w_page() failed index 0x%02x, "
+			"value 0x%02x, error %d",
+				index, page[index], ret);
 			gspca_dev->usb_err = ret;
 			break;
 		}
@@ -900,9 +897,8 @@
 	struct sd *sd = (struct sd *) gspca_dev;
 
 	sd->contrast = val;
-	if (gspca_dev->streaming) {
+	if (gspca_dev->streaming)
 		setbrightcont(gspca_dev);
-	}
 	return gspca_dev->usb_err;
 }
 
@@ -1135,7 +1131,7 @@
 }
 #endif
 
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
 			u8 *data,		/* interrupt packet data */
 			int len)		/* interrput packet length */
@@ -1182,7 +1178,7 @@
 	.set_register = sd_dbg_s_register,
 	.get_chip_ident = sd_chip_ident,
 #endif
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 	.int_pkt_scan = sd_int_pkt_scan,
 #endif
 };
@@ -1226,17 +1222,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
index 1cb7e99e..7657b43 100644
--- a/drivers/media/video/gspca/pac7311.c
+++ b/drivers/media/video/gspca/pac7311.c
@@ -276,9 +276,8 @@
 			index, gspca_dev->usb_buf, len,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_w_buf(): "
-		"Failed to write registers to index 0x%x, error %i",
-		index, ret);
+		err("reg_w_buf() failed index 0x%02x, error %d",
+			index, ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -300,9 +299,8 @@
 			0, index, gspca_dev->usb_buf, 1,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_w(): "
-		"Failed to write register to index 0x%x, value 0x%x, error %i",
-		index, value, ret);
+		err("reg_w() failed index 0x%02x, value 0x%02x, error %d",
+			index, value, ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -336,10 +334,9 @@
 				0, index, gspca_dev->usb_buf, 1,
 				500);
 		if (ret < 0) {
-			PDEBUG(D_ERR, "reg_w_page(): "
-			"Failed to write register to index 0x%x, "
-			"value 0x%x, error %i",
-			index, page[index], ret);
+			err("reg_w_page() failed index 0x%02x, "
+			"value 0x%02x, error %d",
+				index, page[index], ret);
 			gspca_dev->usb_err = ret;
 			break;
 		}
@@ -675,9 +672,8 @@
 	struct sd *sd = (struct sd *) gspca_dev;
 
 	sd->contrast = val;
-	if (gspca_dev->streaming) {
+	if (gspca_dev->streaming)
 		setcontrast(gspca_dev);
-	}
 	return gspca_dev->usb_err;
 }
 
@@ -792,7 +788,7 @@
 	return 0;
 }
 
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
 			u8 *data,		/* interrupt packet data */
 			int len)		/* interrupt packet length */
@@ -835,7 +831,7 @@
 	.stop0 = sd_stop0,
 	.pkt_scan = sd_pkt_scan,
 	.dq_callback = do_autogain,
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 	.int_pkt_scan = sd_int_pkt_scan,
 #endif
 };
@@ -874,17 +870,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/sn9c2028.c b/drivers/media/video/gspca/sn9c2028.c
index 71d9447..40a0668 100644
--- a/drivers/media/video/gspca/sn9c2028.c
+++ b/drivers/media/video/gspca/sn9c2028.c
@@ -75,7 +75,7 @@
 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
 			2, 0, gspca_dev->usb_buf, 6, 500);
 	if (rc < 0) {
-		PDEBUG(D_ERR, "command write [%02x] error %d",
+		err("command write [%02x] error %d",
 				gspca_dev->usb_buf[0], rc);
 		return rc;
 	}
@@ -93,7 +93,7 @@
 			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
 			1, 0, gspca_dev->usb_buf, 1, 500);
 	if (rc != 1) {
-		PDEBUG(D_ERR, "read1 error %d", rc);
+		err("read1 error %d", rc);
 		return (rc < 0) ? rc : -EIO;
 	}
 	PDEBUG(D_USBI, "read1 response %02x", gspca_dev->usb_buf[0]);
@@ -109,7 +109,7 @@
 			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
 			4, 0, gspca_dev->usb_buf, 4, 500);
 	if (rc != 4) {
-		PDEBUG(D_ERR, "read4 error %d", rc);
+		err("read4 error %d", rc);
 		return (rc < 0) ? rc : -EIO;
 	}
 	memcpy(reading, gspca_dev->usb_buf, 4);
@@ -131,7 +131,7 @@
 	for (i = 0; i < 256 && status < 2; i++)
 		status = sn9c2028_read1(gspca_dev);
 	if (status != 2) {
-		PDEBUG(D_ERR, "long command status read error %d", status);
+		err("long command status read error %d", status);
 		return (status < 0) ? status : -EIO;
 	}
 
@@ -638,7 +638,7 @@
 		err_code = start_vivitar_cam(gspca_dev);
 		break;
 	default:
-		PDEBUG(D_ERR, "Starting unknown camera, please report this");
+		err("Starting unknown camera, please report this");
 		return -ENXIO;
 	}
 
@@ -738,19 +738,12 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c
index 9052d57..6b155ae 100644
--- a/drivers/media/video/gspca/sn9c20x.c
+++ b/drivers/media/video/gspca/sn9c20x.c
@@ -18,9 +18,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
-#ifdef CONFIG_INPUT
 #include <linux/input.h>
-#endif
 
 #include "gspca.h"
 #include "jpeg.h"
@@ -347,8 +345,8 @@
 
 static const struct v4l2_pix_format vga_mode[] = {
 	{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
-		.bytesperline = 240,
-		.sizeimage = 240 * 120,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 4 / 8 + 590,
 		.colorspace = V4L2_COLORSPACE_JPEG,
 		.priv = 0 | MODE_JPEG},
 	{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
@@ -357,13 +355,13 @@
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 0 | MODE_RAW},
 	{160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
-		.bytesperline = 240,
+		.bytesperline = 160,
 		.sizeimage = 240 * 120,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 0},
 	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
-		.bytesperline = 480,
-		.sizeimage = 480 * 240 ,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 8 + 590,
 		.colorspace = V4L2_COLORSPACE_JPEG,
 		.priv = 1 | MODE_JPEG},
 	{320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
@@ -372,13 +370,13 @@
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 1 | MODE_RAW},
 	{320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
-		.bytesperline = 480,
+		.bytesperline = 320,
 		.sizeimage = 480 * 240 ,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 1},
 	{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
-		.bytesperline = 960,
-		.sizeimage = 960 * 480,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 3 / 8 + 590,
 		.colorspace = V4L2_COLORSPACE_JPEG,
 		.priv = 2 | MODE_JPEG},
 	{640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
@@ -387,7 +385,7 @@
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 2 | MODE_RAW},
 	{640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
-		.bytesperline = 960,
+		.bytesperline = 640,
 		.sizeimage = 960 * 480,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 2},
@@ -395,8 +393,8 @@
 
 static const struct v4l2_pix_format sxga_mode[] = {
 	{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
-		.bytesperline = 240,
-		.sizeimage = 240 * 120,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 4 / 8 + 590,
 		.colorspace = V4L2_COLORSPACE_JPEG,
 		.priv = 0 | MODE_JPEG},
 	{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
@@ -405,13 +403,13 @@
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 0 | MODE_RAW},
 	{160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
-		.bytesperline = 240,
+		.bytesperline = 160,
 		.sizeimage = 240 * 120,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 0},
 	{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
-		.bytesperline = 480,
-		.sizeimage = 480 * 240 ,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 8 + 590,
 		.colorspace = V4L2_COLORSPACE_JPEG,
 		.priv = 1 | MODE_JPEG},
 	{320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
@@ -420,13 +418,13 @@
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 1 | MODE_RAW},
 	{320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
-		.bytesperline = 480,
+		.bytesperline = 320,
 		.sizeimage = 480 * 240 ,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 1},
 	{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
-		.bytesperline = 960,
-		.sizeimage = 960 * 480,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 3 / 8 + 590,
 		.colorspace = V4L2_COLORSPACE_JPEG,
 		.priv = 2 | MODE_JPEG},
 	{640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
@@ -435,13 +433,13 @@
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 2 | MODE_RAW},
 	{640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
-		.bytesperline = 960,
+		.bytesperline = 640,
 		.sizeimage = 960 * 480,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 2},
 	{1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
 		.bytesperline = 1280,
-		.sizeimage = (1280 * 1024) + 64,
+		.sizeimage = 1280 * 1024,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.priv = 3 | MODE_RAW | MODE_SXGA},
 };
@@ -1272,7 +1270,8 @@
 		}
 	}
 	/* disable hflip and vflip */
-	gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << EXPOSURE_IDX);
+	gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX)
+				| (1 << EXPOSURE_IDX);
 	sd->hstart = 60;
 	sd->vstart = 11;
 	return 0;
@@ -1351,7 +1350,9 @@
 				return -ENODEV;
 			}
 		}
-		gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX) | (1 << GAIN_IDX);
+		gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX)
+					| (1 << AUTOGAIN_IDX)
+					| (1 << GAIN_IDX);
 		sd->hstart = 2;
 		sd->vstart = 2;
 		sd->sensor = SENSOR_MT9V111;
@@ -1395,7 +1396,8 @@
 			return -ENODEV;
 		}
 	}
-	gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX) | (1 << GAIN_IDX);
+	gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX)
+				| (1 << GAIN_IDX);
 	sd->hstart = 0;
 	sd->vstart = 2;
 	return 0;
@@ -1412,7 +1414,8 @@
 			return -ENODEV;
 		}
 	}
-	gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX) | (1 << GAIN_IDX);
+	gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX)
+				| (1 << GAIN_IDX);
 	sd->hstart = 0;
 	sd->vstart = 2;
 	return 0;
@@ -2304,7 +2307,7 @@
 		do_autoexposure(gspca_dev, avg_lum);
 }
 
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
 			u8 *data,		/* interrupt packet */
 			int len)		/* interrupt packet length */
@@ -2386,7 +2389,7 @@
 	.start = sd_start,
 	.stopN = sd_stopN,
 	.pkt_scan = sd_pkt_scan,
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 	.int_pkt_scan = sd_int_pkt_scan,
 #endif
 	.dq_callback = sd_dqcallback,
@@ -2467,17 +2470,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	info("registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	info("deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
index 204bb3a..706f96f 100644
--- a/drivers/media/video/gspca/sonixb.c
+++ b/drivers/media/video/gspca/sonixb.c
@@ -323,10 +323,9 @@
 	0x00, 0x01, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x8b,
 	0x10, 0x1d, 0x10, 0x02, 0x02, 0x09, 0x07
 };
-static const __u8 ov6650_sensor_init[][8] =
-{
+static const __u8 ov6650_sensor_init[][8] = {
 	/* Bright, contrast, etc are set through SCBB interface.
-	 * AVCAP on win2 do not send any data on this 	controls. */
+	 * AVCAP on win2 do not send any data on this controls. */
 	/* Anyway, some registers appears to alter bright and constrat */
 
 	/* Reset sensor */
@@ -544,7 +543,7 @@
 	0x18, 0x10, 0x04, 0x03, 0x11, 0x0c
 };
 static const __u8 tas5130_sensor_init[][8] = {
-/* 	{0x30, 0x11, 0x00, 0x40, 0x47, 0x00, 0x00, 0x10},
+/*	{0x30, 0x11, 0x00, 0x40, 0x47, 0x00, 0x00, 0x10},
 					* shutter 0x47 short exposure? */
 	{0x30, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x10},
 					/* shutter 0x01 long exposure */
@@ -861,7 +860,7 @@
 		i2c[4] |= reg11 - 1;
 
 		/* If register 11 didn't change, don't change it */
-		if (sd->reg11 == reg11 )
+		if (sd->reg11 == reg11)
 			i2c[0] = 0xa0;
 
 		if (i2c_w(gspca_dev, i2c) == 0)
@@ -1388,7 +1387,7 @@
 	return -EINVAL;
 }
 
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
 			u8 *data,		/* interrupt packet data */
 			int len)		/* interrupt packet length */
@@ -1419,7 +1418,7 @@
 	.pkt_scan = sd_pkt_scan,
 	.querymenu = sd_querymenu,
 	.dq_callback = do_autogain,
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 	.int_pkt_scan = sd_int_pkt_scan,
 #endif
 };
@@ -1479,17 +1478,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
index 3705443..330dadc 100644
--- a/drivers/media/video/gspca/sonixj.c
+++ b/drivers/media/video/gspca/sonixj.c
@@ -31,24 +31,32 @@
 MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver");
 MODULE_LICENSE("GPL");
 
+/* controls */
+enum e_ctrl {
+	BRIGHTNESS,
+	CONTRAST,
+	COLORS,
+	BLUE,
+	RED,
+	GAMMA,
+	AUTOGAIN,
+	HFLIP,
+	VFLIP,
+	SHARPNESS,
+	INFRARED,
+	FREQ,
+	NCTRLS		/* number of controls */
+};
+
 /* specific webcam descriptor */
 struct sd {
 	struct gspca_dev gspca_dev;	/* !! must be the first item */
 
+	struct gspca_ctrl ctrls[NCTRLS];
+
 	atomic_t avg_lum;
 	u32 exposure;
 
-	u16 brightness;
-	u8 contrast;
-	u8 colors;
-	u8 autogain;
-	u8 blue;
-	u8 red;
-	u8 gamma;
-	u8 vflip;			/* ov7630/ov7648 only */
-	u8 sharpness;
-	u8 infrared;			/* mt9v111 only */
-	u8 freq;			/* ov76xx only */
 	u8 quality;			/* image quality */
 #define QUALITY_MIN 60
 #define QUALITY_MAX 95
@@ -75,6 +83,7 @@
 	SENSOR_GC0307,
 	SENSOR_HV7131R,
 	SENSOR_MI0360,
+	SENSOR_MI0360B,
 	SENSOR_MO4000,
 	SENSOR_MT9V111,
 	SENSOR_OM6802,
@@ -88,48 +97,31 @@
 };
 
 /* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setinfrared(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setcontrast(struct gspca_dev *gspca_dev);
+static void setcolors(struct gspca_dev *gspca_dev);
+static void setredblue(struct gspca_dev *gspca_dev);
+static void setgamma(struct gspca_dev *gspca_dev);
+static void setautogain(struct gspca_dev *gspca_dev);
+static void sethvflip(struct gspca_dev *gspca_dev);
+static void setsharpness(struct gspca_dev *gspca_dev);
+static void setinfrared(struct gspca_dev *gspca_dev);
+static void setfreq(struct gspca_dev *gspca_dev);
 
-static const struct ctrl sd_ctrls[] = {
-#define BRIGHTNESS_IDX 0
-	{
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[BRIGHTNESS] =  {
 	    {
 		.id      = V4L2_CID_BRIGHTNESS,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
 		.name    = "Brightness",
 		.minimum = 0,
-#define BRIGHTNESS_MAX 0xffff
-		.maximum = BRIGHTNESS_MAX,
+		.maximum = 0xff,
 		.step    = 1,
-#define BRIGHTNESS_DEF 0x8000
-		.default_value = BRIGHTNESS_DEF,
+		.default_value = 0x80,
 	    },
-	    .set = sd_setbrightness,
-	    .get = sd_getbrightness,
+	    .set_control = setbrightness
 	},
-#define CONTRAST_IDX 1
-	{
+[CONTRAST] = {
 	    {
 		.id      = V4L2_CID_CONTRAST,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -138,14 +130,11 @@
 #define CONTRAST_MAX 127
 		.maximum = CONTRAST_MAX,
 		.step    = 1,
-#define CONTRAST_DEF 63
-		.default_value = CONTRAST_DEF,
+		.default_value = 63,
 	    },
-	    .set = sd_setcontrast,
-	    .get = sd_getcontrast,
+	    .set_control = setcontrast
 	},
-#define COLOR_IDX 2
-	{
+[COLORS] = {
 	    {
 		.id      = V4L2_CID_SATURATION,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -153,14 +142,12 @@
 		.minimum = 0,
 		.maximum = 40,
 		.step    = 1,
-#define COLOR_DEF 25
-		.default_value = COLOR_DEF,
+#define COLORS_DEF 25
+		.default_value = COLORS_DEF,
 	    },
-	    .set = sd_setcolors,
-	    .get = sd_getcolors,
+	    .set_control = setcolors
 	},
-#define BLUE_BALANCE_IDX 3
-	{
+[BLUE] = {
 	    {
 		.id      = V4L2_CID_BLUE_BALANCE,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -168,14 +155,11 @@
 		.minimum = 24,
 		.maximum = 40,
 		.step    = 1,
-#define BLUE_BALANCE_DEF 32
-		.default_value = BLUE_BALANCE_DEF,
+		.default_value = 32,
 	    },
-	    .set = sd_setblue_balance,
-	    .get = sd_getblue_balance,
+	    .set_control = setredblue
 	},
-#define RED_BALANCE_IDX 4
-	{
+[RED] = {
 	    {
 		.id      = V4L2_CID_RED_BALANCE,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -183,14 +167,11 @@
 		.minimum = 24,
 		.maximum = 40,
 		.step    = 1,
-#define RED_BALANCE_DEF 32
-		.default_value = RED_BALANCE_DEF,
+		.default_value = 32,
 	    },
-	    .set = sd_setred_balance,
-	    .get = sd_getred_balance,
+	    .set_control = setredblue
 	},
-#define GAMMA_IDX 5
-	{
+[GAMMA] = {
 	    {
 		.id      = V4L2_CID_GAMMA,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -201,11 +182,9 @@
 #define GAMMA_DEF 20
 		.default_value = GAMMA_DEF,
 	    },
-	    .set = sd_setgamma,
-	    .get = sd_getgamma,
+	    .set_control = setgamma
 	},
-#define AUTOGAIN_IDX 6
-	{
+[AUTOGAIN] = {
 	    {
 		.id      = V4L2_CID_AUTOGAIN,
 		.type    = V4L2_CTRL_TYPE_BOOLEAN,
@@ -213,15 +192,23 @@
 		.minimum = 0,
 		.maximum = 1,
 		.step    = 1,
-#define AUTOGAIN_DEF 1
-		.default_value = AUTOGAIN_DEF,
+		.default_value = 1
 	    },
-	    .set = sd_setautogain,
-	    .get = sd_getautogain,
+	    .set_control = setautogain
 	},
-/* ov7630/ov7648 only */
-#define VFLIP_IDX 7
-	{
+[HFLIP] = {
+	    {
+		.id      = V4L2_CID_HFLIP,
+		.type    = V4L2_CTRL_TYPE_BOOLEAN,
+		.name    = "Mirror",
+		.minimum = 0,
+		.maximum = 1,
+		.step    = 1,
+		.default_value = 0,
+	    },
+	    .set_control = sethvflip
+	},
+[VFLIP] = {
 	    {
 		.id      = V4L2_CID_VFLIP,
 		.type    = V4L2_CTRL_TYPE_BOOLEAN,
@@ -229,14 +216,11 @@
 		.minimum = 0,
 		.maximum = 1,
 		.step    = 1,
-#define VFLIP_DEF 0
-		.default_value = VFLIP_DEF,
+		.default_value = 0,
 	    },
-	    .set = sd_setvflip,
-	    .get = sd_getvflip,
+	    .set_control = sethvflip
 	},
-#define SHARPNESS_IDX 8
-	{
+[SHARPNESS] = {
 	    {
 		.id	 = V4L2_CID_SHARPNESS,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -244,15 +228,12 @@
 		.minimum = 0,
 		.maximum = 255,
 		.step    = 1,
-#define SHARPNESS_DEF 90
-		.default_value = SHARPNESS_DEF,
+		.default_value = 90,
 	    },
-	    .set = sd_setsharpness,
-	    .get = sd_getsharpness,
+	    .set_control = setsharpness
 	},
 /* mt9v111 only */
-#define INFRARED_IDX 9
-	{
+[INFRARED] = {
 	    {
 		.id      = V4L2_CID_INFRARED,
 		.type    = V4L2_CTRL_TYPE_BOOLEAN,
@@ -260,15 +241,12 @@
 		.minimum = 0,
 		.maximum = 1,
 		.step    = 1,
-#define INFRARED_DEF 0
-		.default_value = INFRARED_DEF,
+		.default_value = 0,
 	    },
-	    .set = sd_setinfrared,
-	    .get = sd_getinfrared,
+	    .set_control = setinfrared
 	},
 /* ov7630/ov7648/ov7660 only */
-#define FREQ_IDX 10
-	{
+[FREQ] = {
 	    {
 		.id	 = V4L2_CID_POWER_LINE_FREQUENCY,
 		.type    = V4L2_CTRL_TYPE_MENU,
@@ -276,69 +254,85 @@
 		.minimum = 0,
 		.maximum = 2,	/* 0: 0, 1: 50Hz, 2:60Hz */
 		.step    = 1,
-#define FREQ_DEF 1
-		.default_value = FREQ_DEF,
+		.default_value = 1,
 	    },
-	    .set = sd_setfreq,
-	    .get = sd_getfreq,
+	    .set_control = setfreq
 	},
 };
 
 /* table of the disabled controls */
 static const __u32 ctrl_dis[] = {
-[SENSOR_ADCM1700] =	(1 << AUTOGAIN_IDX) |
-			(1 << INFRARED_IDX) |
-			(1 << VFLIP_IDX) |
-			(1 << FREQ_IDX),
+[SENSOR_ADCM1700] =	(1 << AUTOGAIN) |
+			(1 << INFRARED) |
+			(1 << HFLIP) |
+			(1 << VFLIP) |
+			(1 << FREQ),
 
-[SENSOR_GC0307] =	(1 << INFRARED_IDX) |
-			(1 << VFLIP_IDX) |
-			(1 << FREQ_IDX),
+[SENSOR_GC0307] =	(1 << INFRARED) |
+			(1 << HFLIP) |
+			(1 << VFLIP) |
+			(1 << FREQ),
 
-[SENSOR_HV7131R] =	(1 << INFRARED_IDX) |
-			(1 << FREQ_IDX),
+[SENSOR_HV7131R] =	(1 << INFRARED) |
+			(1 << HFLIP) |
+			(1 << FREQ),
 
-[SENSOR_MI0360] =	(1 << INFRARED_IDX) |
-			(1 << VFLIP_IDX) |
-			(1 << FREQ_IDX),
+[SENSOR_MI0360] =	(1 << INFRARED) |
+			(1 << HFLIP) |
+			(1 << VFLIP) |
+			(1 << FREQ),
 
-[SENSOR_MO4000] =	(1 << INFRARED_IDX) |
-			(1 << VFLIP_IDX) |
-			(1 << FREQ_IDX),
+[SENSOR_MI0360B] =	(1 << INFRARED) |
+			(1 << HFLIP) |
+			(1 << VFLIP) |
+			(1 << FREQ),
 
-[SENSOR_MT9V111] =	(1 << VFLIP_IDX) |
-			(1 << FREQ_IDX),
+[SENSOR_MO4000] =	(1 << INFRARED) |
+			(1 << HFLIP) |
+			(1 << VFLIP) |
+			(1 << FREQ),
 
-[SENSOR_OM6802] =	(1 << INFRARED_IDX) |
-			(1 << VFLIP_IDX) |
-			(1 << FREQ_IDX),
+[SENSOR_MT9V111] =	(1 << HFLIP) |
+			(1 << VFLIP) |
+			(1 << FREQ),
 
-[SENSOR_OV7630] =	(1 << INFRARED_IDX),
+[SENSOR_OM6802] =	(1 << INFRARED) |
+			(1 << HFLIP) |
+			(1 << VFLIP) |
+			(1 << FREQ),
 
-[SENSOR_OV7648] =	(1 << INFRARED_IDX),
+[SENSOR_OV7630] =	(1 << INFRARED) |
+			(1 << HFLIP),
 
-[SENSOR_OV7660] =	(1 << AUTOGAIN_IDX) |
-			(1 << INFRARED_IDX) |
-			(1 << VFLIP_IDX),
+[SENSOR_OV7648] =	(1 << INFRARED) |
+			(1 << HFLIP),
 
-[SENSOR_PO1030] =	(1 << AUTOGAIN_IDX) |
-			(1 << INFRARED_IDX) |
-			(1 << VFLIP_IDX) |
-			(1 << FREQ_IDX),
+[SENSOR_OV7660] =	(1 << AUTOGAIN) |
+			(1 << INFRARED) |
+			(1 << HFLIP) |
+			(1 << VFLIP),
 
-[SENSOR_PO2030N] =	(1 << AUTOGAIN_IDX) |
-			(1 << INFRARED_IDX) |
-			(1 << VFLIP_IDX) |
-			(1 << FREQ_IDX),
-[SENSOR_SOI768] =	(1 << AUTOGAIN_IDX) |
-			(1 << INFRARED_IDX) |
-			(1 << VFLIP_IDX) |
-			(1 << FREQ_IDX),
+[SENSOR_PO1030] =	(1 << AUTOGAIN) |
+			(1 << INFRARED) |
+			(1 << HFLIP) |
+			(1 << VFLIP) |
+			(1 << FREQ),
 
-[SENSOR_SP80708] =	(1 << AUTOGAIN_IDX) |
-			(1 << INFRARED_IDX) |
-			(1 << VFLIP_IDX) |
-			(1 << FREQ_IDX),
+[SENSOR_PO2030N] =	(1 << AUTOGAIN) |
+			(1 << INFRARED) |
+			(1 << FREQ),
+
+[SENSOR_SOI768] =	(1 << AUTOGAIN) |
+			(1 << INFRARED) |
+			(1 << HFLIP) |
+			(1 << VFLIP) |
+			(1 << FREQ),
+
+[SENSOR_SP80708] =	(1 << AUTOGAIN) |
+			(1 << INFRARED) |
+			(1 << HFLIP) |
+			(1 << VFLIP) |
+			(1 << FREQ),
 };
 
 static const struct v4l2_pix_format cif_mode[] = {
@@ -411,6 +405,17 @@
 	0x06,	0x00,	0x00,	0x00
 };
 
+static const u8 sn_mi0360b[0x1c] = {
+/*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
+	0x00,	0x61,	0x40,	0x00,	0x1a,	0x00,	0x00,	0x00,
+/*	reg8	reg9	rega	regb	regc	regd	rege	regf */
+	0x81,	0x5d,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
+/*	reg10	reg11	reg12	reg13	reg14	reg15	reg16	reg17 */
+	0x03,	0x00,	0x00,	0x02,	0x0a,	0x28,	0x1e,	0x40,
+/*	reg18	reg19	reg1a	reg1b */
+	0x06,	0x00,	0x00,	0x00
+};
+
 static const u8 sn_mo4000[0x1c] = {
 /*	reg0	reg1	reg2	reg3	reg4	reg5	reg6	reg7 */
 	0x00,	0x23,	0x60,	0x00,	0x1a,	0x00,	0x20,	0x18,
@@ -527,6 +532,7 @@
 [SENSOR_GC0307] =	sn_gc0307,
 [SENSOR_HV7131R] =	sn_hv7131,
 [SENSOR_MI0360] =	sn_mi0360,
+[SENSOR_MI0360B] =	sn_mi0360b,
 [SENSOR_MO4000] =	sn_mo4000,
 [SENSOR_MT9V111] =	sn_mt9v111,
 [SENSOR_OM6802] =	sn_om6802,
@@ -572,20 +578,23 @@
 	0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f,	/* VR VG VB */
 	0x00, 0x00, 0x00			/* YUV offsets */
 };
+
+#define DELAY	0xdd
+
 static const u8 adcm1700_sensor_init[][8] = {
 	{0xa0, 0x51, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x10},
 	{0xb0, 0x51, 0x04, 0x08, 0x00, 0x00, 0x00, 0x10},	/* reset */
-	{0xdd, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	{DELAY, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
 	{0xb0, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10},
-	{0xdd, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	{DELAY, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
 	{0xb0, 0x51, 0x0c, 0xe0, 0x2e, 0x00, 0x00, 0x10},
 	{0xb0, 0x51, 0x10, 0x02, 0x02, 0x00, 0x00, 0x10},
 	{0xb0, 0x51, 0x14, 0x0e, 0x0e, 0x00, 0x00, 0x10},
 	{0xb0, 0x51, 0x1c, 0x00, 0x80, 0x00, 0x00, 0x10},
 	{0xb0, 0x51, 0x20, 0x01, 0x00, 0x00, 0x00, 0x10},
-	{0xdd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	{DELAY, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
 	{0xb0, 0x51, 0x04, 0x04, 0x00, 0x00, 0x00, 0x10},
-	{0xdd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	{DELAY, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
 	{0xb0, 0x51, 0x04, 0x01, 0x00, 0x00, 0x00, 0x10},
 	{0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
 	{0xb0, 0x51, 0x14, 0x01, 0x00, 0x00, 0x00, 0x10},
@@ -629,7 +638,7 @@
 	{0xa0, 0x21, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x10},
 	{0xa0, 0x21, 0x0f, 0xb2, 0x00, 0x00, 0x00, 0x10},
 	{0xa0, 0x21, 0x12, 0x70, 0x00, 0x00, 0x00, 0x10},
-	{0xdd, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 10ms*/
+	{DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 10ms*/
 	{0xa0, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10},
 	{0xa0, 0x21, 0x15, 0xb8, 0x00, 0x00, 0x00, 0x10},
 	{0xa0, 0x21, 0x16, 0x13, 0x00, 0x00, 0x00, 0x10},
@@ -747,6 +756,62 @@
 	{0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */
 	{}
 };
+static const u8 mi0360b_sensor_init[][8] = {
+	{0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
+	{0xb1, 0x5d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10},
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 20ms*/
+	{0xb1, 0x5d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 20ms*/
+	{0xd1, 0x5d, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10},
+	{0xd1, 0x5d, 0x03, 0x01, 0xe2, 0x02, 0x82, 0x10},
+	{0xd1, 0x5d, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xb1, 0x5d, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xb1, 0x5d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10},
+	{0xd1, 0x5d, 0x2f, 0xf7, 0xb0, 0x00, 0x04, 0x10},
+	{0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10},
+	{0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x40, 0x01, 0xe0, 0x00, 0xd1, 0x10},
+	{0xb1, 0x5d, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10},
+	{0xd1, 0x5d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x5e, 0x00, 0x00, 0xa3, 0x1d, 0x10},
+	{0xb1, 0x5d, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10},
+
+	{0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+	{0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+	{0xb1, 0x5d, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10},
+	{0xd1, 0x5d, 0x2b, 0x00, 0x33, 0x00, 0xa0, 0x10},
+	{0xd1, 0x5d, 0x2d, 0x00, 0xa0, 0x00, 0x33, 0x10},
+	{}
+};
+static const u8 mi0360b_sensor_param1[][8] = {
+	{0xb1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
+	{0xb1, 0x5d, 0x06, 0x00, 0x53, 0x00, 0x00, 0x10},
+	{0xb1, 0x5d, 0x05, 0x00, 0x09, 0x00, 0x00, 0x10},
+	{0xb1, 0x5d, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */
+
+	{0xd1, 0x5d, 0x2b, 0x00, 0xd1, 0x01, 0xc9, 0x10},
+	{0xd1, 0x5d, 0x2d, 0x00, 0xed, 0x00, 0xd1, 0x10},
+	{0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */
+	{0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */
+	{}
+};
 static const u8 mo4000_sensor_init[][8] = {
 	{0xa1, 0x21, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10},
 	{0xa1, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10},
@@ -772,7 +837,7 @@
 };
 static const u8 mt9v111_sensor_init[][8] = {
 	{0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */
-	{0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
 	{0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
 	{0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */
 	{0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */
@@ -860,10 +925,10 @@
 static const u8 ov7630_sensor_init[][8] = {
 	{0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10},
 	{0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
-	{0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
 	{0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
 	{0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
-	{0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
 	{0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
 /* win: i2c_r from 00 to 80 */
 	{0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10},
@@ -917,7 +982,7 @@
 static const u8 ov7648_sensor_init[][8] = {
 	{0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10},
 	{0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10},	/* reset */
-	{0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
 	{0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
 	{0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10},
 	{0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10},
@@ -966,7 +1031,7 @@
 
 static const u8 ov7660_sensor_init[][8] = {
 	{0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */
-	{0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
 	{0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10},
 						/* Outformat = rawRGB */
 	{0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */
@@ -1062,7 +1127,7 @@
 static const u8 po1030_sensor_init[][8] = {
 /* the sensor registers are described in m5602/m5602_po1030.h */
 	{0xa1, 0x6e, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x10}, /* sensor reset */
-	{0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+	{DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
 	{0xa1, 0x6e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x10},
 	{0xa1, 0x6e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10},
 	{0xd1, 0x6e, 0x04, 0x02, 0xb1, 0x02, 0x39, 0x10},
@@ -1116,10 +1181,10 @@
 static const u8 po2030n_sensor_init[][8] = {
 	{0xa1, 0x6e, 0x1e, 0x1a, 0x00, 0x00, 0x00, 0x10},
 	{0xa1, 0x6e, 0x1f, 0x99, 0x00, 0x00, 0x00, 0x10},
-	{0xdd, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */
+	{DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */
 	{0xa1, 0x6e, 0x1e, 0x0a, 0x00, 0x00, 0x00, 0x10},
 	{0xa1, 0x6e, 0x1f, 0x19, 0x00, 0x00, 0x00, 0x10},
-	{0xdd, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */
+	{DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */
 	{0xa1, 0x6e, 0x20, 0x44, 0x00, 0x00, 0x00, 0x10},
 	{0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10},
 	{0xa1, 0x6e, 0x05, 0x70, 0x00, 0x00, 0x00, 0x10},
@@ -1168,7 +1233,7 @@
 };
 static const u8 po2030n_sensor_param1[][8] = {
 	{0xa1, 0x6e, 0x1a, 0x01, 0x00, 0x00, 0x00, 0x10},
-	{0xdd, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 8ms */
+	{DELAY, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 8ms */
 	{0xa1, 0x6e, 0x1b, 0xf4, 0x00, 0x00, 0x00, 0x10},
 	{0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10},
 	{0xd1, 0x6e, 0x16, 0x50, 0x40, 0x49, 0x40, 0x10},
@@ -1182,16 +1247,16 @@
 	{0xc1, 0x6e, 0x16, 0x52, 0x40, 0x48, 0x00, 0x10},
 /*after start*/
 	{0xa1, 0x6e, 0x15, 0x0f, 0x00, 0x00, 0x00, 0x10},
-	{0xdd, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */
+	{DELAY, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */
 	{0xa1, 0x6e, 0x1a, 0x05, 0x00, 0x00, 0x00, 0x10},
-	{0xdd, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */
+	{DELAY, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */
 	{0xa1, 0x6e, 0x1b, 0x53, 0x00, 0x00, 0x00, 0x10},
 	{}
 };
 
 static const u8 soi768_sensor_init[][8] = {
 	{0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */
-	{0xdd, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 96ms */
+	{DELAY, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 96ms */
 	{0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
 	{0xa1, 0x21, 0x13, 0x80, 0x00, 0x00, 0x00, 0x10},
 	{0xa1, 0x21, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x10},
@@ -1310,6 +1375,7 @@
 [SENSOR_GC0307] =	gc0307_sensor_init,
 [SENSOR_HV7131R] =	hv7131r_sensor_init,
 [SENSOR_MI0360] =	mi0360_sensor_init,
+[SENSOR_MI0360B] =	mi0360b_sensor_init,
 [SENSOR_MO4000] =	mo4000_sensor_init,
 [SENSOR_MT9V111] =	mt9v111_sensor_init,
 [SENSOR_OM6802] =	om6802_sensor_init,
@@ -1326,13 +1392,17 @@
 static void reg_r(struct gspca_dev *gspca_dev,
 		  u16 value, int len)
 {
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
 #ifdef GSPCA_DEBUG
 	if (len > USB_BUF_SZ) {
 		err("reg_r: buffer overflow");
 		return;
 	}
 #endif
-	usb_control_msg(gspca_dev->dev,
+	ret = usb_control_msg(gspca_dev->dev,
 			usb_rcvctrlpipe(gspca_dev->dev, 0),
 			0,
 			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
@@ -1340,15 +1410,23 @@
 			gspca_dev->usb_buf, len,
 			500);
 	PDEBUG(D_USBI, "reg_r [%02x] -> %02x", value, gspca_dev->usb_buf[0]);
+	if (ret < 0) {
+		err("reg_r err %d", ret);
+		gspca_dev->usb_err = ret;
+	}
 }
 
 static void reg_w1(struct gspca_dev *gspca_dev,
 		   u16 value,
 		   u8 data)
 {
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
 	PDEBUG(D_USBO, "reg_w1 [%04x] = %02x", value, data);
 	gspca_dev->usb_buf[0] = data;
-	usb_control_msg(gspca_dev->dev,
+	ret = usb_control_msg(gspca_dev->dev,
 			usb_sndctrlpipe(gspca_dev->dev, 0),
 			0x08,
 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
@@ -1356,12 +1434,20 @@
 			0,
 			gspca_dev->usb_buf, 1,
 			500);
+	if (ret < 0) {
+		err("reg_w1 err %d", ret);
+		gspca_dev->usb_err = ret;
+	}
 }
 static void reg_w(struct gspca_dev *gspca_dev,
 			  u16 value,
 			  const u8 *buffer,
 			  int len)
 {
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
 	PDEBUG(D_USBO, "reg_w [%04x] = %02x %02x ..",
 		value, buffer[0], buffer[1]);
 #ifdef GSPCA_DEBUG
@@ -1371,20 +1457,27 @@
 	}
 #endif
 	memcpy(gspca_dev->usb_buf, buffer, len);
-	usb_control_msg(gspca_dev->dev,
+	ret = usb_control_msg(gspca_dev->dev,
 			usb_sndctrlpipe(gspca_dev->dev, 0),
 			0x08,
 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
 			value, 0,
 			gspca_dev->usb_buf, len,
 			500);
+	if (ret < 0) {
+		err("reg_w err %d", ret);
+		gspca_dev->usb_err = ret;
+	}
 }
 
 /* I2C write 1 byte */
 static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
+	int ret;
 
+	if (gspca_dev->usb_err < 0)
+		return;
 	PDEBUG(D_USBO, "i2c_w1 [%02x] = %02x", reg, val);
 	switch (sd->sensor) {
 	case SENSOR_ADCM1700:
@@ -1403,7 +1496,7 @@
 	gspca_dev->usb_buf[5] = 0;
 	gspca_dev->usb_buf[6] = 0;
 	gspca_dev->usb_buf[7] = 0x10;
-	usb_control_msg(gspca_dev->dev,
+	ret = usb_control_msg(gspca_dev->dev,
 			usb_sndctrlpipe(gspca_dev->dev, 0),
 			0x08,
 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
@@ -1411,16 +1504,24 @@
 			0,
 			gspca_dev->usb_buf, 8,
 			500);
+	if (ret < 0) {
+		err("i2c_w1 err %d", ret);
+		gspca_dev->usb_err = ret;
+	}
 }
 
 /* I2C write 8 bytes */
 static void i2c_w8(struct gspca_dev *gspca_dev,
 		   const u8 *buffer)
 {
+	int ret;
+
+	if (gspca_dev->usb_err < 0)
+		return;
 	PDEBUG(D_USBO, "i2c_w8 [%02x] = %02x ..",
 		buffer[2], buffer[3]);
 	memcpy(gspca_dev->usb_buf, buffer, 8);
-	usb_control_msg(gspca_dev->dev,
+	ret = usb_control_msg(gspca_dev->dev,
 			usb_sndctrlpipe(gspca_dev->dev, 0),
 			0x08,
 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
@@ -1428,6 +1529,10 @@
 			gspca_dev->usb_buf, 8,
 			500);
 	msleep(2);
+	if (ret < 0) {
+		err("i2c_w8 err %d", ret);
+		gspca_dev->usb_err = ret;
+	}
 }
 
 /* sensor read 'len' (1..5) bytes in gspca_dev->usb_buf */
@@ -1466,7 +1571,7 @@
 			const u8 (*data)[8])
 {
 	while ((*data)[0] != 0) {
-		if ((*data)[0] != 0xdd)
+		if ((*data)[0] != DELAY)
 			i2c_w8(gspca_dev, *data);
 		else
 			msleep((*data)[1]);
@@ -1529,7 +1634,13 @@
 		if (val != 0xffff)
 			break;
 	}
+	if (gspca_dev->usb_err < 0)
+		return;
 	switch (val) {
+	case 0x8221:
+		PDEBUG(D_PROBE, "Sensor mi0360b");
+		sd->sensor = SENSOR_MI0360B;
+		break;
 	case 0x823a:
 		PDEBUG(D_PROBE, "Sensor mt9v111");
 		sd->sensor = SENSOR_MT9V111;
@@ -1556,6 +1667,8 @@
 	val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
 	reg_w1(gspca_dev, 0x01, 0x29);
 	reg_w1(gspca_dev, 0x17, 0x42);
+	if (gspca_dev->usb_err < 0)
+		return;
 	if (val == 0x7628) {			/* soi768 */
 		sd->sensor = SENSOR_SOI768;
 /*fixme: only valid for 0c45:613e?*/
@@ -1593,13 +1706,14 @@
 	val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
 	reg_w1(gspca_dev, 0x01, 0x29);
 	reg_w1(gspca_dev, 0x17, 0x42);
+	if (gspca_dev->usb_err < 0)
+		return;
 	if (val == 0x1030) {			/* po1030 */
 		PDEBUG(D_PROBE, "Sensor po1030");
 		sd->sensor = SENSOR_PO1030;
 		return;
 	}
-
-	PDEBUG(D_PROBE, "Unknown sensor %04x", val);
+	err("Unknown sensor %04x", val);
 }
 
 /* 0c45:6142 sensor may be po2030n, gc0305 or gc0307 */
@@ -1631,11 +1745,13 @@
 	val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
 	reg_w1(gspca_dev, 0x01, 0x29);
 	reg_w1(gspca_dev, 0x17, 0x42);
+	if (gspca_dev->usb_err < 0)
+		return;
 	if (val == 0x2030) {
 		PDEBUG(D_PROBE, "Sensor po2030n");
 /*		sd->sensor = SENSOR_PO2030N; */
 	} else {
-		PDEBUG(D_PROBE, "Unknown sensor ID %04x", val);
+		err("Unknown sensor ID %04x", val);
 	}
 }
 
@@ -1697,6 +1813,12 @@
 		reg_w1(gspca_dev, 0x01, 0x40);
 		msleep(50);
 		break;
+	case SENSOR_MI0360B:
+		reg_w1(gspca_dev, 0x01, 0x61);
+		reg_w1(gspca_dev, 0x17, 0x60);
+		reg_w1(gspca_dev, 0x01, 0x60);
+		reg_w1(gspca_dev, 0x01, 0x40);
+		break;
 	case SENSOR_MT9V111:
 		reg_w1(gspca_dev, 0x01, 0x61);
 		reg_w1(gspca_dev, 0x17, 0x61);
@@ -1762,8 +1884,7 @@
 		reg_w1(gspca_dev, 0x01, 0x43);
 		reg_w1(gspca_dev, 0x17, 0x61);
 		reg_w1(gspca_dev, 0x01, 0x42);
-		if (sd->sensor == SENSOR_HV7131R
-		    && sd->bridge == BRIDGE_SN9C102P)
+		if (sd->sensor == SENSOR_HV7131R)
 			hv7131r_probe(gspca_dev);
 		break;
 	}
@@ -1788,26 +1909,9 @@
 		cam->nmodes = ARRAY_SIZE(vga_mode);
 	}
 	cam->npkt = 24;			/* 24 packets per ISOC message */
+	cam->ctrls = sd->ctrls;
 
-	sd->brightness = BRIGHTNESS_DEF;
-	sd->contrast = CONTRAST_DEF;
-	sd->colors = COLOR_DEF;
-	sd->blue = BLUE_BALANCE_DEF;
-	sd->red = RED_BALANCE_DEF;
-	sd->gamma = GAMMA_DEF;
-	sd->autogain = AUTOGAIN_DEF;
 	sd->ag_cnt = -1;
-	sd->vflip = VFLIP_DEF;
-	switch (sd->sensor) {
-	case SENSOR_OM6802:
-		sd->sharpness = 0x10;
-		break;
-	default:
-		sd->sharpness = SHARPNESS_DEF;
-		break;
-	}
-	sd->infrared = INFRARED_DEF;
-	sd->freq = FREQ_DEF;
 	sd->quality = QUALITY_DEF;
 	sd->jpegqual = 80;
 
@@ -1828,6 +1932,8 @@
 	reg_w1(gspca_dev, 0xf1, gspca_dev->usb_buf[0]);
 	reg_r(gspca_dev, 0x00, 1);		/* get sonix chip id */
 	regF1 = gspca_dev->usb_buf[0];
+	if (gspca_dev->usb_err < 0)
+		return gspca_dev->usb_err;
 	PDEBUG(D_PROBE, "Sonix chip id: %02x", regF1);
 	switch (sd->bridge) {
 	case BRIDGE_SN9C102P:
@@ -1871,6 +1977,9 @@
 		break;
 	}
 
+	if (sd->sensor == SENSOR_OM6802)
+		sd->ctrls[SHARPNESS].def = 0x10;
+
 	/* Note we do not disable the sensor clock here (power saving mode),
 	   as that also disables the button on the cam. */
 	reg_w1(gspca_dev, 0xf1, 0x00);
@@ -1881,7 +1990,7 @@
 
 	gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
 
-	return 0;
+	return gspca_dev->usb_err;
 }
 
 static u32 setexposure(struct gspca_dev *gspca_dev,
@@ -1912,7 +2021,8 @@
 		i2c_w8(gspca_dev, Expodoit);
 		break;
 	    }
-	case SENSOR_MI0360: {
+	case SENSOR_MI0360:
+	case SENSOR_MI0360B: {
 		u8 expoMi[] =		/* exposure 0x0635 -> 4 fp/s 0x10 */
 			{ 0xb1, 0x5d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x16 };
 		static const u8 doit[] =		/* update sensor */
@@ -1991,16 +2101,18 @@
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 	unsigned int expo;
+	int brightness;
 	u8 k2;
 
-	k2 = ((int) sd->brightness - 0x8000) >> 10;
+	brightness = sd->ctrls[BRIGHTNESS].val;
+	k2 = (brightness - 0x80) >> 2;
 	switch (sd->sensor) {
 	case SENSOR_ADCM1700:
 		if (k2 > 0x1f)
 			k2 = 0;		/* only positive Y offset */
 		break;
 	case SENSOR_HV7131R:
-		expo = sd->brightness << 4;
+		expo = brightness << 12;
 		if (expo > 0x002dc6c0)
 			expo = 0x002dc6c0;
 		else if (expo < 0x02a0)
@@ -2009,18 +2121,22 @@
 		break;
 	case SENSOR_MI0360:
 	case SENSOR_MO4000:
-		expo = sd->brightness >> 4;
+		expo = brightness << 4;
+		sd->exposure = setexposure(gspca_dev, expo);
+		break;
+	case SENSOR_MI0360B:
+		expo = brightness << 2;
 		sd->exposure = setexposure(gspca_dev, expo);
 		break;
 	case SENSOR_GC0307:
 	case SENSOR_MT9V111:
-		expo = sd->brightness >> 8;
+		expo = brightness;
 		sd->exposure = setexposure(gspca_dev, expo);
 		return;			/* don't set the Y offset */
 	case SENSOR_OM6802:
-		expo = sd->brightness >> 6;
+		expo = brightness << 2;
 		sd->exposure = setexposure(gspca_dev, expo);
-		k2 = sd->brightness >> 11;
+		k2 = brightness >> 3;
 		break;
 	}
 
@@ -2033,7 +2149,8 @@
 	u8 k2;
 	u8 contrast[6];
 
-	k2 = sd->contrast * 0x30 / (CONTRAST_MAX + 1) + 0x10;	/* 10..40 */
+	k2 = sd->ctrls[CONTRAST].val * 0x30 / (CONTRAST_MAX + 1)
+				+ 0x10;		/* 10..40 */
 	contrast[0] = (k2 + 1) / 2;		/* red */
 	contrast[1] = 0;
 	contrast[2] = k2;			/* green */
@@ -2046,15 +2163,25 @@
 static void setcolors(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
-	int i, v;
+	int i, v, colors;
+	const s16 *uv;
 	u8 reg8a[12];			/* U & V gains */
-	static const s16 uv[6] = {	/* same as reg84 in signed decimal */
+	static const s16 uv_com[6] = {	/* same as reg84 in signed decimal */
 		-24, -38, 64,		/* UR UG UB */
 		 62, -51, -9		/* VR VG VB */
 	};
+	static const s16 uv_mi0360b[6] = {
+		-20, -38, 64,		/* UR UG UB */
+		 60, -51, -9		/* VR VG VB */
+	};
 
+	colors = sd->ctrls[COLORS].val;
+	if (sd->sensor == SENSOR_MI0360B)
+		uv = uv_mi0360b;
+	else
+		uv = uv_com;
 	for (i = 0; i < 6; i++) {
-		v = uv[i] * sd->colors / COLOR_DEF;
+		v = uv[i] * colors / COLORS_DEF;
 		reg8a[i * 2] = v;
 		reg8a[i * 2 + 1] = (v >> 8) & 0x0f;
 	}
@@ -2065,15 +2192,15 @@
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 
-	reg_w1(gspca_dev, 0x05, sd->red);
+	reg_w1(gspca_dev, 0x05, sd->ctrls[RED].val);
 /*	reg_w1(gspca_dev, 0x07, 32); */
-	reg_w1(gspca_dev, 0x06, sd->blue);
+	reg_w1(gspca_dev, 0x06, sd->ctrls[BLUE].val);
 }
 
 static void setgamma(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
-	int i;
+	int i, val;
 	u8 gamma[17];
 	const u8 *gamma_base;
 	static const u8 delta[17] = {
@@ -2086,6 +2213,7 @@
 		gamma_base = gamma_spec_0;
 		break;
 	case SENSOR_HV7131R:
+	case SENSOR_MI0360B:
 	case SENSOR_MT9V111:
 		gamma_base = gamma_spec_1;
 		break;
@@ -2100,9 +2228,10 @@
 		break;
 	}
 
+	val = sd->ctrls[GAMMA].val;
 	for (i = 0; i < sizeof gamma; i++)
 		gamma[i] = gamma_base[i]
-			+ delta[i] * (sd->gamma - GAMMA_DEF) / 32;
+			+ delta[i] * (val - GAMMA_DEF) / 32;
 	reg_w(gspca_dev, 0x20, gamma, sizeof gamma);
 }
 
@@ -2110,7 +2239,7 @@
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 
-	if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX))
+	if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
 		return;
 	switch (sd->sensor) {
 	case SENSOR_OV7630:
@@ -2121,74 +2250,91 @@
 			comb = 0xc0;
 		else
 			comb = 0xa0;
-		if (sd->autogain)
+		if (sd->ctrls[AUTOGAIN].val)
 			comb |= 0x03;
 		i2c_w1(&sd->gspca_dev, 0x13, comb);
 		return;
 	    }
 	}
-	if (sd->autogain)
+	if (sd->ctrls[AUTOGAIN].val)
 		sd->ag_cnt = AG_CNT_START;
 	else
 		sd->ag_cnt = -1;
 }
 
-/* hv7131r/ov7630/ov7648 only */
-static void setvflip(struct sd *sd)
+static void sethvflip(struct gspca_dev *gspca_dev)
 {
+	struct sd *sd = (struct sd *) gspca_dev;
 	u8 comn;
 
-	if (sd->gspca_dev.ctrl_dis & (1 << VFLIP_IDX))
-		return;
 	switch (sd->sensor) {
 	case SENSOR_HV7131R:
 		comn = 0x18;			/* clkdiv = 1, ablcen = 1 */
-		if (sd->vflip)
+		if (sd->ctrls[VFLIP].val)
 			comn |= 0x01;
-		i2c_w1(&sd->gspca_dev, 0x01, comn);	/* sctra */
+		i2c_w1(gspca_dev, 0x01, comn);	/* sctra */
 		break;
 	case SENSOR_OV7630:
 		comn = 0x02;
-		if (!sd->vflip)
+		if (!sd->ctrls[VFLIP].val)
 			comn |= 0x80;
-		i2c_w1(&sd->gspca_dev, 0x75, comn);
+		i2c_w1(gspca_dev, 0x75, comn);
 		break;
-	default:
-/*	case SENSOR_OV7648: */
+	case SENSOR_OV7648:
 		comn = 0x06;
-		if (sd->vflip)
+		if (sd->ctrls[VFLIP].val)
 			comn |= 0x80;
-		i2c_w1(&sd->gspca_dev, 0x75, comn);
+		i2c_w1(gspca_dev, 0x75, comn);
+		break;
+	case SENSOR_PO2030N:
+		/* Reg. 0x1E: Timing Generator Control Register 2 (Tgcontrol2)
+		 * (reset value: 0x0A)
+		 * bit7: HM: Horizontal Mirror: 0: disable, 1: enable
+		 * bit6: VM: Vertical Mirror: 0: disable, 1: enable
+		 * bit5: ST: Shutter Selection: 0: electrical, 1: mechanical
+		 * bit4: FT: Single Frame Transfer: 0: disable, 1: enable
+		 * bit3-0: X
+		 */
+		comn = 0x0a;
+		if (sd->ctrls[HFLIP].val)
+			comn |= 0x80;
+		if (sd->ctrls[VFLIP].val)
+			comn |= 0x40;
+		i2c_w1(&sd->gspca_dev, 0x1e, comn);
 		break;
 	}
 }
 
-static void setsharpness(struct sd *sd)
+static void setsharpness(struct gspca_dev *gspca_dev)
 {
-	reg_w1(&sd->gspca_dev, 0x99, sd->sharpness);
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	reg_w1(gspca_dev, 0x99, sd->ctrls[SHARPNESS].val);
 }
 
-static void setinfrared(struct sd *sd)
+static void setinfrared(struct gspca_dev *gspca_dev)
 {
-	if (sd->gspca_dev.ctrl_dis & (1 << INFRARED_IDX))
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	if (gspca_dev->ctrl_dis & (1 << INFRARED))
 		return;
 /*fixme: different sequence for StarCam Clip and StarCam 370i */
 /* Clip */
-	i2c_w1(&sd->gspca_dev, 0x02,			/* gpio */
-		sd->infrared ? 0x66 : 0x64);
+	i2c_w1(gspca_dev, 0x02,				/* gpio */
+		sd->ctrls[INFRARED].val ? 0x66 : 0x64);
 }
 
 static void setfreq(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 
-	if (gspca_dev->ctrl_dis & (1 << FREQ_IDX))
+	if (gspca_dev->ctrl_dis & (1 << FREQ))
 		return;
 	if (sd->sensor == SENSOR_OV7660) {
 		u8 com8;
 
 		com8 = 0xdf;		/* auto gain/wb/expo */
-		switch (sd->freq) {
+		switch (sd->ctrls[FREQ].val) {
 		case 0: /* Banding filter disabled */
 			i2c_w1(gspca_dev, 0x13, com8 | 0x20);
 			break;
@@ -2216,7 +2362,7 @@
 			break;
 		}
 
-		switch (sd->freq) {
+		switch (sd->ctrls[FREQ].val) {
 		case 0: /* Banding filter disabled */
 			break;
 		case 1: /* 50 hz (filter on and framerate adj) */
@@ -2334,6 +2480,7 @@
 		reg17 = 0xa2;
 		break;
 	case SENSOR_MT9V111:
+	case SENSOR_MI0360B:
 		reg17 = 0xe0;
 		break;
 	case SENSOR_ADCM1700:
@@ -2375,6 +2522,7 @@
 		break;
 	case SENSOR_GC0307:
 	case SENSOR_MT9V111:
+	case SENSOR_MI0360B:
 		reg_w1(gspca_dev, 0x9a, 0x07);
 		break;
 	case SENSOR_OV7630:
@@ -2389,7 +2537,7 @@
 		reg_w1(gspca_dev, 0x9a, 0x08);
 		break;
 	}
-	setsharpness(sd);
+	setsharpness(gspca_dev);
 
 	reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
 	reg_w1(gspca_dev, 0x05, 0x20);		/* red */
@@ -2414,6 +2562,11 @@
 		reg17 = 0xa2;
 		reg1 = 0x44;
 		break;
+	case SENSOR_MI0360B:
+		init = mi0360b_sensor_param1;
+		reg1 &= ~0x02;		/* don't inverse pin S_PWR_DN */
+		reg17 = 0xe2;
+		break;
 	case SENSOR_MO4000:
 		if (mode) {
 /*			reg1 = 0x46;	 * 320 clk 48Mhz 60fp/s */
@@ -2474,8 +2627,7 @@
 		reg1 = 0x44;
 		reg17 = 0xa2;
 		break;
-	default:
-/*	case SENSOR_SP80708: */
+	case SENSOR_SP80708:
 		init = sp80708_sensor_param1;
 		if (mode) {
 /*??			reg1 = 0x04;	 * 320 clk 48Mhz */
@@ -2526,7 +2678,6 @@
 		break;
 	}
 
-
 	/* here change size mode 0 -> VGA; 1 -> CIF */
 	sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40;
 	reg_w1(gspca_dev, 0x18, sd->reg18);
@@ -2535,13 +2686,13 @@
 	reg_w1(gspca_dev, 0x17, reg17);
 	reg_w1(gspca_dev, 0x01, reg1);
 
-	setvflip(sd);
+	sethvflip(gspca_dev);
 	setbrightness(gspca_dev);
 	setcontrast(gspca_dev);
 	setcolors(gspca_dev);
 	setautogain(gspca_dev);
 	setfreq(gspca_dev);
-	return 0;
+	return gspca_dev->usb_err;
 }
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -2568,6 +2719,7 @@
 		data = 0x2b;
 		break;
 	case SENSOR_MI0360:
+	case SENSOR_MI0360B:
 		i2c_w8(gspca_dev, stopmi0360);
 		data = 0x29;
 		break;
@@ -2641,6 +2793,7 @@
 		default:
 /*		case SENSOR_MO4000: */
 /*		case SENSOR_MI0360: */
+/*		case SENSOR_MI0360B: */
 /*		case SENSOR_MT9V111: */
 			expotimes = sd->exposure;
 			expotimes += (luma_mean - delta) >> 6;
@@ -2663,236 +2816,52 @@
 	struct sd *sd = (struct sd *) gspca_dev;
 	int sof, avg_lum;
 
-	sof = len - 64;
-	if (sof >= 0 && data[sof] == 0xff && data[sof + 1] == 0xd9) {
+	/* the image ends on a 64 bytes block starting with
+	 *	ff d9 ff ff 00 c4 c4 96
+	 * and followed by various information including luminosity */
+	/* this block may be splitted between two packets */
+	/* a new image always starts in a new packet */
+	switch (gspca_dev->last_packet_type) {
+	case DISCARD_PACKET:		/* restart image building */
+		sof = len - 64;
+		if (sof >= 0 && data[sof] == 0xff && data[sof + 1] == 0xd9)
+			gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+		return;
+	case LAST_PACKET:		/* put the JPEG 422 header */
+		gspca_frame_add(gspca_dev, FIRST_PACKET,
+				sd->jpeg_hdr, JPEG_HDR_SZ);
+		break;
+	}
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 
-		/* end of frame */
-		gspca_frame_add(gspca_dev, LAST_PACKET,
-				data, sof + 2);
-		if (sd->ag_cnt < 0)
-			return;
+	data = gspca_dev->image;
+	if (data == NULL)
+		return;
+	sof = gspca_dev->image_len - 64;
+	if (data[sof] != 0xff
+	 || data[sof + 1] != 0xd9)
+		return;
+
+	/* end of image found - remove the trailing data */
+	gspca_dev->image_len = sof + 2;
+	gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+	if (sd->ag_cnt < 0)
+		return;
 /* w1 w2 w3 */
 /* w4 w5 w6 */
 /* w7 w8 */
 /* w4 */
-		avg_lum = ((data[sof + 29] << 8) | data[sof + 30]) >> 6;
+	avg_lum = ((data[sof + 29] << 8) | data[sof + 30]) >> 6;
 /* w6 */
-		avg_lum += ((data[sof + 33] << 8) | data[sof + 34]) >> 6;
+	avg_lum += ((data[sof + 33] << 8) | data[sof + 34]) >> 6;
 /* w2 */
-		avg_lum += ((data[sof + 25] << 8) | data[sof + 26]) >> 6;
+	avg_lum += ((data[sof + 25] << 8) | data[sof + 26]) >> 6;
 /* w8 */
-		avg_lum += ((data[sof + 37] << 8) | data[sof + 38]) >> 6;
+	avg_lum += ((data[sof + 37] << 8) | data[sof + 38]) >> 6;
 /* w5 */
-		avg_lum += ((data[sof + 31] << 8) | data[sof + 32]) >> 4;
-		avg_lum >>= 4;
-		atomic_set(&sd->avg_lum, avg_lum);
-		return;
-	}
-	if (gspca_dev->last_packet_type == LAST_PACKET) {
-
-		/* put the JPEG 422 header */
-		gspca_frame_add(gspca_dev, FIRST_PACKET,
-			sd->jpeg_hdr, JPEG_HDR_SZ);
-	}
-	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
-}
-
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->brightness = val;
-	if (gspca_dev->streaming)
-		setbrightness(gspca_dev);
-	return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->brightness;
-	return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->contrast = val;
-	if (gspca_dev->streaming)
-		setcontrast(gspca_dev);
-	return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->contrast;
-	return 0;
-}
-
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->colors = val;
-	if (gspca_dev->streaming)
-		setcolors(gspca_dev);
-	return 0;
-}
-
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->colors;
-	return 0;
-}
-
-static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->blue = val;
-	if (gspca_dev->streaming)
-		setredblue(gspca_dev);
-	return 0;
-}
-
-static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->blue;
-	return 0;
-}
-
-static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->red = val;
-	if (gspca_dev->streaming)
-		setredblue(gspca_dev);
-	return 0;
-}
-
-static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->red;
-	return 0;
-}
-
-static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->gamma = val;
-	if (gspca_dev->streaming)
-		setgamma(gspca_dev);
-	return 0;
-}
-
-static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->gamma;
-	return 0;
-}
-
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->autogain = val;
-	if (gspca_dev->streaming)
-		setautogain(gspca_dev);
-	return 0;
-}
-
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->autogain;
-	return 0;
-}
-
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->sharpness = val;
-	if (gspca_dev->streaming)
-		setsharpness(sd);
-	return 0;
-}
-
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->sharpness;
-	return 0;
-}
-
-static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->vflip = val;
-	if (gspca_dev->streaming)
-		setvflip(sd);
-	return 0;
-}
-
-static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->vflip;
-	return 0;
-}
-
-static int sd_setinfrared(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->infrared = val;
-	if (gspca_dev->streaming)
-		setinfrared(sd);
-	return 0;
-}
-
-static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->infrared;
-	return 0;
-}
-
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->freq = val;
-	if (gspca_dev->streaming)
-		setfreq(gspca_dev);
-	return 0;
-}
-
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->freq;
-	return 0;
+	avg_lum += ((data[sof + 31] << 8) | data[sof + 32]) >> 4;
+	avg_lum >>= 4;
+	atomic_set(&sd->avg_lum, avg_lum);
 }
 
 static int sd_set_jcomp(struct gspca_dev *gspca_dev,
@@ -2944,7 +2913,7 @@
 	return -EINVAL;
 }
 
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
 			u8 *data,		/* interrupt packet data */
 			int len)		/* interrupt packet length */
@@ -2967,7 +2936,7 @@
 static const struct sd_desc sd_desc = {
 	.name = MODULE_NAME,
 	.ctrls = sd_ctrls,
-	.nctrls = ARRAY_SIZE(sd_ctrls),
+	.nctrls = NCTRLS,
 	.config = sd_config,
 	.init = sd_init,
 	.start = sd_start,
@@ -2977,7 +2946,7 @@
 	.get_jcomp = sd_get_jcomp,
 	.set_jcomp = sd_set_jcomp,
 	.querymenu = sd_querymenu,
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 	.int_pkt_scan = sd_int_pkt_scan,
 #endif
 };
@@ -3005,6 +2974,7 @@
 	{USB_DEVICE(0x0c45, 0x607c), BS(SN9C102P, HV7131R)},
 /*	{USB_DEVICE(0x0c45, 0x607e), BS(SN9C102P, OV7630)}, */
 	{USB_DEVICE(0x0c45, 0x60c0), BS(SN9C105, MI0360)},
+						/* or MT9V111 */
 /*	{USB_DEVICE(0x0c45, 0x60c2), BS(SN9C105, P1030xC)}, */
 /*	{USB_DEVICE(0x0c45, 0x60c8), BS(SN9C105, OM6802)}, */
 /*	{USB_DEVICE(0x0c45, 0x60cc), BS(SN9C105, HV7131GP)}, */
@@ -3019,7 +2989,7 @@
 	{USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)},
 #endif
 	{USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)},	/*sn9c128*/
-/*	{USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, PO2030N)}, * / GC0305*/
+	{USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, PO2030N)},	/* /GC0305*/
 /*	{USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */
 	{USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)},	/*sn9c128*/
 	{USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)},	/*sn9c128*/
@@ -3031,12 +3001,12 @@
 	{USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)},	/*sn9c325?*/
 /*bw600.inf:*/
 	{USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)},	/*sn9c325?*/
+	{USB_DEVICE(0x0c45, 0x612b), BS(SN9C110, ADCM1700)},
 	{USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)},
 	{USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)},
 /*	{USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */
-#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
 	{USB_DEVICE(0x0c45, 0x6130), BS(SN9C120, MI0360)},
-#endif
+						/* or MT9V111 / MI0360B */
 /*	{USB_DEVICE(0x0c45, 0x6132), BS(SN9C120, OV7670)}, */
 	{USB_DEVICE(0x0c45, 0x6138), BS(SN9C120, MO4000)},
 	{USB_DEVICE(0x0c45, 0x613a), BS(SN9C120, OV7648)},
@@ -3076,17 +3046,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	info("registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	info("deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/spca1528.c b/drivers/media/video/gspca/spca1528.c
index 3f514eb..e643386 100644
--- a/drivers/media/video/gspca/spca1528.c
+++ b/drivers/media/video/gspca/spca1528.c
@@ -171,7 +171,7 @@
 	PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", req, index,
 			 gspca_dev->usb_buf[0]);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_r err %d", ret);
+		err("reg_r err %d", ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -193,7 +193,7 @@
 			value, index,
 			NULL, 0, 500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_w err %d", ret);
+		err("reg_w err %d", ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -217,7 +217,7 @@
 			value, index,
 			gspca_dev->usb_buf, 1, 500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_w err %d", ret);
+		err("reg_w err %d", ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -587,18 +587,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	info("registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	info("deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c
index c02beb6..8e202b9 100644
--- a/drivers/media/video/gspca/spca500.c
+++ b/drivers/media/video/gspca/spca500.c
@@ -396,7 +396,7 @@
 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 			value, index, NULL, 0, 500);
 	if (ret < 0)
-		PDEBUG(D_ERR, "reg write: error %d", ret);
+		err("reg write: error %d", ret);
 	return ret;
 }
 
@@ -418,8 +418,8 @@
 			gspca_dev->usb_buf, length,
 			500);		/* timeout */
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_r_12 err %d", ret);
-		return -1;
+		err("reg_r_12 err %d", ret);
+		return ret;
 	}
 	return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
 }
@@ -1093,17 +1093,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c
index c993339..642839a 100644
--- a/drivers/media/video/gspca/spca501.c
+++ b/drivers/media/video/gspca/spca501.c
@@ -1724,7 +1724,7 @@
 	{0x00, 0x0000, 0x0048},
 	{0x00, 0x0000, 0x0049},
 	{0x00, 0x0008, 0x004a},
-/* DSP Registers	 */
+/* DSP Registers	*/
 	{0x01, 0x00a6, 0x0000},
 	{0x01, 0x0028, 0x0001},
 	{0x01, 0x0000, 0x0002},
@@ -1788,7 +1788,7 @@
 	{0x05, 0x0022, 0x0004},
 	{0x05, 0x0025, 0x0001},
 	{0x05, 0x0000, 0x0000},
-/* Part 4             */
+/* Part 4 */
 	{0x05, 0x0026, 0x0001},
 	{0x05, 0x0001, 0x0000},
 	{0x05, 0x0027, 0x0001},
@@ -1806,7 +1806,7 @@
 	{0x05, 0x0001, 0x0000},
 	{0x05, 0x0027, 0x0001},
 	{0x05, 0x004e, 0x0000},
-/* Part 5        	 */
+/* Part 5 */
 	{0x01, 0x0003, 0x003f},
 	{0x01, 0x0001, 0x0056},
 	{0x01, 0x000f, 0x0008},
@@ -1852,7 +1852,7 @@
 	PDEBUG(D_USBO, "reg write: 0x%02x 0x%02x 0x%02x",
 		req, index, value);
 	if (ret < 0)
-		PDEBUG(D_ERR, "reg write: error %d", ret);
+		err("reg write: error %d", ret);
 	return ret;
 }
 
@@ -2189,17 +2189,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c
index c576eed..bc9dd90 100644
--- a/drivers/media/video/gspca/spca505.c
+++ b/drivers/media/video/gspca/spca505.c
@@ -368,10 +368,6 @@
 	{0x08, 0x00, 0x00},
 	{0x08, 0x00, 0x01},
 	{0x08, 0x00, 0x02},
-	{0x00, 0x01, 0x00},
-	{0x00, 0x01, 0x01},
-	{0x00, 0x01, 0x34},
-	{0x00, 0x01, 0x35},
 	{0x06, 0x18, 0x08},
 	{0x06, 0xfc, 0x09},
 	{0x06, 0xfc, 0x0a},
@@ -582,7 +578,7 @@
 	PDEBUG(D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d",
 		req, index, value, ret);
 	if (ret < 0)
-		PDEBUG(D_ERR, "reg write: error %d", ret);
+		err("reg write: error %d", ret);
 	return ret;
 }
 
@@ -689,8 +685,7 @@
 		return ret;
 	}
 	if (ret != 0x0101) {
-		PDEBUG(D_ERR|D_CONF,
-			"After vector read returns 0x%04x should be 0x0101",
+		err("After vector read returns 0x%04x should be 0x0101",
 			ret);
 	}
 
@@ -821,18 +816,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c
index edf0fe1..7307638 100644
--- a/drivers/media/video/gspca/spca508.c
+++ b/drivers/media/video/gspca/spca508.c
@@ -92,8 +92,7 @@
  * Initialization data: this is the first set-up data written to the
  * device (before the open data).
  */
-static const u16 spca508_init_data[][2] =
-{
+static const u16 spca508_init_data[][2] = {
 	{0x0000, 0x870b},
 
 	{0x0020, 0x8112},	/* Video drop enable, ISO streaming disable */
@@ -1276,7 +1275,7 @@
 	PDEBUG(D_USBO, "reg write i:0x%04x = 0x%02x",
 		index, value);
 	if (ret < 0)
-		PDEBUG(D_ERR|D_USBO, "reg write: error %d", ret);
+		err("reg write: error %d", ret);
 	return ret;
 }
 
@@ -1298,7 +1297,7 @@
 	PDEBUG(D_USBI, "reg read i:%04x --> %02x",
 		index, gspca_dev->usb_buf[0]);
 	if (ret < 0) {
-		PDEBUG(D_ERR|D_USBI, "reg_read err %d", ret);
+		err("reg_read err %d", ret);
 		return ret;
 	}
 	return gspca_dev->usb_buf[0];
@@ -1543,18 +1542,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c
index 7bb2355..ad73f48 100644
--- a/drivers/media/video/gspca/spca561.c
+++ b/drivers/media/video/gspca/spca561.c
@@ -315,7 +315,7 @@
 			      value, index, NULL, 0, 500);
 	PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);
 	if (ret < 0)
-		PDEBUG(D_ERR, "reg write: error %d", ret);
+		err("reg write: error %d", ret);
 }
 
 static void write_vector(struct gspca_dev *gspca_dev,
@@ -787,7 +787,7 @@
 			return;
 		}
 
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 		if (data[0] & 0x20) {
 			input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
 			input_sync(gspca_dev->input_dev);
@@ -1037,7 +1037,7 @@
 	.start = sd_start_12a,
 	.stopN = sd_stopN,
 	.pkt_scan = sd_pkt_scan,
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 	.other_input = 1,
 #endif
 };
@@ -1051,7 +1051,7 @@
 	.stopN = sd_stopN,
 	.pkt_scan = sd_pkt_scan,
 	.dq_callback = do_autogain,
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 	.other_input = 1,
 #endif
 };
@@ -1107,17 +1107,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c
index 09b3f93f..4040677 100644
--- a/drivers/media/video/gspca/sq905.c
+++ b/drivers/media/video/gspca/sq905.c
@@ -123,7 +123,7 @@
 			      SQ905_COMMAND, index, gspca_dev->usb_buf, 1,
 			      SQ905_CMD_TIMEOUT);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)",
+		err("%s: usb_control_msg failed (%d)",
 			__func__, ret);
 		return ret;
 	}
@@ -135,7 +135,7 @@
 			      SQ905_PING, 0, gspca_dev->usb_buf, 1,
 			      SQ905_CMD_TIMEOUT);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "%s: usb_control_msg failed 2 (%d)",
+		err("%s: usb_control_msg failed 2 (%d)",
 			__func__, ret);
 		return ret;
 	}
@@ -158,7 +158,7 @@
 			      SQ905_READ_DONE, 0, gspca_dev->usb_buf, 1,
 			      SQ905_CMD_TIMEOUT);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", __func__, ret);
+		err("%s: usb_control_msg failed (%d)", __func__, ret);
 		return ret;
 	}
 
@@ -186,7 +186,7 @@
 	if (need_lock)
 		mutex_unlock(&gspca_dev->usb_lock);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", __func__, ret);
+		err("%s: usb_control_msg failed (%d)", __func__, ret);
 		return ret;
 	}
 	ret = usb_bulk_msg(gspca_dev->dev,
@@ -195,7 +195,7 @@
 
 	/* successful, it returns 0, otherwise  negative */
 	if (ret < 0 || act_len != size) {
-		PDEBUG(D_ERR, "bulk read fail (%d) len %d/%d",
+		err("bulk read fail (%d) len %d/%d",
 			ret, act_len, size);
 		return -EIO;
 	}
@@ -226,7 +226,7 @@
 
 	buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
 	if (!buffer) {
-		PDEBUG(D_ERR, "Couldn't allocate USB buffer");
+		err("Couldn't allocate USB buffer");
 		goto quit_stream;
 	}
 
@@ -436,19 +436,12 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c
index 4c70628..c2e88b5 100644
--- a/drivers/media/video/gspca/sq905c.c
+++ b/drivers/media/video/gspca/sq905c.c
@@ -95,7 +95,7 @@
 			      command, index, NULL, 0,
 			      SQ905C_CMD_TIMEOUT);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)",
+		err("%s: usb_control_msg failed (%d)",
 			__func__, ret);
 		return ret;
 	}
@@ -115,7 +115,7 @@
 			      command, index, gspca_dev->usb_buf, size,
 			      SQ905C_CMD_TIMEOUT);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)",
+		err("%s: usb_control_msg failed (%d)",
 		       __func__, ret);
 		return ret;
 	}
@@ -146,7 +146,7 @@
 
 	buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
 	if (!buffer) {
-		PDEBUG(D_ERR, "Couldn't allocate USB buffer");
+		err("Couldn't allocate USB buffer");
 		goto quit_stream;
 	}
 
@@ -341,19 +341,12 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/sq930x.c b/drivers/media/video/gspca/sq930x.c
index 7ae6522..3e4b0b9 100644
--- a/drivers/media/video/gspca/sq930x.c
+++ b/drivers/media/video/gspca/sq930x.c
@@ -468,7 +468,7 @@
 			value, 0, gspca_dev->usb_buf, len,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_r %04x failed %d", value, ret);
+		err("reg_r %04x failed %d", value, ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -488,7 +488,7 @@
 			500);
 	msleep(30);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_w %04x %04x failed %d", value, index, ret);
+		err("reg_w %04x %04x failed %d", value, index, ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -511,7 +511,7 @@
 			1000);
 	msleep(30);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_wb %04x %04x failed %d", value, index, ret);
+		err("reg_wb %04x %04x failed %d", value, index, ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -556,7 +556,7 @@
 			gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "i2c_write failed %d", ret);
+		err("i2c_write failed %d", ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -612,7 +612,7 @@
 				gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
 				500);
 		if (ret < 0) {
-			PDEBUG(D_ERR, "ucbus_write failed %d", ret);
+			err("ucbus_write failed %d", ret);
 			gspca_dev->usb_err = ret;
 			return;
 		}
@@ -688,7 +688,7 @@
 			break;
 	}
 	if (i >= ARRAY_SIZE(probe_order))
-		PDEBUG(D_PROBE, "Unknown sensor");
+		err("Unknown sensor");
 	else
 		sd->sensor = probe_order[i];
 }
@@ -1079,7 +1079,7 @@
 	gspca_dev->cam.bulk_nurbs = 1;
 	ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC);
 	if (ret < 0)
-		PDEBUG(D_ERR|D_PACK, "sd_dq_callback() err %d", ret);
+		err("sd_dq_callback() err %d", ret);
 
 	/* wait a little time, otherwise the webcam crashes */
 	msleep(100);
@@ -1185,18 +1185,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	info("registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	info("deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c
index 2aedf4b..11a192b 100644
--- a/drivers/media/video/gspca/stk014.c
+++ b/drivers/media/video/gspca/stk014.c
@@ -27,14 +27,21 @@
 MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver");
 MODULE_LICENSE("GPL");
 
+/* controls */
+enum e_ctrl {
+	BRIGHTNESS,
+	CONTRAST,
+	COLORS,
+	LIGHTFREQ,
+	NCTRLS		/* number of controls */
+};
+
 /* specific webcam descriptor */
 struct sd {
 	struct gspca_dev gspca_dev;	/* !! must be the first item */
 
-	unsigned char brightness;
-	unsigned char contrast;
-	unsigned char colors;
-	unsigned char lightfreq;
+	struct gspca_ctrl ctrls[NCTRLS];
+
 	u8 quality;
 #define QUALITY_MIN 70
 #define QUALITY_MAX 95
@@ -44,17 +51,13 @@
 };
 
 /* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setcontrast(struct gspca_dev *gspca_dev);
+static void setcolors(struct gspca_dev *gspca_dev);
+static void setlightfreq(struct gspca_dev *gspca_dev);
 
-static const struct ctrl sd_ctrls[] = {
-	{
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[BRIGHTNESS] = {
 	    {
 		.id      = V4L2_CID_BRIGHTNESS,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -62,13 +65,11 @@
 		.minimum = 0,
 		.maximum = 255,
 		.step    = 1,
-#define BRIGHTNESS_DEF 127
-		.default_value = BRIGHTNESS_DEF,
+		.default_value = 127,
 	    },
-	    .set = sd_setbrightness,
-	    .get = sd_getbrightness,
+	    .set_control = setbrightness
 	},
-	{
+[CONTRAST] = {
 	    {
 		.id      = V4L2_CID_CONTRAST,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -76,13 +77,11 @@
 		.minimum = 0,
 		.maximum = 255,
 		.step    = 1,
-#define CONTRAST_DEF 127
-		.default_value = CONTRAST_DEF,
+		.default_value = 127,
 	    },
-	    .set = sd_setcontrast,
-	    .get = sd_getcontrast,
+	    .set_control = setcontrast
 	},
-	{
+[COLORS] = {
 	    {
 		.id      = V4L2_CID_SATURATION,
 		.type    = V4L2_CTRL_TYPE_INTEGER,
@@ -90,13 +89,11 @@
 		.minimum = 0,
 		.maximum = 255,
 		.step    = 1,
-#define COLOR_DEF 127
-		.default_value = COLOR_DEF,
+		.default_value = 127,
 	    },
-	    .set = sd_setcolors,
-	    .get = sd_getcolors,
+	    .set_control = setcolors
 	},
-	{
+[LIGHTFREQ] = {
 	    {
 		.id	 = V4L2_CID_POWER_LINE_FREQUENCY,
 		.type    = V4L2_CTRL_TYPE_MENU,
@@ -104,11 +101,9 @@
 		.minimum = 1,
 		.maximum = 2,	/* 0: 0, 1: 50Hz, 2:60Hz */
 		.step    = 1,
-#define FREQ_DEF 1
-		.default_value = FREQ_DEF,
+		.default_value = 1,
 	    },
-	    .set = sd_setfreq,
-	    .get = sd_getfreq,
+	    .set_control = setlightfreq
 	},
 };
 
@@ -142,7 +137,7 @@
 			gspca_dev->usb_buf, 1,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_r err %d", ret);
+		err("reg_r err %d", ret);
 		gspca_dev->usb_err = ret;
 		return 0;
 	}
@@ -167,7 +162,7 @@
 			0,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_w err %d", ret);
+		err("reg_w err %d", ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -197,7 +192,7 @@
 			&alen,
 			500);		/* timeout in milliseconds */
 	if (ret < 0) {
-		PDEBUG(D_ERR, "rcv_val err %d", ret);
+		err("rcv_val err %d", ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -240,7 +235,7 @@
 			&alen,
 			500);	/* timeout in milliseconds */
 	if (ret < 0) {
-		PDEBUG(D_ERR, "snd_val err %d", ret);
+		err("snd_val err %d", ret);
 		gspca_dev->usb_err = ret;
 	} else {
 		if (ads == 0x003f08) {
@@ -264,7 +259,7 @@
 	int parval;
 
 	parval = 0x06000000		/* whiteness */
-		+ (sd->brightness << 16);
+		+ (sd->ctrls[BRIGHTNESS].val << 16);
 	set_par(gspca_dev, parval);
 }
 
@@ -274,7 +269,7 @@
 	int parval;
 
 	parval = 0x07000000		/* contrast */
-		+ (sd->contrast << 16);
+		+ (sd->ctrls[CONTRAST].val << 16);
 	set_par(gspca_dev, parval);
 }
 
@@ -284,15 +279,15 @@
 	int parval;
 
 	parval = 0x08000000		/* saturation */
-		+ (sd->colors << 16);
+		+ (sd->ctrls[COLORS].val << 16);
 	set_par(gspca_dev, parval);
 }
 
-static void setfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev)
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 
-	set_par(gspca_dev, sd->lightfreq == 1
+	set_par(gspca_dev, sd->ctrls[LIGHTFREQ].val == 1
 			? 0x33640000		/* 50 Hz */
 			: 0x33780000);		/* 60 Hz */
 }
@@ -305,10 +300,7 @@
 
 	gspca_dev->cam.cam_mode = vga_mode;
 	gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
-	sd->brightness = BRIGHTNESS_DEF;
-	sd->contrast = CONTRAST_DEF;
-	sd->colors = COLOR_DEF;
-	sd->lightfreq = FREQ_DEF;
+	gspca_dev->cam.ctrls = sd->ctrls;
 	sd->quality = QUALITY_DEF;
 	return 0;
 }
@@ -323,7 +315,7 @@
 	ret = reg_r(gspca_dev, 0x0740);
 	if (gspca_dev->usb_err >= 0) {
 		if (ret != 0xff) {
-			PDEBUG(D_ERR|D_STREAM, "init reg: 0x%02x", ret);
+			err("init reg: 0x%02x", ret);
 			gspca_dev->usb_err = -EIO;
 		}
 	}
@@ -357,7 +349,7 @@
 					gspca_dev->iface,
 					gspca_dev->alt);
 	if (ret < 0) {
-		PDEBUG(D_ERR|D_STREAM, "set intf %d %d failed",
+		err("set intf %d %d failed",
 			gspca_dev->iface, gspca_dev->alt);
 		gspca_dev->usb_err = ret;
 		goto out;
@@ -378,7 +370,7 @@
 	set_par(gspca_dev, 0x0a800000);		/* Green ? */
 	set_par(gspca_dev, 0x0b800000);		/* Blue ? */
 	set_par(gspca_dev, 0x0d030000);		/* Gamma ? */
-	setfreq(gspca_dev);			/* light frequency */
+	setlightfreq(gspca_dev);
 
 	/* start the video flow */
 	set_par(gspca_dev, 0x01000000);
@@ -441,78 +433,6 @@
 	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->brightness = val;
-	if (gspca_dev->streaming)
-		setbrightness(gspca_dev);
-	return gspca_dev->usb_err;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->brightness;
-	return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->contrast = val;
-	if (gspca_dev->streaming)
-		setcontrast(gspca_dev);
-	return gspca_dev->usb_err;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->contrast;
-	return 0;
-}
-
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->colors = val;
-	if (gspca_dev->streaming)
-		setcolors(gspca_dev);
-	return gspca_dev->usb_err;
-}
-
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->colors;
-	return 0;
-}
-
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	sd->lightfreq = val;
-	if (gspca_dev->streaming)
-		setfreq(gspca_dev);
-	return gspca_dev->usb_err;
-}
-
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
-{
-	struct sd *sd = (struct sd *) gspca_dev;
-
-	*val = sd->lightfreq;
-	return 0;
-}
-
 static int sd_querymenu(struct gspca_dev *gspca_dev,
 			struct v4l2_querymenu *menu)
 {
@@ -563,7 +483,7 @@
 static const struct sd_desc sd_desc = {
 	.name = MODULE_NAME,
 	.ctrls = sd_ctrls,
-	.nctrls = ARRAY_SIZE(sd_ctrls),
+	.nctrls = NCTRLS,
 	.config = sd_config,
 	.init = sd_init,
 	.start = sd_start,
@@ -603,17 +523,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	info("registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	info("deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c
index e50dd76..b199ad4 100644
--- a/drivers/media/video/gspca/stv0680.c
+++ b/drivers/media/video/gspca/stv0680.c
@@ -1,7 +1,7 @@
 /*
  * STV0680 USB Camera Driver
  *
- * Copyright (C) 2009 Hans de Goede <hdgoede@redhat.com>
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
  *
  * This module is adapted from the in kernel v4l1 stv680 driver:
  *
@@ -31,7 +31,7 @@
 
 #include "gspca.h"
 
-MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
 MODULE_DESCRIPTION("STV0680 USB Camera Driver");
 MODULE_LICENSE("GPL");
 
@@ -79,8 +79,7 @@
 			      val, 0, gspca_dev->usb_buf, size, 500);
 
 	if ((ret < 0) && (req != 0x0a))
-		PDEBUG(D_ERR,
-		       "usb_control_msg error %i, request = 0x%x, error = %i",
+		err("usb_control_msg error %i, request = 0x%x, error = %i",
 		       set, req, ret);
 
 	return ret;
@@ -237,7 +236,7 @@
 
 	if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0100, 0x12) != 0x12 ||
 	    gspca_dev->usb_buf[8] != 0x53 || gspca_dev->usb_buf[9] != 0x05) {
-		PDEBUG(D_ERR, "Could not get descriptor 0100.");
+		err("Could not get descriptor 0100.");
 		return stv0680_handle_error(gspca_dev, -EIO);
 	}
 
@@ -357,17 +356,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c
index 14f179a..086de44 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.c
@@ -189,7 +189,7 @@
 			      0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH,
 			      STV06XX_URB_MSG_TIMEOUT);
 	if (err < 0) {
-		PDEBUG(D_ERR, "I2C: Read error writing address: %d", err);
+		err("I2C: Read error writing address: %d", err);
 		return err;
 	}
 
@@ -428,7 +428,7 @@
 	}
 }
 
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
 			u8 *data,		/* interrupt packet data */
 			int len)		/* interrupt packet length */
@@ -462,7 +462,7 @@
 	.start = stv06xx_start,
 	.stopN = stv06xx_stopN,
 	.pkt_scan = stv06xx_pkt_scan,
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 	.int_pkt_scan = sd_int_pkt_scan,
 #endif
 };
@@ -562,17 +562,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h
index 053a27e..e0f63c5 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx.h
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.h
@@ -37,7 +37,7 @@
 
 #define STV_ISOC_ENDPOINT_ADDR		0x81
 
-#define STV_REG23 			0x0423
+#define STV_REG23			0x0423
 
 /* Control registers of the STV0600 ASIC */
 #define STV_I2C_PARTNER			0x1420
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
index 706e08d..17531b4 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
@@ -39,8 +39,8 @@
 			.minimum	= 0x00,
 			.maximum	= 0xff,
 			.step		= 0x1,
-			.default_value 	= HDCS_DEFAULT_EXPOSURE,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.default_value	= HDCS_DEFAULT_EXPOSURE,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = hdcs_set_exposure,
 		.get = hdcs_get_exposure
@@ -52,8 +52,8 @@
 			.minimum	= 0x00,
 			.maximum	= 0xff,
 			.step		= 0x1,
-			.default_value 	= HDCS_DEFAULT_GAIN,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.default_value	= HDCS_DEFAULT_GAIN,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = hdcs_set_gain,
 		.get = hdcs_get_gain
@@ -83,8 +83,8 @@
 			.minimum	= 0x00,
 			.maximum	= 0xffff,
 			.step		= 0x1,
-			.default_value 	= HDCS_DEFAULT_EXPOSURE,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.default_value	= HDCS_DEFAULT_EXPOSURE,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = hdcs_set_exposure,
 		.get = hdcs_get_exposure
@@ -96,8 +96,8 @@
 			.minimum	= 0x00,
 			.maximum	= 0xff,
 			.step		= 0x1,
-			.default_value 	= HDCS_DEFAULT_GAIN,
-			.flags         	= V4L2_CTRL_FLAG_SLIDER
+			.default_value	= HDCS_DEFAULT_GAIN,
+			.flags		= V4L2_CTRL_FLAG_SLIDER
 		},
 		.set = hdcs_set_gain,
 		.get = hdcs_get_gain
@@ -163,7 +163,8 @@
 	for (i = 0; i < len; i++) {
 		regs[2 * i] = reg;
 		regs[2 * i + 1] = vals[i];
-		/* All addresses are shifted left one bit as bit 0 toggles r/w */
+		/* All addresses are shifted left one bit
+		 * as bit 0 toggles r/w */
 		reg += 2;
 	}
 
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
index 37b31c9..cf3d0cc 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
@@ -37,7 +37,7 @@
 #define HDCS_REG_CONTROL(sd)	(IS_1020(sd) ? HDCS20_CONTROL : HDCS00_CONTROL)
 
 #define HDCS_1X00_DEF_WIDTH	360
-#define HDCS_1X00_DEF_HEIGHT 	296
+#define HDCS_1X00_DEF_HEIGHT	296
 
 #define HDCS_1020_DEF_WIDTH	352
 #define HDCS_1020_DEF_HEIGHT	292
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
index c11f06e..3af5326 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
@@ -246,7 +246,7 @@
 	intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
 	alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
 	if (!alt) {
-		PDEBUG(D_ERR, "Couldn't get altsetting");
+		err("Couldn't get altsetting");
 		return -EIO;
 	}
 
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
index 11a0c00..f839843 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
@@ -66,7 +66,7 @@
 			.minimum	= 0,
 			.maximum	= 1,
 			.step		= 1,
-			.default_value 	= 0
+			.default_value	= 0
 		},
 		.set = vv6410_set_vflip,
 		.get = vv6410_get_vflip
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
index 96c6192..b3b5508 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
@@ -157,8 +157,8 @@
 /* Audio Amplifier Setup Register */
 #define VV6410_AT1			0x79
 
-#define VV6410_HFLIP 			(1 << 3)
-#define VV6410_VFLIP 			(1 << 4)
+#define VV6410_HFLIP			(1 << 3)
+#define VV6410_VFLIP			(1 << 4)
 
 #define VV6410_LOW_POWER_MODE		(1 << 0)
 #define VV6410_SOFT_RESET		(1 << 2)
diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
index 9494f86..a9cbcd6 100644
--- a/drivers/media/video/gspca/sunplus.c
+++ b/drivers/media/video/gspca/sunplus.c
@@ -343,7 +343,7 @@
 			len ? gspca_dev->usb_buf : NULL, len,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_r err %d", ret);
+		err("reg_r err %d", ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -368,7 +368,7 @@
 			gspca_dev->usb_buf, 1,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_w_1 err %d", ret);
+		err("reg_w_1 err %d", ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -388,7 +388,7 @@
 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 			value, index, NULL, 0, 500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_w_riv err %d", ret);
+		err("reg_w_riv err %d", ret);
 		gspca_dev->usb_err = ret;
 		return;
 	}
@@ -413,7 +413,7 @@
 			gspca_dev->usb_buf, 1,
 			500);			/* timeout */
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_r_1 err %d", ret);
+		err("reg_r_1 err %d", ret);
 		gspca_dev->usb_err = ret;
 		return 0;
 	}
@@ -440,7 +440,7 @@
 			gspca_dev->usb_buf, length,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_r_12 err %d", ret);
+		err("reg_r_12 err %d", ret);
 		gspca_dev->usb_err = ret;
 		return 0;
 	}
@@ -463,7 +463,7 @@
 
 	/* loop over y components */
 	for (i = 0; i < 64; i++)
-		 reg_w_riv(gspca_dev, 0x00, 0x2800 + i, qtable[0][i]);
+		reg_w_riv(gspca_dev, 0x00, 0x2800 + i, qtable[0][i]);
 
 	/* loop over c components */
 	for (i = 0; i < 64; i++)
@@ -712,8 +712,9 @@
 	sd->subtype = id->driver_info;
 
 	if (sd->subtype == AiptekMiniPenCam13) {
-/* try to get the firmware as some cam answer 2.0.1.2.2
- * and should be a spca504b then overwrite that setting */
+
+		/* try to get the firmware as some cam answer 2.0.1.2.2
+		 * and should be a spca504b then overwrite that setting */
 		reg_r(gspca_dev, 0x20, 0, 1);
 		switch (gspca_dev->usb_buf[0]) {
 		case 1:
@@ -733,7 +734,7 @@
 /*	case BRIDGE_SPCA504: */
 /*	case BRIDGE_SPCA536: */
 		cam->cam_mode = vga_mode;
-		cam->nmodes =ARRAY_SIZE(vga_mode);
+		cam->nmodes = ARRAY_SIZE(vga_mode);
 		break;
 	case BRIDGE_SPCA533:
 		cam->cam_mode = custom_mode;
@@ -1247,17 +1248,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
index 3b3b983..b45f4d0 100644
--- a/drivers/media/video/gspca/t613.c
+++ b/drivers/media/video/gspca/t613.c
@@ -892,7 +892,7 @@
 		sd->sensor = SENSOR_OM6802;
 		break;
 	default:
-		PDEBUG(D_ERR|D_PROBE, "unknown sensor %04x", sensor_id);
+		err("unknown sensor %04x", sensor_id);
 		return -EINVAL;
 	}
 
@@ -1444,17 +1444,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c
index d9c5bf3..d9e3c60 100644
--- a/drivers/media/video/gspca/tv8532.c
+++ b/drivers/media/video/gspca/tv8532.c
@@ -421,18 +421,12 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
index b16fd47..38a6efe 100644
--- a/drivers/media/video/gspca/vc032x.c
+++ b/drivers/media/video/gspca/vc032x.c
@@ -3164,7 +3164,7 @@
 			index, gspca_dev->usb_buf, len,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_r err %d", ret);
+		err("reg_r err %d", ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -3205,7 +3205,7 @@
 			value, index, NULL, 0,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_w err %d", ret);
+		err("reg_w err %d", ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -3230,7 +3230,7 @@
 
 	reg_r(gspca_dev, 0xa1, 0xb33f, 1);
 	if (!(gspca_dev->usb_buf[0] & 0x02)) {
-		PDEBUG(D_ERR, "I2c Bus Busy Wait %02x",
+		err("I2c Bus Busy Wait %02x",
 			gspca_dev->usb_buf[0]);
 		return 0;
 	}
@@ -3344,7 +3344,7 @@
 		msleep(20);
 	} while (--retry > 0);
 	if (retry <= 0)
-		PDEBUG(D_ERR, "i2c_write timeout");
+		err("i2c_write timeout");
 }
 
 static void put_tab_to_reg(struct gspca_dev *gspca_dev,
@@ -3440,7 +3440,7 @@
 
 	switch (sensor) {
 	case -1:
-		PDEBUG(D_PROBE, "Unknown sensor...");
+		err("Unknown sensor...");
 		return -EINVAL;
 	case SENSOR_HV7131R:
 		PDEBUG(D_PROBE, "Find Sensor HV7131R");
@@ -4226,18 +4226,11 @@
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-	int ret;
-
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c
index 38a6859..4066ac8 100644
--- a/drivers/media/video/gspca/w996Xcf.c
+++ b/drivers/media/video/gspca/w996Xcf.c
@@ -67,7 +67,7 @@
   --------------------------------------------------------------------------*/
 static int w9968cf_write_fsb(struct sd *sd, u16* data)
 {
-	struct usb_device* udev = sd->gspca_dev.dev;
+	struct usb_device *udev = sd->gspca_dev.dev;
 	u16 value;
 	int ret;
 
@@ -78,7 +78,7 @@
 			      USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
 			      value, 0x06, sd->gspca_dev.usb_buf, 6, 500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "Write FSB registers failed (%d)", ret);
+		err("Write FSB registers failed (%d)", ret);
 		return ret;
 	}
 
@@ -104,7 +104,7 @@
 	udelay(W9968CF_I2C_BUS_DELAY);
 
 	if (ret < 0) {
-		PDEBUG(D_ERR, "Write SB reg [01] %04x failed", value);
+		err("Write SB reg [01] %04x failed", value);
 		return ret;
 	}
 
@@ -130,7 +130,7 @@
 		ret = sd->gspca_dev.usb_buf[0] |
 		      (sd->gspca_dev.usb_buf[1] << 8);
 	else
-		PDEBUG(D_ERR, "Read SB reg [01] failed");
+		err("Read SB reg [01] failed");
 
 	udelay(W9968CF_I2C_BUS_DELAY);
 
@@ -437,7 +437,7 @@
 	if (sd->sensor == SEN_OV7620) {
 		/* Sigh, this is dependend on the clock / framerate changes
 		   made by the frequency control, sick. */
-		if (sd->freq == 1) {
+		if (sd->ctrls[FREQ].val == 1) {
 			start_cropx = 277;
 			start_cropy = 37;
 		} else {
diff --git a/drivers/media/video/gspca/xirlink_cit.c b/drivers/media/video/gspca/xirlink_cit.c
new file mode 100644
index 0000000..8715577
--- /dev/null
+++ b/drivers/media/video/gspca/xirlink_cit.c
@@ -0,0 +1,3253 @@
+/*
+ * USB IBM C-It Video Camera driver
+ *
+ * Supports Xirlink C-It Video Camera, IBM PC Camera,
+ * IBM NetCamera and Veo Stingray.
+ *
+ * Copyright (C) 2010 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This driver is based on earlier work of:
+ *
+ * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Randy Dunlap
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#define MODULE_NAME "xirlink-cit"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Xirlink C-IT");
+MODULE_LICENSE("GPL");
+
+/* FIXME we should autodetect this */
+static int ibm_netcam_pro;
+module_param(ibm_netcam_pro, int, 0);
+MODULE_PARM_DESC(ibm_netcam_pro,
+		 "Use IBM Netcamera Pro init sequences for Model 3 cams");
+
+/* FIXME this should be handled through the V4L2 input selection API */
+static int rca_input;
+module_param(rca_input, int, 0644);
+MODULE_PARM_DESC(rca_input,
+		 "Use rca input instead of ccd sensor on Model 3 cams");
+
+/* specific webcam descriptor */
+struct sd {
+	struct gspca_dev gspca_dev;		/* !! must be the first item */
+	u8 model;
+#define CIT_MODEL0 0 /* bcd version 0.01 cams ie the xvp-500 */
+#define CIT_MODEL1 1 /* The model 1 - 4 nomenclature comes from the old */
+#define CIT_MODEL2 2 /* ibmcam driver */
+#define CIT_MODEL3 3
+#define CIT_MODEL4 4
+#define CIT_IBM_NETCAM_PRO 5
+	u8 input_index;
+	u8 stop_on_control_change;
+	u8 sof_read;
+	u8 sof_len;
+	u8 contrast;
+	u8 brightness;
+	u8 hue;
+	u8 sharpness;
+	u8 lighting;
+	u8 hflip;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setlighting(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getlighting(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
+static void sd_stop0(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+	{
+	    {
+		.id      = V4L2_CID_BRIGHTNESS,
+		.type    = V4L2_CTRL_TYPE_INTEGER,
+		.name    = "Brightness",
+		.minimum = 0,
+		.maximum = 63,
+		.step = 1,
+#define BRIGHTNESS_DEFAULT 32
+		.default_value = BRIGHTNESS_DEFAULT,
+		.flags = 0,
+	    },
+	    .set = sd_setbrightness,
+	    .get = sd_getbrightness,
+	},
+#define SD_CONTRAST 1
+	{
+	    {
+		.id = V4L2_CID_CONTRAST,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "contrast",
+		.minimum = 0,
+		.maximum = 20,
+		.step = 1,
+#define CONTRAST_DEFAULT 10
+		.default_value = CONTRAST_DEFAULT,
+		.flags = 0,
+	    },
+	    .set = sd_setcontrast,
+	    .get = sd_getcontrast,
+	},
+#define SD_HUE 2
+	{
+	    {
+		.id	= V4L2_CID_HUE,
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.name	= "Hue",
+		.minimum = 0,
+		.maximum = 127,
+		.step	= 1,
+#define HUE_DEFAULT 63
+		.default_value = HUE_DEFAULT,
+		.flags = 0,
+	    },
+	    .set = sd_sethue,
+	    .get = sd_gethue,
+	},
+#define SD_SHARPNESS 3
+	{
+	    {
+		.id = V4L2_CID_SHARPNESS,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Sharpness",
+		.minimum = 0,
+		.maximum = 6,
+		.step = 1,
+#define SHARPNESS_DEFAULT 3
+		.default_value = SHARPNESS_DEFAULT,
+		.flags = 0,
+	    },
+	    .set = sd_setsharpness,
+	    .get = sd_getsharpness,
+	},
+#define SD_LIGHTING 4
+	{
+	    {
+		.id = V4L2_CID_BACKLIGHT_COMPENSATION,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Lighting",
+		.minimum = 0,
+		.maximum = 2,
+		.step = 1,
+#define LIGHTING_DEFAULT 1
+		.default_value = LIGHTING_DEFAULT,
+		.flags = 0,
+	    },
+	    .set = sd_setlighting,
+	    .get = sd_getlighting,
+	},
+#define SD_HFLIP 5
+	{
+	    {
+		.id      = V4L2_CID_HFLIP,
+		.type    = V4L2_CTRL_TYPE_BOOLEAN,
+		.name    = "Mirror",
+		.minimum = 0,
+		.maximum = 1,
+		.step    = 1,
+#define HFLIP_DEFAULT 0
+		.default_value = HFLIP_DEFAULT,
+	    },
+	    .set = sd_sethflip,
+	    .get = sd_gethflip,
+	},
+};
+
+static const struct v4l2_pix_format cif_yuv_mode[] = {
+	{176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{352, 288, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+static const struct v4l2_pix_format vga_yuv_mode[] = {
+	{160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{320, 240, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{640, 480, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+static const struct v4l2_pix_format model0_mode[] = {
+	{160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{320, 240, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+static const struct v4l2_pix_format model2_mode[] = {
+	{160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 160,
+		.sizeimage = 160 * 120 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+		.bytesperline = 176,
+		.sizeimage = 176 * 144 * 3 / 2,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{320, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+	{352, 288, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+		.bytesperline = 352,
+		.sizeimage = 352 * 288,
+		.colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+/*
+ * 01.01.08 - Added for RCA video in support -LO
+ * This struct is used to init the Model3 cam to use the RCA video in port
+ * instead of the CCD sensor.
+ */
+static const u16 rca_initdata[][3] = {
+	{0, 0x0000, 0x010c},
+	{0, 0x0006, 0x012c},
+	{0, 0x0078, 0x012d},
+	{0, 0x0046, 0x012f},
+	{0, 0xd141, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfea8, 0x0124},
+	{1, 0x0000, 0x0116},
+	{0, 0x0064, 0x0116},
+	{1, 0x0000, 0x0115},
+	{0, 0x0003, 0x0115},
+	{0, 0x0008, 0x0123},
+	{0, 0x0000, 0x0117},
+	{0, 0x0000, 0x0112},
+	{0, 0x0080, 0x0100},
+	{0, 0x0000, 0x0100},
+	{1, 0x0000, 0x0116},
+	{0, 0x0060, 0x0116},
+	{0, 0x0002, 0x0112},
+	{0, 0x0000, 0x0123},
+	{0, 0x0001, 0x0117},
+	{0, 0x0040, 0x0108},
+	{0, 0x0019, 0x012c},
+	{0, 0x0040, 0x0116},
+	{0, 0x000a, 0x0115},
+	{0, 0x000b, 0x0115},
+	{0, 0x0078, 0x012d},
+	{0, 0x0046, 0x012f},
+	{0, 0xd141, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfea8, 0x0124},
+	{0, 0x0064, 0x0116},
+	{0, 0x0000, 0x0115},
+	{0, 0x0001, 0x0115},
+	{0, 0xffff, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x00aa, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xffff, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x00f2, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x000f, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xffff, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x00f8, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x00fc, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xffff, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x00f9, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x003c, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xffff, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0027, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0019, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0021, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0006, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0045, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x002a, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x000e, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x002b, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x00f4, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x002c, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0004, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x002d, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0014, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x002e, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0003, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x002f, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0003, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0014, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0040, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0040, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0053, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0x0000, 0x0101},
+	{0, 0x00a0, 0x0103},
+	{0, 0x0078, 0x0105},
+	{0, 0x0000, 0x010a},
+	{0, 0x0024, 0x010b},
+	{0, 0x0028, 0x0119},
+	{0, 0x0088, 0x011b},
+	{0, 0x0002, 0x011d},
+	{0, 0x0003, 0x011e},
+	{0, 0x0000, 0x0129},
+	{0, 0x00fc, 0x012b},
+	{0, 0x0008, 0x0102},
+	{0, 0x0000, 0x0104},
+	{0, 0x0008, 0x011a},
+	{0, 0x0028, 0x011c},
+	{0, 0x0021, 0x012a},
+	{0, 0x0000, 0x0118},
+	{0, 0x0000, 0x0132},
+	{0, 0x0000, 0x0109},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0031, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0040, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0040, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x00dc, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0032, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0020, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0001, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0040, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0040, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0037, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0030, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0xfff9, 0x0124},
+	{0, 0x0086, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0038, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0008, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0x0000, 0x0127},
+	{0, 0xfff8, 0x0124},
+	{0, 0xfffd, 0x0124},
+	{0, 0xfffa, 0x0124},
+	{0, 0x0003, 0x0111},
+};
+
+/* TESTME the old ibmcam driver repeats certain commands to Model1 cameras, we
+   do the same for now (testing needed to see if this is really necessary) */
+static const int cit_model1_ntries = 5;
+static const int cit_model1_ntries2 = 2;
+
+static int cit_write_reg(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+	struct usb_device *udev = gspca_dev->dev;
+	int err;
+
+	err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00,
+			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+			value, index, NULL, 0, 1000);
+	if (err < 0)
+		err("Failed to write a register (index 0x%04X,"
+			" value 0x%02X, error %d)", index, value, err);
+
+	return 0;
+}
+
+static int cit_read_reg(struct gspca_dev *gspca_dev, u16 index)
+{
+	struct usb_device *udev = gspca_dev->dev;
+	__u8 *buf = gspca_dev->usb_buf;
+	int res;
+
+	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x01,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+			0x00, index, buf, 8, 1000);
+	if (res < 0) {
+		err("Failed to read a register (index 0x%04X, error %d)",
+			index, res);
+		return res;
+	}
+
+	PDEBUG(D_PROBE,
+	       "Register %04x value: %02x %02x %02x %02x %02x %02x %02x %02x",
+	       index,
+	       buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+
+	return 0;
+}
+
+/*
+ * cit_send_FF_04_02()
+ *
+ * This procedure sends magic 3-command prefix to the camera.
+ * The purpose of this prefix is not known.
+ *
+ * History:
+ * 1/2/00   Created.
+ */
+static void cit_send_FF_04_02(struct gspca_dev *gspca_dev)
+{
+	cit_write_reg(gspca_dev, 0x00FF, 0x0127);
+	cit_write_reg(gspca_dev, 0x0004, 0x0124);
+	cit_write_reg(gspca_dev, 0x0002, 0x0124);
+}
+
+static void cit_send_00_04_06(struct gspca_dev *gspca_dev)
+{
+	cit_write_reg(gspca_dev, 0x0000, 0x0127);
+	cit_write_reg(gspca_dev, 0x0004, 0x0124);
+	cit_write_reg(gspca_dev, 0x0006, 0x0124);
+}
+
+static void cit_send_x_00(struct gspca_dev *gspca_dev, unsigned short x)
+{
+	cit_write_reg(gspca_dev, x,      0x0127);
+	cit_write_reg(gspca_dev, 0x0000, 0x0124);
+}
+
+static void cit_send_x_00_05(struct gspca_dev *gspca_dev, unsigned short x)
+{
+	cit_send_x_00(gspca_dev, x);
+	cit_write_reg(gspca_dev, 0x0005, 0x0124);
+}
+
+static void cit_send_x_00_05_02(struct gspca_dev *gspca_dev, unsigned short x)
+{
+	cit_write_reg(gspca_dev, x,      0x0127);
+	cit_write_reg(gspca_dev, 0x0000, 0x0124);
+	cit_write_reg(gspca_dev, 0x0005, 0x0124);
+	cit_write_reg(gspca_dev, 0x0002, 0x0124);
+}
+
+static void cit_send_x_01_00_05(struct gspca_dev *gspca_dev, u16 x)
+{
+	cit_write_reg(gspca_dev, x,      0x0127);
+	cit_write_reg(gspca_dev, 0x0001, 0x0124);
+	cit_write_reg(gspca_dev, 0x0000, 0x0124);
+	cit_write_reg(gspca_dev, 0x0005, 0x0124);
+}
+
+static void cit_send_x_00_05_02_01(struct gspca_dev *gspca_dev, u16 x)
+{
+	cit_write_reg(gspca_dev, x,      0x0127);
+	cit_write_reg(gspca_dev, 0x0000, 0x0124);
+	cit_write_reg(gspca_dev, 0x0005, 0x0124);
+	cit_write_reg(gspca_dev, 0x0002, 0x0124);
+	cit_write_reg(gspca_dev, 0x0001, 0x0124);
+}
+
+static void cit_send_x_00_05_02_08_01(struct gspca_dev *gspca_dev, u16 x)
+{
+	cit_write_reg(gspca_dev, x,      0x0127);
+	cit_write_reg(gspca_dev, 0x0000, 0x0124);
+	cit_write_reg(gspca_dev, 0x0005, 0x0124);
+	cit_write_reg(gspca_dev, 0x0002, 0x0124);
+	cit_write_reg(gspca_dev, 0x0008, 0x0124);
+	cit_write_reg(gspca_dev, 0x0001, 0x0124);
+}
+
+static void cit_Packet_Format1(struct gspca_dev *gspca_dev, u16 fkey, u16 val)
+{
+	cit_send_x_01_00_05(gspca_dev, 0x0088);
+	cit_send_x_00_05(gspca_dev, fkey);
+	cit_send_x_00_05_02_08_01(gspca_dev, val);
+	cit_send_x_00_05(gspca_dev, 0x0088);
+	cit_send_x_00_05_02_01(gspca_dev, fkey);
+	cit_send_x_00_05(gspca_dev, 0x0089);
+	cit_send_x_00(gspca_dev, fkey);
+	cit_send_00_04_06(gspca_dev);
+	cit_read_reg(gspca_dev, 0x0126);
+	cit_send_FF_04_02(gspca_dev);
+}
+
+static void cit_PacketFormat2(struct gspca_dev *gspca_dev, u16 fkey, u16 val)
+{
+	cit_send_x_01_00_05(gspca_dev, 0x0088);
+	cit_send_x_00_05(gspca_dev, fkey);
+	cit_send_x_00_05_02(gspca_dev, val);
+}
+
+static void cit_model2_Packet2(struct gspca_dev *gspca_dev)
+{
+	cit_write_reg(gspca_dev, 0x00ff, 0x012d);
+	cit_write_reg(gspca_dev, 0xfea3, 0x0124);
+}
+
+static void cit_model2_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
+{
+	cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+	cit_write_reg(gspca_dev, 0x00ff, 0x012e);
+	cit_write_reg(gspca_dev, v1,     0x012f);
+	cit_write_reg(gspca_dev, 0x00ff, 0x0130);
+	cit_write_reg(gspca_dev, 0xc719, 0x0124);
+	cit_write_reg(gspca_dev, v2,     0x0127);
+
+	cit_model2_Packet2(gspca_dev);
+}
+
+/*
+ * cit_model3_Packet1()
+ *
+ * 00_0078_012d
+ * 00_0097_012f
+ * 00_d141_0124
+ * 00_0096_0127
+ * 00_fea8_0124
+*/
+static void cit_model3_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
+{
+	cit_write_reg(gspca_dev, 0x0078, 0x012d);
+	cit_write_reg(gspca_dev, v1,     0x012f);
+	cit_write_reg(gspca_dev, 0xd141, 0x0124);
+	cit_write_reg(gspca_dev, v2,     0x0127);
+	cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+}
+
+static void cit_model4_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
+{
+	cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+	cit_write_reg(gspca_dev, v1,     0x012f);
+	cit_write_reg(gspca_dev, 0xd141, 0x0124);
+	cit_write_reg(gspca_dev, v2,     0x0127);
+	cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+}
+
+static void cit_model4_BrightnessPacket(struct gspca_dev *gspca_dev, u16 val)
+{
+	cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+	cit_write_reg(gspca_dev, 0x0026, 0x012f);
+	cit_write_reg(gspca_dev, 0xd141, 0x0124);
+	cit_write_reg(gspca_dev, val,    0x0127);
+	cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+	cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+	cit_write_reg(gspca_dev, 0x0038, 0x012d);
+	cit_write_reg(gspca_dev, 0x0004, 0x012f);
+	cit_write_reg(gspca_dev, 0xd145, 0x0124);
+	cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+		     const struct usb_device_id *id)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct cam *cam;
+
+	sd->model = id->driver_info;
+	if (sd->model == CIT_MODEL3 && ibm_netcam_pro)
+		sd->model = CIT_IBM_NETCAM_PRO;
+
+	cam = &gspca_dev->cam;
+	switch (sd->model) {
+	case CIT_MODEL0:
+		cam->cam_mode = model0_mode;
+		cam->nmodes = ARRAY_SIZE(model0_mode);
+		cam->reverse_alts = 1;
+		gspca_dev->ctrl_dis = ~((1 << SD_CONTRAST) | (1 << SD_HFLIP));
+		sd->sof_len = 4;
+		break;
+	case CIT_MODEL1:
+		cam->cam_mode = cif_yuv_mode;
+		cam->nmodes = ARRAY_SIZE(cif_yuv_mode);
+		cam->reverse_alts = 1;
+		gspca_dev->ctrl_dis = (1 << SD_HUE) | (1 << SD_HFLIP);
+		sd->sof_len = 4;
+		break;
+	case CIT_MODEL2:
+		cam->cam_mode = model2_mode + 1; /* no 160x120 */
+		cam->nmodes = 3;
+		gspca_dev->ctrl_dis = (1 << SD_CONTRAST) |
+				      (1 << SD_SHARPNESS) |
+				      (1 << SD_HFLIP);
+		break;
+	case CIT_MODEL3:
+		cam->cam_mode = vga_yuv_mode;
+		cam->nmodes = ARRAY_SIZE(vga_yuv_mode);
+		gspca_dev->ctrl_dis = (1 << SD_HUE) |
+				      (1 << SD_LIGHTING) |
+				      (1 << SD_HFLIP);
+		sd->stop_on_control_change = 1;
+		sd->sof_len = 4;
+		break;
+	case CIT_MODEL4:
+		cam->cam_mode = model2_mode;
+		cam->nmodes = ARRAY_SIZE(model2_mode);
+		gspca_dev->ctrl_dis = (1 << SD_CONTRAST) |
+				      (1 << SD_SHARPNESS) |
+				      (1 << SD_LIGHTING) |
+				      (1 << SD_HFLIP);
+		break;
+	case CIT_IBM_NETCAM_PRO:
+		cam->cam_mode = vga_yuv_mode;
+		cam->nmodes = 2; /* no 640 x 480 */
+		cam->input_flags = V4L2_IN_ST_VFLIP;
+		gspca_dev->ctrl_dis = ~(1 << SD_CONTRAST);
+		sd->stop_on_control_change = 1;
+		sd->sof_len = 4;
+		break;
+	}
+
+	sd->brightness = BRIGHTNESS_DEFAULT;
+	sd->contrast = CONTRAST_DEFAULT;
+	sd->hue = HUE_DEFAULT;
+	sd->sharpness = SHARPNESS_DEFAULT;
+	sd->lighting = LIGHTING_DEFAULT;
+	sd->hflip = HFLIP_DEFAULT;
+
+	return 0;
+}
+
+static int cit_init_model0(struct gspca_dev *gspca_dev)
+{
+	cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */
+	cit_write_reg(gspca_dev, 0x0001, 0x0112); /* turn on autogain ? */
+	cit_write_reg(gspca_dev, 0x0000, 0x0400);
+	cit_write_reg(gspca_dev, 0x0001, 0x0400);
+	cit_write_reg(gspca_dev, 0x0000, 0x0420);
+	cit_write_reg(gspca_dev, 0x0001, 0x0420);
+	cit_write_reg(gspca_dev, 0x000d, 0x0409);
+	cit_write_reg(gspca_dev, 0x0002, 0x040a);
+	cit_write_reg(gspca_dev, 0x0018, 0x0405);
+	cit_write_reg(gspca_dev, 0x0008, 0x0435);
+	cit_write_reg(gspca_dev, 0x0026, 0x040b);
+	cit_write_reg(gspca_dev, 0x0007, 0x0437);
+	cit_write_reg(gspca_dev, 0x0015, 0x042f);
+	cit_write_reg(gspca_dev, 0x002b, 0x0439);
+	cit_write_reg(gspca_dev, 0x0026, 0x043a);
+	cit_write_reg(gspca_dev, 0x0008, 0x0438);
+	cit_write_reg(gspca_dev, 0x001e, 0x042b);
+	cit_write_reg(gspca_dev, 0x0041, 0x042c);
+
+	return 0;
+}
+
+static int cit_init_ibm_netcam_pro(struct gspca_dev *gspca_dev)
+{
+	cit_read_reg(gspca_dev, 0x128);
+	cit_write_reg(gspca_dev, 0x0003, 0x0133);
+	cit_write_reg(gspca_dev, 0x0000, 0x0117);
+	cit_write_reg(gspca_dev, 0x0008, 0x0123);
+	cit_write_reg(gspca_dev, 0x0000, 0x0100);
+	cit_read_reg(gspca_dev, 0x0116);
+	cit_write_reg(gspca_dev, 0x0060, 0x0116);
+	cit_write_reg(gspca_dev, 0x0002, 0x0112);
+	cit_write_reg(gspca_dev, 0x0000, 0x0133);
+	cit_write_reg(gspca_dev, 0x0000, 0x0123);
+	cit_write_reg(gspca_dev, 0x0001, 0x0117);
+	cit_write_reg(gspca_dev, 0x0040, 0x0108);
+	cit_write_reg(gspca_dev, 0x0019, 0x012c);
+	cit_write_reg(gspca_dev, 0x0060, 0x0116);
+	cit_write_reg(gspca_dev, 0x0002, 0x0115);
+	cit_write_reg(gspca_dev, 0x000b, 0x0115);
+
+	cit_write_reg(gspca_dev, 0x0078, 0x012d);
+	cit_write_reg(gspca_dev, 0x0001, 0x012f);
+	cit_write_reg(gspca_dev, 0xd141, 0x0124);
+	cit_write_reg(gspca_dev, 0x0079, 0x012d);
+	cit_write_reg(gspca_dev, 0x00ff, 0x0130);
+	cit_write_reg(gspca_dev, 0xcd41, 0x0124);
+	cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+	cit_read_reg(gspca_dev, 0x0126);
+
+	cit_model3_Packet1(gspca_dev, 0x0000, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0000, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x000b, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x000c, 0x0008);
+	cit_model3_Packet1(gspca_dev, 0x000d, 0x003a);
+	cit_model3_Packet1(gspca_dev, 0x000e, 0x0060);
+	cit_model3_Packet1(gspca_dev, 0x000f, 0x0060);
+	cit_model3_Packet1(gspca_dev, 0x0010, 0x0008);
+	cit_model3_Packet1(gspca_dev, 0x0011, 0x0004);
+	cit_model3_Packet1(gspca_dev, 0x0012, 0x0028);
+	cit_model3_Packet1(gspca_dev, 0x0013, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x0014, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0015, 0x00fb);
+	cit_model3_Packet1(gspca_dev, 0x0016, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x0017, 0x0037);
+	cit_model3_Packet1(gspca_dev, 0x0018, 0x0036);
+	cit_model3_Packet1(gspca_dev, 0x001e, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x001f, 0x0008);
+	cit_model3_Packet1(gspca_dev, 0x0020, 0x00c1);
+	cit_model3_Packet1(gspca_dev, 0x0021, 0x0034);
+	cit_model3_Packet1(gspca_dev, 0x0022, 0x0034);
+	cit_model3_Packet1(gspca_dev, 0x0025, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x0028, 0x0022);
+	cit_model3_Packet1(gspca_dev, 0x0029, 0x000a);
+	cit_model3_Packet1(gspca_dev, 0x002b, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x002c, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x002d, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x002e, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x002f, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x0030, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x0031, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x0032, 0x0007);
+	cit_model3_Packet1(gspca_dev, 0x0033, 0x0005);
+	cit_model3_Packet1(gspca_dev, 0x0037, 0x0040);
+	cit_model3_Packet1(gspca_dev, 0x0039, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x003a, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x003b, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x003c, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0040, 0x000c);
+	cit_model3_Packet1(gspca_dev, 0x0041, 0x00fb);
+	cit_model3_Packet1(gspca_dev, 0x0042, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x0043, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0045, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0046, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0047, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0048, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0049, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x004a, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x004b, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x004c, 0x00ff);
+	cit_model3_Packet1(gspca_dev, 0x004f, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0050, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0051, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x0055, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0056, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0057, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0058, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x0059, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x005c, 0x0016);
+	cit_model3_Packet1(gspca_dev, 0x005d, 0x0022);
+	cit_model3_Packet1(gspca_dev, 0x005e, 0x003c);
+	cit_model3_Packet1(gspca_dev, 0x005f, 0x0050);
+	cit_model3_Packet1(gspca_dev, 0x0060, 0x0044);
+	cit_model3_Packet1(gspca_dev, 0x0061, 0x0005);
+	cit_model3_Packet1(gspca_dev, 0x006a, 0x007e);
+	cit_model3_Packet1(gspca_dev, 0x006f, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0072, 0x001b);
+	cit_model3_Packet1(gspca_dev, 0x0073, 0x0005);
+	cit_model3_Packet1(gspca_dev, 0x0074, 0x000a);
+	cit_model3_Packet1(gspca_dev, 0x0075, 0x001b);
+	cit_model3_Packet1(gspca_dev, 0x0076, 0x002a);
+	cit_model3_Packet1(gspca_dev, 0x0077, 0x003c);
+	cit_model3_Packet1(gspca_dev, 0x0078, 0x0050);
+	cit_model3_Packet1(gspca_dev, 0x007b, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x007c, 0x0011);
+	cit_model3_Packet1(gspca_dev, 0x007d, 0x0024);
+	cit_model3_Packet1(gspca_dev, 0x007e, 0x0043);
+	cit_model3_Packet1(gspca_dev, 0x007f, 0x005a);
+	cit_model3_Packet1(gspca_dev, 0x0084, 0x0020);
+	cit_model3_Packet1(gspca_dev, 0x0085, 0x0033);
+	cit_model3_Packet1(gspca_dev, 0x0086, 0x000a);
+	cit_model3_Packet1(gspca_dev, 0x0087, 0x0030);
+	cit_model3_Packet1(gspca_dev, 0x0088, 0x0070);
+	cit_model3_Packet1(gspca_dev, 0x008b, 0x0008);
+	cit_model3_Packet1(gspca_dev, 0x008f, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0090, 0x0006);
+	cit_model3_Packet1(gspca_dev, 0x0091, 0x0028);
+	cit_model3_Packet1(gspca_dev, 0x0092, 0x005a);
+	cit_model3_Packet1(gspca_dev, 0x0093, 0x0082);
+	cit_model3_Packet1(gspca_dev, 0x0096, 0x0014);
+	cit_model3_Packet1(gspca_dev, 0x0097, 0x0020);
+	cit_model3_Packet1(gspca_dev, 0x0098, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00b0, 0x0046);
+	cit_model3_Packet1(gspca_dev, 0x00b1, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00b2, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00b3, 0x0004);
+	cit_model3_Packet1(gspca_dev, 0x00b4, 0x0007);
+	cit_model3_Packet1(gspca_dev, 0x00b6, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x00b7, 0x0004);
+	cit_model3_Packet1(gspca_dev, 0x00bb, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00bc, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x00bd, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00bf, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00c0, 0x00c8);
+	cit_model3_Packet1(gspca_dev, 0x00c1, 0x0014);
+	cit_model3_Packet1(gspca_dev, 0x00c2, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x00c3, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00c4, 0x0004);
+	cit_model3_Packet1(gspca_dev, 0x00cb, 0x00bf);
+	cit_model3_Packet1(gspca_dev, 0x00cc, 0x00bf);
+	cit_model3_Packet1(gspca_dev, 0x00cd, 0x00bf);
+	cit_model3_Packet1(gspca_dev, 0x00ce, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00cf, 0x0020);
+	cit_model3_Packet1(gspca_dev, 0x00d0, 0x0040);
+	cit_model3_Packet1(gspca_dev, 0x00d1, 0x00bf);
+	cit_model3_Packet1(gspca_dev, 0x00d1, 0x00bf);
+	cit_model3_Packet1(gspca_dev, 0x00d2, 0x00bf);
+	cit_model3_Packet1(gspca_dev, 0x00d3, 0x00bf);
+	cit_model3_Packet1(gspca_dev, 0x00ea, 0x0008);
+	cit_model3_Packet1(gspca_dev, 0x00eb, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00ec, 0x00e8);
+	cit_model3_Packet1(gspca_dev, 0x00ed, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x00ef, 0x0022);
+	cit_model3_Packet1(gspca_dev, 0x00f0, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00f2, 0x0028);
+	cit_model3_Packet1(gspca_dev, 0x00f4, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x00f5, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00fa, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00fb, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x00fc, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00fd, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00fe, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00ff, 0x0000);
+
+	cit_model3_Packet1(gspca_dev, 0x00be, 0x0003);
+	cit_model3_Packet1(gspca_dev, 0x00c8, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00c9, 0x0020);
+	cit_model3_Packet1(gspca_dev, 0x00ca, 0x0040);
+	cit_model3_Packet1(gspca_dev, 0x0053, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x0082, 0x000e);
+	cit_model3_Packet1(gspca_dev, 0x0083, 0x0020);
+	cit_model3_Packet1(gspca_dev, 0x0034, 0x003c);
+	cit_model3_Packet1(gspca_dev, 0x006e, 0x0055);
+	cit_model3_Packet1(gspca_dev, 0x0062, 0x0005);
+	cit_model3_Packet1(gspca_dev, 0x0063, 0x0008);
+	cit_model3_Packet1(gspca_dev, 0x0066, 0x000a);
+	cit_model3_Packet1(gspca_dev, 0x0067, 0x0006);
+	cit_model3_Packet1(gspca_dev, 0x006b, 0x0010);
+	cit_model3_Packet1(gspca_dev, 0x005a, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x005b, 0x000a);
+	cit_model3_Packet1(gspca_dev, 0x0023, 0x0006);
+	cit_model3_Packet1(gspca_dev, 0x0026, 0x0004);
+	cit_model3_Packet1(gspca_dev, 0x0036, 0x0069);
+	cit_model3_Packet1(gspca_dev, 0x0038, 0x0064);
+	cit_model3_Packet1(gspca_dev, 0x003d, 0x0003);
+	cit_model3_Packet1(gspca_dev, 0x003e, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x00b8, 0x0014);
+	cit_model3_Packet1(gspca_dev, 0x00b9, 0x0014);
+	cit_model3_Packet1(gspca_dev, 0x00e6, 0x0004);
+	cit_model3_Packet1(gspca_dev, 0x00e8, 0x0001);
+
+	return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+		cit_init_model0(gspca_dev);
+		sd_stop0(gspca_dev);
+		break;
+	case CIT_MODEL1:
+	case CIT_MODEL2:
+	case CIT_MODEL3:
+	case CIT_MODEL4:
+		break; /* All is done in sd_start */
+	case CIT_IBM_NETCAM_PRO:
+		cit_init_ibm_netcam_pro(gspca_dev);
+		sd_stop0(gspca_dev);
+		break;
+	}
+	return 0;
+}
+
+static int cit_set_brightness(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+	case CIT_IBM_NETCAM_PRO:
+		/* No (known) brightness control for these */
+		break;
+	case CIT_MODEL1:
+		/* Model 1: Brightness range 0 - 63 */
+		cit_Packet_Format1(gspca_dev, 0x0031, sd->brightness);
+		cit_Packet_Format1(gspca_dev, 0x0032, sd->brightness);
+		cit_Packet_Format1(gspca_dev, 0x0033, sd->brightness);
+		break;
+	case CIT_MODEL2:
+		/* Model 2: Brightness range 0x60 - 0xee */
+		/* Scale 0 - 63 to 0x60 - 0xee */
+		i = 0x60 + sd->brightness * 2254 / 1000;
+		cit_model2_Packet1(gspca_dev, 0x001a, i);
+		break;
+	case CIT_MODEL3:
+		/* Model 3: Brightness range 'i' in [0x0C..0x3F] */
+		i = sd->brightness;
+		if (i < 0x0c)
+			i = 0x0c;
+		cit_model3_Packet1(gspca_dev, 0x0036, i);
+		break;
+	case CIT_MODEL4:
+		/* Model 4: Brightness range 'i' in [0x04..0xb4] */
+		/* Scale 0 - 63 to 0x04 - 0xb4 */
+		i = 0x04 + sd->brightness * 2794 / 1000;
+		cit_model4_BrightnessPacket(gspca_dev, i);
+		break;
+	}
+
+	return 0;
+}
+
+static int cit_set_contrast(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL0: {
+		int i;
+		/* gain 0-15, 0-20 -> 0-15 */
+		i = sd->contrast * 1000 / 1333;
+		cit_write_reg(gspca_dev, i, 0x0422);
+		/* gain 0-31, may not be lower then 0x0422, 0-20 -> 0-31 */
+		i = sd->contrast * 2000 / 1333;
+		cit_write_reg(gspca_dev, i, 0x0423);
+		/* gain 0-127, may not be lower then 0x0423, 0-20 -> 0-63  */
+		i = sd->contrast * 4000 / 1333;
+		cit_write_reg(gspca_dev, i, 0x0424);
+		/* gain 0-127, may not be lower then 0x0424, , 0-20 -> 0-127 */
+		i = sd->contrast * 8000 / 1333;
+		cit_write_reg(gspca_dev, i, 0x0425);
+		break;
+	}
+	case CIT_MODEL2:
+	case CIT_MODEL4:
+		/* These models do not have this control. */
+		break;
+	case CIT_MODEL1:
+	{
+		/* Scale 0 - 20 to 15 - 0 */
+		int i, new_contrast = (20 - sd->contrast) * 1000 / 1333;
+		for (i = 0; i < cit_model1_ntries; i++) {
+			cit_Packet_Format1(gspca_dev, 0x0014, new_contrast);
+			cit_send_FF_04_02(gspca_dev);
+		}
+		break;
+	}
+	case CIT_MODEL3:
+	{	/* Preset hardware values */
+		static const struct {
+			unsigned short cv1;
+			unsigned short cv2;
+			unsigned short cv3;
+		} cv[7] = {
+			{ 0x05, 0x05, 0x0f },	/* Minimum */
+			{ 0x04, 0x04, 0x16 },
+			{ 0x02, 0x03, 0x16 },
+			{ 0x02, 0x08, 0x16 },
+			{ 0x01, 0x0c, 0x16 },
+			{ 0x01, 0x0e, 0x16 },
+			{ 0x01, 0x10, 0x16 }	/* Maximum */
+		};
+		int i = sd->contrast / 3;
+		cit_model3_Packet1(gspca_dev, 0x0067, cv[i].cv1);
+		cit_model3_Packet1(gspca_dev, 0x005b, cv[i].cv2);
+		cit_model3_Packet1(gspca_dev, 0x005c, cv[i].cv3);
+		break;
+	}
+	case CIT_IBM_NETCAM_PRO:
+		cit_model3_Packet1(gspca_dev, 0x005b, sd->contrast + 1);
+		break;
+	}
+	return 0;
+}
+
+static int cit_set_hue(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+	case CIT_MODEL1:
+	case CIT_IBM_NETCAM_PRO:
+		/* No hue control for these models */
+		break;
+	case CIT_MODEL2:
+		cit_model2_Packet1(gspca_dev, 0x0024, sd->hue);
+		/* cit_model2_Packet1(gspca_dev, 0x0020, sat); */
+		break;
+	case CIT_MODEL3: {
+		/* Model 3: Brightness range 'i' in [0x05..0x37] */
+		/* TESTME according to the ibmcam driver this does not work */
+		if (0) {
+			/* Scale 0 - 127 to 0x05 - 0x37 */
+			int i = 0x05 + sd->hue * 1000 / 2540;
+			cit_model3_Packet1(gspca_dev, 0x007e, i);
+		}
+		break;
+	}
+	case CIT_MODEL4:
+		/* HDG: taken from ibmcam, setting the color gains does not
+		 * really belong here.
+		 *
+		 * I am not sure r/g/b_gain variables exactly control gain
+		 * of those channels. Most likely they subtly change some
+		 * very internal image processing settings in the camera.
+		 * In any case, here is what they do, and feel free to tweak:
+		 *
+		 * r_gain: seriously affects red gain
+		 * g_gain: seriously affects green gain
+		 * b_gain: seriously affects blue gain
+		 * hue: changes average color from violet (0) to red (0xFF)
+		 */
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x001e, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev,    160, 0x0127);  /* Green gain */
+		cit_write_reg(gspca_dev,    160, 0x012e);  /* Red gain */
+		cit_write_reg(gspca_dev,    160, 0x0130);  /* Blue gain */
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, sd->hue, 0x012d); /* Hue */
+		cit_write_reg(gspca_dev, 0xf545, 0x0124);
+		break;
+	}
+	return 0;
+}
+
+static int cit_set_sharpness(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+	case CIT_MODEL2:
+	case CIT_MODEL4:
+	case CIT_IBM_NETCAM_PRO:
+		/* These models do not have this control */
+		break;
+	case CIT_MODEL1: {
+		int i;
+		const unsigned short sa[] = {
+			0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a };
+
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_PacketFormat2(gspca_dev, 0x0013, sa[sd->sharpness]);
+		break;
+	}
+	case CIT_MODEL3:
+	{	/*
+		 * "Use a table of magic numbers.
+		 *  This setting doesn't really change much.
+		 *  But that's how Windows does it."
+		 */
+		static const struct {
+			unsigned short sv1;
+			unsigned short sv2;
+			unsigned short sv3;
+			unsigned short sv4;
+		} sv[7] = {
+			{ 0x00, 0x00, 0x05, 0x14 },	/* Smoothest */
+			{ 0x01, 0x04, 0x05, 0x14 },
+			{ 0x02, 0x04, 0x05, 0x14 },
+			{ 0x03, 0x04, 0x05, 0x14 },
+			{ 0x03, 0x05, 0x05, 0x14 },
+			{ 0x03, 0x06, 0x05, 0x14 },
+			{ 0x03, 0x07, 0x05, 0x14 }	/* Sharpest */
+		};
+		cit_model3_Packet1(gspca_dev, 0x0060, sv[sd->sharpness].sv1);
+		cit_model3_Packet1(gspca_dev, 0x0061, sv[sd->sharpness].sv2);
+		cit_model3_Packet1(gspca_dev, 0x0062, sv[sd->sharpness].sv3);
+		cit_model3_Packet1(gspca_dev, 0x0063, sv[sd->sharpness].sv4);
+		break;
+	}
+	}
+	return 0;
+}
+
+/*
+ * cit_set_lighting()
+ *
+ * Camera model 1:
+ * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low.
+ *
+ * Camera model 2:
+ * We have 16 levels of lighting, 0 for bright light and up to 15 for
+ * low light. But values above 5 or so are useless because camera is
+ * not really capable to produce anything worth viewing at such light.
+ * This setting may be altered only in certain camera state.
+ *
+ * Low lighting forces slower FPS.
+ *
+ * History:
+ * 1/5/00   Created.
+ * 2/20/00  Added support for Model 2 cameras.
+ */
+static void cit_set_lighting(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+	case CIT_MODEL2:
+	case CIT_MODEL3:
+	case CIT_MODEL4:
+	case CIT_IBM_NETCAM_PRO:
+		break;
+	case CIT_MODEL1: {
+		int i;
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x0027, sd->lighting);
+		break;
+	}
+	}
+}
+
+static void cit_set_hflip(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+		if (sd->hflip)
+			cit_write_reg(gspca_dev, 0x0020, 0x0115);
+		else
+			cit_write_reg(gspca_dev, 0x0040, 0x0115);
+		break;
+	case CIT_MODEL1:
+	case CIT_MODEL2:
+	case CIT_MODEL3:
+	case CIT_MODEL4:
+	case CIT_IBM_NETCAM_PRO:
+		break;
+	}
+}
+
+static int cit_restart_stream(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+	case CIT_MODEL1:
+	case CIT_MODEL3:
+	case CIT_IBM_NETCAM_PRO:
+		cit_write_reg(gspca_dev, 0x0001, 0x0114);
+		/* Fall through */
+	case CIT_MODEL2:
+	case CIT_MODEL4:
+		cit_write_reg(gspca_dev, 0x00c0, 0x010c); /* Go! */
+		usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
+		/* This happens repeatedly while streaming with the ibm netcam
+		   pro and the ibmcam driver did it for model3 after changing
+		   settings, but it does not seem to have any effect. */
+		/* cit_write_reg(gspca_dev, 0x0001, 0x0113); */
+		break;
+	}
+
+	sd->sof_read = 0;
+
+	return 0;
+}
+
+static int cit_get_packet_size(struct gspca_dev *gspca_dev)
+{
+	struct usb_host_interface *alt;
+	struct usb_interface *intf;
+
+	intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+	alt = usb_altnum_to_altsetting(intf, gspca_dev->alt);
+	if (!alt) {
+		err("Couldn't get altsetting");
+		return -EIO;
+	}
+
+	return le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+}
+
+/* Calculate the clockdiv giving us max fps given the available bandwidth */
+static int cit_get_clock_div(struct gspca_dev *gspca_dev)
+{
+	int clock_div = 7; /* 0=30 1=25 2=20 3=15 4=12 5=7.5 6=6 7=3fps ?? */
+	int fps[8] = { 30, 25, 20, 15, 12, 8, 6, 3 };
+	int packet_size;
+
+	packet_size = cit_get_packet_size(gspca_dev);
+	if (packet_size < 0)
+		return packet_size;
+
+	while (clock_div > 3 &&
+			1000 * packet_size >
+			gspca_dev->width * gspca_dev->height *
+			fps[clock_div - 1] * 3 / 2)
+		clock_div--;
+
+	PDEBUG(D_PROBE,
+	       "PacketSize: %d, res: %dx%d -> using clockdiv: %d (%d fps)",
+	       packet_size, gspca_dev->width, gspca_dev->height, clock_div,
+	       fps[clock_div]);
+
+	return clock_div;
+}
+
+static int cit_start_model0(struct gspca_dev *gspca_dev)
+{
+	const unsigned short compression = 0; /* 0=none, 7=best frame rate */
+	int clock_div;
+
+	clock_div = cit_get_clock_div(gspca_dev);
+	if (clock_div < 0)
+		return clock_div;
+
+	cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */
+	cit_write_reg(gspca_dev, 0x0003, 0x0438);
+	cit_write_reg(gspca_dev, 0x001e, 0x042b);
+	cit_write_reg(gspca_dev, 0x0041, 0x042c);
+	cit_write_reg(gspca_dev, 0x0008, 0x0436);
+	cit_write_reg(gspca_dev, 0x0024, 0x0403);
+	cit_write_reg(gspca_dev, 0x002c, 0x0404);
+	cit_write_reg(gspca_dev, 0x0002, 0x0426);
+	cit_write_reg(gspca_dev, 0x0014, 0x0427);
+
+	switch (gspca_dev->width) {
+	case 160: /* 160x120 */
+		cit_write_reg(gspca_dev, 0x0004, 0x010b);
+		cit_write_reg(gspca_dev, 0x0001, 0x010a);
+		cit_write_reg(gspca_dev, 0x0010, 0x0102);
+		cit_write_reg(gspca_dev, 0x00a0, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);
+		cit_write_reg(gspca_dev, 0x0078, 0x0105);
+		break;
+
+	case 176: /* 176x144 */
+		cit_write_reg(gspca_dev, 0x0006, 0x010b);
+		cit_write_reg(gspca_dev, 0x0000, 0x010a);
+		cit_write_reg(gspca_dev, 0x0005, 0x0102);
+		cit_write_reg(gspca_dev, 0x00b0, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);
+		cit_write_reg(gspca_dev, 0x0090, 0x0105);
+		break;
+
+	case 320: /* 320x240 */
+		cit_write_reg(gspca_dev, 0x0008, 0x010b);
+		cit_write_reg(gspca_dev, 0x0004, 0x010a);
+		cit_write_reg(gspca_dev, 0x0005, 0x0102);
+		cit_write_reg(gspca_dev, 0x00a0, 0x0103);
+		cit_write_reg(gspca_dev, 0x0010, 0x0104);
+		cit_write_reg(gspca_dev, 0x0078, 0x0105);
+		break;
+	}
+
+	cit_write_reg(gspca_dev, compression, 0x0109);
+	cit_write_reg(gspca_dev, clock_div, 0x0111);
+
+	return 0;
+}
+
+static int cit_start_model1(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int i, clock_div;
+
+	clock_div = cit_get_clock_div(gspca_dev);
+	if (clock_div < 0)
+		return clock_div;
+
+	cit_read_reg(gspca_dev, 0x0128);
+	cit_read_reg(gspca_dev, 0x0100);
+	cit_write_reg(gspca_dev, 0x01, 0x0100);	/* LED On  */
+	cit_read_reg(gspca_dev, 0x0100);
+	cit_write_reg(gspca_dev, 0x81, 0x0100);	/* LED Off */
+	cit_read_reg(gspca_dev, 0x0100);
+	cit_write_reg(gspca_dev, 0x01, 0x0100);	/* LED On  */
+	cit_write_reg(gspca_dev, 0x01, 0x0108);
+
+	cit_write_reg(gspca_dev, 0x03, 0x0112);
+	cit_read_reg(gspca_dev, 0x0115);
+	cit_write_reg(gspca_dev, 0x06, 0x0115);
+	cit_read_reg(gspca_dev, 0x0116);
+	cit_write_reg(gspca_dev, 0x44, 0x0116);
+	cit_read_reg(gspca_dev, 0x0116);
+	cit_write_reg(gspca_dev, 0x40, 0x0116);
+	cit_read_reg(gspca_dev, 0x0115);
+	cit_write_reg(gspca_dev, 0x0e, 0x0115);
+	cit_write_reg(gspca_dev, 0x19, 0x012c);
+
+	cit_Packet_Format1(gspca_dev, 0x00, 0x1e);
+	cit_Packet_Format1(gspca_dev, 0x39, 0x0d);
+	cit_Packet_Format1(gspca_dev, 0x39, 0x09);
+	cit_Packet_Format1(gspca_dev, 0x3b, 0x00);
+	cit_Packet_Format1(gspca_dev, 0x28, 0x22);
+	cit_Packet_Format1(gspca_dev, 0x27, 0x00);
+	cit_Packet_Format1(gspca_dev, 0x2b, 0x1f);
+	cit_Packet_Format1(gspca_dev, 0x39, 0x08);
+
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x2c, 0x00);
+
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x30, 0x14);
+
+	cit_PacketFormat2(gspca_dev, 0x39, 0x02);
+	cit_PacketFormat2(gspca_dev, 0x01, 0xe1);
+	cit_PacketFormat2(gspca_dev, 0x02, 0xcd);
+	cit_PacketFormat2(gspca_dev, 0x03, 0xcd);
+	cit_PacketFormat2(gspca_dev, 0x04, 0xfa);
+	cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
+	cit_PacketFormat2(gspca_dev, 0x39, 0x00);
+
+	cit_PacketFormat2(gspca_dev, 0x39, 0x02);
+	cit_PacketFormat2(gspca_dev, 0x0a, 0x37);
+	cit_PacketFormat2(gspca_dev, 0x0b, 0xb8);
+	cit_PacketFormat2(gspca_dev, 0x0c, 0xf3);
+	cit_PacketFormat2(gspca_dev, 0x0d, 0xe3);
+	cit_PacketFormat2(gspca_dev, 0x0e, 0x0d);
+	cit_PacketFormat2(gspca_dev, 0x0f, 0xf2);
+	cit_PacketFormat2(gspca_dev, 0x10, 0xd5);
+	cit_PacketFormat2(gspca_dev, 0x11, 0xba);
+	cit_PacketFormat2(gspca_dev, 0x12, 0x53);
+	cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
+	cit_PacketFormat2(gspca_dev, 0x39, 0x00);
+
+	cit_PacketFormat2(gspca_dev, 0x39, 0x02);
+	cit_PacketFormat2(gspca_dev, 0x16, 0x00);
+	cit_PacketFormat2(gspca_dev, 0x17, 0x28);
+	cit_PacketFormat2(gspca_dev, 0x18, 0x7d);
+	cit_PacketFormat2(gspca_dev, 0x19, 0xbe);
+	cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
+	cit_PacketFormat2(gspca_dev, 0x39, 0x00);
+
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x00, 0x18);
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x13, 0x18);
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x14, 0x06);
+
+	/* TESTME These are handled through controls
+	   KEEP until someone can test leaving this out is ok */
+	if (0) {
+		/* This is default brightness */
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x31, 0x37);
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x32, 0x46);
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x33, 0x55);
+	}
+
+	cit_Packet_Format1(gspca_dev, 0x2e, 0x04);
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x2d, 0x04);
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x29, 0x80);
+	cit_Packet_Format1(gspca_dev, 0x2c, 0x01);
+	cit_Packet_Format1(gspca_dev, 0x30, 0x17);
+	cit_Packet_Format1(gspca_dev, 0x39, 0x08);
+	for (i = 0; i < cit_model1_ntries; i++)
+		cit_Packet_Format1(gspca_dev, 0x34, 0x00);
+
+	cit_write_reg(gspca_dev, 0x00, 0x0101);
+	cit_write_reg(gspca_dev, 0x00, 0x010a);
+
+	switch (gspca_dev->width) {
+	case 128: /* 128x96 */
+		cit_write_reg(gspca_dev, 0x80, 0x0103);
+		cit_write_reg(gspca_dev, 0x60, 0x0105);
+		cit_write_reg(gspca_dev, 0x0c, 0x010b);
+		cit_write_reg(gspca_dev, 0x04, 0x011b);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x0b, 0x011d);
+		cit_write_reg(gspca_dev, 0x00, 0x011e);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x00, 0x0129);
+		break;
+	case 176: /* 176x144 */
+		cit_write_reg(gspca_dev, 0xb0, 0x0103);
+		cit_write_reg(gspca_dev, 0x8f, 0x0105);
+		cit_write_reg(gspca_dev, 0x06, 0x010b);
+		cit_write_reg(gspca_dev, 0x04, 0x011b);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x0d, 0x011d);
+		cit_write_reg(gspca_dev, 0x00, 0x011e);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x03, 0x0129);
+		break;
+	case 352: /* 352x288 */
+		cit_write_reg(gspca_dev, 0xb0, 0x0103);
+		cit_write_reg(gspca_dev, 0x90, 0x0105);
+		cit_write_reg(gspca_dev, 0x02, 0x010b);
+		cit_write_reg(gspca_dev, 0x04, 0x011b);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x05, 0x011d);
+		cit_write_reg(gspca_dev, 0x00, 0x011e);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x00, 0x0129);
+		break;
+	}
+
+	cit_write_reg(gspca_dev, 0xff, 0x012b);
+
+	/* TESTME These are handled through controls
+	   KEEP until someone can test leaving this out is ok */
+	if (0) {
+		/* This is another brightness - don't know why */
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x31, 0xc3);
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x32, 0xd2);
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x33, 0xe1);
+
+		/* Default contrast */
+		for (i = 0; i < cit_model1_ntries; i++)
+			cit_Packet_Format1(gspca_dev, 0x14, 0x0a);
+
+		/* Default sharpness */
+		for (i = 0; i < cit_model1_ntries2; i++)
+			cit_PacketFormat2(gspca_dev, 0x13, 0x1a);
+
+		/* Default lighting conditions */
+		cit_Packet_Format1(gspca_dev, 0x0027, sd->lighting);
+	}
+
+	/* Assorted init */
+	switch (gspca_dev->width) {
+	case 128: /* 128x96 */
+		cit_Packet_Format1(gspca_dev, 0x2b, 0x1e);
+		cit_write_reg(gspca_dev, 0xc9, 0x0119);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x80, 0x0109);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x36, 0x0102);
+		cit_write_reg(gspca_dev, 0x1a, 0x0104);
+		cit_write_reg(gspca_dev, 0x04, 0x011a);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x2b, 0x011c);
+		cit_write_reg(gspca_dev, 0x23, 0x012a);	/* Same everywhere */
+		break;
+	case 176: /* 176x144 */
+		cit_Packet_Format1(gspca_dev, 0x2b, 0x1e);
+		cit_write_reg(gspca_dev, 0xc9, 0x0119);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x80, 0x0109);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x04, 0x0102);
+		cit_write_reg(gspca_dev, 0x02, 0x0104);
+		cit_write_reg(gspca_dev, 0x04, 0x011a);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x2b, 0x011c);
+		cit_write_reg(gspca_dev, 0x23, 0x012a);	/* Same everywhere */
+		break;
+	case 352: /* 352x288 */
+		cit_Packet_Format1(gspca_dev, 0x2b, 0x1f);
+		cit_write_reg(gspca_dev, 0xc9, 0x0119);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x80, 0x0109);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x08, 0x0102);
+		cit_write_reg(gspca_dev, 0x01, 0x0104);
+		cit_write_reg(gspca_dev, 0x04, 0x011a);	/* Same everywhere */
+		cit_write_reg(gspca_dev, 0x2f, 0x011c);
+		cit_write_reg(gspca_dev, 0x23, 0x012a);	/* Same everywhere */
+		break;
+	}
+
+	cit_write_reg(gspca_dev, 0x01, 0x0100);	/* LED On  */
+	cit_write_reg(gspca_dev, clock_div, 0x0111);
+
+	return 0;
+}
+
+static int cit_start_model2(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int clock_div = 0;
+
+	cit_write_reg(gspca_dev, 0x0000, 0x0100);	/* LED on */
+	cit_read_reg(gspca_dev, 0x0116);
+	cit_write_reg(gspca_dev, 0x0060, 0x0116);
+	cit_write_reg(gspca_dev, 0x0002, 0x0112);
+	cit_write_reg(gspca_dev, 0x00bc, 0x012c);
+	cit_write_reg(gspca_dev, 0x0008, 0x012b);
+	cit_write_reg(gspca_dev, 0x0000, 0x0108);
+	cit_write_reg(gspca_dev, 0x0001, 0x0133);
+	cit_write_reg(gspca_dev, 0x0001, 0x0102);
+	switch (gspca_dev->width) {
+	case 176: /* 176x144 */
+		cit_write_reg(gspca_dev, 0x002c, 0x0103);	/* All except 320x240 */
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);	/* Same */
+		cit_write_reg(gspca_dev, 0x0024, 0x0105);	/* 176x144, 352x288 */
+		cit_write_reg(gspca_dev, 0x00b9, 0x010a);	/* Unique to this mode */
+		cit_write_reg(gspca_dev, 0x0038, 0x0119);	/* Unique to this mode */
+		/* TESTME HDG: this does not seem right
+		   (it is 2 for all other resolutions) */
+		sd->sof_len = 10;
+		break;
+	case 320: /* 320x240 */
+		cit_write_reg(gspca_dev, 0x0028, 0x0103);	/* Unique to this mode */
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);	/* Same */
+		cit_write_reg(gspca_dev, 0x001e, 0x0105);	/* 320x240, 352x240 */
+		cit_write_reg(gspca_dev, 0x0039, 0x010a);	/* All except 176x144 */
+		cit_write_reg(gspca_dev, 0x0070, 0x0119);	/* All except 176x144 */
+		sd->sof_len = 2;
+		break;
+	/* case VIDEOSIZE_352x240: */
+		cit_write_reg(gspca_dev, 0x002c, 0x0103);	/* All except 320x240 */
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);	/* Same */
+		cit_write_reg(gspca_dev, 0x001e, 0x0105);	/* 320x240, 352x240 */
+		cit_write_reg(gspca_dev, 0x0039, 0x010a);	/* All except 176x144 */
+		cit_write_reg(gspca_dev, 0x0070, 0x0119);	/* All except 176x144 */
+		sd->sof_len = 2;
+		break;
+	case 352: /* 352x288 */
+		cit_write_reg(gspca_dev, 0x002c, 0x0103);	/* All except 320x240 */
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);	/* Same */
+		cit_write_reg(gspca_dev, 0x0024, 0x0105);	/* 176x144, 352x288 */
+		cit_write_reg(gspca_dev, 0x0039, 0x010a);	/* All except 176x144 */
+		cit_write_reg(gspca_dev, 0x0070, 0x0119);	/* All except 176x144 */
+		sd->sof_len = 2;
+		break;
+	}
+
+	cit_write_reg(gspca_dev, 0x0000, 0x0100);	/* LED on */
+
+	switch (gspca_dev->width) {
+	case 176: /* 176x144 */
+		cit_write_reg(gspca_dev, 0x0050, 0x0111);
+		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+		break;
+	case 320: /* 320x240 */
+	case 352: /* 352x288 */
+		cit_write_reg(gspca_dev, 0x0040, 0x0111);
+		cit_write_reg(gspca_dev, 0x00c0, 0x0111);
+		break;
+	}
+	cit_write_reg(gspca_dev, 0x009b, 0x010f);
+	cit_write_reg(gspca_dev, 0x00bb, 0x010f);
+
+	/*
+	 * Hardware settings, may affect CMOS sensor; not user controls!
+	 * -------------------------------------------------------------
+	 * 0x0004: no effect
+	 * 0x0006: hardware effect
+	 * 0x0008: no effect
+	 * 0x000a: stops video stream, probably important h/w setting
+	 * 0x000c: changes color in hardware manner (not user setting)
+	 * 0x0012: changes number of colors (does not affect speed)
+	 * 0x002a: no effect
+	 * 0x002c: hardware setting (related to scan lines)
+	 * 0x002e: stops video stream, probably important h/w setting
+	 */
+	cit_model2_Packet1(gspca_dev, 0x000a, 0x005c);
+	cit_model2_Packet1(gspca_dev, 0x0004, 0x0000);
+	cit_model2_Packet1(gspca_dev, 0x0006, 0x00fb);
+	cit_model2_Packet1(gspca_dev, 0x0008, 0x0000);
+	cit_model2_Packet1(gspca_dev, 0x000c, 0x0009);
+	cit_model2_Packet1(gspca_dev, 0x0012, 0x000a);
+	cit_model2_Packet1(gspca_dev, 0x002a, 0x0000);
+	cit_model2_Packet1(gspca_dev, 0x002c, 0x0000);
+	cit_model2_Packet1(gspca_dev, 0x002e, 0x0008);
+
+	/*
+	 * Function 0x0030 pops up all over the place. Apparently
+	 * it is a hardware control register, with every bit assigned to
+	 * do something.
+	 */
+	cit_model2_Packet1(gspca_dev, 0x0030, 0x0000);
+
+	/*
+	 * Magic control of CMOS sensor. Only lower values like
+	 * 0-3 work, and picture shifts left or right. Don't change.
+	 */
+	switch (gspca_dev->width) {
+	case 176: /* 176x144 */
+		cit_model2_Packet1(gspca_dev, 0x0014, 0x0002);
+		cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */
+		cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */
+		clock_div = 6;
+		break;
+	case 320: /* 320x240 */
+		cit_model2_Packet1(gspca_dev, 0x0014, 0x0009);
+		cit_model2_Packet1(gspca_dev, 0x0016, 0x0005); /* Horizontal shift */
+		cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Another hardware setting */
+		clock_div = 8;
+		break;
+	/* case VIDEOSIZE_352x240: */
+		/* This mode doesn't work as Windows programs it; changed to work */
+		cit_model2_Packet1(gspca_dev, 0x0014, 0x0009); /* Windows sets this to 8 */
+		cit_model2_Packet1(gspca_dev, 0x0016, 0x0003); /* Horizontal shift */
+		cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Windows sets this to 0x0045 */
+		clock_div = 10;
+		break;
+	case 352: /* 352x288 */
+		cit_model2_Packet1(gspca_dev, 0x0014, 0x0003);
+		cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */
+		cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */
+		clock_div = 16;
+		break;
+	}
+
+	/* TESTME These are handled through controls
+	   KEEP until someone can test leaving this out is ok */
+	if (0)
+		cit_model2_Packet1(gspca_dev, 0x001a, 0x005a);
+
+	/*
+	 * We have our own frame rate setting varying from 0 (slowest) to 6
+	 * (fastest). The camera model 2 allows frame rate in range [0..0x1F]
+	 # where 0 is also the slowest setting. However for all practical
+	 # reasons high settings make no sense because USB is not fast enough
+	 # to support high FPS. Be aware that the picture datastream will be
+	 # severely disrupted if you ask for frame rate faster than allowed
+	 # for the video size - see below:
+	 *
+	 * Allowable ranges (obtained experimentally on OHCI, K6-3, 450 MHz):
+	 * -----------------------------------------------------------------
+	 * 176x144: [6..31]
+	 * 320x240: [8..31]
+	 * 352x240: [10..31]
+	 * 352x288: [16..31] I have to raise lower threshold for stability...
+	 *
+	 * As usual, slower FPS provides better sensitivity.
+	 */
+	cit_model2_Packet1(gspca_dev, 0x001c, clock_div);
+
+	/*
+	 * This setting does not visibly affect pictures; left it here
+	 * because it was present in Windows USB data stream. This function
+	 * does not allow arbitrary values and apparently is a bit mask, to
+	 * be activated only at appropriate time. Don't change it randomly!
+	 */
+	switch (gspca_dev->width) {
+	case 176: /* 176x144 */
+		cit_model2_Packet1(gspca_dev, 0x0026, 0x00c2);
+		break;
+	case 320: /* 320x240 */
+		cit_model2_Packet1(gspca_dev, 0x0026, 0x0044);
+		break;
+	/* case VIDEOSIZE_352x240: */
+		cit_model2_Packet1(gspca_dev, 0x0026, 0x0046);
+		break;
+	case 352: /* 352x288 */
+		cit_model2_Packet1(gspca_dev, 0x0026, 0x0048);
+		break;
+	}
+
+	/* FIXME this cannot be changed while streaming, so we
+	   should report a grabbed flag for this control. */
+	cit_model2_Packet1(gspca_dev, 0x0028, sd->lighting);
+	/* color balance rg2 */
+	cit_model2_Packet1(gspca_dev, 0x001e, 0x002f);
+	/* saturation */
+	cit_model2_Packet1(gspca_dev, 0x0020, 0x0034);
+	/* color balance yb */
+	cit_model2_Packet1(gspca_dev, 0x0022, 0x00a0);
+
+	/* Hardware control command */
+	cit_model2_Packet1(gspca_dev, 0x0030, 0x0004);
+
+	return 0;
+}
+
+static int cit_start_model3(struct gspca_dev *gspca_dev)
+{
+	const unsigned short compression = 0; /* 0=none, 7=best frame rate */
+	int i, clock_div = 0;
+
+	/* HDG not in ibmcam driver, added to see if it helps with
+	   auto-detecting between model3 and ibm netcamera pro */
+	cit_read_reg(gspca_dev, 0x128);
+
+	cit_write_reg(gspca_dev, 0x0000, 0x0100);
+	cit_read_reg(gspca_dev, 0x0116);
+	cit_write_reg(gspca_dev, 0x0060, 0x0116);
+	cit_write_reg(gspca_dev, 0x0002, 0x0112);
+	cit_write_reg(gspca_dev, 0x0000, 0x0123);
+	cit_write_reg(gspca_dev, 0x0001, 0x0117);
+	cit_write_reg(gspca_dev, 0x0040, 0x0108);
+	cit_write_reg(gspca_dev, 0x0019, 0x012c);
+	cit_write_reg(gspca_dev, 0x0060, 0x0116);
+	cit_write_reg(gspca_dev, 0x0002, 0x0115);
+	cit_write_reg(gspca_dev, 0x0003, 0x0115);
+	cit_read_reg(gspca_dev, 0x0115);
+	cit_write_reg(gspca_dev, 0x000b, 0x0115);
+
+	/* TESTME HDG not in ibmcam driver, added to see if it helps with
+	   auto-detecting between model3 and ibm netcamera pro */
+	if (0) {
+		cit_write_reg(gspca_dev, 0x0078, 0x012d);
+		cit_write_reg(gspca_dev, 0x0001, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0079, 0x012d);
+		cit_write_reg(gspca_dev, 0x00ff, 0x0130);
+		cit_write_reg(gspca_dev, 0xcd41, 0x0124);
+		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+		cit_read_reg(gspca_dev, 0x0126);
+	}
+
+	cit_model3_Packet1(gspca_dev, 0x000a, 0x0040);
+	cit_model3_Packet1(gspca_dev, 0x000b, 0x00f6);
+	cit_model3_Packet1(gspca_dev, 0x000c, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x000d, 0x0020);
+	cit_model3_Packet1(gspca_dev, 0x000e, 0x0033);
+	cit_model3_Packet1(gspca_dev, 0x000f, 0x0007);
+	cit_model3_Packet1(gspca_dev, 0x0010, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0011, 0x0070);
+	cit_model3_Packet1(gspca_dev, 0x0012, 0x0030);
+	cit_model3_Packet1(gspca_dev, 0x0013, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0014, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x0015, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x0016, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x0017, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x0018, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x001e, 0x00c3);
+	cit_model3_Packet1(gspca_dev, 0x0020, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0028, 0x0010);
+	cit_model3_Packet1(gspca_dev, 0x0029, 0x0054);
+	cit_model3_Packet1(gspca_dev, 0x002a, 0x0013);
+	cit_model3_Packet1(gspca_dev, 0x002b, 0x0007);
+	cit_model3_Packet1(gspca_dev, 0x002d, 0x0028);
+	cit_model3_Packet1(gspca_dev, 0x002e, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0031, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0032, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0033, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0034, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0035, 0x0038);
+	cit_model3_Packet1(gspca_dev, 0x003a, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x003c, 0x001e);
+	cit_model3_Packet1(gspca_dev, 0x003f, 0x000a);
+	cit_model3_Packet1(gspca_dev, 0x0041, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0046, 0x003f);
+	cit_model3_Packet1(gspca_dev, 0x0047, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0050, 0x0005);
+	cit_model3_Packet1(gspca_dev, 0x0052, 0x001a);
+	cit_model3_Packet1(gspca_dev, 0x0053, 0x0003);
+	cit_model3_Packet1(gspca_dev, 0x005a, 0x006b);
+	cit_model3_Packet1(gspca_dev, 0x005d, 0x001e);
+	cit_model3_Packet1(gspca_dev, 0x005e, 0x0030);
+	cit_model3_Packet1(gspca_dev, 0x005f, 0x0041);
+	cit_model3_Packet1(gspca_dev, 0x0064, 0x0008);
+	cit_model3_Packet1(gspca_dev, 0x0065, 0x0015);
+	cit_model3_Packet1(gspca_dev, 0x0068, 0x000f);
+	cit_model3_Packet1(gspca_dev, 0x0079, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x007a, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x007c, 0x003f);
+	cit_model3_Packet1(gspca_dev, 0x0082, 0x000f);
+	cit_model3_Packet1(gspca_dev, 0x0085, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0099, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x009b, 0x0023);
+	cit_model3_Packet1(gspca_dev, 0x009c, 0x0022);
+	cit_model3_Packet1(gspca_dev, 0x009d, 0x0096);
+	cit_model3_Packet1(gspca_dev, 0x009e, 0x0096);
+	cit_model3_Packet1(gspca_dev, 0x009f, 0x000a);
+
+	switch (gspca_dev->width) {
+	case 160:
+		cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
+		cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
+		cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
+		cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+		cit_write_reg(gspca_dev, 0x0024, 0x010b); /* Differs everywhere */
+		cit_write_reg(gspca_dev, 0x00a9, 0x0119);
+		cit_write_reg(gspca_dev, 0x0016, 0x011b);
+		cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same on 160x120, 320x240 */
+		cit_write_reg(gspca_dev, 0x0003, 0x011e); /* Same on 160x120, 640x480 */
+		cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+		cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+		cit_write_reg(gspca_dev, 0x0018, 0x0102);
+		cit_write_reg(gspca_dev, 0x0004, 0x0104);
+		cit_write_reg(gspca_dev, 0x0004, 0x011a);
+		cit_write_reg(gspca_dev, 0x0028, 0x011c);
+		cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+		cit_write_reg(gspca_dev, 0x0000, 0x0118);
+		cit_write_reg(gspca_dev, 0x0000, 0x0132);
+		cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
+		cit_write_reg(gspca_dev, compression, 0x0109);
+		clock_div = 3;
+		break;
+	case 320:
+		cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
+		cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
+		cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
+		cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+		cit_write_reg(gspca_dev, 0x0028, 0x010b); /* Differs everywhere */
+		cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same */
+		cit_write_reg(gspca_dev, 0x0000, 0x011e);
+		cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+		cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+		/* 4 commands from 160x120 skipped */
+		cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+		cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
+		cit_write_reg(gspca_dev, compression, 0x0109);
+		cit_write_reg(gspca_dev, 0x00d9, 0x0119);
+		cit_write_reg(gspca_dev, 0x0006, 0x011b);
+		cit_write_reg(gspca_dev, 0x0021, 0x0102); /* Same on 320x240, 640x480 */
+		cit_write_reg(gspca_dev, 0x0010, 0x0104);
+		cit_write_reg(gspca_dev, 0x0004, 0x011a);
+		cit_write_reg(gspca_dev, 0x003f, 0x011c);
+		cit_write_reg(gspca_dev, 0x001c, 0x0118);
+		cit_write_reg(gspca_dev, 0x0000, 0x0132);
+		clock_div = 5;
+		break;
+	case 640:
+		cit_write_reg(gspca_dev, 0x00f0, 0x0105);
+		cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+		cit_write_reg(gspca_dev, 0x0038, 0x010b); /* Differs everywhere */
+		cit_write_reg(gspca_dev, 0x00d9, 0x0119); /* Same on 320x240, 640x480 */
+		cit_write_reg(gspca_dev, 0x0006, 0x011b); /* Same on 320x240, 640x480 */
+		cit_write_reg(gspca_dev, 0x0004, 0x011d); /* NC */
+		cit_write_reg(gspca_dev, 0x0003, 0x011e); /* Same on 160x120, 640x480 */
+		cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+		cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+		cit_write_reg(gspca_dev, 0x0021, 0x0102); /* Same on 320x240, 640x480 */
+		cit_write_reg(gspca_dev, 0x0016, 0x0104); /* NC */
+		cit_write_reg(gspca_dev, 0x0004, 0x011a); /* Same on 320x240, 640x480 */
+		cit_write_reg(gspca_dev, 0x003f, 0x011c); /* Same on 320x240, 640x480 */
+		cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+		cit_write_reg(gspca_dev, 0x001c, 0x0118); /* Same on 320x240, 640x480 */
+		cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
+		cit_write_reg(gspca_dev, compression, 0x0109);
+		cit_write_reg(gspca_dev, 0x0040, 0x0101);
+		cit_write_reg(gspca_dev, 0x0040, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0132); /* Same on 320x240, 640x480 */
+		clock_div = 7;
+		break;
+	}
+
+	cit_model3_Packet1(gspca_dev, 0x007e, 0x000e);	/* Hue */
+	cit_model3_Packet1(gspca_dev, 0x0036, 0x0011);	/* Brightness */
+	cit_model3_Packet1(gspca_dev, 0x0060, 0x0002);	/* Sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0061, 0x0004);	/* Sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0062, 0x0005);	/* Sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0063, 0x0014);	/* Sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0096, 0x00a0);	/* Red sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0097, 0x0096);	/* Blue sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0067, 0x0001);	/* Contrast */
+	cit_model3_Packet1(gspca_dev, 0x005b, 0x000c);	/* Contrast */
+	cit_model3_Packet1(gspca_dev, 0x005c, 0x0016);	/* Contrast */
+	cit_model3_Packet1(gspca_dev, 0x0098, 0x000b);
+	cit_model3_Packet1(gspca_dev, 0x002c, 0x0003);	/* Was 1, broke 640x480 */
+	cit_model3_Packet1(gspca_dev, 0x002f, 0x002a);
+	cit_model3_Packet1(gspca_dev, 0x0030, 0x0029);
+	cit_model3_Packet1(gspca_dev, 0x0037, 0x0002);
+	cit_model3_Packet1(gspca_dev, 0x0038, 0x0059);
+	cit_model3_Packet1(gspca_dev, 0x003d, 0x002e);
+	cit_model3_Packet1(gspca_dev, 0x003e, 0x0028);
+	cit_model3_Packet1(gspca_dev, 0x0078, 0x0005);
+	cit_model3_Packet1(gspca_dev, 0x007b, 0x0011);
+	cit_model3_Packet1(gspca_dev, 0x007d, 0x004b);
+	cit_model3_Packet1(gspca_dev, 0x007f, 0x0022);
+	cit_model3_Packet1(gspca_dev, 0x0080, 0x000c);
+	cit_model3_Packet1(gspca_dev, 0x0081, 0x000b);
+	cit_model3_Packet1(gspca_dev, 0x0083, 0x00fd);
+	cit_model3_Packet1(gspca_dev, 0x0086, 0x000b);
+	cit_model3_Packet1(gspca_dev, 0x0087, 0x000b);
+	cit_model3_Packet1(gspca_dev, 0x007e, 0x000e);
+	cit_model3_Packet1(gspca_dev, 0x0096, 0x00a0);	/* Red sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0097, 0x0096);	/* Blue sharpness */
+	cit_model3_Packet1(gspca_dev, 0x0098, 0x000b);
+
+	/* FIXME we should probably use cit_get_clock_div() here (in
+	   combination with isoc negotiation using the programmable isoc size)
+	   like with the IBM netcam pro). */
+	cit_write_reg(gspca_dev, clock_div, 0x0111); /* Clock Divider */
+
+	switch (gspca_dev->width) {
+	case 160:
+		cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */
+		cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */
+		cit_model3_Packet1(gspca_dev, 0x003b, 0x003c); /* Same */
+		cit_model3_Packet1(gspca_dev, 0x0040, 0x000a);
+		cit_model3_Packet1(gspca_dev, 0x0051, 0x000a);
+		break;
+	case 320:
+		cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */
+		cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */
+		cit_model3_Packet1(gspca_dev, 0x003b, 0x003c); /* Same */
+		cit_model3_Packet1(gspca_dev, 0x0040, 0x0008);
+		cit_model3_Packet1(gspca_dev, 0x0051, 0x000b);
+		break;
+	case 640:
+		cit_model3_Packet1(gspca_dev, 0x001f, 0x0002);	/* !Same */
+		cit_model3_Packet1(gspca_dev, 0x0039, 0x003e);	/* !Same */
+		cit_model3_Packet1(gspca_dev, 0x0040, 0x0008);
+		cit_model3_Packet1(gspca_dev, 0x0051, 0x000a);
+		break;
+	}
+
+/*	if (sd->input_index) { */
+	if (rca_input) {
+		for (i = 0; i < ARRAY_SIZE(rca_initdata); i++) {
+			if (rca_initdata[i][0])
+				cit_read_reg(gspca_dev, rca_initdata[i][2]);
+			else
+				cit_write_reg(gspca_dev, rca_initdata[i][1],
+					      rca_initdata[i][2]);
+		}
+	}
+
+	return 0;
+}
+
+static int cit_start_model4(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	cit_write_reg(gspca_dev, 0x0000, 0x0100);
+	cit_write_reg(gspca_dev, 0x00c0, 0x0111);
+	cit_write_reg(gspca_dev, 0x00bc, 0x012c);
+	cit_write_reg(gspca_dev, 0x0080, 0x012b);
+	cit_write_reg(gspca_dev, 0x0000, 0x0108);
+	cit_write_reg(gspca_dev, 0x0001, 0x0133);
+	cit_write_reg(gspca_dev, 0x009b, 0x010f);
+	cit_write_reg(gspca_dev, 0x00bb, 0x010f);
+	cit_model4_Packet1(gspca_dev, 0x0038, 0x0000);
+	cit_model4_Packet1(gspca_dev, 0x000a, 0x005c);
+
+	cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+	cit_write_reg(gspca_dev, 0x0004, 0x012f);
+	cit_write_reg(gspca_dev, 0xd141, 0x0124);
+	cit_write_reg(gspca_dev, 0x0000, 0x0127);
+	cit_write_reg(gspca_dev, 0x00fb, 0x012e);
+	cit_write_reg(gspca_dev, 0x0000, 0x0130);
+	cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+	cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+	cit_write_reg(gspca_dev, 0xd055, 0x0124);
+	cit_write_reg(gspca_dev, 0x000c, 0x0127);
+	cit_write_reg(gspca_dev, 0x0009, 0x012e);
+	cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+
+	cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+	cit_write_reg(gspca_dev, 0x0012, 0x012f);
+	cit_write_reg(gspca_dev, 0xd141, 0x0124);
+	cit_write_reg(gspca_dev, 0x0008, 0x0127);
+	cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+	cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+	cit_write_reg(gspca_dev, 0x002a, 0x012d);
+	cit_write_reg(gspca_dev, 0x0000, 0x012f);
+	cit_write_reg(gspca_dev, 0xd145, 0x0124);
+	cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+	cit_model4_Packet1(gspca_dev, 0x0034, 0x0000);
+
+	switch (gspca_dev->width) {
+	case 128: /* 128x96 */
+		cit_write_reg(gspca_dev, 0x0070, 0x0119);
+		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+		cit_write_reg(gspca_dev, 0x0039, 0x010a);
+		cit_write_reg(gspca_dev, 0x0001, 0x0102);
+		cit_write_reg(gspca_dev, 0x0028, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);
+		cit_write_reg(gspca_dev, 0x001e, 0x0105);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0016, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x000a, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0014, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+		cit_write_reg(gspca_dev, 0x001a, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+		cit_write_reg(gspca_dev, 0x005a, 0x012d);
+		cit_write_reg(gspca_dev, 0x9545, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+		cit_write_reg(gspca_dev, 0x0018, 0x012e);
+		cit_write_reg(gspca_dev, 0x0043, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+		cit_write_reg(gspca_dev, 0xd055, 0x0124);
+		cit_write_reg(gspca_dev, 0x001c, 0x0127);
+		cit_write_reg(gspca_dev, 0x00eb, 0x012e);
+		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0032, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0000, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0036, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x001e, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0017, 0x0127);
+		cit_write_reg(gspca_dev, 0x0013, 0x012e);
+		cit_write_reg(gspca_dev, 0x0031, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x0017, 0x012d);
+		cit_write_reg(gspca_dev, 0x0078, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x0000, 0x0127);
+		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+		sd->sof_len = 2;
+		break;
+	case 160: /* 160x120 */
+		cit_write_reg(gspca_dev, 0x0038, 0x0119);
+		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+		cit_write_reg(gspca_dev, 0x00b9, 0x010a);
+		cit_write_reg(gspca_dev, 0x0001, 0x0102);
+		cit_write_reg(gspca_dev, 0x0028, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);
+		cit_write_reg(gspca_dev, 0x001e, 0x0105);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0016, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x000b, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0014, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+		cit_write_reg(gspca_dev, 0x001a, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+		cit_write_reg(gspca_dev, 0x005a, 0x012d);
+		cit_write_reg(gspca_dev, 0x9545, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+		cit_write_reg(gspca_dev, 0x0018, 0x012e);
+		cit_write_reg(gspca_dev, 0x0043, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+		cit_write_reg(gspca_dev, 0xd055, 0x0124);
+		cit_write_reg(gspca_dev, 0x001c, 0x0127);
+		cit_write_reg(gspca_dev, 0x00c7, 0x012e);
+		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0032, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0025, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0036, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x001e, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0048, 0x0127);
+		cit_write_reg(gspca_dev, 0x0035, 0x012e);
+		cit_write_reg(gspca_dev, 0x00d0, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x0048, 0x012d);
+		cit_write_reg(gspca_dev, 0x0090, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x0001, 0x0127);
+		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+		sd->sof_len = 2;
+		break;
+	case 176: /* 176x144 */
+		cit_write_reg(gspca_dev, 0x0038, 0x0119);
+		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+		cit_write_reg(gspca_dev, 0x00b9, 0x010a);
+		cit_write_reg(gspca_dev, 0x0001, 0x0102);
+		cit_write_reg(gspca_dev, 0x002c, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);
+		cit_write_reg(gspca_dev, 0x0024, 0x0105);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0016, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0007, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0014, 0x012d);
+		cit_write_reg(gspca_dev, 0x0001, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+		cit_write_reg(gspca_dev, 0x001a, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+		cit_write_reg(gspca_dev, 0x005e, 0x012d);
+		cit_write_reg(gspca_dev, 0x9545, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+		cit_write_reg(gspca_dev, 0x0018, 0x012e);
+		cit_write_reg(gspca_dev, 0x0049, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+		cit_write_reg(gspca_dev, 0xd055, 0x0124);
+		cit_write_reg(gspca_dev, 0x001c, 0x0127);
+		cit_write_reg(gspca_dev, 0x00c7, 0x012e);
+		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0032, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0028, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0036, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x001e, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0010, 0x0127);
+		cit_write_reg(gspca_dev, 0x0013, 0x012e);
+		cit_write_reg(gspca_dev, 0x002a, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x0010, 0x012d);
+		cit_write_reg(gspca_dev, 0x006d, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x0001, 0x0127);
+		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+		/* TESTME HDG: this does not seem right
+		   (it is 2 for all other resolutions) */
+		sd->sof_len = 10;
+		break;
+	case 320: /* 320x240 */
+		cit_write_reg(gspca_dev, 0x0070, 0x0119);
+		cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+		cit_write_reg(gspca_dev, 0x0039, 0x010a);
+		cit_write_reg(gspca_dev, 0x0001, 0x0102);
+		cit_write_reg(gspca_dev, 0x0028, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);
+		cit_write_reg(gspca_dev, 0x001e, 0x0105);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0016, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x000a, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0014, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+		cit_write_reg(gspca_dev, 0x001a, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+		cit_write_reg(gspca_dev, 0x005a, 0x012d);
+		cit_write_reg(gspca_dev, 0x9545, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+		cit_write_reg(gspca_dev, 0x0018, 0x012e);
+		cit_write_reg(gspca_dev, 0x0043, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+		cit_write_reg(gspca_dev, 0xd055, 0x0124);
+		cit_write_reg(gspca_dev, 0x001c, 0x0127);
+		cit_write_reg(gspca_dev, 0x00eb, 0x012e);
+		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0032, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0000, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0036, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x001e, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0017, 0x0127);
+		cit_write_reg(gspca_dev, 0x0013, 0x012e);
+		cit_write_reg(gspca_dev, 0x0031, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x0017, 0x012d);
+		cit_write_reg(gspca_dev, 0x0078, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x0000, 0x0127);
+		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+		sd->sof_len = 2;
+		break;
+	case 352: /* 352x288 */
+		cit_write_reg(gspca_dev, 0x0070, 0x0119);
+		cit_write_reg(gspca_dev, 0x00c0, 0x0111);
+		cit_write_reg(gspca_dev, 0x0039, 0x010a);
+		cit_write_reg(gspca_dev, 0x0001, 0x0102);
+		cit_write_reg(gspca_dev, 0x002c, 0x0103);
+		cit_write_reg(gspca_dev, 0x0000, 0x0104);
+		cit_write_reg(gspca_dev, 0x0024, 0x0105);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0016, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0006, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0014, 0x012d);
+		cit_write_reg(gspca_dev, 0x0002, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+		cit_write_reg(gspca_dev, 0x001a, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+		cit_write_reg(gspca_dev, 0x005e, 0x012d);
+		cit_write_reg(gspca_dev, 0x9545, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+		cit_write_reg(gspca_dev, 0x0018, 0x012e);
+		cit_write_reg(gspca_dev, 0x0049, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+		cit_write_reg(gspca_dev, 0xd055, 0x0124);
+		cit_write_reg(gspca_dev, 0x001c, 0x0127);
+		cit_write_reg(gspca_dev, 0x00cf, 0x012e);
+		cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x0032, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0000, 0x0127);
+		cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+		cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+		cit_write_reg(gspca_dev, 0x0036, 0x012d);
+		cit_write_reg(gspca_dev, 0x0008, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+		cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+		cit_write_reg(gspca_dev, 0x001e, 0x012f);
+		cit_write_reg(gspca_dev, 0xd141, 0x0124);
+		cit_write_reg(gspca_dev, 0x0010, 0x0127);
+		cit_write_reg(gspca_dev, 0x0013, 0x012e);
+		cit_write_reg(gspca_dev, 0x0025, 0x0130);
+		cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+		cit_write_reg(gspca_dev, 0x0010, 0x012d);
+		cit_write_reg(gspca_dev, 0x0048, 0x012f);
+		cit_write_reg(gspca_dev, 0xd145, 0x0124);
+		cit_write_reg(gspca_dev, 0x0000, 0x0127);
+		cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+		sd->sof_len = 2;
+		break;
+	}
+
+	cit_model4_Packet1(gspca_dev, 0x0038, 0x0004);
+
+	return 0;
+}
+
+static int cit_start_ibm_netcam_pro(struct gspca_dev *gspca_dev)
+{
+	const unsigned short compression = 0; /* 0=none, 7=best frame rate */
+	int i, clock_div;
+
+	clock_div = cit_get_clock_div(gspca_dev);
+	if (clock_div < 0)
+		return clock_div;
+
+	cit_write_reg(gspca_dev, 0x0003, 0x0133);
+	cit_write_reg(gspca_dev, 0x0000, 0x0117);
+	cit_write_reg(gspca_dev, 0x0008, 0x0123);
+	cit_write_reg(gspca_dev, 0x0000, 0x0100);
+	cit_write_reg(gspca_dev, 0x0060, 0x0116);
+	/* cit_write_reg(gspca_dev, 0x0002, 0x0112); see sd_stop0 */
+	cit_write_reg(gspca_dev, 0x0000, 0x0133);
+	cit_write_reg(gspca_dev, 0x0000, 0x0123);
+	cit_write_reg(gspca_dev, 0x0001, 0x0117);
+	cit_write_reg(gspca_dev, 0x0040, 0x0108);
+	cit_write_reg(gspca_dev, 0x0019, 0x012c);
+	cit_write_reg(gspca_dev, 0x0060, 0x0116);
+	/* cit_write_reg(gspca_dev, 0x000b, 0x0115); see sd_stop0 */
+
+	cit_model3_Packet1(gspca_dev, 0x0049, 0x0000);
+
+	cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
+	cit_write_reg(gspca_dev, 0x003a, 0x0102); /* Hstart */
+	cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
+	cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
+	cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+	cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same on 160x120, 320x240 */
+	cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+	cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+	cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+
+	switch (gspca_dev->width) {
+	case 160: /* 160x120 */
+		cit_write_reg(gspca_dev, 0x0024, 0x010b);
+		cit_write_reg(gspca_dev, 0x0089, 0x0119);
+		cit_write_reg(gspca_dev, 0x000a, 0x011b);
+		cit_write_reg(gspca_dev, 0x0003, 0x011e);
+		cit_write_reg(gspca_dev, 0x0007, 0x0104);
+		cit_write_reg(gspca_dev, 0x0009, 0x011a);
+		cit_write_reg(gspca_dev, 0x008b, 0x011c);
+		cit_write_reg(gspca_dev, 0x0008, 0x0118);
+		cit_write_reg(gspca_dev, 0x0000, 0x0132);
+		break;
+	case 320: /* 320x240 */
+		cit_write_reg(gspca_dev, 0x0028, 0x010b);
+		cit_write_reg(gspca_dev, 0x00d9, 0x0119);
+		cit_write_reg(gspca_dev, 0x0006, 0x011b);
+		cit_write_reg(gspca_dev, 0x0000, 0x011e);
+		cit_write_reg(gspca_dev, 0x000e, 0x0104);
+		cit_write_reg(gspca_dev, 0x0004, 0x011a);
+		cit_write_reg(gspca_dev, 0x003f, 0x011c);
+		cit_write_reg(gspca_dev, 0x000c, 0x0118);
+		cit_write_reg(gspca_dev, 0x0000, 0x0132);
+		break;
+	}
+
+	cit_model3_Packet1(gspca_dev, 0x0019, 0x0031);
+	cit_model3_Packet1(gspca_dev, 0x001a, 0x0003);
+	cit_model3_Packet1(gspca_dev, 0x001b, 0x0038);
+	cit_model3_Packet1(gspca_dev, 0x001c, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0024, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x0027, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x002a, 0x0004);
+	cit_model3_Packet1(gspca_dev, 0x0035, 0x000b);
+	cit_model3_Packet1(gspca_dev, 0x003f, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x0044, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x0054, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00c4, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00e7, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x00e9, 0x0001);
+	cit_model3_Packet1(gspca_dev, 0x00ee, 0x0000);
+	cit_model3_Packet1(gspca_dev, 0x00f3, 0x00c0);
+
+	cit_write_reg(gspca_dev, compression, 0x0109);
+	cit_write_reg(gspca_dev, clock_div, 0x0111);
+
+/*	if (sd->input_index) { */
+	if (rca_input) {
+		for (i = 0; i < ARRAY_SIZE(rca_initdata); i++) {
+			if (rca_initdata[i][0])
+				cit_read_reg(gspca_dev, rca_initdata[i][2]);
+			else
+				cit_write_reg(gspca_dev, rca_initdata[i][1],
+					      rca_initdata[i][2]);
+		}
+	}
+
+	return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	int packet_size;
+
+	packet_size = cit_get_packet_size(gspca_dev);
+	if (packet_size < 0)
+		return packet_size;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+		cit_start_model0(gspca_dev);
+		break;
+	case CIT_MODEL1:
+		cit_start_model1(gspca_dev);
+		break;
+	case CIT_MODEL2:
+		cit_start_model2(gspca_dev);
+		break;
+	case CIT_MODEL3:
+		cit_start_model3(gspca_dev);
+		break;
+	case CIT_MODEL4:
+		cit_start_model4(gspca_dev);
+		break;
+	case CIT_IBM_NETCAM_PRO:
+		cit_start_ibm_netcam_pro(gspca_dev);
+		break;
+	}
+
+	cit_set_brightness(gspca_dev);
+	cit_set_contrast(gspca_dev);
+	cit_set_hue(gspca_dev);
+	cit_set_sharpness(gspca_dev);
+	cit_set_lighting(gspca_dev);
+	cit_set_hflip(gspca_dev);
+
+	/* Program max isoc packet size */
+	cit_write_reg(gspca_dev, packet_size >> 8, 0x0106);
+	cit_write_reg(gspca_dev, packet_size & 0xff, 0x0107);
+
+	cit_restart_stream(gspca_dev);
+
+	return 0;
+}
+
+static int sd_isoc_nego(struct gspca_dev *gspca_dev)
+{
+	int ret, packet_size;
+	struct usb_host_interface *alt;
+
+	alt = &gspca_dev->dev->config->intf_cache[0]->altsetting[1];
+	packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+	packet_size -= 100;
+	if (packet_size < 300)
+		return -EIO;
+	alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(packet_size);
+
+	ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
+	if (ret < 0)
+		err("set alt 1 err %d", ret);
+
+	return ret;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+	cit_write_reg(gspca_dev, 0x0000, 0x010c);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct usb_host_interface *alt;
+
+	/* We cannot use gspca_dev->present here as that is not set when
+	   sd_init gets called and we get called from sd_init */
+	if (!gspca_dev->dev)
+		return;
+
+	alt = &gspca_dev->dev->config->intf_cache[0]->altsetting[1];
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+		/* HDG windows does this, but it causes the cams autogain to
+		   restart from a gain of 0, which does not look good when
+		   changing resolutions. */
+		/* cit_write_reg(gspca_dev, 0x0000, 0x0112); */
+		cit_write_reg(gspca_dev, 0x00c0, 0x0100); /* LED Off */
+		break;
+	case CIT_MODEL1:
+		cit_send_FF_04_02(gspca_dev);
+		cit_read_reg(gspca_dev, 0x0100);
+		cit_write_reg(gspca_dev, 0x81, 0x0100);	/* LED Off */
+		break;
+	case CIT_MODEL2:
+	case CIT_MODEL4:
+		cit_model2_Packet1(gspca_dev, 0x0030, 0x0004);
+
+		cit_write_reg(gspca_dev, 0x0080, 0x0100);	/* LED Off */
+		cit_write_reg(gspca_dev, 0x0020, 0x0111);
+		cit_write_reg(gspca_dev, 0x00a0, 0x0111);
+
+		cit_model2_Packet1(gspca_dev, 0x0030, 0x0002);
+
+		cit_write_reg(gspca_dev, 0x0020, 0x0111);
+		cit_write_reg(gspca_dev, 0x0000, 0x0112);
+		break;
+	case CIT_MODEL3:
+		cit_write_reg(gspca_dev, 0x0006, 0x012c);
+		cit_model3_Packet1(gspca_dev, 0x0046, 0x0000);
+		cit_read_reg(gspca_dev, 0x0116);
+		cit_write_reg(gspca_dev, 0x0064, 0x0116);
+		cit_read_reg(gspca_dev, 0x0115);
+		cit_write_reg(gspca_dev, 0x0003, 0x0115);
+		cit_write_reg(gspca_dev, 0x0008, 0x0123);
+		cit_write_reg(gspca_dev, 0x0000, 0x0117);
+		cit_write_reg(gspca_dev, 0x0000, 0x0112);
+		cit_write_reg(gspca_dev, 0x0080, 0x0100);
+		break;
+	case CIT_IBM_NETCAM_PRO:
+		cit_model3_Packet1(gspca_dev, 0x0049, 0x00ff);
+		cit_write_reg(gspca_dev, 0x0006, 0x012c);
+		cit_write_reg(gspca_dev, 0x0000, 0x0116);
+		/* HDG windows does this, but I cannot get the camera
+		   to restart with this without redoing the entire init
+		   sequence which makes switching modes really slow */
+		/* cit_write_reg(gspca_dev, 0x0006, 0x0115); */
+		cit_write_reg(gspca_dev, 0x0008, 0x0123);
+		cit_write_reg(gspca_dev, 0x0000, 0x0117);
+		cit_write_reg(gspca_dev, 0x0003, 0x0133);
+		cit_write_reg(gspca_dev, 0x0000, 0x0111);
+		/* HDG windows does this, but I get a green picture when
+		   restarting the stream after this */
+		/* cit_write_reg(gspca_dev, 0x0000, 0x0112); */
+		cit_write_reg(gspca_dev, 0x00c0, 0x0100);
+
+		/* Start isoc bandwidth "negotiation" at max isoc bandwith
+		   next stream start */
+		alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(1022);
+		break;
+	}
+}
+
+static u8 *cit_find_sof(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	u8 byte3 = 0, byte4 = 0;
+	int i;
+
+	switch (sd->model) {
+	case CIT_MODEL0:
+	case CIT_MODEL1:
+	case CIT_MODEL3:
+	case CIT_IBM_NETCAM_PRO:
+		switch (gspca_dev->width) {
+		case 160: /* 160x120 */
+			byte3 = 0x02;
+			byte4 = 0x0a;
+			break;
+		case 176: /* 176x144 */
+			byte3 = 0x02;
+			byte4 = 0x0e;
+			break;
+		case 320: /* 320x240 */
+			byte3 = 0x02;
+			byte4 = 0x08;
+			break;
+		case 352: /* 352x288 */
+			byte3 = 0x02;
+			byte4 = 0x00;
+			break;
+		case 640:
+			byte3 = 0x03;
+			byte4 = 0x08;
+			break;
+		}
+
+		/* These have a different byte3 */
+		if (sd->model <= CIT_MODEL1)
+			byte3 = 0x00;
+
+		for (i = 0; i < len; i++) {
+			/* For this model the SOF always starts at offset 0
+			   so no need to search the entire frame */
+			if (sd->model == CIT_MODEL0 && sd->sof_read != i)
+				break;
+
+			switch (sd->sof_read) {
+			case 0:
+				if (data[i] == 0x00)
+					sd->sof_read++;
+				break;
+			case 1:
+				if (data[i] == 0xff)
+					sd->sof_read++;
+				else if (data[i] == 0x00)
+					sd->sof_read = 1;
+				else
+					sd->sof_read = 0;
+				break;
+			case 2:
+				if (data[i] == byte3)
+					sd->sof_read++;
+				else if (data[i] == 0x00)
+					sd->sof_read = 1;
+				else
+					sd->sof_read = 0;
+				break;
+			case 3:
+				if (data[i] == byte4) {
+					sd->sof_read = 0;
+					return data + i + (sd->sof_len - 3);
+				}
+				if (byte3 == 0x00 && data[i] == 0xff)
+					sd->sof_read = 2;
+				else if (data[i] == 0x00)
+					sd->sof_read = 1;
+				else
+					sd->sof_read = 0;
+				break;
+			}
+		}
+		break;
+	case CIT_MODEL2:
+	case CIT_MODEL4:
+		/* TESTME we need to find a longer sof signature to avoid
+		   false positives */
+		for (i = 0; i < len; i++) {
+			switch (sd->sof_read) {
+			case 0:
+				if (data[i] == 0x00)
+					sd->sof_read++;
+				break;
+			case 1:
+				sd->sof_read = 0;
+				if (data[i] == 0xff) {
+					if (i >= 4)
+						PDEBUG(D_FRAM,
+						       "header found at offset: %d: %02x %02x 00 %02x %02x %02x\n",
+						       i - 1,
+						       data[i - 4],
+						       data[i - 3],
+						       data[i],
+						       data[i + 1],
+						       data[i + 2]);
+					else
+						PDEBUG(D_FRAM,
+						       "header found at offset: %d: 00 %02x %02x %02x\n",
+						       i - 1,
+						       data[i],
+						       data[i + 1],
+						       data[i + 2]);
+					return data + i + (sd->sof_len - 1);
+				}
+				break;
+			}
+		}
+		break;
+	}
+	return NULL;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+			u8 *data, int len)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	unsigned char *sof;
+
+	sof = cit_find_sof(gspca_dev, data, len);
+	if (sof) {
+		int n;
+
+		/* finish decoding current frame */
+		n = sof - data;
+		if (n > sd->sof_len)
+			n -= sd->sof_len;
+		else
+			n = 0;
+		gspca_frame_add(gspca_dev, LAST_PACKET,
+				data, n);
+		gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+		len -= sof - data;
+		data = sof;
+	}
+
+	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->brightness = val;
+	if (gspca_dev->streaming) {
+		if (sd->stop_on_control_change)
+			sd_stopN(gspca_dev);
+		cit_set_brightness(gspca_dev);
+		if (sd->stop_on_control_change)
+			cit_restart_stream(gspca_dev);
+	}
+
+	return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->brightness;
+
+	return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->contrast = val;
+	if (gspca_dev->streaming) {
+		if (sd->stop_on_control_change)
+			sd_stopN(gspca_dev);
+		cit_set_contrast(gspca_dev);
+		if (sd->stop_on_control_change)
+			cit_restart_stream(gspca_dev);
+	}
+
+	return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->contrast;
+
+	return 0;
+}
+
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->hue = val;
+	if (gspca_dev->streaming) {
+		if (sd->stop_on_control_change)
+			sd_stopN(gspca_dev);
+		cit_set_hue(gspca_dev);
+		if (sd->stop_on_control_change)
+			cit_restart_stream(gspca_dev);
+	}
+	return 0;
+}
+
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->hue;
+
+	return 0;
+}
+
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->sharpness = val;
+	if (gspca_dev->streaming) {
+		if (sd->stop_on_control_change)
+			sd_stopN(gspca_dev);
+		cit_set_sharpness(gspca_dev);
+		if (sd->stop_on_control_change)
+			cit_restart_stream(gspca_dev);
+	}
+	return 0;
+}
+
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->sharpness;
+
+	return 0;
+}
+
+static int sd_setlighting(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->lighting = val;
+	if (gspca_dev->streaming) {
+		if (sd->stop_on_control_change)
+			sd_stopN(gspca_dev);
+		cit_set_lighting(gspca_dev);
+		if (sd->stop_on_control_change)
+			cit_restart_stream(gspca_dev);
+	}
+	return 0;
+}
+
+static int sd_getlighting(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->lighting;
+
+	return 0;
+}
+
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	sd->hflip = val;
+	if (gspca_dev->streaming) {
+		if (sd->stop_on_control_change)
+			sd_stopN(gspca_dev);
+		cit_set_hflip(gspca_dev);
+		if (sd->stop_on_control_change)
+			cit_restart_stream(gspca_dev);
+	}
+	return 0;
+}
+
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+
+	*val = sd->hflip;
+
+	return 0;
+}
+
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+	.name = MODULE_NAME,
+	.ctrls = sd_ctrls,
+	.nctrls = ARRAY_SIZE(sd_ctrls),
+	.config = sd_config,
+	.init = sd_init,
+	.start = sd_start,
+	.stopN = sd_stopN,
+	.stop0 = sd_stop0,
+	.pkt_scan = sd_pkt_scan,
+};
+
+static const struct sd_desc sd_desc_isoc_nego = {
+	.name = MODULE_NAME,
+	.ctrls = sd_ctrls,
+	.nctrls = ARRAY_SIZE(sd_ctrls),
+	.config = sd_config,
+	.init = sd_init,
+	.start = sd_start,
+	.isoc_nego = sd_isoc_nego,
+	.stopN = sd_stopN,
+	.stop0 = sd_stop0,
+	.pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const __devinitdata struct usb_device_id device_table[] = {
+	{ USB_DEVICE_VER(0x0545, 0x8080, 0x0001, 0x0001), .driver_info = CIT_MODEL0 },
+	{ USB_DEVICE_VER(0x0545, 0x8080, 0x0002, 0x0002), .driver_info = CIT_MODEL1 },
+	{ USB_DEVICE_VER(0x0545, 0x8080, 0x030a, 0x030a), .driver_info = CIT_MODEL2 },
+	{ USB_DEVICE_VER(0x0545, 0x8080, 0x0301, 0x0301), .driver_info = CIT_MODEL3 },
+	{ USB_DEVICE_VER(0x0545, 0x8002, 0x030a, 0x030a), .driver_info = CIT_MODEL4 },
+	{ USB_DEVICE_VER(0x0545, 0x800c, 0x030a, 0x030a), .driver_info = CIT_MODEL2 },
+	{ USB_DEVICE_VER(0x0545, 0x800d, 0x030a, 0x030a), .driver_info = CIT_MODEL4 },
+	{}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	const struct sd_desc *desc = &sd_desc;
+
+	switch (id->driver_info) {
+	case CIT_MODEL0:
+	case CIT_MODEL1:
+		if (intf->cur_altsetting->desc.bInterfaceNumber != 2)
+			return -ENODEV;
+		break;
+	case CIT_MODEL2:
+	case CIT_MODEL4:
+		if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+			return -ENODEV;
+		break;
+	case CIT_MODEL3:
+		if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+			return -ENODEV;
+		/* FIXME this likely applies to all model3 cams and probably
+		   to other models too. */
+		if (ibm_netcam_pro)
+			desc = &sd_desc_isoc_nego;
+		break;
+	}
+
+	return gspca_dev_probe2(intf, id, desc, sizeof(struct sd), THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+	.name = MODULE_NAME,
+	.id_table = device_table,
+	.probe = sd_probe,
+	.disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+	.suspend = gspca_suspend,
+	.resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+	return usb_register(&sd_driver);
+}
+static void __exit sd_mod_exit(void)
+{
+	usb_deregister(&sd_driver);
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
index 0666038..c7e1970 100644
--- a/drivers/media/video/gspca/zc3xx.c
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -21,9 +21,7 @@
 
 #define MODULE_NAME "zc3xx"
 
-#ifdef CONFIG_INPUT
 #include <linux/input.h>
-#endif
 #include "gspca.h"
 #include "jpeg.h"
 
@@ -2953,7 +2951,7 @@
 	{}
 };
 
-static const struct usb_action mc501cb_InitialScale[] = {	 /* 320x240 */
+static const struct usb_action mc501cb_InitialScale[] = {	/* 320x240 */
 	{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
 	{0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
 	{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
@@ -3731,7 +3729,6 @@
 	{0xaa, 0x0d, 0x0000},
 	{0xaa, 0x0e, 0x0002},
 	{0xaa, 0x14, 0x0081},
-
 /* Other registers */
 	{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
 /* Frame retreiving */
@@ -3785,7 +3782,6 @@
 	{0xa0, 0x05, ZC3XX_R185_WINYWIDTH},
 	{0xa0, 0x14, ZC3XX_R186_WINYCENTER},
 	{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
-
 /* Auto exposure and white balance */
 	{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
 	{0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
@@ -3849,7 +3845,6 @@
 	{0xaa, 0x0d, 0x0000},
 	{0xaa, 0x0e, 0x0002},
 	{0xaa, 0x14, 0x0081},
-
 /* Other registers */
 	{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
 /* Frame retreiving */
@@ -5698,7 +5693,7 @@
 			index, gspca_dev->usb_buf, 1,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_r_i err %d", ret);
+		err("reg_r_i err %d", ret);
 		gspca_dev->usb_err = ret;
 		return 0;
 	}
@@ -5730,7 +5725,7 @@
 			value, index, NULL, 0,
 			500);
 	if (ret < 0) {
-		PDEBUG(D_ERR, "reg_w_i err %d", ret);
+		err("reg_w_i err %d", ret);
 		gspca_dev->usb_err = ret;
 	}
 }
@@ -6309,8 +6304,7 @@
 		if (chipset_revision_sensor[i].revision == retword) {
 			sd->chip_revision = retword;
 			send_unknown(gspca_dev, SENSOR_PB0330);
-			return chipset_revision_sensor[i]
-						.internal_sensor_id;
+			return chipset_revision_sensor[i].internal_sensor_id;
 		}
 	}
 
@@ -6503,8 +6497,7 @@
 				PDEBUG(D_PROBE, "Sensor Tas5130 (VF0250)");
 				break;
 			default:
-				PDEBUG(D_PROBE,
-					"Unknown sensor - set to TAS5130C");
+				warn("Unknown sensor - set to TAS5130C");
 				sd->sensor = SENSOR_TAS5130C;
 			}
 			break;
@@ -6610,7 +6603,7 @@
 			sd->sensor = SENSOR_OV7620;	/* same sensor (?) */
 			break;
 		default:
-			PDEBUG(D_ERR|D_PROBE, "Unknown sensor %04x", sensor);
+			err("Unknown sensor %04x", sensor);
 			return -EINVAL;
 		}
 	}
@@ -6790,7 +6783,7 @@
 		/* fall thru */
 	case SENSOR_PAS202B:
 	case SENSOR_PO2030:
-/*		reg_w(gspca_dev, 0x40, ZC3XX_R117_GGAIN);  * (from win traces) */
+/*		reg_w(gspca_dev, 0x40, ZC3XX_R117_GGAIN); in win traces */
 		reg_r(gspca_dev, 0x0180);
 		break;
 	case SENSOR_OV7620:
@@ -6798,7 +6791,7 @@
 		reg_w(gspca_dev, 0x15, 0x01ae);
 		i2c_read(gspca_dev, 0x13);	/*fixme: returns 0xa3 */
 		i2c_write(gspca_dev, 0x13, 0xa3, 0x00);
-					 /*fixme: returned value to send? */
+					/*fixme: returned value to send? */
 		reg_w(gspca_dev, 0x40, 0x0117);
 		reg_r(gspca_dev, 0x0180);
 		break;
@@ -6841,7 +6834,7 @@
 		/* remove the webcam's header:
 		 * ff d8 ff fe 00 0e 00 00 ss ss 00 01 ww ww hh hh pp pp
 		 *	- 'ss ss' is the frame sequence number (BE)
-		 * 	- 'ww ww' and 'hh hh' are the window dimensions (BE)
+		 *	- 'ww ww' and 'hh hh' are the window dimensions (BE)
 		 *	- 'pp pp' is the packet sequence number (BE)
 		 */
 		data += 18;
@@ -7007,7 +7000,7 @@
 	return 0;
 }
 
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
 			u8 *data,		/* interrupt packet data */
 			int len)		/* interrput packet length */
@@ -7035,7 +7028,7 @@
 	.querymenu = sd_querymenu,
 	.get_jcomp = sd_get_jcomp,
 	.set_jcomp = sd_set_jcomp,
-#ifdef CONFIG_INPUT
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
 	.int_pkt_scan = sd_int_pkt_scan,
 #endif
 };
@@ -7120,18 +7113,12 @@
 
 static int __init sd_mod_init(void)
 {
-	int ret;
-	ret = usb_register(&sd_driver);
-	if (ret < 0)
-		return ret;
-	PDEBUG(D_PROBE, "registered");
-	return 0;
+	return usb_register(&sd_driver);
 }
 
 static void __exit sd_mod_exit(void)
 {
 	usb_deregister(&sd_driver);
-	PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);
diff --git a/drivers/media/video/hdpvr/hdpvr-control.c b/drivers/media/video/hdpvr/hdpvr-control.c
index 5a6b78b..068df4b 100644
--- a/drivers/media/video/hdpvr/hdpvr-control.c
+++ b/drivers/media/video/hdpvr/hdpvr-control.c
@@ -29,8 +29,6 @@
 	int ret;
 	char request_type = 0x38, snd_request = 0x01;
 
-	msleep(10);
-
 	mutex_lock(&dev->usbc_mutex);
 	dev->usbc_buf[0] = valbuf;
 	ret = usb_control_msg(dev->udev,
@@ -170,8 +168,7 @@
 		if (ret == 2)
 			ret = 0;
 	} else
-		ret = hdpvr_config_call(dev, CTRL_AUDIO_INPUT_VALUE,
-					dev->options.audio_input+1);
+		ret = hdpvr_config_call(dev, CTRL_AUDIO_INPUT_VALUE, input);
 error:
 	return ret;
 }
diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c
index 0cae5b8..b70d6af 100644
--- a/drivers/media/video/hdpvr/hdpvr-core.c
+++ b/drivers/media/video/hdpvr/hdpvr-core.c
@@ -60,6 +60,7 @@
 	{ USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID1) },
 	{ USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID2) },
 	{ USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID3) },
+	{ USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID4) },
 	{ }					/* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, hdpvr_table);
@@ -152,19 +153,26 @@
 			 ret, print_buf);
 	}
 #endif
-	if (dev->usbc_buf[1] == HDPVR_FIRMWARE_VERSION) {
+
+	v4l2_info(&dev->v4l2_dev, "firmware version 0x%x dated %s\n",
+			  dev->usbc_buf[1], &dev->usbc_buf[2]);
+
+	switch (dev->usbc_buf[1]) {
+	case HDPVR_FIRMWARE_VERSION:
 		dev->flags &= ~HDPVR_FLAG_AC3_CAP;
-	} else if (dev->usbc_buf[1] == HDPVR_FIRMWARE_VERSION_AC3) {
+		break;
+	case HDPVR_FIRMWARE_VERSION_AC3:
+	case HDPVR_FIRMWARE_VERSION_0X12:
+	case HDPVR_FIRMWARE_VERSION_0X15:
 		dev->flags |= HDPVR_FLAG_AC3_CAP;
-	} else if (dev->usbc_buf[1] > HDPVR_FIRMWARE_VERSION_AC3) {
-		v4l2_info(&dev->v4l2_dev, "untested firmware version 0x%x, "
-			  "the driver might not work\n", dev->usbc_buf[1]);
-		dev->flags |= HDPVR_FLAG_AC3_CAP;
-	} else {
-		v4l2_err(&dev->v4l2_dev, "unknown firmware version 0x%x\n",
-			dev->usbc_buf[1]);
-		ret = -EINVAL;
-		goto unlock;
+		break;
+	default:
+		v4l2_info(&dev->v4l2_dev, "untested firmware, the driver might"
+			  " not work.\n");
+		if (dev->usbc_buf[1] >= HDPVR_FIRMWARE_VERSION_AC3)
+			dev->flags |= HDPVR_FLAG_AC3_CAP;
+		else
+			dev->flags &= ~HDPVR_FLAG_AC3_CAP;
 	}
 
 	response = dev->usbc_buf+38;
@@ -319,8 +327,12 @@
 	if (default_video_input < HDPVR_VIDEO_INPUTS)
 		dev->options.video_input = default_video_input;
 
-	if (default_audio_input < HDPVR_AUDIO_INPUTS)
+	if (default_audio_input < HDPVR_AUDIO_INPUTS) {
 		dev->options.audio_input = default_audio_input;
+		if (default_audio_input == HDPVR_SPDIF)
+			dev->options.audio_codec =
+				V4L2_MPEG_AUDIO_ENCODING_AC3;
+	}
 
 	dev->udev = usb_get_dev(interface_to_usbdev(interface));
 
diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c
index 463b81b..409de11 100644
--- a/drivers/media/video/hdpvr/hdpvr-i2c.c
+++ b/drivers/media/video/hdpvr/hdpvr-i2c.c
@@ -127,7 +127,6 @@
 	strlcpy(i2c_adap->name, "Hauppauge HD PVR I2C",
 		sizeof(i2c_adap->name));
 	i2c_adap->algo  = &hdpvr_algo;
-	i2c_adap->class = I2C_CLASS_TV_ANALOG;
 	i2c_adap->owner = THIS_MODULE;
 	i2c_adap->dev.parent = &dev->udev->dev;
 
diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
index 4863a21..d38fe10 100644
--- a/drivers/media/video/hdpvr/hdpvr-video.c
+++ b/drivers/media/video/hdpvr/hdpvr-video.c
@@ -26,7 +26,7 @@
 #include <media/v4l2-ioctl.h>
 #include "hdpvr.h"
 
-#define BULK_URB_TIMEOUT 1250 /* 1.25 seconds */
+#define BULK_URB_TIMEOUT   90 /* 0.09 seconds */
 
 #define print_buffer_status() { \
 		v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,	\
@@ -157,6 +157,7 @@
 				  mem, dev->bulk_in_size,
 				  hdpvr_read_bulk_callback, buf);
 
+		buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 		buf->status = BUFSTAT_AVAILABLE;
 		list_add_tail(&buf->buff_list, &dev->free_buff_list);
 	}
@@ -337,8 +338,6 @@
 					     dev->bulk_in_endpointAddr),
 			     buf, dev->bulk_in_size, &actual_length,
 			     BULK_URB_TIMEOUT)) {
-		/* wait */
-		msleep(5);
 		v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
 			 "%2d: got %d bytes\n", c, actual_length);
 	}
diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h
index b0f046d..5efc963 100644
--- a/drivers/media/video/hdpvr/hdpvr.h
+++ b/drivers/media/video/hdpvr/hdpvr.h
@@ -30,14 +30,17 @@
 #define HD_PVR_PRODUCT_ID	0x4900
 #define HD_PVR_PRODUCT_ID1	0x4901
 #define HD_PVR_PRODUCT_ID2	0x4902
+#define HD_PVR_PRODUCT_ID4	0x4903
 #define HD_PVR_PRODUCT_ID3	0x4982
 
 #define UNSET    (-1U)
 
 #define NUM_BUFFERS 64
 
-#define HDPVR_FIRMWARE_VERSION		0x8
-#define HDPVR_FIRMWARE_VERSION_AC3	0xd
+#define HDPVR_FIRMWARE_VERSION		0x08
+#define HDPVR_FIRMWARE_VERSION_AC3	0x0d
+#define HDPVR_FIRMWARE_VERSION_0X12	0x12
+#define HDPVR_FIRMWARE_VERSION_0X15	0x15
 
 /* #define HDPVR_DEBUG */
 
diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c
index ad2c232..7ae9636 100644
--- a/drivers/media/video/hexium_gemini.c
+++ b/drivers/media/video/hexium_gemini.c
@@ -367,7 +367,6 @@
 	saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
 
 	hexium->i2c_adapter = (struct i2c_adapter) {
-		.class = I2C_CLASS_TV_ANALOG,
 		.name = "hexium gemini",
 	};
 	saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c
index 938a1f8..b72d0f0 100644
--- a/drivers/media/video/hexium_orion.c
+++ b/drivers/media/video/hexium_orion.c
@@ -230,7 +230,6 @@
 	saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
 
 	hexium->i2c_adapter = (struct i2c_adapter) {
-		.class = I2C_CLASS_TV_ANALOG,
 		.name = "hexium orion",
 	};
 	saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
diff --git a/drivers/media/video/imx074.c b/drivers/media/video/imx074.c
new file mode 100644
index 0000000..380e459
--- /dev/null
+++ b/drivers/media/video/imx074.c
@@ -0,0 +1,508 @@
+/*
+ * Driver for IMX074 CMOS Image Sensor from Sony
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * Partially inspired by the IMX074 driver from the Android / MSM tree
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-chip-ident.h>
+
+/* IMX074 registers */
+
+#define MODE_SELECT			0x0100
+#define IMAGE_ORIENTATION		0x0101
+#define GROUPED_PARAMETER_HOLD		0x0104
+
+/* Integration Time */
+#define COARSE_INTEGRATION_TIME_HI	0x0202
+#define COARSE_INTEGRATION_TIME_LO	0x0203
+/* Gain */
+#define ANALOGUE_GAIN_CODE_GLOBAL_HI	0x0204
+#define ANALOGUE_GAIN_CODE_GLOBAL_LO	0x0205
+
+/* PLL registers */
+#define PRE_PLL_CLK_DIV			0x0305
+#define PLL_MULTIPLIER			0x0307
+#define PLSTATIM			0x302b
+#define VNDMY_ABLMGSHLMT		0x300a
+#define Y_OPBADDR_START_DI		0x3014
+/* mode setting */
+#define FRAME_LENGTH_LINES_HI		0x0340
+#define FRAME_LENGTH_LINES_LO		0x0341
+#define LINE_LENGTH_PCK_HI		0x0342
+#define LINE_LENGTH_PCK_LO		0x0343
+#define YADDR_START			0x0347
+#define YADDR_END			0x034b
+#define X_OUTPUT_SIZE_MSB		0x034c
+#define X_OUTPUT_SIZE_LSB		0x034d
+#define Y_OUTPUT_SIZE_MSB		0x034e
+#define Y_OUTPUT_SIZE_LSB		0x034f
+#define X_EVEN_INC			0x0381
+#define X_ODD_INC			0x0383
+#define Y_EVEN_INC			0x0385
+#define Y_ODD_INC			0x0387
+
+#define HMODEADD			0x3001
+#define VMODEADD			0x3016
+#define VAPPLINE_START			0x3069
+#define VAPPLINE_END			0x306b
+#define SHUTTER				0x3086
+#define HADDAVE				0x30e8
+#define LANESEL				0x3301
+
+/* IMX074 supported geometry */
+#define IMX074_WIDTH			1052
+#define IMX074_HEIGHT			780
+
+/* IMX074 has only one fixed colorspace per pixelcode */
+struct imx074_datafmt {
+	enum v4l2_mbus_pixelcode	code;
+	enum v4l2_colorspace		colorspace;
+};
+
+struct imx074 {
+	struct v4l2_subdev		subdev;
+	const struct imx074_datafmt	*fmt;
+};
+
+static const struct imx074_datafmt imx074_colour_fmts[] = {
+	{V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
+};
+
+static struct imx074 *to_imx074(const struct i2c_client *client)
+{
+	return container_of(i2c_get_clientdata(client), struct imx074, subdev);
+}
+
+/* Find a data format by a pixel code in an array */
+static const struct imx074_datafmt *imx074_find_datafmt(enum v4l2_mbus_pixelcode code)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++)
+		if (imx074_colour_fmts[i].code == code)
+			return imx074_colour_fmts + i;
+
+	return NULL;
+}
+
+static int reg_write(struct i2c_client *client, const u16 addr, const u8 data)
+{
+	struct i2c_adapter *adap = client->adapter;
+	struct i2c_msg msg;
+	unsigned char tx[3];
+	int ret;
+
+	msg.addr = client->addr;
+	msg.buf = tx;
+	msg.len = 3;
+	msg.flags = 0;
+
+	tx[0] = addr >> 8;
+	tx[1] = addr & 0xff;
+	tx[2] = data;
+
+	ret = i2c_transfer(adap, &msg, 1);
+
+	mdelay(2);
+
+	return ret == 1 ? 0 : -EIO;
+}
+
+static int reg_read(struct i2c_client *client, const u16 addr)
+{
+	u8 buf[2] = {addr >> 8, addr & 0xff};
+	int ret;
+	struct i2c_msg msgs[] = {
+		{
+			.addr  = client->addr,
+			.flags = 0,
+			.len   = 2,
+			.buf   = buf,
+		}, {
+			.addr  = client->addr,
+			.flags = I2C_M_RD,
+			.len   = 2,
+			.buf   = buf,
+		},
+	};
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret < 0) {
+		dev_warn(&client->dev, "Reading register %x from %x failed\n",
+			 addr, client->addr);
+		return ret;
+	}
+
+	return buf[0] & 0xff; /* no sign-extension */
+}
+
+static int imx074_try_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_mbus_framefmt *mf)
+{
+	const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code);
+
+	dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
+
+	if (!fmt) {
+		mf->code	= imx074_colour_fmts[0].code;
+		mf->colorspace	= imx074_colour_fmts[0].colorspace;
+	}
+
+	mf->width	= IMX074_WIDTH;
+	mf->height	= IMX074_HEIGHT;
+	mf->field	= V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int imx074_s_fmt(struct v4l2_subdev *sd,
+			struct v4l2_mbus_framefmt *mf)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct imx074 *priv = to_imx074(client);
+
+	dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
+
+	/* MIPI CSI could have changed the format, double-check */
+	if (!imx074_find_datafmt(mf->code))
+		return -EINVAL;
+
+	imx074_try_fmt(sd, mf);
+
+	priv->fmt = imx074_find_datafmt(mf->code);
+
+	return 0;
+}
+
+static int imx074_g_fmt(struct v4l2_subdev *sd,
+			struct v4l2_mbus_framefmt *mf)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct imx074 *priv = to_imx074(client);
+
+	const struct imx074_datafmt *fmt = priv->fmt;
+
+	mf->code	= fmt->code;
+	mf->colorspace	= fmt->colorspace;
+	mf->width	= IMX074_WIDTH;
+	mf->height	= IMX074_HEIGHT;
+	mf->field	= V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int imx074_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+	struct v4l2_rect *rect = &a->c;
+
+	a->type		= V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	rect->top	= 0;
+	rect->left	= 0;
+	rect->width	= IMX074_WIDTH;
+	rect->height	= IMX074_HEIGHT;
+
+	return 0;
+}
+
+static int imx074_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+	a->bounds.left			= 0;
+	a->bounds.top			= 0;
+	a->bounds.width			= IMX074_WIDTH;
+	a->bounds.height		= IMX074_HEIGHT;
+	a->defrect			= a->bounds;
+	a->type				= V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	a->pixelaspect.numerator	= 1;
+	a->pixelaspect.denominator	= 1;
+
+	return 0;
+}
+
+static int imx074_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+			   enum v4l2_mbus_pixelcode *code)
+{
+	if ((unsigned int)index >= ARRAY_SIZE(imx074_colour_fmts))
+		return -EINVAL;
+
+	*code = imx074_colour_fmts[index].code;
+	return 0;
+}
+
+static int imx074_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	/* MODE_SELECT: stream or standby */
+	return reg_write(client, MODE_SELECT, !!enable);
+}
+
+static int imx074_g_chip_ident(struct v4l2_subdev *sd,
+			       struct v4l2_dbg_chip_ident *id)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+		return -EINVAL;
+
+	if (id->match.addr != client->addr)
+		return -ENODEV;
+
+	id->ident	= V4L2_IDENT_IMX074;
+	id->revision	= 0;
+
+	return 0;
+}
+
+static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
+	.s_stream	= imx074_s_stream,
+	.s_mbus_fmt	= imx074_s_fmt,
+	.g_mbus_fmt	= imx074_g_fmt,
+	.try_mbus_fmt	= imx074_try_fmt,
+	.enum_mbus_fmt	= imx074_enum_fmt,
+	.g_crop		= imx074_g_crop,
+	.cropcap	= imx074_cropcap,
+};
+
+static struct v4l2_subdev_core_ops imx074_subdev_core_ops = {
+	.g_chip_ident	= imx074_g_chip_ident,
+};
+
+static struct v4l2_subdev_ops imx074_subdev_ops = {
+	.core	= &imx074_subdev_core_ops,
+	.video	= &imx074_subdev_video_ops,
+};
+
+/*
+ * We have to provide soc-camera operations, but we don't have anything to say
+ * there. The MIPI CSI2 driver will provide .query_bus_param and .set_bus_param
+ */
+static unsigned long imx074_query_bus_param(struct soc_camera_device *icd)
+{
+	return 0;
+}
+
+static int imx074_set_bus_param(struct soc_camera_device *icd,
+				 unsigned long flags)
+{
+	return -1;
+}
+
+static struct soc_camera_ops imx074_ops = {
+	.query_bus_param	= imx074_query_bus_param,
+	.set_bus_param		= imx074_set_bus_param,
+};
+
+static int imx074_video_probe(struct soc_camera_device *icd,
+			      struct i2c_client *client)
+{
+	int ret;
+	u16 id;
+
+	/* Read sensor Model ID */
+	ret = reg_read(client, 0);
+	if (ret < 0)
+		return ret;
+
+	id = ret << 8;
+
+	ret = reg_read(client, 1);
+	if (ret < 0)
+		return ret;
+
+	id |= ret;
+
+	dev_info(&client->dev, "Chip ID 0x%04x detected\n", id);
+
+	if (id != 0x74)
+		return -ENODEV;
+
+	/* PLL Setting EXTCLK=24MHz, 22.5times */
+	reg_write(client, PLL_MULTIPLIER, 0x2D);
+	reg_write(client, PRE_PLL_CLK_DIV, 0x02);
+	reg_write(client, PLSTATIM, 0x4B);
+
+	/* 2-lane mode */
+	reg_write(client, 0x3024, 0x00);
+
+	reg_write(client, IMAGE_ORIENTATION, 0x00);
+
+	/* select RAW mode:
+	 * 0x08+0x08 = top 8 bits
+	 * 0x0a+0x08 = compressed 8-bits
+	 * 0x0a+0x0a = 10 bits
+	 */
+	reg_write(client, 0x0112, 0x08);
+	reg_write(client, 0x0113, 0x08);
+
+	/* Base setting for High frame mode */
+	reg_write(client, VNDMY_ABLMGSHLMT, 0x80);
+	reg_write(client, Y_OPBADDR_START_DI, 0x08);
+	reg_write(client, 0x3015, 0x37);
+	reg_write(client, 0x301C, 0x01);
+	reg_write(client, 0x302C, 0x05);
+	reg_write(client, 0x3031, 0x26);
+	reg_write(client, 0x3041, 0x60);
+	reg_write(client, 0x3051, 0x24);
+	reg_write(client, 0x3053, 0x34);
+	reg_write(client, 0x3057, 0xC0);
+	reg_write(client, 0x305C, 0x09);
+	reg_write(client, 0x305D, 0x07);
+	reg_write(client, 0x3060, 0x30);
+	reg_write(client, 0x3065, 0x00);
+	reg_write(client, 0x30AA, 0x08);
+	reg_write(client, 0x30AB, 0x1C);
+	reg_write(client, 0x30B0, 0x32);
+	reg_write(client, 0x30B2, 0x83);
+	reg_write(client, 0x30D3, 0x04);
+	reg_write(client, 0x3106, 0x78);
+	reg_write(client, 0x310C, 0x82);
+	reg_write(client, 0x3304, 0x05);
+	reg_write(client, 0x3305, 0x04);
+	reg_write(client, 0x3306, 0x11);
+	reg_write(client, 0x3307, 0x02);
+	reg_write(client, 0x3308, 0x0C);
+	reg_write(client, 0x3309, 0x06);
+	reg_write(client, 0x330A, 0x08);
+	reg_write(client, 0x330B, 0x04);
+	reg_write(client, 0x330C, 0x08);
+	reg_write(client, 0x330D, 0x06);
+	reg_write(client, 0x330E, 0x01);
+	reg_write(client, 0x3381, 0x00);
+
+	/* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */
+	/* 1608 = 1560 + 48 (black lines) */
+	reg_write(client, FRAME_LENGTH_LINES_HI, 0x06);
+	reg_write(client, FRAME_LENGTH_LINES_LO, 0x48);
+	reg_write(client, YADDR_START, 0x00);
+	reg_write(client, YADDR_END, 0x2F);
+	/* 0x838 == 2104 */
+	reg_write(client, X_OUTPUT_SIZE_MSB, 0x08);
+	reg_write(client, X_OUTPUT_SIZE_LSB, 0x38);
+	/* 0x618 == 1560 */
+	reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06);
+	reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18);
+	reg_write(client, X_EVEN_INC, 0x01);
+	reg_write(client, X_ODD_INC, 0x03);
+	reg_write(client, Y_EVEN_INC, 0x01);
+	reg_write(client, Y_ODD_INC, 0x03);
+	reg_write(client, HMODEADD, 0x00);
+	reg_write(client, VMODEADD, 0x16);
+	reg_write(client, VAPPLINE_START, 0x24);
+	reg_write(client, VAPPLINE_END, 0x53);
+	reg_write(client, SHUTTER, 0x00);
+	reg_write(client, HADDAVE, 0x80);
+
+	reg_write(client, LANESEL, 0x00);
+
+	reg_write(client, GROUPED_PARAMETER_HOLD, 0x00);	/* off */
+
+	return 0;
+}
+
+static int imx074_probe(struct i2c_client *client,
+			const struct i2c_device_id *did)
+{
+	struct imx074 *priv;
+	struct soc_camera_device *icd = client->dev.platform_data;
+	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+	struct soc_camera_link *icl;
+	int ret;
+
+	if (!icd) {
+		dev_err(&client->dev, "IMX074: missing soc-camera data!\n");
+		return -EINVAL;
+	}
+
+	icl = to_soc_camera_link(icd);
+	if (!icl) {
+		dev_err(&client->dev, "IMX074: missing platform data!\n");
+		return -EINVAL;
+	}
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_warn(&adapter->dev,
+			 "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
+		return -EIO;
+	}
+
+	priv = kzalloc(sizeof(struct imx074), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops);
+
+	icd->ops	= &imx074_ops;
+	priv->fmt	= &imx074_colour_fmts[0];
+
+	ret = imx074_video_probe(icd, client);
+	if (ret < 0) {
+		icd->ops = NULL;
+		i2c_set_clientdata(client, NULL);
+		kfree(priv);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int imx074_remove(struct i2c_client *client)
+{
+	struct imx074 *priv = to_imx074(client);
+	struct soc_camera_device *icd = client->dev.platform_data;
+	struct soc_camera_link *icl = to_soc_camera_link(icd);
+
+	icd->ops = NULL;
+	if (icl->free_bus)
+		icl->free_bus(icl);
+	i2c_set_clientdata(client, NULL);
+	client->driver = NULL;
+	kfree(priv);
+
+	return 0;
+}
+
+static const struct i2c_device_id imx074_id[] = {
+	{ "imx074", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, imx074_id);
+
+static struct i2c_driver imx074_i2c_driver = {
+	.driver = {
+		.name = "imx074",
+	},
+	.probe		= imx074_probe,
+	.remove		= imx074_remove,
+	.id_table	= imx074_id,
+};
+
+static int __init imx074_mod_init(void)
+{
+	return i2c_add_driver(&imx074_i2c_driver);
+}
+
+static void __exit imx074_mod_exit(void)
+{
+	i2c_del_driver(&imx074_i2c_driver);
+}
+
+module_init(imx074_mod_init);
+module_exit(imx074_mod_exit);
+
+MODULE_DESCRIPTION("Sony IMX074 Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/indycam.c b/drivers/media/video/indycam.c
index 3d69401..e5ed4db 100644
--- a/drivers/media/video/indycam.c
+++ b/drivers/media/video/indycam.c
@@ -24,7 +24,6 @@
 #include <linux/i2c.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 #include "indycam.h"
 
@@ -378,9 +377,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, indycam_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "indycam",
-	.probe = indycam_probe,
-	.remove = indycam_remove,
-	.id_table = indycam_id,
+static struct i2c_driver indycam_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "indycam",
+	},
+	.probe		= indycam_probe,
+	.remove		= indycam_remove,
+	.id_table	= indycam_id,
 };
+
+static __init int init_indycam(void)
+{
+	return i2c_add_driver(&indycam_driver);
+}
+
+static __exit void exit_indycam(void)
+{
+	i2c_del_driver(&indycam_driver);
+}
+
+module_init(init_indycam);
+module_exit(exit_indycam);
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
index 27ae8bb..5a000c6 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -146,26 +146,6 @@
 	return 1;
 }
 
-static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
-{
-	unsigned char b;
-
-	/* poll IR chip */
-	if (1 != i2c_master_recv(ir->c, &b, 1)) {
-		dprintk(1,"read error\n");
-		return -EIO;
-	}
-
-	/* ignore 0xaa */
-	if (b==0xaa)
-		return 0;
-	dprintk(2,"key %02x\n", b);
-
-	*ir_key = b;
-	*ir_raw = b;
-	return 1;
-}
-
 static int get_key_fusionhdtv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
 {
 	unsigned char buf[4];
@@ -279,15 +259,9 @@
 static void ir_work(struct work_struct *work)
 {
 	struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work);
-	int polling_interval = 100;
-
-	/* MSI TV@nywhere Plus requires more frequent polling
-	   otherwise it will miss some keypresses */
-	if (ir->c->adapter->id == I2C_HW_SAA7134 && ir->c->addr == 0x30)
-		polling_interval = 50;
 
 	ir_key_poll(ir);
-	schedule_delayed_work(&ir->work, msecs_to_jiffies(polling_interval));
+	schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling_interval));
 }
 
 /* ----------------------------------------------------------------------- */
@@ -312,6 +286,7 @@
 
 	ir->c = client;
 	ir->input = input_dev;
+	ir->polling_interval = DEFAULT_POLLING_INTERVAL;
 	i2c_set_clientdata(client, ir);
 
 	switch(addr) {
@@ -321,12 +296,6 @@
 		ir_type     = IR_TYPE_OTHER;
 		ir_codes    = RC_MAP_EMPTY;
 		break;
-	case 0x4b:
-		name        = "PV951";
-		ir->get_key = get_key_pv951;
-		ir_type     = IR_TYPE_OTHER;
-		ir_codes    = RC_MAP_PV951;
-		break;
 	case 0x18:
 	case 0x1f:
 	case 0x1a:
@@ -351,27 +320,6 @@
 		ir_type     = IR_TYPE_RC5;
 		ir_codes    = RC_MAP_FUSIONHDTV_MCE;
 		break;
-	case 0x0b:
-	case 0x47:
-	case 0x71:
-		if (adap->id == I2C_HW_B_CX2388x ||
-		    adap->id == I2C_HW_B_CX2341X) {
-			/* Handled by cx88-input */
-			name = adap->id == I2C_HW_B_CX2341X ? "CX2341x remote"
-							    : "CX2388x remote";
-			ir_type     = IR_TYPE_RC5;
-			ir->get_key = get_key_haup_xvr;
-			if (hauppauge == 1) {
-				ir_codes    = RC_MAP_HAUPPAUGE_NEW;
-			} else {
-				ir_codes    = RC_MAP_RC5_TV;
-			}
-		} else {
-			/* Handled by saa7134-input */
-			name        = "SAA713x remote";
-			ir_type     = IR_TYPE_OTHER;
-		}
-		break;
 	case 0x40:
 		name        = "AVerMedia Cardbus remote";
 		ir->get_key = get_key_avermedia_cardbus;
@@ -390,6 +338,9 @@
 		if (init_data->type)
 			ir_type = init_data->type;
 
+		if (init_data->polling_interval)
+			ir->polling_interval = init_data->polling_interval;
+
 		switch (init_data->internal_get_key_func) {
 		case IR_KBD_GET_KEY_CUSTOM:
 			/* The bridge driver provided us its own function */
@@ -398,9 +349,6 @@
 		case IR_KBD_GET_KEY_PIXELVIEW:
 			ir->get_key = get_key_pixelview;
 			break;
-		case IR_KBD_GET_KEY_PV951:
-			ir->get_key = get_key_pv951;
-			break;
 		case IR_KBD_GET_KEY_HAUP:
 			ir->get_key = get_key_haup;
 			break;
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index 7580314..04bacdb 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -811,15 +811,23 @@
 /* Call the specified callback for all subdevs matching hw (if 0, then
    match them all). Ignore any errors. */
 #define ivtv_call_hw(itv, hw, o, f, args...) 				\
-	__v4l2_device_call_subdevs(&(itv)->v4l2_dev, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
+	do {								\
+		struct v4l2_subdev *__sd;				\
+		__v4l2_device_call_subdevs_p(&(itv)->v4l2_dev, __sd,	\
+			!(hw) || (__sd->grp_id & (hw)), o, f , ##args);	\
+	} while (0)
 
 #define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args)
 
 /* Call the specified callback for all subdevs matching hw (if 0, then
    match them all). If the callback returns an error other than 0 or
    -ENOIOCTLCMD, then return with that error code. */
-#define ivtv_call_hw_err(itv, hw, o, f, args...)  		\
-	__v4l2_device_call_subdevs_until_err(&(itv)->v4l2_dev, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
+#define ivtv_call_hw_err(itv, hw, o, f, args...)			\
+({									\
+	struct v4l2_subdev *__sd;					\
+	__v4l2_device_call_subdevs_until_err_p(&(itv)->v4l2_dev, __sd,	\
+		!(hw) || (__sd->grp_id & (hw)), o, f , ##args);		\
+})
 
 #define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args)
 
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
index a74fa09..9e8039a 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.c
+++ b/drivers/media/video/ivtv/ivtv-i2c.c
@@ -121,31 +121,6 @@
 };
 
 /* This array should match the IVTV_HW_ defines */
-static const char *hw_modules[] = {
-	"cx25840",
-	"saa7115",
-	"saa7127",
-	"msp3400",
-	"tuner",
-	"wm8775",
-	"cs53l32a",
-	NULL,
-	"saa7115",
-	"upd64031a",
-	"upd64083",
-	"saa717x",
-	"wm8739",
-	"vp27smpx",
-	"m52790",
-	NULL,
-	NULL,		/* IVTV_HW_I2C_IR_RX_AVER */
-	NULL,		/* IVTV_HW_I2C_IR_RX_HAUP_EXT */
-	NULL,		/* IVTV_HW_I2C_IR_RX_HAUP_INT */
-	NULL,		/* IVTV_HW_Z8F0811_IR_TX_HAUP */
-	NULL,		/* IVTV_HW_Z8F0811_IR_RX_HAUP */
-};
-
-/* This array should match the IVTV_HW_ defines */
 static const char * const hw_devicenames[] = {
 	"cx25840",
 	"saa7115",
@@ -257,7 +232,6 @@
 {
 	struct v4l2_subdev *sd;
 	struct i2c_adapter *adap = &itv->i2c_adap;
-	const char *mod = hw_modules[idx];
 	const char *type = hw_devicenames[idx];
 	u32 hw = 1 << idx;
 
@@ -266,17 +240,17 @@
 	if (hw == IVTV_HW_TUNER) {
 		/* special tuner handling */
 		sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
-				adap, mod, type,
+				adap, NULL, type,
 				0, itv->card_i2c->radio);
 		if (sd)
 			sd->grp_id = 1 << idx;
 		sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
-				adap, mod, type,
+				adap, NULL, type,
 				0, itv->card_i2c->demod);
 		if (sd)
 			sd->grp_id = 1 << idx;
 		sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
-				adap, mod, type,
+				adap, NULL, type,
 				0, itv->card_i2c->tv);
 		if (sd)
 			sd->grp_id = 1 << idx;
@@ -293,16 +267,17 @@
 	/* It's an I2C device other than an analog tuner or IR chip */
 	if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) {
 		sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
-				adap, mod, type, 0, I2C_ADDRS(hw_addrs[idx]));
+				adap, NULL, type, 0, I2C_ADDRS(hw_addrs[idx]));
 	} else if (hw == IVTV_HW_CX25840) {
 		struct cx25840_platform_data pdata;
 
 		pdata.pvr150_workaround = itv->pvr150_workaround;
 		sd = v4l2_i2c_new_subdev_cfg(&itv->v4l2_dev,
-				adap, mod, type, 0, &pdata, hw_addrs[idx], NULL);
+				adap, NULL, type, 0, &pdata, hw_addrs[idx],
+				NULL);
 	} else {
 		sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
-				adap, mod, type, hw_addrs[idx], NULL);
+				adap, NULL, type, hw_addrs[idx], NULL);
 	}
 	if (sd)
 		sd->grp_id = 1 << idx;
@@ -706,8 +681,7 @@
 	/* Sanity checks for the I2C hardware arrays. They must be the
 	 * same size.
 	 */
-	if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||
-	    ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules)) {
+	if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs)) {
 		IVTV_ERR("Mismatched I2C hardware arrays\n");
 		return -ENODEV;
 	}
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index 4eed912..b686da5 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -37,7 +37,6 @@
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-event.h>
 #include <linux/dvb/audio.h>
-#include <linux/i2c-id.h>
 
 u16 ivtv_service2vbi(int type)
 {
diff --git a/drivers/media/video/ks0127.c b/drivers/media/video/ks0127.c
index 9473482..afa9118 100644
--- a/drivers/media/video/ks0127.c
+++ b/drivers/media/video/ks0127.c
@@ -43,7 +43,6 @@
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 #include "ks0127.h"
 
 MODULE_DESCRIPTION("KS0127 video decoder driver");
@@ -712,9 +711,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, ks0127_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "ks0127",
-	.probe = ks0127_probe,
-	.remove = ks0127_remove,
-	.id_table = ks0127_id,
+static struct i2c_driver ks0127_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "ks0127",
+	},
+	.probe		= ks0127_probe,
+	.remove		= ks0127_remove,
+	.id_table	= ks0127_id,
 };
+
+static __init int init_ks0127(void)
+{
+	return i2c_add_driver(&ks0127_driver);
+}
+
+static __exit void exit_ks0127(void)
+{
+	i2c_del_driver(&ks0127_driver);
+}
+
+module_init(init_ks0127);
+module_exit(exit_ks0127);
diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c
index 4491d01..5e1c9a8 100644
--- a/drivers/media/video/m52790.c
+++ b/drivers/media/video/m52790.c
@@ -26,12 +26,10 @@
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/videodev2.h>
 #include <media/m52790.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("i2c device driver for m52790 A/V switch");
 MODULE_AUTHOR("Hans Verkuil");
@@ -205,9 +203,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, m52790_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "m52790",
-	.probe = m52790_probe,
-	.remove = m52790_remove,
-	.id_table = m52790_id,
+static struct i2c_driver m52790_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "m52790",
+	},
+	.probe		= m52790_probe,
+	.remove		= m52790_remove,
+	.id_table	= m52790_id,
 };
+
+static __init int init_m52790(void)
+{
+	return i2c_add_driver(&m52790_driver);
+}
+
+static __exit void exit_m52790(void)
+{
+	i2c_del_driver(&m52790_driver);
+}
+
+module_init(init_m52790);
+module_exit(exit_m52790);
diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c
index a7210d9..3b19f5b 100644
--- a/drivers/media/video/mem2mem_testdev.c
+++ b/drivers/media/video/mem2mem_testdev.c
@@ -848,7 +848,7 @@
 
 	videobuf_queue_vmalloc_init(vq, &m2mtest_qops, ctx->dev->v4l2_dev.dev,
 				    &ctx->dev->irqlock, type, V4L2_FIELD_NONE,
-				    sizeof(struct m2mtest_buffer), priv);
+				    sizeof(struct m2mtest_buffer), priv, NULL);
 }
 
 
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
index 0e41213..b1763ac 100644
--- a/drivers/media/video/msp3400-driver.c
+++ b/drivers/media/video/msp3400-driver.c
@@ -56,7 +56,6 @@
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
-#include <media/v4l2-i2c-drv.h>
 #include <media/msp3400.h>
 #include <media/tvaudio.h>
 #include "msp3400-driver.h"
@@ -382,7 +381,12 @@
 
 void msp_update_volume(struct msp_state *state)
 {
-	v4l2_ctrl_s_ctrl(state->volume, v4l2_ctrl_g_ctrl(state->volume));
+	/* Force an update of the volume/mute cluster */
+	v4l2_ctrl_lock(state->volume);
+	state->volume->val = state->volume->cur.val;
+	state->muted->val = state->muted->cur.val;
+	msp_s_ctrl(state->volume);
+	v4l2_ctrl_unlock(state->volume);
 }
 
 /* --- v4l2 ioctls --- */
@@ -843,15 +847,31 @@
 };
 MODULE_DEVICE_TABLE(i2c, msp_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "msp3400",
-	.probe = msp_probe,
-	.remove = msp_remove,
-	.suspend = msp_suspend,
-	.resume = msp_resume,
-	.id_table = msp_id,
+static struct i2c_driver msp_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "msp3400",
+	},
+	.probe		= msp_probe,
+	.remove		= msp_remove,
+	.suspend	= msp_suspend,
+	.resume		= msp_resume,
+	.id_table	= msp_id,
 };
 
+static __init int init_msp(void)
+{
+	return i2c_add_driver(&msp_driver);
+}
+
+static __exit void exit_msp(void)
+{
+	i2c_del_driver(&msp_driver);
+}
+
+module_init(init_msp);
+module_exit(exit_msp);
+
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * ---------------------------------------------------------------------------
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
index 79f096d..fcb4cd9 100644
--- a/drivers/media/video/mt9m001.c
+++ b/drivers/media/video/mt9m001.c
@@ -157,7 +157,7 @@
 
 static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	/* Switch to master "normal" mode or stop sensor readout */
 	if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0)
@@ -206,7 +206,7 @@
 
 static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
 	struct v4l2_rect rect = a->c;
 	struct soc_camera_device *icd = client->dev.platform_data;
@@ -271,7 +271,7 @@
 
 static int mt9m001_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
 
 	a->c	= mt9m001->rect;
@@ -297,7 +297,7 @@
 static int mt9m001_g_fmt(struct v4l2_subdev *sd,
 			 struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
 
 	mf->width	= mt9m001->rect.width;
@@ -312,7 +312,7 @@
 static int mt9m001_s_fmt(struct v4l2_subdev *sd,
 			 struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
 	struct v4l2_crop a = {
 		.c = {
@@ -340,7 +340,7 @@
 static int mt9m001_try_fmt(struct v4l2_subdev *sd,
 			   struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
 	const struct mt9m001_datafmt *fmt;
 
@@ -367,7 +367,7 @@
 static int mt9m001_g_chip_ident(struct v4l2_subdev *sd,
 				struct v4l2_dbg_chip_ident *id)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
 
 	if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
@@ -386,7 +386,7 @@
 static int mt9m001_g_register(struct v4l2_subdev *sd,
 			      struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
 		return -EINVAL;
@@ -406,7 +406,7 @@
 static int mt9m001_s_register(struct v4l2_subdev *sd,
 			      struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
 		return -EINVAL;
@@ -468,7 +468,7 @@
 
 static int mt9m001_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
 	int data;
 
@@ -494,7 +494,7 @@
 
 static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
 	struct soc_camera_device *icd = client->dev.platform_data;
 	const struct v4l2_queryctrl *qctrl;
@@ -683,7 +683,7 @@
 
 static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
 
 	*lines = mt9m001->y_skip_top;
@@ -704,7 +704,7 @@
 static int mt9m001_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
 			    enum v4l2_mbus_pixelcode *code)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
 
 	if (index >= mt9m001->num_fmts)
diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
index c71af4e..525a16e 100644
--- a/drivers/media/video/mt9m111.c
+++ b/drivers/media/video/mt9m111.c
@@ -100,14 +100,14 @@
 #define MT9M111_OUTFMT_BYPASS_IFP	(1 << 10)
 #define MT9M111_OUTFMT_INV_PIX_CLOCK	(1 << 9)
 #define MT9M111_OUTFMT_RGB		(1 << 8)
-#define MT9M111_OUTFMT_RGB565		(0x0 << 6)
-#define MT9M111_OUTFMT_RGB555		(0x1 << 6)
-#define MT9M111_OUTFMT_RGB444x		(0x2 << 6)
-#define MT9M111_OUTFMT_RGBx444		(0x3 << 6)
-#define MT9M111_OUTFMT_TST_RAMP_OFF	(0x0 << 4)
-#define MT9M111_OUTFMT_TST_RAMP_COL	(0x1 << 4)
-#define MT9M111_OUTFMT_TST_RAMP_ROW	(0x2 << 4)
-#define MT9M111_OUTFMT_TST_RAMP_FRAME	(0x3 << 4)
+#define MT9M111_OUTFMT_RGB565		(0 << 6)
+#define MT9M111_OUTFMT_RGB555		(1 << 6)
+#define MT9M111_OUTFMT_RGB444x		(2 << 6)
+#define MT9M111_OUTFMT_RGBx444		(3 << 6)
+#define MT9M111_OUTFMT_TST_RAMP_OFF	(0 << 4)
+#define MT9M111_OUTFMT_TST_RAMP_COL	(1 << 4)
+#define MT9M111_OUTFMT_TST_RAMP_ROW	(2 << 4)
+#define MT9M111_OUTFMT_TST_RAMP_FRAME	(3 << 4)
 #define MT9M111_OUTFMT_SHIFT_3_UP	(1 << 3)
 #define MT9M111_OUTFMT_AVG_CHROMA	(1 << 2)
 #define MT9M111_OUTFMT_SWAP_YCbCr_C_Y	(1 << 1)
@@ -124,7 +124,7 @@
 #define reg_clear(reg, val) mt9m111_reg_clear(client, MT9M111_##reg, (val))
 
 #define MT9M111_MIN_DARK_ROWS	8
-#define MT9M111_MIN_DARK_COLS	24
+#define MT9M111_MIN_DARK_COLS	26
 #define MT9M111_MAX_HEIGHT	1024
 #define MT9M111_MAX_WIDTH	1280
 
@@ -440,7 +440,7 @@
 static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
 	struct v4l2_rect rect = a->c;
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m111 *mt9m111 = to_mt9m111(client);
 	int ret;
 
@@ -458,7 +458,7 @@
 
 static int mt9m111_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m111 *mt9m111 = to_mt9m111(client);
 
 	a->c	= mt9m111->rect;
@@ -486,7 +486,7 @@
 static int mt9m111_g_fmt(struct v4l2_subdev *sd,
 			 struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m111 *mt9m111 = to_mt9m111(client);
 
 	mf->width	= mt9m111->rect.width;
@@ -549,7 +549,7 @@
 static int mt9m111_s_fmt(struct v4l2_subdev *sd,
 			 struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	const struct mt9m111_datafmt *fmt;
 	struct mt9m111 *mt9m111 = to_mt9m111(client);
 	struct v4l2_rect rect = {
@@ -584,7 +584,7 @@
 static int mt9m111_try_fmt(struct v4l2_subdev *sd,
 			   struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m111 *mt9m111 = to_mt9m111(client);
 	const struct mt9m111_datafmt *fmt;
 	bool bayer = mf->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
@@ -624,7 +624,7 @@
 static int mt9m111_g_chip_ident(struct v4l2_subdev *sd,
 				struct v4l2_dbg_chip_ident *id)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m111 *mt9m111 = to_mt9m111(client);
 
 	if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
@@ -643,7 +643,7 @@
 static int mt9m111_g_register(struct v4l2_subdev *sd,
 			      struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	int val;
 
 	if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff)
@@ -664,7 +664,7 @@
 static int mt9m111_s_register(struct v4l2_subdev *sd,
 			      struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff)
 		return -EINVAL;
@@ -812,7 +812,7 @@
 
 static int mt9m111_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m111 *mt9m111 = to_mt9m111(client);
 	int data;
 
@@ -855,7 +855,7 @@
 
 static int mt9m111_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m111 *mt9m111 = to_mt9m111(client);
 	const struct v4l2_queryctrl *qctrl;
 	int ret;
diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c
index a9a28b2..9bd44a8 100644
--- a/drivers/media/video/mt9t031.c
+++ b/drivers/media/video/mt9t031.c
@@ -163,7 +163,7 @@
 
 static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	int ret;
 
 	if (enable)
@@ -393,7 +393,7 @@
 static int mt9t031_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
 	struct v4l2_rect rect = a->c;
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
 
 	rect.width = ALIGN(rect.width, 2);
@@ -410,7 +410,7 @@
 
 static int mt9t031_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
 
 	a->c	= mt9t031->rect;
@@ -436,7 +436,7 @@
 static int mt9t031_g_fmt(struct v4l2_subdev *sd,
 			 struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
 
 	mf->width	= mt9t031->rect.width / mt9t031->xskip;
@@ -451,7 +451,7 @@
 static int mt9t031_s_fmt(struct v4l2_subdev *sd,
 			 struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
 	u16 xskip, yskip;
 	struct v4l2_rect rect = mt9t031->rect;
@@ -490,7 +490,7 @@
 static int mt9t031_g_chip_ident(struct v4l2_subdev *sd,
 				struct v4l2_dbg_chip_ident *id)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
 
 	if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
@@ -509,7 +509,7 @@
 static int mt9t031_g_register(struct v4l2_subdev *sd,
 			      struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
 		return -EINVAL;
@@ -528,7 +528,7 @@
 static int mt9t031_s_register(struct v4l2_subdev *sd,
 			      struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
 		return -EINVAL;
@@ -545,7 +545,7 @@
 
 static int mt9t031_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
 	int data;
 
@@ -577,7 +577,7 @@
 
 static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
 	const struct v4l2_queryctrl *qctrl;
 	int data;
@@ -703,7 +703,7 @@
 	struct soc_camera_device *icd = container_of(vdev->parent,
 		struct soc_camera_device, dev);
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
 
 	int ret;
@@ -780,7 +780,7 @@
 
 static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
 
 	*lines = mt9t031->y_skip_top;
diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c
index 8ec47e4..bffa9ee 100644
--- a/drivers/media/video/mt9t112.c
+++ b/drivers/media/video/mt9t112.c
@@ -804,7 +804,7 @@
 static int mt9t112_g_chip_ident(struct v4l2_subdev *sd,
 				struct v4l2_dbg_chip_ident *id)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t112_priv *priv = to_mt9t112(client);
 
 	id->ident    = priv->model;
@@ -817,7 +817,7 @@
 static int mt9t112_g_register(struct v4l2_subdev *sd,
 			      struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	int                ret;
 
 	reg->size = 2;
@@ -831,7 +831,7 @@
 static int mt9t112_s_register(struct v4l2_subdev *sd,
 			      struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	int ret;
 
 	mt9t112_reg_write(ret, client, reg->reg, reg->val);
@@ -858,7 +858,7 @@
 ************************************************************************/
 static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t112_priv *priv = to_mt9t112(client);
 	int ret = 0;
 
@@ -968,7 +968,7 @@
 
 static int mt9t112_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct v4l2_rect *rect = &a->c;
 
 	return mt9t112_set_params(client, rect->width, rect->height,
@@ -978,7 +978,7 @@
 static int mt9t112_g_fmt(struct v4l2_subdev *sd,
 			 struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t112_priv *priv = to_mt9t112(client);
 
 	if (!priv->format) {
@@ -1000,7 +1000,7 @@
 static int mt9t112_s_fmt(struct v4l2_subdev *sd,
 			 struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	/* TODO: set colorspace */
 	return mt9t112_set_params(client, mf->width, mf->height, mf->code);
diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c
index f5e778d..209ff97 100644
--- a/drivers/media/video/mt9v011.c
+++ b/drivers/media/video/mt9v011.c
@@ -11,9 +11,8 @@
 #include <linux/delay.h>
 #include <asm/div64.h>
 #include <media/v4l2-device.h>
-#include "mt9v011.h"
-#include <media/v4l2-i2c-drv.h>
 #include <media/v4l2-chip-ident.h>
+#include "mt9v011.h"
 
 MODULE_DESCRIPTION("Micron mt9v011 sensor driver");
 MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
@@ -624,9 +623,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, mt9v011_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "mt9v011",
-	.probe = mt9v011_probe,
-	.remove = mt9v011_remove,
-	.id_table = mt9v011_id,
+static struct i2c_driver mt9v011_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "mt9v011",
+	},
+	.probe		= mt9v011_probe,
+	.remove		= mt9v011_remove,
+	.id_table	= mt9v011_id,
 };
+
+static __init int init_mt9v011(void)
+{
+	return i2c_add_driver(&mt9v011_driver);
+}
+
+static __exit void exit_mt9v011(void)
+{
+	i2c_del_driver(&mt9v011_driver);
+}
+
+module_init(init_mt9v011);
+module_exit(exit_mt9v011);
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
index b48473c..b96171c 100644
--- a/drivers/media/video/mt9v022.c
+++ b/drivers/media/video/mt9v022.c
@@ -184,7 +184,7 @@
 
 static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9v022 *mt9v022 = to_mt9v022(client);
 
 	if (enable)
@@ -273,7 +273,7 @@
 
 static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9v022 *mt9v022 = to_mt9v022(client);
 	struct v4l2_rect rect = a->c;
 	int ret;
@@ -334,7 +334,7 @@
 
 static int mt9v022_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9v022 *mt9v022 = to_mt9v022(client);
 
 	a->c	= mt9v022->rect;
@@ -360,7 +360,7 @@
 static int mt9v022_g_fmt(struct v4l2_subdev *sd,
 			 struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9v022 *mt9v022 = to_mt9v022(client);
 
 	mf->width	= mt9v022->rect.width;
@@ -375,7 +375,7 @@
 static int mt9v022_s_fmt(struct v4l2_subdev *sd,
 			 struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9v022 *mt9v022 = to_mt9v022(client);
 	struct v4l2_crop a = {
 		.c = {
@@ -422,7 +422,7 @@
 static int mt9v022_try_fmt(struct v4l2_subdev *sd,
 			   struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9v022 *mt9v022 = to_mt9v022(client);
 	const struct mt9v022_datafmt *fmt;
 	int align = mf->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
@@ -448,7 +448,7 @@
 static int mt9v022_g_chip_ident(struct v4l2_subdev *sd,
 				struct v4l2_dbg_chip_ident *id)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9v022 *mt9v022 = to_mt9v022(client);
 
 	if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
@@ -467,7 +467,7 @@
 static int mt9v022_g_register(struct v4l2_subdev *sd,
 			      struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
 		return -EINVAL;
@@ -487,7 +487,7 @@
 static int mt9v022_s_register(struct v4l2_subdev *sd,
 			      struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
 		return -EINVAL;
@@ -565,7 +565,7 @@
 
 static int mt9v022_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	const struct v4l2_queryctrl *qctrl;
 	unsigned long range;
 	int data;
@@ -622,7 +622,7 @@
 static int mt9v022_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
 	int data;
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	const struct v4l2_queryctrl *qctrl;
 
 	qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id);
@@ -817,7 +817,7 @@
 
 static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9v022 *mt9v022 = to_mt9v022(client);
 
 	*lines = mt9v022->y_skip_top;
@@ -838,7 +838,7 @@
 static int mt9v022_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
 			    enum v4l2_mbus_pixelcode *code)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9v022 *mt9v022 = to_mt9v022(client);
 
 	if (index >= mt9v022->num_fmts)
diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c
index 5c17f9e..5e486a8 100644
--- a/drivers/media/video/mx1_camera.c
+++ b/drivers/media/video/mx1_camera.c
@@ -161,7 +161,7 @@
 	 * This waits until this buffer is out of danger, i.e., until it is no
 	 * longer in STATE_QUEUED or STATE_ACTIVE
 	 */
-	videobuf_waiton(vb, 0, 0);
+	videobuf_waiton(vq, vb, 0, 0);
 	videobuf_dma_contig_free(vq, vb);
 
 	vb->state = VIDEOBUF_NEEDS_INIT;
@@ -385,7 +385,7 @@
 					&pcdev->lock,
 					V4L2_BUF_TYPE_VIDEO_CAPTURE,
 					V4L2_FIELD_NONE,
-					sizeof(struct mx1_buffer), icd);
+					sizeof(struct mx1_buffer), icd, NULL);
 }
 
 static int mclk_get_divisor(struct mx1_camera_dev *pcdev)
@@ -638,7 +638,7 @@
 	return 0;
 }
 
-static int mx1_camera_reqbufs(struct soc_camera_file *icf,
+static int mx1_camera_reqbufs(struct soc_camera_device *icd,
 			      struct v4l2_requestbuffers *p)
 {
 	int i;
@@ -650,7 +650,7 @@
 	 * it hadn't triggered
 	 */
 	for (i = 0; i < p->count; i++) {
-		struct mx1_buffer *buf = container_of(icf->vb_vidq.bufs[i],
+		struct mx1_buffer *buf = container_of(icd->vb_vidq.bufs[i],
 						      struct mx1_buffer, vb);
 		buf->inwork = 0;
 		INIT_LIST_HEAD(&buf->vb.queue);
@@ -661,10 +661,10 @@
 
 static unsigned int mx1_camera_poll(struct file *file, poll_table *pt)
 {
-	struct soc_camera_file *icf = file->private_data;
+	struct soc_camera_device *icd = file->private_data;
 	struct mx1_buffer *buf;
 
-	buf = list_entry(icf->vb_vidq.stream.next, struct mx1_buffer,
+	buf = list_entry(icd->vb_vidq.stream.next, struct mx1_buffer,
 			 vb.stream);
 
 	poll_wait(file, &buf->vb.done, pt);
diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
index b6ea672..4a27862 100644
--- a/drivers/media/video/mx2_camera.c
+++ b/drivers/media/video/mx2_camera.c
@@ -461,9 +461,9 @@
 
 	/*
 	 * This waits until this buffer is out of danger, i.e., until it is no
-	 * longer in STATE_QUEUED or STATE_ACTIVE
+	 * longer in state VIDEOBUF_QUEUED or VIDEOBUF_ACTIVE
 	 */
-	videobuf_waiton(vb, 0, 0);
+	videobuf_waiton(vq, vb, 0, 0);
 
 	videobuf_dma_contig_free(vq, vb);
 	dev_dbg(&icd->dev, "%s freed\n", __func__);
@@ -640,15 +640,27 @@
 	 * Terminate only queued but inactive buffers. Active buffers are
 	 * released when they become inactive after videobuf_waiton().
 	 *
-	 * FIXME: implement forced termination of active buffers, so that the
-	 * user won't get stuck in an uninterruptible state. This requires a
-	 * specific handling for each of the three DMA types that this driver
-	 * supports.
+	 * FIXME: implement forced termination of active buffers for mx27 and
+	 * mx27 eMMA, so that the user won't get stuck in an uninterruptible
+	 * state. This requires a specific handling for each of the these DMA
+	 * types.
 	 */
 	spin_lock_irqsave(&pcdev->lock, flags);
 	if (vb->state == VIDEOBUF_QUEUED) {
 		list_del(&vb->queue);
 		vb->state = VIDEOBUF_ERROR;
+	} else if (cpu_is_mx25() && vb->state == VIDEOBUF_ACTIVE) {
+		if (pcdev->fb1_active == buf) {
+			pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN;
+			writel(0, pcdev->base_csi + CSIDMASA_FB1);
+			pcdev->fb1_active = NULL;
+		} else if (pcdev->fb2_active == buf) {
+			pcdev->csicr1 &= ~CSICR1_FB2_DMA_INTEN;
+			writel(0, pcdev->base_csi + CSIDMASA_FB2);
+			pcdev->fb2_active = NULL;
+		}
+		writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+		vb->state = VIDEOBUF_ERROR;
 	}
 	spin_unlock_irqrestore(&pcdev->lock, flags);
 
@@ -670,7 +682,7 @@
 
 	videobuf_queue_dma_contig_init(q, &mx2_videobuf_ops, pcdev->dev,
 			&pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
-			V4L2_FIELD_NONE, sizeof(struct mx2_buffer), icd);
+			V4L2_FIELD_NONE, sizeof(struct mx2_buffer), icd, NULL);
 }
 
 #define MX2_BUS_FLAGS	(SOCAM_DATAWIDTH_8 | \
@@ -716,8 +728,11 @@
 	/*
 	 * We only use the EMMA engine to get rid of the broken
 	 * DMA Engine. No color space consversion at the moment.
-	 * We adjust incoming and outgoing pixelformat to rgb16
-	 * and adjust the bytesperline accordingly.
+	 * We set the incomming and outgoing pixelformat to an
+	 * 16 Bit wide format and adjust the bytesperline
+	 * accordingly. With this configuration the inputdata
+	 * will not be changed by the emma and could be any type
+	 * of 16 Bit Pixelformat.
 	 */
 	writel(PRP_CNTL_CH1EN |
 			PRP_CNTL_CSIEN |
@@ -903,10 +918,6 @@
 		return -EINVAL;
 	}
 
-	/* eMMA can only do RGB565 */
-	if (mx27_camera_emma(pcdev) && pix->pixelformat != V4L2_PIX_FMT_RGB565)
-		return -EINVAL;
-
 	mf.width	= pix->width;
 	mf.height	= pix->height;
 	mf.field	= pix->field;
@@ -950,10 +961,6 @@
 
 	/* FIXME: implement MX27 limits */
 
-	/* eMMA can only do RGB565 */
-	if (mx27_camera_emma(pcdev) && pixfmt != V4L2_PIX_FMT_RGB565)
-		return -EINVAL;
-
 	/* limit to MX25 hardware capabilities */
 	if (cpu_is_mx25()) {
 		if (xlate->host_fmt->bits_per_sample <= 8)
@@ -1426,6 +1433,9 @@
 	if (err)
 		goto exit_free_emma;
 
+	dev_info(&pdev->dev, "MX2 Camera (CSI) driver probed, clock frequency: %ld\n",
+			clk_get_rate(pcdev->clk_csi));
+
 	return 0;
 
 exit_free_emma:
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
index a9be14c..29c5fc3 100644
--- a/drivers/media/video/mx3_camera.c
+++ b/drivers/media/video/mx3_camera.c
@@ -185,7 +185,7 @@
 	 * This waits until this buffer is out of danger, i.e., until it is no
 	 * longer in STATE_QUEUED or STATE_ACTIVE
 	 */
-	videobuf_waiton(vb, 0, 0);
+	videobuf_waiton(vq, vb, 0, 0);
 	if (txd) {
 		ichan = to_idmac_chan(txd->chan);
 		async_tx_ack(txd);
@@ -441,7 +441,8 @@
 				       &mx3_cam->lock,
 				       V4L2_BUF_TYPE_VIDEO_CAPTURE,
 				       V4L2_FIELD_NONE,
-				       sizeof(struct mx3_camera_buffer), icd);
+				       sizeof(struct mx3_camera_buffer), icd,
+				       NULL);
 }
 
 /* First part of ipu_csi_init_interface() */
@@ -976,7 +977,7 @@
 	return ret;
 }
 
-static int mx3_camera_reqbufs(struct soc_camera_file *icf,
+static int mx3_camera_reqbufs(struct soc_camera_device *icd,
 			      struct v4l2_requestbuffers *p)
 {
 	return 0;
@@ -984,9 +985,9 @@
 
 static unsigned int mx3_camera_poll(struct file *file, poll_table *pt)
 {
-	struct soc_camera_file *icf = file->private_data;
+	struct soc_camera_device *icd = file->private_data;
 
-	return videobuf_poll_stream(file, &icf->vb_vidq, pt);
+	return videobuf_poll_stream(file, &icd->vb_vidq, pt);
 }
 
 static int mx3_camera_querycap(struct soc_camera_host *ici,
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
index b1dbcf1..94ba698 100644
--- a/drivers/media/video/mxb.c
+++ b/drivers/media/video/mxb.c
@@ -32,7 +32,6 @@
 #include "tea6415c.h"
 #include "tea6420.h"
 
-#define	I2C_SAA5246A  0x11
 #define I2C_SAA7111A  0x24
 #define	I2C_TDA9840   0x42
 #define	I2C_TEA6415C  0x43
@@ -186,21 +185,17 @@
 	}
 
 	mxb->saa7111a = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
-			"saa7115", "saa7111", I2C_SAA7111A, NULL);
+			NULL, "saa7111", I2C_SAA7111A, NULL);
 	mxb->tea6420_1 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
-			"tea6420", "tea6420", I2C_TEA6420_1, NULL);
+			NULL, "tea6420", I2C_TEA6420_1, NULL);
 	mxb->tea6420_2 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
-			"tea6420", "tea6420", I2C_TEA6420_2, NULL);
+			NULL, "tea6420", I2C_TEA6420_2, NULL);
 	mxb->tea6415c = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
-			"tea6415c", "tea6415c", I2C_TEA6415C, NULL);
+			NULL, "tea6415c", I2C_TEA6415C, NULL);
 	mxb->tda9840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
-			"tda9840", "tda9840", I2C_TDA9840, NULL);
+			NULL, "tda9840", I2C_TDA9840, NULL);
 	mxb->tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
-			"tuner", "tuner", I2C_TUNER, NULL);
-	if (v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
-			"saa5246a", "saa5246a", I2C_SAA5246A, NULL)) {
-		printk(KERN_INFO "mxb: found teletext decoder\n");
-	}
+			NULL, "tuner", I2C_TUNER, NULL);
 
 	/* check if all devices are present */
 	if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c ||
diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c
index 4ed51b1..15f8793 100644
--- a/drivers/media/video/omap/omap_vout.c
+++ b/drivers/media/video/omap/omap_vout.c
@@ -1341,7 +1341,7 @@
 
 	videobuf_queue_dma_contig_init(q, &video_vbq_ops, q->dev,
 			&vout->vbq_lock, vout->type, V4L2_FIELD_NONE,
-			sizeof(struct videobuf_buffer), vout);
+			sizeof(struct videobuf_buffer), vout, NULL);
 
 	v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
 	return 0;
diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c
new file mode 100644
index 0000000..7c30e62b
--- /dev/null
+++ b/drivers/media/video/omap1_camera.c
@@ -0,0 +1,1702 @@
+/*
+ * V4L2 SoC Camera driver for OMAP1 Camera Interface
+ *
+ * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Based on V4L2 Driver for i.MXL/i.MXL camera (CSI) host
+ * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com>
+ *
+ * Based on PXA SoC camera driver
+ * Copyright (C) 2006, Sascha Hauer, Pengutronix
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * Hardware specific bits initialy based on former work by Matt Callow
+ * drivers/media/video/omap/omap1510cam.c
+ * Copyright (C) 2006 Matt Callow
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+
+#include <media/omap1_camera.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/videobuf-dma-contig.h>
+#include <media/videobuf-dma-sg.h>
+
+#include <plat/dma.h>
+
+
+#define DRIVER_NAME		"omap1-camera"
+#define VERSION_CODE		KERNEL_VERSION(0, 0, 1)
+
+
+/*
+ * ---------------------------------------------------------------------------
+ *  OMAP1 Camera Interface registers
+ * ---------------------------------------------------------------------------
+ */
+
+#define REG_CTRLCLOCK		0x00
+#define REG_IT_STATUS		0x04
+#define REG_MODE		0x08
+#define REG_STATUS		0x0C
+#define REG_CAMDATA		0x10
+#define REG_GPIO		0x14
+#define REG_PEAK_COUNTER	0x18
+
+/* CTRLCLOCK bit shifts */
+#define LCLK_EN			BIT(7)
+#define DPLL_EN			BIT(6)
+#define MCLK_EN			BIT(5)
+#define CAMEXCLK_EN		BIT(4)
+#define POLCLK			BIT(3)
+#define FOSCMOD_SHIFT		0
+#define FOSCMOD_MASK		(0x7 << FOSCMOD_SHIFT)
+#define FOSCMOD_12MHz		0x0
+#define FOSCMOD_6MHz		0x2
+#define FOSCMOD_9_6MHz		0x4
+#define FOSCMOD_24MHz		0x5
+#define FOSCMOD_8MHz		0x6
+
+/* IT_STATUS bit shifts */
+#define DATA_TRANSFER		BIT(5)
+#define FIFO_FULL		BIT(4)
+#define H_DOWN			BIT(3)
+#define H_UP			BIT(2)
+#define V_DOWN			BIT(1)
+#define V_UP			BIT(0)
+
+/* MODE bit shifts */
+#define RAZ_FIFO		BIT(18)
+#define EN_FIFO_FULL		BIT(17)
+#define EN_NIRQ			BIT(16)
+#define THRESHOLD_SHIFT		9
+#define THRESHOLD_MASK		(0x7f << THRESHOLD_SHIFT)
+#define DMA			BIT(8)
+#define EN_H_DOWN		BIT(7)
+#define EN_H_UP			BIT(6)
+#define EN_V_DOWN		BIT(5)
+#define EN_V_UP			BIT(4)
+#define ORDERCAMD		BIT(3)
+
+#define IRQ_MASK		(EN_V_UP | EN_V_DOWN | EN_H_UP | EN_H_DOWN | \
+				 EN_NIRQ | EN_FIFO_FULL)
+
+/* STATUS bit shifts */
+#define HSTATUS			BIT(1)
+#define VSTATUS			BIT(0)
+
+/* GPIO bit shifts */
+#define CAM_RST			BIT(0)
+
+/* end of OMAP1 Camera Interface registers */
+
+
+#define SOCAM_BUS_FLAGS	(SOCAM_MASTER | \
+			SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | \
+			SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \
+			SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8)
+
+
+#define FIFO_SIZE		((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1)
+#define FIFO_SHIFT		__fls(FIFO_SIZE)
+
+#define DMA_BURST_SHIFT		(1 + OMAP_DMA_DATA_BURST_4)
+#define DMA_BURST_SIZE		(1 << DMA_BURST_SHIFT)
+
+#define DMA_ELEMENT_SHIFT	OMAP_DMA_DATA_TYPE_S32
+#define DMA_ELEMENT_SIZE	(1 << DMA_ELEMENT_SHIFT)
+
+#define DMA_FRAME_SHIFT_CONTIG	(FIFO_SHIFT - 1)
+#define DMA_FRAME_SHIFT_SG	DMA_BURST_SHIFT
+
+#define DMA_FRAME_SHIFT(x)	((x) == OMAP1_CAM_DMA_CONTIG ? \
+						DMA_FRAME_SHIFT_CONTIG : \
+						DMA_FRAME_SHIFT_SG)
+#define DMA_FRAME_SIZE(x)	(1 << DMA_FRAME_SHIFT(x))
+#define DMA_SYNC		OMAP_DMA_SYNC_FRAME
+#define THRESHOLD_LEVEL		DMA_FRAME_SIZE
+
+
+#define MAX_VIDEO_MEM		4	/* arbitrary video memory limit in MB */
+
+
+/*
+ * Structures
+ */
+
+/* buffer for one video frame */
+struct omap1_cam_buf {
+	struct videobuf_buffer		vb;
+	enum v4l2_mbus_pixelcode	code;
+	int				inwork;
+	struct scatterlist		*sgbuf;
+	int				sgcount;
+	int				bytes_left;
+	enum videobuf_state		result;
+};
+
+struct omap1_cam_dev {
+	struct soc_camera_host		soc_host;
+	struct soc_camera_device	*icd;
+	struct clk			*clk;
+
+	unsigned int			irq;
+	void __iomem			*base;
+
+	int				dma_ch;
+
+	struct omap1_cam_platform_data	*pdata;
+	struct resource			*res;
+	unsigned long			pflags;
+	unsigned long			camexclk;
+
+	struct list_head		capture;
+
+	/* lock used to protect videobuf */
+	spinlock_t			lock;
+
+	/* Pointers to DMA buffers */
+	struct omap1_cam_buf		*active;
+	struct omap1_cam_buf		*ready;
+
+	enum omap1_cam_vb_mode		vb_mode;
+	int				(*mmap_mapper)(struct videobuf_queue *q,
+						struct videobuf_buffer *buf,
+						struct vm_area_struct *vma);
+
+	u32				reg_cache[0];
+};
+
+
+static void cam_write(struct omap1_cam_dev *pcdev, u16 reg, u32 val)
+{
+	pcdev->reg_cache[reg / sizeof(u32)] = val;
+	__raw_writel(val, pcdev->base + reg);
+}
+
+static u32 cam_read(struct omap1_cam_dev *pcdev, u16 reg, bool from_cache)
+{
+	return !from_cache ? __raw_readl(pcdev->base + reg) :
+			pcdev->reg_cache[reg / sizeof(u32)];
+}
+
+#define CAM_READ(pcdev, reg) \
+		cam_read(pcdev, REG_##reg, false)
+#define CAM_WRITE(pcdev, reg, val) \
+		cam_write(pcdev, REG_##reg, val)
+#define CAM_READ_CACHE(pcdev, reg) \
+		cam_read(pcdev, REG_##reg, true)
+
+/*
+ *  Videobuf operations
+ */
+static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
+		unsigned int *size)
+{
+	struct soc_camera_device *icd = vq->priv_data;
+	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+			icd->current_fmt->host_fmt);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+
+	if (bytes_per_line < 0)
+		return bytes_per_line;
+
+	*size = bytes_per_line * icd->user_height;
+
+	if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode))
+		*count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode);
+
+	if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
+		*count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
+
+	dev_dbg(icd->dev.parent,
+			"%s: count=%d, size=%d\n", __func__, *count, *size);
+
+	return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct omap1_cam_buf *buf,
+		enum omap1_cam_vb_mode vb_mode)
+{
+	struct videobuf_buffer *vb = &buf->vb;
+
+	BUG_ON(in_interrupt());
+
+	videobuf_waiton(vb, 0, 0);
+
+	if (vb_mode == OMAP1_CAM_DMA_CONTIG) {
+		videobuf_dma_contig_free(vq, vb);
+	} else {
+		struct soc_camera_device *icd = vq->priv_data;
+		struct device *dev = icd->dev.parent;
+		struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+		videobuf_dma_unmap(dev, dma);
+		videobuf_dma_free(dma);
+	}
+
+	vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int omap1_videobuf_prepare(struct videobuf_queue *vq,
+		struct videobuf_buffer *vb, enum v4l2_field field)
+{
+	struct soc_camera_device *icd = vq->priv_data;
+	struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb);
+	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+			icd->current_fmt->host_fmt);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	int ret;
+
+	if (bytes_per_line < 0)
+		return bytes_per_line;
+
+	WARN_ON(!list_empty(&vb->queue));
+
+	BUG_ON(NULL == icd->current_fmt);
+
+	buf->inwork = 1;
+
+	if (buf->code != icd->current_fmt->code || vb->field != field ||
+			vb->width  != icd->user_width ||
+			vb->height != icd->user_height) {
+		buf->code  = icd->current_fmt->code;
+		vb->width  = icd->user_width;
+		vb->height = icd->user_height;
+		vb->field  = field;
+		vb->state  = VIDEOBUF_NEEDS_INIT;
+	}
+
+	vb->size = bytes_per_line * vb->height;
+
+	if (vb->baddr && vb->bsize < vb->size) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (vb->state == VIDEOBUF_NEEDS_INIT) {
+		ret = videobuf_iolock(vq, vb, NULL);
+		if (ret)
+			goto fail;
+
+		vb->state = VIDEOBUF_PREPARED;
+	}
+	buf->inwork = 0;
+
+	return 0;
+fail:
+	free_buffer(vq, buf, pcdev->vb_mode);
+out:
+	buf->inwork = 0;
+	return ret;
+}
+
+static void set_dma_dest_params(int dma_ch, struct omap1_cam_buf *buf,
+		enum omap1_cam_vb_mode vb_mode)
+{
+	dma_addr_t dma_addr;
+	unsigned int block_size;
+
+	if (vb_mode == OMAP1_CAM_DMA_CONTIG) {
+		dma_addr = videobuf_to_dma_contig(&buf->vb);
+		block_size = buf->vb.size;
+	} else {
+		if (WARN_ON(!buf->sgbuf)) {
+			buf->result = VIDEOBUF_ERROR;
+			return;
+		}
+		dma_addr = sg_dma_address(buf->sgbuf);
+		if (WARN_ON(!dma_addr)) {
+			buf->sgbuf = NULL;
+			buf->result = VIDEOBUF_ERROR;
+			return;
+		}
+		block_size = sg_dma_len(buf->sgbuf);
+		if (WARN_ON(!block_size)) {
+			buf->sgbuf = NULL;
+			buf->result = VIDEOBUF_ERROR;
+			return;
+		}
+		if (unlikely(buf->bytes_left < block_size))
+			block_size = buf->bytes_left;
+		if (WARN_ON(dma_addr & (DMA_FRAME_SIZE(vb_mode) *
+				DMA_ELEMENT_SIZE - 1))) {
+			dma_addr = ALIGN(dma_addr, DMA_FRAME_SIZE(vb_mode) *
+					DMA_ELEMENT_SIZE);
+			block_size &= ~(DMA_FRAME_SIZE(vb_mode) *
+					DMA_ELEMENT_SIZE - 1);
+		}
+		buf->bytes_left -= block_size;
+		buf->sgcount++;
+	}
+
+	omap_set_dma_dest_params(dma_ch,
+		OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0);
+	omap_set_dma_transfer_params(dma_ch,
+		OMAP_DMA_DATA_TYPE_S32, DMA_FRAME_SIZE(vb_mode),
+		block_size >> (DMA_FRAME_SHIFT(vb_mode) + DMA_ELEMENT_SHIFT),
+		DMA_SYNC, 0, 0);
+}
+
+static struct omap1_cam_buf *prepare_next_vb(struct omap1_cam_dev *pcdev)
+{
+	struct omap1_cam_buf *buf;
+
+	/*
+	 * If there is already a buffer pointed out by the pcdev->ready,
+	 * (re)use it, otherwise try to fetch and configure a new one.
+	 */
+	buf = pcdev->ready;
+	if (!buf) {
+		if (list_empty(&pcdev->capture))
+			return buf;
+		buf = list_entry(pcdev->capture.next,
+				struct omap1_cam_buf, vb.queue);
+		buf->vb.state = VIDEOBUF_ACTIVE;
+		pcdev->ready = buf;
+		list_del_init(&buf->vb.queue);
+	}
+
+	if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+		/*
+		 * In CONTIG mode, we can safely enter next buffer parameters
+		 * into the DMA programming register set after the DMA
+		 * has already been activated on the previous buffer
+		 */
+		set_dma_dest_params(pcdev->dma_ch, buf, pcdev->vb_mode);
+	} else {
+		/*
+		 * In SG mode, the above is not safe since there are probably
+		 * a bunch of sgbufs from previous sglist still pending.
+		 * Instead, mark the sglist fresh for the upcoming
+		 * try_next_sgbuf().
+		 */
+		buf->sgbuf = NULL;
+	}
+
+	return buf;
+}
+
+static struct scatterlist *try_next_sgbuf(int dma_ch, struct omap1_cam_buf *buf)
+{
+	struct scatterlist *sgbuf;
+
+	if (likely(buf->sgbuf)) {
+		/* current sglist is active */
+		if (unlikely(!buf->bytes_left)) {
+			/* indicate sglist complete */
+			sgbuf = NULL;
+		} else {
+			/* process next sgbuf */
+			sgbuf = sg_next(buf->sgbuf);
+			if (WARN_ON(!sgbuf)) {
+				buf->result = VIDEOBUF_ERROR;
+			} else if (WARN_ON(!sg_dma_len(sgbuf))) {
+				sgbuf = NULL;
+				buf->result = VIDEOBUF_ERROR;
+			}
+		}
+		buf->sgbuf = sgbuf;
+	} else {
+		/* sglist is fresh, initialize it before using */
+		struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+
+		sgbuf = dma->sglist;
+		if (!(WARN_ON(!sgbuf))) {
+			buf->sgbuf = sgbuf;
+			buf->sgcount = 0;
+			buf->bytes_left = buf->vb.size;
+			buf->result = VIDEOBUF_DONE;
+		}
+	}
+	if (sgbuf)
+		/*
+		 * Put our next sgbuf parameters (address, size)
+		 * into the DMA programming register set.
+		 */
+		set_dma_dest_params(dma_ch, buf, OMAP1_CAM_DMA_SG);
+
+	return sgbuf;
+}
+
+static void start_capture(struct omap1_cam_dev *pcdev)
+{
+	struct omap1_cam_buf *buf = pcdev->active;
+	u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+	u32 mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN;
+
+	if (WARN_ON(!buf))
+		return;
+
+	/*
+	 * Enable start of frame interrupt, which we will use for activating
+	 * our end of frame watchdog when capture actually starts.
+	 */
+	mode |= EN_V_UP;
+
+	if (unlikely(ctrlclock & LCLK_EN))
+		/* stop pixel clock before FIFO reset */
+		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+	/* reset FIFO */
+	CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO);
+
+	omap_start_dma(pcdev->dma_ch);
+
+	if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+		/*
+		 * In SG mode, it's a good moment for fetching next sgbuf
+		 * from the current sglist and, if available, already putting
+		 * its parameters into the DMA programming register set.
+		 */
+		try_next_sgbuf(pcdev->dma_ch, buf);
+	}
+
+	/* (re)enable pixel clock */
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN);
+	/* release FIFO reset */
+	CAM_WRITE(pcdev, MODE, mode);
+}
+
+static void suspend_capture(struct omap1_cam_dev *pcdev)
+{
+	u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+	omap_stop_dma(pcdev->dma_ch);
+}
+
+static void disable_capture(struct omap1_cam_dev *pcdev)
+{
+	u32 mode = CAM_READ_CACHE(pcdev, MODE);
+
+	CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA));
+}
+
+static void omap1_videobuf_queue(struct videobuf_queue *vq,
+						struct videobuf_buffer *vb)
+{
+	struct soc_camera_device *icd = vq->priv_data;
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	struct omap1_cam_buf *buf;
+	u32 mode;
+
+	list_add_tail(&vb->queue, &pcdev->capture);
+	vb->state = VIDEOBUF_QUEUED;
+
+	if (pcdev->active) {
+		/*
+		 * Capture in progress, so don't touch pcdev->ready even if
+		 * empty. Since the transfer of the DMA programming register set
+		 * content to the DMA working register set is done automatically
+		 * by the DMA hardware, this can pretty well happen while we
+		 * are keeping the lock here. Levae fetching it from the queue
+		 * to be done when a next DMA interrupt occures instead.
+		 */
+		return;
+	}
+
+	WARN_ON(pcdev->ready);
+
+	buf = prepare_next_vb(pcdev);
+	if (WARN_ON(!buf))
+		return;
+
+	pcdev->active = buf;
+	pcdev->ready = NULL;
+
+	dev_dbg(icd->dev.parent,
+		"%s: capture not active, setup FIFO, start DMA\n", __func__);
+	mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK;
+	mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT;
+	CAM_WRITE(pcdev, MODE, mode | EN_FIFO_FULL | DMA);
+
+	if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+		/*
+		 * In SG mode, the above prepare_next_vb() didn't actually
+		 * put anything into the DMA programming register set,
+		 * so we have to do it now, before activating DMA.
+		 */
+		try_next_sgbuf(pcdev->dma_ch, buf);
+	}
+
+	start_capture(pcdev);
+}
+
+static void omap1_videobuf_release(struct videobuf_queue *vq,
+				 struct videobuf_buffer *vb)
+{
+	struct omap1_cam_buf *buf =
+			container_of(vb, struct omap1_cam_buf, vb);
+	struct soc_camera_device *icd = vq->priv_data;
+	struct device *dev = icd->dev.parent;
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+
+	switch (vb->state) {
+	case VIDEOBUF_DONE:
+		dev_dbg(dev, "%s (done)\n", __func__);
+		break;
+	case VIDEOBUF_ACTIVE:
+		dev_dbg(dev, "%s (active)\n", __func__);
+		break;
+	case VIDEOBUF_QUEUED:
+		dev_dbg(dev, "%s (queued)\n", __func__);
+		break;
+	case VIDEOBUF_PREPARED:
+		dev_dbg(dev, "%s (prepared)\n", __func__);
+		break;
+	default:
+		dev_dbg(dev, "%s (unknown %d)\n", __func__, vb->state);
+		break;
+	}
+
+	free_buffer(vq, buf, pcdev->vb_mode);
+}
+
+static void videobuf_done(struct omap1_cam_dev *pcdev,
+		enum videobuf_state result)
+{
+	struct omap1_cam_buf *buf = pcdev->active;
+	struct videobuf_buffer *vb;
+	struct device *dev = pcdev->icd->dev.parent;
+
+	if (WARN_ON(!buf)) {
+		suspend_capture(pcdev);
+		disable_capture(pcdev);
+		return;
+	}
+
+	if (result == VIDEOBUF_ERROR)
+		suspend_capture(pcdev);
+
+	vb = &buf->vb;
+	if (waitqueue_active(&vb->done)) {
+		if (!pcdev->ready && result != VIDEOBUF_ERROR) {
+			/*
+			 * No next buffer has been entered into the DMA
+			 * programming register set on time (could be done only
+			 * while the previous DMA interurpt was processed, not
+			 * later), so the last DMA block, be it a whole buffer
+			 * if in CONTIG or its last sgbuf if in SG mode, is
+			 * about to be reused by the just autoreinitialized DMA
+			 * engine, and overwritten with next frame data. Best we
+			 * can do is stopping the capture as soon as possible,
+			 * hopefully before the next frame start.
+			 */
+			suspend_capture(pcdev);
+		}
+		vb->state = result;
+		do_gettimeofday(&vb->ts);
+		if (result != VIDEOBUF_ERROR)
+			vb->field_count++;
+		wake_up(&vb->done);
+
+		/* shift in next buffer */
+		buf = pcdev->ready;
+		pcdev->active = buf;
+		pcdev->ready = NULL;
+
+		if (!buf) {
+			/*
+			 * No next buffer was ready on time (see above), so
+			 * indicate error condition to force capture restart or
+			 * stop, depending on next buffer already queued or not.
+			 */
+			result = VIDEOBUF_ERROR;
+			prepare_next_vb(pcdev);
+
+			buf = pcdev->ready;
+			pcdev->active = buf;
+			pcdev->ready = NULL;
+		}
+	} else if (pcdev->ready) {
+		/*
+		 * In both CONTIG and SG mode, the DMA engine has possibly
+		 * been already autoreinitialized with the preprogrammed
+		 * pcdev->ready buffer.  We can either accept this fact
+		 * and just swap the buffers, or provoke an error condition
+		 * and restart capture.  The former seems less intrusive.
+		 */
+		dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n",
+				__func__);
+		pcdev->active = pcdev->ready;
+
+		if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+			/*
+			 * In SG mode, we have to make sure that the buffer we
+			 * are putting back into the pcdev->ready is marked
+			 * fresh.
+			 */
+			buf->sgbuf = NULL;
+		}
+		pcdev->ready = buf;
+
+		buf = pcdev->active;
+	} else {
+		/*
+		 * No next buffer has been entered into
+		 * the DMA programming register set on time.
+		 */
+		if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+			/*
+			 * In CONTIG mode, the DMA engine has already been
+			 * reinitialized with the current buffer. Best we can do
+			 * is not touching it.
+			 */
+			dev_dbg(dev,
+				"%s: nobody waiting on videobuf, reuse it\n",
+				__func__);
+		} else {
+			/*
+			 * In SG mode, the DMA engine has just been
+			 * autoreinitialized with the last sgbuf from the
+			 * current list. Restart capture in order to transfer
+			 * next frame start into the first sgbuf, not the last
+			 * one.
+			 */
+			if (result != VIDEOBUF_ERROR) {
+				suspend_capture(pcdev);
+				result = VIDEOBUF_ERROR;
+			}
+		}
+	}
+
+	if (!buf) {
+		dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__);
+		disable_capture(pcdev);
+		return;
+	}
+
+	if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+		/*
+		 * In CONTIG mode, the current buffer parameters had already
+		 * been entered into the DMA programming register set while the
+		 * buffer was fetched with prepare_next_vb(), they may have also
+		 * been transfered into the runtime set and already active if
+		 * the DMA still running.
+		 */
+	} else {
+		/* In SG mode, extra steps are required */
+		if (result == VIDEOBUF_ERROR)
+			/* make sure we (re)use sglist from start on error */
+			buf->sgbuf = NULL;
+
+		/*
+		 * In any case, enter the next sgbuf parameters into the DMA
+		 * programming register set.  They will be used either during
+		 * nearest DMA autoreinitialization or, in case of an error,
+		 * on DMA startup below.
+		 */
+		try_next_sgbuf(pcdev->dma_ch, buf);
+	}
+
+	if (result == VIDEOBUF_ERROR) {
+		dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n",
+				__func__);
+		start_capture(pcdev);
+		/*
+		 * In SG mode, the above also resulted in the next sgbuf
+		 * parameters being entered into the DMA programming register
+		 * set, making them ready for next DMA autoreinitialization.
+		 */
+	}
+
+	/*
+	 * Finally, try fetching next buffer.
+	 * In CONTIG mode, it will also enter it into the DMA programming
+	 * register set, making it ready for next DMA autoreinitialization.
+	 */
+	prepare_next_vb(pcdev);
+}
+
+static void dma_isr(int channel, unsigned short status, void *data)
+{
+	struct omap1_cam_dev *pcdev = data;
+	struct omap1_cam_buf *buf = pcdev->active;
+	unsigned long flags;
+
+	spin_lock_irqsave(&pcdev->lock, flags);
+
+	if (WARN_ON(!buf)) {
+		suspend_capture(pcdev);
+		disable_capture(pcdev);
+		goto out;
+	}
+
+	if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+		/*
+		 * In CONTIG mode, assume we have just managed to collect the
+		 * whole frame, hopefully before our end of frame watchdog is
+		 * triggered. Then, all we have to do is disabling the watchdog
+		 * for this frame, and calling videobuf_done() with success
+		 * indicated.
+		 */
+		CAM_WRITE(pcdev, MODE,
+				CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN);
+		videobuf_done(pcdev, VIDEOBUF_DONE);
+	} else {
+		/*
+		 * In SG mode, we have to process every sgbuf from the current
+		 * sglist, one after another.
+		 */
+		if (buf->sgbuf) {
+			/*
+			 * Current sglist not completed yet, try fetching next
+			 * sgbuf, hopefully putting it into the DMA programming
+			 * register set, making it ready for next DMA
+			 * autoreinitialization.
+			 */
+			try_next_sgbuf(pcdev->dma_ch, buf);
+			if (buf->sgbuf)
+				goto out;
+
+			/*
+			 * No more sgbufs left in the current sglist. This
+			 * doesn't mean that the whole videobuffer is already
+			 * complete, but only that the last sgbuf from the
+			 * current sglist is about to be filled. It will be
+			 * ready on next DMA interrupt, signalled with the
+			 * buf->sgbuf set back to NULL.
+			 */
+			if (buf->result != VIDEOBUF_ERROR) {
+				/*
+				 * Video frame collected without errors so far,
+				 * we can prepare for collecting a next one
+				 * as soon as DMA gets autoreinitialized
+				 * after the current (last) sgbuf is completed.
+				 */
+				buf = prepare_next_vb(pcdev);
+				if (!buf)
+					goto out;
+
+				try_next_sgbuf(pcdev->dma_ch, buf);
+				goto out;
+			}
+		}
+		/* end of videobuf */
+		videobuf_done(pcdev, buf->result);
+	}
+
+out:
+	spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static irqreturn_t cam_isr(int irq, void *data)
+{
+	struct omap1_cam_dev *pcdev = data;
+	struct device *dev = pcdev->icd->dev.parent;
+	struct omap1_cam_buf *buf = pcdev->active;
+	u32 it_status;
+	unsigned long flags;
+
+	it_status = CAM_READ(pcdev, IT_STATUS);
+	if (!it_status)
+		return IRQ_NONE;
+
+	spin_lock_irqsave(&pcdev->lock, flags);
+
+	if (WARN_ON(!buf)) {
+		dev_warn(dev, "%s: unhandled camera interrupt, status == "
+				"%#x\n", __func__, it_status);
+		suspend_capture(pcdev);
+		disable_capture(pcdev);
+		goto out;
+	}
+
+	if (unlikely(it_status & FIFO_FULL)) {
+		dev_warn(dev, "%s: FIFO overflow\n", __func__);
+
+	} else if (it_status & V_DOWN) {
+		/* end of video frame watchdog */
+		if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+			/*
+			 * In CONTIG mode, the watchdog is disabled with
+			 * successful DMA end of block interrupt, and reenabled
+			 * on next frame start. If we get here, there is nothing
+			 * to check, we must be out of sync.
+			 */
+		} else {
+			if (buf->sgcount == 2) {
+				/*
+				 * If exactly 2 sgbufs from the next sglist have
+				 * been programmed into the DMA engine (the
+				 * frist one already transfered into the DMA
+				 * runtime register set, the second one still
+				 * in the programming set), then we are in sync.
+				 */
+				goto out;
+			}
+		}
+		dev_notice(dev, "%s: unexpected end of video frame\n",
+				__func__);
+
+	} else if (it_status & V_UP) {
+		u32 mode;
+
+		if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+			/*
+			 * In CONTIG mode, we need this interrupt every frame
+			 * in oredr to reenable our end of frame watchdog.
+			 */
+			mode = CAM_READ_CACHE(pcdev, MODE);
+		} else {
+			/*
+			 * In SG mode, the below enabled end of frame watchdog
+			 * is kept on permanently, so we can turn this one shot
+			 * setup off.
+			 */
+			mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_UP;
+		}
+
+		if (!(mode & EN_V_DOWN)) {
+			/* (re)enable end of frame watchdog interrupt */
+			mode |= EN_V_DOWN;
+		}
+		CAM_WRITE(pcdev, MODE, mode);
+		goto out;
+
+	} else {
+		dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
+				__func__, it_status);
+		goto out;
+	}
+
+	videobuf_done(pcdev, VIDEOBUF_ERROR);
+out:
+	spin_unlock_irqrestore(&pcdev->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static struct videobuf_queue_ops omap1_videobuf_ops = {
+	.buf_setup	= omap1_videobuf_setup,
+	.buf_prepare	= omap1_videobuf_prepare,
+	.buf_queue	= omap1_videobuf_queue,
+	.buf_release	= omap1_videobuf_release,
+};
+
+
+/*
+ * SOC Camera host operations
+ */
+
+static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset)
+{
+	/* apply/release camera sensor reset if requested by platform data */
+	if (pcdev->pflags & OMAP1_CAMERA_RST_HIGH)
+		CAM_WRITE(pcdev, GPIO, reset);
+	else if (pcdev->pflags & OMAP1_CAMERA_RST_LOW)
+		CAM_WRITE(pcdev, GPIO, !reset);
+}
+
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on OMAP1 camera sensor interface
+ */
+static int omap1_cam_add_device(struct soc_camera_device *icd)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	u32 ctrlclock;
+
+	if (pcdev->icd)
+		return -EBUSY;
+
+	clk_enable(pcdev->clk);
+
+	/* setup sensor clock */
+	ctrlclock = CAM_READ(pcdev, CTRLCLOCK);
+	ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN);
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+	ctrlclock &= ~FOSCMOD_MASK;
+	switch (pcdev->camexclk) {
+	case 6000000:
+		ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz;
+		break;
+	case 8000000:
+		ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN;
+		break;
+	case 9600000:
+		ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN;
+		break;
+	case 12000000:
+		ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz;
+		break;
+	case 24000000:
+		ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN;
+	default:
+		break;
+	}
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN);
+
+	/* enable internal clock */
+	ctrlclock |= MCLK_EN;
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+	sensor_reset(pcdev, false);
+
+	pcdev->icd = icd;
+
+	dev_dbg(icd->dev.parent, "OMAP1 Camera driver attached to camera %d\n",
+			icd->devnum);
+	return 0;
+}
+
+static void omap1_cam_remove_device(struct soc_camera_device *icd)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	u32 ctrlclock;
+
+	BUG_ON(icd != pcdev->icd);
+
+	suspend_capture(pcdev);
+	disable_capture(pcdev);
+
+	sensor_reset(pcdev, true);
+
+	/* disable and release system clocks */
+	ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+	ctrlclock &= ~(MCLK_EN | DPLL_EN | CAMEXCLK_EN);
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+	ctrlclock = (ctrlclock & ~FOSCMOD_MASK) | FOSCMOD_12MHz;
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | MCLK_EN);
+
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN);
+
+	clk_disable(pcdev->clk);
+
+	pcdev->icd = NULL;
+
+	dev_dbg(icd->dev.parent,
+		"OMAP1 Camera driver detached from camera %d\n", icd->devnum);
+}
+
+/* Duplicate standard formats based on host capability of byte swapping */
+static const struct soc_mbus_pixelfmt omap1_cam_formats[] = {
+	[V4L2_MBUS_FMT_UYVY8_2X8] = {
+		.fourcc			= V4L2_PIX_FMT_YUYV,
+		.name			= "YUYV",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+	[V4L2_MBUS_FMT_VYUY8_2X8] = {
+		.fourcc			= V4L2_PIX_FMT_YVYU,
+		.name			= "YVYU",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+	[V4L2_MBUS_FMT_YUYV8_2X8] = {
+		.fourcc			= V4L2_PIX_FMT_UYVY,
+		.name			= "UYVY",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+	[V4L2_MBUS_FMT_YVYU8_2X8] = {
+		.fourcc			= V4L2_PIX_FMT_VYUY,
+		.name			= "VYUY",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+	[V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE] = {
+		.fourcc			= V4L2_PIX_FMT_RGB555,
+		.name			= "RGB555",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+	[V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE] = {
+		.fourcc			= V4L2_PIX_FMT_RGB555X,
+		.name			= "RGB555X",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+	[V4L2_MBUS_FMT_RGB565_2X8_BE] = {
+		.fourcc			= V4L2_PIX_FMT_RGB565,
+		.name			= "RGB565",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+	[V4L2_MBUS_FMT_RGB565_2X8_LE] = {
+		.fourcc			= V4L2_PIX_FMT_RGB565X,
+		.name			= "RGB565X",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
+		.order			= SOC_MBUS_ORDER_BE,
+	},
+};
+
+static int omap1_cam_get_formats(struct soc_camera_device *icd,
+		unsigned int idx, struct soc_camera_format_xlate *xlate)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	struct device *dev = icd->dev.parent;
+	int formats = 0, ret;
+	enum v4l2_mbus_pixelcode code;
+	const struct soc_mbus_pixelfmt *fmt;
+
+	ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+	if (ret < 0)
+		/* No more formats */
+		return 0;
+
+	fmt = soc_mbus_get_fmtdesc(code);
+	if (!fmt) {
+		dev_err(dev, "%s: invalid format code #%d: %d\n", __func__,
+				idx, code);
+		return 0;
+	}
+
+	/* Check support for the requested bits-per-sample */
+	if (fmt->bits_per_sample != 8)
+		return 0;
+
+	switch (code) {
+	case V4L2_MBUS_FMT_YUYV8_2X8:
+	case V4L2_MBUS_FMT_YVYU8_2X8:
+	case V4L2_MBUS_FMT_UYVY8_2X8:
+	case V4L2_MBUS_FMT_VYUY8_2X8:
+	case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
+	case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+	case V4L2_MBUS_FMT_RGB565_2X8_BE:
+	case V4L2_MBUS_FMT_RGB565_2X8_LE:
+		formats++;
+		if (xlate) {
+			xlate->host_fmt	= &omap1_cam_formats[code];
+			xlate->code	= code;
+			xlate++;
+			dev_dbg(dev, "%s: providing format %s "
+					"as byte swapped code #%d\n", __func__,
+					omap1_cam_formats[code].name, code);
+		}
+	default:
+		if (xlate)
+			dev_dbg(dev, "%s: providing format %s "
+					"in pass-through mode\n", __func__,
+					fmt->name);
+	}
+	formats++;
+	if (xlate) {
+		xlate->host_fmt	= fmt;
+		xlate->code	= code;
+		xlate++;
+	}
+
+	return formats;
+}
+
+static bool is_dma_aligned(s32 bytes_per_line, unsigned int height,
+		enum omap1_cam_vb_mode vb_mode)
+{
+	int size = bytes_per_line * height;
+
+	return IS_ALIGNED(bytes_per_line, DMA_ELEMENT_SIZE) &&
+		IS_ALIGNED(size, DMA_FRAME_SIZE(vb_mode) * DMA_ELEMENT_SIZE);
+}
+
+static int dma_align(int *width, int *height,
+		const struct soc_mbus_pixelfmt *fmt,
+		enum omap1_cam_vb_mode vb_mode, bool enlarge)
+{
+	s32 bytes_per_line = soc_mbus_bytes_per_line(*width, fmt);
+
+	if (bytes_per_line < 0)
+		return bytes_per_line;
+
+	if (!is_dma_aligned(bytes_per_line, *height, vb_mode)) {
+		unsigned int pxalign = __fls(bytes_per_line / *width);
+		unsigned int salign  = DMA_FRAME_SHIFT(vb_mode) +
+				DMA_ELEMENT_SHIFT - pxalign;
+		unsigned int incr    = enlarge << salign;
+
+		v4l_bound_align_image(width, 1, *width + incr, 0,
+				height, 1, *height + incr, 0, salign);
+		return 0;
+	}
+	return 1;
+}
+
+#define subdev_call_with_sense(pcdev, dev, icd, sd, function, args...)	   \
+({									   \
+	struct soc_camera_sense sense = {				   \
+		.master_clock		= pcdev->camexclk,		   \
+		.pixel_clock_max	= 0,				   \
+	};								   \
+	int __ret;							   \
+									   \
+	if (pcdev->pdata)						   \
+		sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \
+	icd->sense = &sense;						   \
+	__ret = v4l2_subdev_call(sd, video, function, ##args);		   \
+	icd->sense = NULL;						   \
+									   \
+	if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {			   \
+		if (sense.pixel_clock > sense.pixel_clock_max) {	   \
+			dev_err(dev, "%s: pixel clock %lu "		   \
+					"set by the camera too high!\n",   \
+					__func__, sense.pixel_clock);	   \
+			__ret = -EINVAL;				   \
+		}							   \
+	}								   \
+	__ret;								   \
+})
+
+static int set_mbus_format(struct omap1_cam_dev *pcdev, struct device *dev,
+		struct soc_camera_device *icd, struct v4l2_subdev *sd,
+		struct v4l2_mbus_framefmt *mf,
+		const struct soc_camera_format_xlate *xlate)
+{
+	s32 bytes_per_line;
+	int ret = subdev_call_with_sense(pcdev, dev, icd, sd, s_mbus_fmt, mf);
+
+	if (ret < 0) {
+		dev_err(dev, "%s: s_mbus_fmt failed\n", __func__);
+		return ret;
+	}
+
+	if (mf->code != xlate->code) {
+		dev_err(dev, "%s: unexpected pixel code change\n", __func__);
+		return -EINVAL;
+	}
+
+	bytes_per_line = soc_mbus_bytes_per_line(mf->width, xlate->host_fmt);
+	if (bytes_per_line < 0) {
+		dev_err(dev, "%s: soc_mbus_bytes_per_line() failed\n",
+				__func__);
+		return bytes_per_line;
+	}
+
+	if (!is_dma_aligned(bytes_per_line, mf->height, pcdev->vb_mode)) {
+		dev_err(dev, "%s: resulting geometry %ux%u not DMA aligned\n",
+				__func__, mf->width, mf->height);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int omap1_cam_set_crop(struct soc_camera_device *icd,
+			       struct v4l2_crop *crop)
+{
+	struct v4l2_rect *rect = &crop->c;
+	const struct soc_camera_format_xlate *xlate = icd->current_fmt;
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	struct device *dev = icd->dev.parent;
+	struct v4l2_mbus_framefmt mf;
+	int ret;
+
+	ret = subdev_call_with_sense(pcdev, dev, icd, sd, s_crop, crop);
+	if (ret < 0) {
+		dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
+			 rect->width, rect->height, rect->left, rect->top);
+		return ret;
+	}
+
+	ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+	if (ret < 0) {
+		dev_warn(dev, "%s: failed to fetch current format\n", __func__);
+		return ret;
+	}
+
+	ret = dma_align(&mf.width, &mf.height, xlate->host_fmt, pcdev->vb_mode,
+			false);
+	if (ret < 0) {
+		dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
+				__func__, mf.width, mf.height,
+				xlate->host_fmt->name);
+		return ret;
+	}
+
+	if (!ret) {
+		/* sensor returned geometry not DMA aligned, trying to fix */
+		ret = set_mbus_format(pcdev, dev, icd, sd, &mf, xlate);
+		if (ret < 0) {
+			dev_err(dev, "%s: failed to set format\n", __func__);
+			return ret;
+		}
+	}
+
+	icd->user_width	 = mf.width;
+	icd->user_height = mf.height;
+
+	return 0;
+}
+
+static int omap1_cam_set_fmt(struct soc_camera_device *icd,
+			      struct v4l2_format *f)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	const struct soc_camera_format_xlate *xlate;
+	struct device *dev = icd->dev.parent;
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+	struct v4l2_mbus_framefmt mf;
+	int ret;
+
+	xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+	if (!xlate) {
+		dev_warn(dev, "%s: format %#x not found\n", __func__,
+				pix->pixelformat);
+		return -EINVAL;
+	}
+
+	mf.width	= pix->width;
+	mf.height	= pix->height;
+	mf.field	= pix->field;
+	mf.colorspace	= pix->colorspace;
+	mf.code		= xlate->code;
+
+	ret = dma_align(&mf.width, &mf.height, xlate->host_fmt, pcdev->vb_mode,
+			true);
+	if (ret < 0) {
+		dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
+				__func__, pix->width, pix->height,
+				xlate->host_fmt->name);
+		return ret;
+	}
+
+	ret = set_mbus_format(pcdev, dev, icd, sd, &mf, xlate);
+	if (ret < 0) {
+		dev_err(dev, "%s: failed to set format\n", __func__);
+		return ret;
+	}
+
+	pix->width	 = mf.width;
+	pix->height	 = mf.height;
+	pix->field	 = mf.field;
+	pix->colorspace  = mf.colorspace;
+	icd->current_fmt = xlate;
+
+	return 0;
+}
+
+static int omap1_cam_try_fmt(struct soc_camera_device *icd,
+			      struct v4l2_format *f)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	const struct soc_camera_format_xlate *xlate;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+	struct v4l2_mbus_framefmt mf;
+	int ret;
+	/* TODO: limit to mx1 hardware capabilities */
+
+	xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+	if (!xlate) {
+		dev_warn(icd->dev.parent, "Format %#x not found\n",
+			 pix->pixelformat);
+		return -EINVAL;
+	}
+
+	mf.width	= pix->width;
+	mf.height	= pix->height;
+	mf.field	= pix->field;
+	mf.colorspace	= pix->colorspace;
+	mf.code		= xlate->code;
+
+	/* limit to sensor capabilities */
+	ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+	if (ret < 0)
+		return ret;
+
+	pix->width	= mf.width;
+	pix->height	= mf.height;
+	pix->field	= mf.field;
+	pix->colorspace	= mf.colorspace;
+
+	return 0;
+}
+
+static bool sg_mode;
+
+/*
+ * Local mmap_mapper wrapper,
+ * used for detecting videobuf-dma-contig buffer allocation failures
+ * and switching to videobuf-dma-sg automatically for future attempts.
+ */
+static int omap1_cam_mmap_mapper(struct videobuf_queue *q,
+				  struct videobuf_buffer *buf,
+				  struct vm_area_struct *vma)
+{
+	struct soc_camera_device *icd = q->priv_data;
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	int ret;
+
+	ret = pcdev->mmap_mapper(q, buf, vma);
+
+	if (ret == -ENOMEM)
+		sg_mode = true;
+
+	return ret;
+}
+
+static void omap1_cam_init_videobuf(struct videobuf_queue *q,
+				     struct soc_camera_device *icd)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+
+	if (!sg_mode)
+		videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops,
+				icd->dev.parent, &pcdev->lock,
+				V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+				sizeof(struct omap1_cam_buf), icd);
+	else
+		videobuf_queue_sg_init(q, &omap1_videobuf_ops,
+				icd->dev.parent, &pcdev->lock,
+				V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+				sizeof(struct omap1_cam_buf), icd);
+
+	/* use videobuf mode (auto)selected with the module parameter */
+	pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG;
+
+	/*
+	 * Ensure we substitute the videobuf-dma-contig version of the
+	 * mmap_mapper() callback with our own wrapper, used for switching
+	 * automatically to videobuf-dma-sg on buffer allocation failure.
+	 */
+	if (!sg_mode && q->int_ops->mmap_mapper != omap1_cam_mmap_mapper) {
+		pcdev->mmap_mapper = q->int_ops->mmap_mapper;
+		q->int_ops->mmap_mapper = omap1_cam_mmap_mapper;
+	}
+}
+
+static int omap1_cam_reqbufs(struct soc_camera_file *icf,
+			      struct v4l2_requestbuffers *p)
+{
+	int i;
+
+	/*
+	 * This is for locking debugging only. I removed spinlocks and now I
+	 * check whether .prepare is ever called on a linked buffer, or whether
+	 * a dma IRQ can occur for an in-work or unlinked buffer. Until now
+	 * it hadn't triggered
+	 */
+	for (i = 0; i < p->count; i++) {
+		struct omap1_cam_buf *buf = container_of(icf->vb_vidq.bufs[i],
+						      struct omap1_cam_buf, vb);
+		buf->inwork = 0;
+		INIT_LIST_HEAD(&buf->vb.queue);
+	}
+
+	return 0;
+}
+
+static int omap1_cam_querycap(struct soc_camera_host *ici,
+			       struct v4l2_capability *cap)
+{
+	/* cap->name is set by the friendly caller:-> */
+	strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card));
+	cap->version = VERSION_CODE;
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+	return 0;
+}
+
+static int omap1_cam_set_bus_param(struct soc_camera_device *icd,
+		__u32 pixfmt)
+{
+	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+	struct omap1_cam_dev *pcdev = ici->priv;
+	struct device *dev = icd->dev.parent;
+	const struct soc_camera_format_xlate *xlate;
+	const struct soc_mbus_pixelfmt *fmt;
+	unsigned long camera_flags, common_flags;
+	u32 ctrlclock, mode;
+	int ret;
+
+	camera_flags = icd->ops->query_bus_param(icd);
+
+	common_flags = soc_camera_bus_param_compatible(camera_flags,
+			SOCAM_BUS_FLAGS);
+	if (!common_flags)
+		return -EINVAL;
+
+	/* Make choices, possibly based on platform configuration */
+	if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
+			(common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+		if (!pcdev->pdata ||
+				pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
+			common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+		else
+			common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+	}
+
+	ret = icd->ops->set_bus_param(icd, common_flags);
+	if (ret < 0)
+		return ret;
+
+	ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+	if (ctrlclock & LCLK_EN)
+		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+
+	if (common_flags & SOCAM_PCLK_SAMPLE_RISING) {
+		dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
+		ctrlclock |= POLCLK;
+	} else {
+		dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n");
+		ctrlclock &= ~POLCLK;
+	}
+	CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+
+	if (ctrlclock & LCLK_EN)
+		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+	/* select bus endianess */
+	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+	fmt = xlate->host_fmt;
+
+	mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA);
+	if (fmt->order == SOC_MBUS_ORDER_LE) {
+		dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n");
+		CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD);
+	} else {
+		dev_dbg(dev, "MODE_REG |= ORDERCAMD\n");
+		CAM_WRITE(pcdev, MODE, mode | ORDERCAMD);
+	}
+
+	return 0;
+}
+
+static unsigned int omap1_cam_poll(struct file *file, poll_table *pt)
+{
+	struct soc_camera_file *icf = file->private_data;
+	struct omap1_cam_buf *buf;
+
+	buf = list_entry(icf->vb_vidq.stream.next, struct omap1_cam_buf,
+			 vb.stream);
+
+	poll_wait(file, &buf->vb.done, pt);
+
+	if (buf->vb.state == VIDEOBUF_DONE ||
+	    buf->vb.state == VIDEOBUF_ERROR)
+		return POLLIN | POLLRDNORM;
+
+	return 0;
+}
+
+static struct soc_camera_host_ops omap1_host_ops = {
+	.owner		= THIS_MODULE,
+	.add		= omap1_cam_add_device,
+	.remove		= omap1_cam_remove_device,
+	.get_formats	= omap1_cam_get_formats,
+	.set_crop	= omap1_cam_set_crop,
+	.set_fmt	= omap1_cam_set_fmt,
+	.try_fmt	= omap1_cam_try_fmt,
+	.init_videobuf	= omap1_cam_init_videobuf,
+	.reqbufs	= omap1_cam_reqbufs,
+	.querycap	= omap1_cam_querycap,
+	.set_bus_param	= omap1_cam_set_bus_param,
+	.poll		= omap1_cam_poll,
+};
+
+static int __init omap1_cam_probe(struct platform_device *pdev)
+{
+	struct omap1_cam_dev *pcdev;
+	struct resource *res;
+	struct clk *clk;
+	void __iomem *base;
+	unsigned int irq;
+	int err = 0;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_get_irq(pdev, 0);
+	if (!res || (int)irq <= 0) {
+		err = -ENODEV;
+		goto exit;
+	}
+
+	clk = clk_get(&pdev->dev, "armper_ck");
+	if (IS_ERR(clk)) {
+		err = PTR_ERR(clk);
+		goto exit;
+	}
+
+	pcdev = kzalloc(sizeof(*pcdev) + resource_size(res), GFP_KERNEL);
+	if (!pcdev) {
+		dev_err(&pdev->dev, "Could not allocate pcdev\n");
+		err = -ENOMEM;
+		goto exit_put_clk;
+	}
+
+	pcdev->res = res;
+	pcdev->clk = clk;
+
+	pcdev->pdata = pdev->dev.platform_data;
+	pcdev->pflags = pcdev->pdata->flags;
+
+	if (pcdev->pdata)
+		pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000;
+
+	switch (pcdev->camexclk) {
+	case 6000000:
+	case 8000000:
+	case 9600000:
+	case 12000000:
+	case 24000000:
+		break;
+	default:
+		dev_warn(&pdev->dev,
+				"Incorrect sensor clock frequency %ld kHz, "
+				"should be one of 0, 6, 8, 9.6, 12 or 24 MHz, "
+				"please correct your platform data\n",
+				pcdev->pdata->camexclk_khz);
+		pcdev->camexclk = 0;
+	case 0:
+		dev_info(&pdev->dev,
+				"Not providing sensor clock\n");
+	}
+
+	INIT_LIST_HEAD(&pcdev->capture);
+	spin_lock_init(&pcdev->lock);
+
+	/*
+	 * Request the region.
+	 */
+	if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) {
+		err = -EBUSY;
+		goto exit_kfree;
+	}
+
+	base = ioremap(res->start, resource_size(res));
+	if (!base) {
+		err = -ENOMEM;
+		goto exit_release;
+	}
+	pcdev->irq = irq;
+	pcdev->base = base;
+
+	sensor_reset(pcdev, true);
+
+	err = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, DRIVER_NAME,
+			dma_isr, (void *)pcdev, &pcdev->dma_ch);
+	if (err < 0) {
+		dev_err(&pdev->dev, "Can't request DMA for OMAP1 Camera\n");
+		err = -EBUSY;
+		goto exit_iounmap;
+	}
+	dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_ch);
+
+	/* preconfigure DMA */
+	omap_set_dma_src_params(pcdev->dma_ch, OMAP_DMA_PORT_TIPB,
+			OMAP_DMA_AMODE_CONSTANT, res->start + REG_CAMDATA,
+			0, 0);
+	omap_set_dma_dest_burst_mode(pcdev->dma_ch, OMAP_DMA_DATA_BURST_4);
+	/* setup DMA autoinitialization */
+	omap_dma_link_lch(pcdev->dma_ch, pcdev->dma_ch);
+
+	err = request_irq(pcdev->irq, cam_isr, 0, DRIVER_NAME, pcdev);
+	if (err) {
+		dev_err(&pdev->dev, "Camera interrupt register failed\n");
+		goto exit_free_dma;
+	}
+
+	pcdev->soc_host.drv_name	= DRIVER_NAME;
+	pcdev->soc_host.ops		= &omap1_host_ops;
+	pcdev->soc_host.priv		= pcdev;
+	pcdev->soc_host.v4l2_dev.dev	= &pdev->dev;
+	pcdev->soc_host.nr		= pdev->id;
+
+	err = soc_camera_host_register(&pcdev->soc_host);
+	if (err)
+		goto exit_free_irq;
+
+	dev_info(&pdev->dev, "OMAP1 Camera Interface driver loaded\n");
+
+	return 0;
+
+exit_free_irq:
+	free_irq(pcdev->irq, pcdev);
+exit_free_dma:
+	omap_free_dma(pcdev->dma_ch);
+exit_iounmap:
+	iounmap(base);
+exit_release:
+	release_mem_region(res->start, resource_size(res));
+exit_kfree:
+	kfree(pcdev);
+exit_put_clk:
+	clk_put(clk);
+exit:
+	return err;
+}
+
+static int __exit omap1_cam_remove(struct platform_device *pdev)
+{
+	struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+	struct omap1_cam_dev *pcdev = container_of(soc_host,
+					struct omap1_cam_dev, soc_host);
+	struct resource *res;
+
+	free_irq(pcdev->irq, pcdev);
+
+	omap_free_dma(pcdev->dma_ch);
+
+	soc_camera_host_unregister(soc_host);
+
+	iounmap(pcdev->base);
+
+	res = pcdev->res;
+	release_mem_region(res->start, resource_size(res));
+
+	kfree(pcdev);
+
+	clk_put(pcdev->clk);
+
+	dev_info(&pdev->dev, "OMAP1 Camera Interface driver unloaded\n");
+
+	return 0;
+}
+
+static struct platform_driver omap1_cam_driver = {
+	.driver		= {
+		.name	= DRIVER_NAME,
+	},
+	.probe		= omap1_cam_probe,
+	.remove		= __exit_p(omap1_cam_remove),
+};
+
+static int __init omap1_cam_init(void)
+{
+	return platform_driver_register(&omap1_cam_driver);
+}
+module_init(omap1_cam_init);
+
+static void __exit omap1_cam_exit(void)
+{
+	platform_driver_unregister(&omap1_cam_driver);
+}
+module_exit(omap1_cam_exit);
+
+module_param(sg_mode, bool, 0644);
+MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg");
+
+MODULE_DESCRIPTION("OMAP1 Camera Interface driver");
+MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c
index 926a5aa..378b094 100644
--- a/drivers/media/video/omap24xxcam.c
+++ b/drivers/media/video/omap24xxcam.c
@@ -420,7 +420,7 @@
 	struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
 
 	/* wait for buffer, especially to get out of the sgdma queue */
-	videobuf_waiton(vb, 0, 0);
+	videobuf_waiton(vbq, vb, 0, 0);
 	if (vb->memory == V4L2_MEMORY_MMAP) {
 		dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen,
 			     dma->direction);
@@ -1491,7 +1491,7 @@
 	videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL,
 				&fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
 				V4L2_FIELD_NONE,
-				sizeof(struct videobuf_buffer), fh);
+				sizeof(struct videobuf_buffer), fh, NULL);
 
 	return 0;
 
diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c
new file mode 100644
index 0000000..b7cfeab
--- /dev/null
+++ b/drivers/media/video/ov6650.c
@@ -0,0 +1,1225 @@
+/*
+ * V4L2 SoC Camera driver for OmniVision OV6650 Camera Sensor
+ *
+ * Copyright (C) 2010 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Based on OmniVision OV96xx Camera Driver
+ * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * Based on ov772x camera driver:
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov7670 and soc_camera_platform driver,
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * Hardware specific bits initialy based on former work by Matt Callow
+ * drivers/media/video/omap/sensor_ov6650.c
+ * Copyright (C) 2006 Matt Callow
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+
+
+/* Register definitions */
+#define REG_GAIN		0x00	/* range 00 - 3F */
+#define REG_BLUE		0x01
+#define REG_RED			0x02
+#define REG_SAT			0x03	/* [7:4] saturation [0:3] reserved */
+#define REG_HUE			0x04	/* [7:6] rsrvd [5] hue en [4:0] hue */
+
+#define REG_BRT			0x06
+
+#define REG_PIDH		0x0a
+#define REG_PIDL		0x0b
+
+#define REG_AECH		0x10
+#define REG_CLKRC		0x11	/* Data Format and Internal Clock */
+					/* [7:6] Input system clock (MHz)*/
+					/*   00=8, 01=12, 10=16, 11=24 */
+					/* [5:0]: Internal Clock Pre-Scaler */
+#define REG_COMA		0x12	/* [7] Reset */
+#define REG_COMB		0x13
+#define REG_COMC		0x14
+#define REG_COMD		0x15
+#define REG_COML		0x16
+#define REG_HSTRT		0x17
+#define REG_HSTOP		0x18
+#define REG_VSTRT		0x19
+#define REG_VSTOP		0x1a
+#define REG_PSHFT		0x1b
+#define REG_MIDH		0x1c
+#define REG_MIDL		0x1d
+#define REG_HSYNS		0x1e
+#define REG_HSYNE		0x1f
+#define REG_COME		0x20
+#define REG_YOFF		0x21
+#define REG_UOFF		0x22
+#define REG_VOFF		0x23
+#define REG_AEW			0x24
+#define REG_AEB			0x25
+#define REG_COMF		0x26
+#define REG_COMG		0x27
+#define REG_COMH		0x28
+#define REG_COMI		0x29
+
+#define REG_FRARL		0x2b
+#define REG_COMJ		0x2c
+#define REG_COMK		0x2d
+#define REG_AVGY		0x2e
+#define REG_REF0		0x2f
+#define REG_REF1		0x30
+#define REG_REF2		0x31
+#define REG_FRAJH		0x32
+#define REG_FRAJL		0x33
+#define REG_FACT		0x34
+#define REG_L1AEC		0x35
+#define REG_AVGU		0x36
+#define REG_AVGV		0x37
+
+#define REG_SPCB		0x60
+#define REG_SPCC		0x61
+#define REG_GAM1		0x62
+#define REG_GAM2		0x63
+#define REG_GAM3		0x64
+#define REG_SPCD		0x65
+
+#define REG_SPCE		0x68
+#define REG_ADCL		0x69
+
+#define REG_RMCO		0x6c
+#define REG_GMCO		0x6d
+#define REG_BMCO		0x6e
+
+
+/* Register bits, values, etc. */
+#define OV6650_PIDH		0x66	/* high byte of product ID number */
+#define OV6650_PIDL		0x50	/* low byte of product ID number */
+#define OV6650_MIDH		0x7F	/* high byte of mfg ID */
+#define OV6650_MIDL		0xA2	/* low byte of mfg ID */
+
+#define DEF_GAIN		0x00
+#define DEF_BLUE		0x80
+#define DEF_RED			0x80
+
+#define SAT_SHIFT		4
+#define SAT_MASK		(0xf << SAT_SHIFT)
+#define SET_SAT(x)		(((x) << SAT_SHIFT) & SAT_MASK)
+
+#define HUE_EN			BIT(5)
+#define HUE_MASK		0x1f
+#define DEF_HUE			0x10
+#define SET_HUE(x)		(HUE_EN | ((x) & HUE_MASK))
+
+#define DEF_AECH		0x4D
+
+#define CLKRC_6MHz		0x00
+#define CLKRC_12MHz		0x40
+#define CLKRC_16MHz		0x80
+#define CLKRC_24MHz		0xc0
+#define CLKRC_DIV_MASK		0x3f
+#define GET_CLKRC_DIV(x)	(((x) & CLKRC_DIV_MASK) + 1)
+
+#define COMA_RESET		BIT(7)
+#define COMA_QCIF		BIT(5)
+#define COMA_RAW_RGB		BIT(4)
+#define COMA_RGB		BIT(3)
+#define COMA_BW			BIT(2)
+#define COMA_WORD_SWAP		BIT(1)
+#define COMA_BYTE_SWAP		BIT(0)
+#define DEF_COMA		0x00
+
+#define COMB_FLIP_V		BIT(7)
+#define COMB_FLIP_H		BIT(5)
+#define COMB_BAND_FILTER	BIT(4)
+#define COMB_AWB		BIT(2)
+#define COMB_AGC		BIT(1)
+#define COMB_AEC		BIT(0)
+#define DEF_COMB		0x5f
+
+#define COML_ONE_CHANNEL	BIT(7)
+
+#define DEF_HSTRT		0x24
+#define DEF_HSTOP		0xd4
+#define DEF_VSTRT		0x04
+#define DEF_VSTOP		0x94
+
+#define COMF_HREF_LOW		BIT(4)
+
+#define COMJ_PCLK_RISING	BIT(4)
+#define COMJ_VSYNC_HIGH		BIT(0)
+
+/* supported resolutions */
+#define W_QCIF			(DEF_HSTOP - DEF_HSTRT)
+#define W_CIF			(W_QCIF << 1)
+#define H_QCIF			(DEF_VSTOP - DEF_VSTRT)
+#define H_CIF			(H_QCIF << 1)
+
+#define FRAME_RATE_MAX		30
+
+
+struct ov6650_reg {
+	u8	reg;
+	u8	val;
+};
+
+struct ov6650 {
+	struct v4l2_subdev	subdev;
+
+	int			gain;
+	int			blue;
+	int			red;
+	int			saturation;
+	int			hue;
+	int			brightness;
+	int			exposure;
+	int			gamma;
+	int			aec;
+	bool			vflip;
+	bool			hflip;
+	bool			awb;
+	bool			agc;
+	bool			half_scale;	/* scale down output by 2 */
+	struct v4l2_rect	rect;		/* sensor cropping window */
+	unsigned long		pclk_limit;	/* from host */
+	unsigned long		pclk_max;	/* from resolution and format */
+	struct v4l2_fract	tpf;		/* as requested with s_parm */
+	enum v4l2_mbus_pixelcode code;
+	enum v4l2_colorspace	colorspace;
+};
+
+
+static enum v4l2_mbus_pixelcode ov6650_codes[] = {
+	V4L2_MBUS_FMT_YUYV8_2X8,
+	V4L2_MBUS_FMT_UYVY8_2X8,
+	V4L2_MBUS_FMT_YVYU8_2X8,
+	V4L2_MBUS_FMT_VYUY8_2X8,
+	V4L2_MBUS_FMT_SBGGR8_1X8,
+	V4L2_MBUS_FMT_GREY8_1X8,
+};
+
+static const struct v4l2_queryctrl ov6650_controls[] = {
+	{
+		.id		= V4L2_CID_AUTOGAIN,
+		.type		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name		= "AGC",
+		.minimum	= 0,
+		.maximum	= 1,
+		.step		= 1,
+		.default_value	= 1,
+	},
+	{
+		.id		= V4L2_CID_GAIN,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Gain",
+		.minimum	= 0,
+		.maximum	= 0x3f,
+		.step		= 1,
+		.default_value	= DEF_GAIN,
+	},
+	{
+		.id		= V4L2_CID_AUTO_WHITE_BALANCE,
+		.type		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name		= "AWB",
+		.minimum	= 0,
+		.maximum	= 1,
+		.step		= 1,
+		.default_value	= 1,
+	},
+	{
+		.id		= V4L2_CID_BLUE_BALANCE,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Blue",
+		.minimum	= 0,
+		.maximum	= 0xff,
+		.step		= 1,
+		.default_value	= DEF_BLUE,
+	},
+	{
+		.id		= V4L2_CID_RED_BALANCE,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Red",
+		.minimum	= 0,
+		.maximum	= 0xff,
+		.step		= 1,
+		.default_value	= DEF_RED,
+	},
+	{
+		.id		= V4L2_CID_SATURATION,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Saturation",
+		.minimum	= 0,
+		.maximum	= 0xf,
+		.step		= 1,
+		.default_value	= 0x8,
+	},
+	{
+		.id		= V4L2_CID_HUE,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Hue",
+		.minimum	= 0,
+		.maximum	= HUE_MASK,
+		.step		= 1,
+		.default_value	= DEF_HUE,
+	},
+	{
+		.id		= V4L2_CID_BRIGHTNESS,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Brightness",
+		.minimum	= 0,
+		.maximum	= 0xff,
+		.step		= 1,
+		.default_value	= 0x80,
+	},
+	{
+		.id		= V4L2_CID_EXPOSURE_AUTO,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "AEC",
+		.minimum	= 0,
+		.maximum	= 3,
+		.step		= 1,
+		.default_value	= 0,
+	},
+	{
+		.id		= V4L2_CID_EXPOSURE,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Exposure",
+		.minimum	= 0,
+		.maximum	= 0xff,
+		.step		= 1,
+		.default_value	= DEF_AECH,
+	},
+	{
+		.id		= V4L2_CID_GAMMA,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Gamma",
+		.minimum	= 0,
+		.maximum	= 0xff,
+		.step		= 1,
+		.default_value	= 0x12,
+	},
+	{
+		.id		= V4L2_CID_VFLIP,
+		.type		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name		= "Flip Vertically",
+		.minimum	= 0,
+		.maximum	= 1,
+		.step		= 1,
+		.default_value	= 0,
+	},
+	{
+		.id		= V4L2_CID_HFLIP,
+		.type		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name		= "Flip Horizontally",
+		.minimum	= 0,
+		.maximum	= 1,
+		.step		= 1,
+		.default_value	= 0,
+	},
+};
+
+/* read a register */
+static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+	int ret;
+	u8 data = reg;
+	struct i2c_msg msg = {
+		.addr	= client->addr,
+		.flags	= 0,
+		.len	= 1,
+		.buf	= &data,
+	};
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		goto err;
+
+	msg.flags = I2C_M_RD;
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		goto err;
+
+	*val = data;
+	return 0;
+
+err:
+	dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
+	return ret;
+}
+
+/* write a register */
+static int ov6650_reg_write(struct i2c_client *client, u8 reg, u8 val)
+{
+	int ret;
+	unsigned char data[2] = { reg, val };
+	struct i2c_msg msg = {
+		.addr	= client->addr,
+		.flags	= 0,
+		.len	= 2,
+		.buf	= data,
+	};
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	udelay(100);
+
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
+		return ret;
+	}
+	return 0;
+}
+
+
+/* Read a register, alter its bits, write it back */
+static int ov6650_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 mask)
+{
+	u8 val;
+	int ret;
+
+	ret = ov6650_reg_read(client, reg, &val);
+	if (ret) {
+		dev_err(&client->dev,
+			"[Read]-Modify-Write of register 0x%02x failed!\n",
+			reg);
+		return ret;
+	}
+
+	val &= ~mask;
+	val |= set;
+
+	ret = ov6650_reg_write(client, reg, val);
+	if (ret)
+		dev_err(&client->dev,
+			"Read-Modify-[Write] of register 0x%02x failed!\n",
+			reg);
+
+	return ret;
+}
+
+static struct ov6650 *to_ov6650(const struct i2c_client *client)
+{
+	return container_of(i2c_get_clientdata(client), struct ov6650, subdev);
+}
+
+/* Start/Stop streaming from the device */
+static int ov6650_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	return 0;
+}
+
+/* Alter bus settings on camera side */
+static int ov6650_set_bus_param(struct soc_camera_device *icd,
+				unsigned long flags)
+{
+	struct soc_camera_link *icl = to_soc_camera_link(icd);
+	struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
+	int ret;
+
+	flags = soc_camera_apply_sensor_flags(icl, flags);
+
+	if (flags & SOCAM_PCLK_SAMPLE_RISING)
+		ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
+	else
+		ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
+	if (ret)
+		return ret;
+
+	if (flags & SOCAM_HSYNC_ACTIVE_LOW)
+		ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
+	else
+		ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
+	if (ret)
+		return ret;
+
+	if (flags & SOCAM_VSYNC_ACTIVE_HIGH)
+		ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
+	else
+		ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
+
+	return ret;
+}
+
+/* Request bus settings on camera side */
+static unsigned long ov6650_query_bus_param(struct soc_camera_device *icd)
+{
+	struct soc_camera_link *icl = to_soc_camera_link(icd);
+
+	unsigned long flags = SOCAM_MASTER |
+		SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
+		SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW |
+		SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW |
+		SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
+
+	return soc_camera_apply_sensor_flags(icl, flags);
+}
+
+/* Get status of additional camera capabilities */
+static int ov6650_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov6650 *priv = to_ov6650(client);
+	uint8_t reg;
+	int ret = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTOGAIN:
+		ctrl->value = priv->agc;
+		break;
+	case V4L2_CID_GAIN:
+		if (priv->agc) {
+			ret = ov6650_reg_read(client, REG_GAIN, &reg);
+			ctrl->value = reg;
+		} else {
+			ctrl->value = priv->gain;
+		}
+		break;
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ctrl->value = priv->awb;
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		if (priv->awb) {
+			ret = ov6650_reg_read(client, REG_BLUE, &reg);
+			ctrl->value = reg;
+		} else {
+			ctrl->value = priv->blue;
+		}
+		break;
+	case V4L2_CID_RED_BALANCE:
+		if (priv->awb) {
+			ret = ov6650_reg_read(client, REG_RED, &reg);
+			ctrl->value = reg;
+		} else {
+			ctrl->value = priv->red;
+		}
+		break;
+	case V4L2_CID_SATURATION:
+		ctrl->value = priv->saturation;
+		break;
+	case V4L2_CID_HUE:
+		ctrl->value = priv->hue;
+		break;
+	case V4L2_CID_BRIGHTNESS:
+		ctrl->value = priv->brightness;
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+		ctrl->value = priv->aec;
+		break;
+	case V4L2_CID_EXPOSURE:
+		if (priv->aec) {
+			ret = ov6650_reg_read(client, REG_AECH, &reg);
+			ctrl->value = reg;
+		} else {
+			ctrl->value = priv->exposure;
+		}
+		break;
+	case V4L2_CID_GAMMA:
+		ctrl->value = priv->gamma;
+		break;
+	case V4L2_CID_VFLIP:
+		ctrl->value = priv->vflip;
+		break;
+	case V4L2_CID_HFLIP:
+		ctrl->value = priv->hflip;
+		break;
+	}
+	return ret;
+}
+
+/* Set status of additional camera capabilities */
+static int ov6650_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov6650 *priv = to_ov6650(client);
+	int ret = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTOGAIN:
+		ret = ov6650_reg_rmw(client, REG_COMB,
+				ctrl->value ? COMB_AGC : 0, COMB_AGC);
+		if (!ret)
+			priv->agc = ctrl->value;
+		break;
+	case V4L2_CID_GAIN:
+		ret = ov6650_reg_write(client, REG_GAIN, ctrl->value);
+		if (!ret)
+			priv->gain = ctrl->value;
+		break;
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ret = ov6650_reg_rmw(client, REG_COMB,
+				ctrl->value ? COMB_AWB : 0, COMB_AWB);
+		if (!ret)
+			priv->awb = ctrl->value;
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		ret = ov6650_reg_write(client, REG_BLUE, ctrl->value);
+		if (!ret)
+			priv->blue = ctrl->value;
+		break;
+	case V4L2_CID_RED_BALANCE:
+		ret = ov6650_reg_write(client, REG_RED, ctrl->value);
+		if (!ret)
+			priv->red = ctrl->value;
+		break;
+	case V4L2_CID_SATURATION:
+		ret = ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->value),
+				SAT_MASK);
+		if (!ret)
+			priv->saturation = ctrl->value;
+		break;
+	case V4L2_CID_HUE:
+		ret = ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->value),
+				HUE_MASK);
+		if (!ret)
+			priv->hue = ctrl->value;
+		break;
+	case V4L2_CID_BRIGHTNESS:
+		ret = ov6650_reg_write(client, REG_BRT, ctrl->value);
+		if (!ret)
+			priv->brightness = ctrl->value;
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+		switch (ctrl->value) {
+		case V4L2_EXPOSURE_AUTO:
+			ret = ov6650_reg_rmw(client, REG_COMB, COMB_AEC, 0);
+			break;
+		default:
+			ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_AEC);
+			break;
+		}
+		if (!ret)
+			priv->aec = ctrl->value;
+		break;
+	case V4L2_CID_EXPOSURE:
+		ret = ov6650_reg_write(client, REG_AECH, ctrl->value);
+		if (!ret)
+			priv->exposure = ctrl->value;
+		break;
+	case V4L2_CID_GAMMA:
+		ret = ov6650_reg_write(client, REG_GAM1, ctrl->value);
+		if (!ret)
+			priv->gamma = ctrl->value;
+		break;
+	case V4L2_CID_VFLIP:
+		ret = ov6650_reg_rmw(client, REG_COMB,
+				ctrl->value ? COMB_FLIP_V : 0, COMB_FLIP_V);
+		if (!ret)
+			priv->vflip = ctrl->value;
+		break;
+	case V4L2_CID_HFLIP:
+		ret = ov6650_reg_rmw(client, REG_COMB,
+				ctrl->value ? COMB_FLIP_H : 0, COMB_FLIP_H);
+		if (!ret)
+			priv->hflip = ctrl->value;
+		break;
+	}
+
+	return ret;
+}
+
+/* Get chip identification */
+static int ov6650_g_chip_ident(struct v4l2_subdev *sd,
+				struct v4l2_dbg_chip_ident *id)
+{
+	id->ident	= V4L2_IDENT_OV6650;
+	id->revision	= 0;
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov6650_get_register(struct v4l2_subdev *sd,
+				struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+	u8 val;
+
+	if (reg->reg & ~0xff)
+		return -EINVAL;
+
+	reg->size = 1;
+
+	ret = ov6650_reg_read(client, reg->reg, &val);
+	if (!ret)
+		reg->val = (__u64)val;
+
+	return ret;
+}
+
+static int ov6650_set_register(struct v4l2_subdev *sd,
+				struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	if (reg->reg & ~0xff || reg->val & ~0xff)
+		return -EINVAL;
+
+	return ov6650_reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov6650 *priv = to_ov6650(client);
+
+	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	a->c = priv->rect;
+
+	return 0;
+}
+
+static int ov6650_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov6650 *priv = to_ov6650(client);
+	struct v4l2_rect *rect = &a->c;
+	int ret;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	rect->left   = ALIGN(rect->left,   2);
+	rect->width  = ALIGN(rect->width,  2);
+	rect->top    = ALIGN(rect->top,    2);
+	rect->height = ALIGN(rect->height, 2);
+	soc_camera_limit_side(&rect->left, &rect->width,
+			DEF_HSTRT << 1, 2, W_CIF);
+	soc_camera_limit_side(&rect->top, &rect->height,
+			DEF_VSTRT << 1, 2, H_CIF);
+
+	ret = ov6650_reg_write(client, REG_HSTRT, rect->left >> 1);
+	if (!ret) {
+		priv->rect.left = rect->left;
+		ret = ov6650_reg_write(client, REG_HSTOP,
+				(rect->left + rect->width) >> 1);
+	}
+	if (!ret) {
+		priv->rect.width = rect->width;
+		ret = ov6650_reg_write(client, REG_VSTRT, rect->top >> 1);
+	}
+	if (!ret) {
+		priv->rect.top = rect->top;
+		ret = ov6650_reg_write(client, REG_VSTOP,
+				(rect->top + rect->height) >> 1);
+	}
+	if (!ret)
+		priv->rect.height = rect->height;
+
+	return ret;
+}
+
+static int ov6650_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	a->bounds.left			= DEF_HSTRT << 1;
+	a->bounds.top			= DEF_VSTRT << 1;
+	a->bounds.width			= W_CIF;
+	a->bounds.height		= H_CIF;
+	a->defrect			= a->bounds;
+	a->pixelaspect.numerator	= 1;
+	a->pixelaspect.denominator	= 1;
+
+	return 0;
+}
+
+static int ov6650_g_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_mbus_framefmt *mf)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov6650 *priv = to_ov6650(client);
+
+	mf->width	= priv->rect.width >> priv->half_scale;
+	mf->height	= priv->rect.height >> priv->half_scale;
+	mf->code	= priv->code;
+	mf->colorspace	= priv->colorspace;
+	mf->field	= V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static bool is_unscaled_ok(int width, int height, struct v4l2_rect *rect)
+{
+	return (width > rect->width >> 1 || height > rect->height >> 1);
+}
+
+static u8 to_clkrc(struct v4l2_fract *timeperframe,
+		unsigned long pclk_limit, unsigned long pclk_max)
+{
+	unsigned long pclk;
+
+	if (timeperframe->numerator && timeperframe->denominator)
+		pclk = pclk_max * timeperframe->denominator /
+				(FRAME_RATE_MAX * timeperframe->numerator);
+	else
+		pclk = pclk_max;
+
+	if (pclk_limit && pclk_limit < pclk)
+		pclk = pclk_limit;
+
+	return (pclk_max - 1) / pclk;
+}
+
+/* set the format we will capture in */
+static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_device *icd = client->dev.platform_data;
+	struct soc_camera_sense *sense = icd->sense;
+	struct ov6650 *priv = to_ov6650(client);
+	bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect);
+	struct v4l2_crop a = {
+		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+		.c = {
+			.left	= priv->rect.left + (priv->rect.width >> 1) -
+					(mf->width >> (1 - half_scale)),
+			.top	= priv->rect.top + (priv->rect.height >> 1) -
+					(mf->height >> (1 - half_scale)),
+			.width	= mf->width << half_scale,
+			.height	= mf->height << half_scale,
+		},
+	};
+	enum v4l2_mbus_pixelcode code = mf->code;
+	unsigned long mclk, pclk;
+	u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask, clkrc;
+	int ret;
+
+	/* select color matrix configuration for given color encoding */
+	switch (code) {
+	case V4L2_MBUS_FMT_GREY8_1X8:
+		dev_dbg(&client->dev, "pixel format GREY8_1X8\n");
+		coma_mask |= COMA_RGB | COMA_WORD_SWAP | COMA_BYTE_SWAP;
+		coma_set |= COMA_BW;
+		break;
+	case V4L2_MBUS_FMT_YUYV8_2X8:
+		dev_dbg(&client->dev, "pixel format YUYV8_2X8_LE\n");
+		coma_mask |= COMA_RGB | COMA_BW | COMA_BYTE_SWAP;
+		coma_set |= COMA_WORD_SWAP;
+		break;
+	case V4L2_MBUS_FMT_YVYU8_2X8:
+		dev_dbg(&client->dev, "pixel format YVYU8_2X8_LE (untested)\n");
+		coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP |
+				COMA_BYTE_SWAP;
+		break;
+	case V4L2_MBUS_FMT_UYVY8_2X8:
+		dev_dbg(&client->dev, "pixel format YUYV8_2X8_BE\n");
+		if (half_scale) {
+			coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
+			coma_set |= COMA_BYTE_SWAP;
+		} else {
+			coma_mask |= COMA_RGB | COMA_BW;
+			coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
+		}
+		break;
+	case V4L2_MBUS_FMT_VYUY8_2X8:
+		dev_dbg(&client->dev, "pixel format YVYU8_2X8_BE (untested)\n");
+		if (half_scale) {
+			coma_mask |= COMA_RGB | COMA_BW;
+			coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
+		} else {
+			coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
+			coma_set |= COMA_BYTE_SWAP;
+		}
+		break;
+	case V4L2_MBUS_FMT_SBGGR8_1X8:
+		dev_dbg(&client->dev, "pixel format SBGGR8_1X8 (untested)\n");
+		coma_mask |= COMA_BW | COMA_BYTE_SWAP | COMA_WORD_SWAP;
+		coma_set |= COMA_RAW_RGB | COMA_RGB;
+		break;
+	case 0:
+		break;
+	default:
+		dev_err(&client->dev, "Pixel format not handled: 0x%x\n", code);
+		return -EINVAL;
+	}
+	priv->code = code;
+
+	if (code == V4L2_MBUS_FMT_GREY8_1X8 ||
+			code == V4L2_MBUS_FMT_SBGGR8_1X8) {
+		coml_mask = COML_ONE_CHANNEL;
+		coml_set = 0;
+		priv->pclk_max = 4000000;
+	} else {
+		coml_mask = 0;
+		coml_set = COML_ONE_CHANNEL;
+		priv->pclk_max = 8000000;
+	}
+
+	if (code == V4L2_MBUS_FMT_SBGGR8_1X8)
+		priv->colorspace = V4L2_COLORSPACE_SRGB;
+	else if (code != 0)
+		priv->colorspace = V4L2_COLORSPACE_JPEG;
+
+	if (half_scale) {
+		dev_dbg(&client->dev, "max resolution: QCIF\n");
+		coma_set |= COMA_QCIF;
+		priv->pclk_max /= 2;
+	} else {
+		dev_dbg(&client->dev, "max resolution: CIF\n");
+		coma_mask |= COMA_QCIF;
+	}
+	priv->half_scale = half_scale;
+
+	if (sense) {
+		if (sense->master_clock == 8000000) {
+			dev_dbg(&client->dev, "8MHz input clock\n");
+			clkrc = CLKRC_6MHz;
+		} else if (sense->master_clock == 12000000) {
+			dev_dbg(&client->dev, "12MHz input clock\n");
+			clkrc = CLKRC_12MHz;
+		} else if (sense->master_clock == 16000000) {
+			dev_dbg(&client->dev, "16MHz input clock\n");
+			clkrc = CLKRC_16MHz;
+		} else if (sense->master_clock == 24000000) {
+			dev_dbg(&client->dev, "24MHz input clock\n");
+			clkrc = CLKRC_24MHz;
+		} else {
+			dev_err(&client->dev,
+				"unspported input clock, check platform data\n");
+			return -EINVAL;
+		}
+		mclk = sense->master_clock;
+		priv->pclk_limit = sense->pixel_clock_max;
+	} else {
+		clkrc = CLKRC_24MHz;
+		mclk = 24000000;
+		priv->pclk_limit = 0;
+		dev_dbg(&client->dev, "using default 24MHz input clock\n");
+	}
+
+	clkrc |= to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max);
+
+	pclk = priv->pclk_max / GET_CLKRC_DIV(clkrc);
+	dev_dbg(&client->dev, "pixel clock divider: %ld.%ld\n",
+			mclk / pclk, 10 * mclk % pclk / pclk);
+
+	ret = ov6650_s_crop(sd, &a);
+	if (!ret)
+		ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask);
+	if (!ret)
+		ret = ov6650_reg_write(client, REG_CLKRC, clkrc);
+	if (!ret)
+		ret = ov6650_reg_rmw(client, REG_COML, coml_set, coml_mask);
+
+	if (!ret) {
+		mf->colorspace	= priv->colorspace;
+		mf->width = priv->rect.width >> half_scale;
+		mf->height = priv->rect.height >> half_scale;
+	}
+
+	return ret;
+}
+
+static int ov6650_try_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_mbus_framefmt *mf)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov6650 *priv = to_ov6650(client);
+
+	if (is_unscaled_ok(mf->width, mf->height, &priv->rect))
+		v4l_bound_align_image(&mf->width, 2, W_CIF, 1,
+				&mf->height, 2, H_CIF, 1, 0);
+
+	mf->field = V4L2_FIELD_NONE;
+
+	switch (mf->code) {
+	case V4L2_MBUS_FMT_Y10_1X10:
+		mf->code = V4L2_MBUS_FMT_GREY8_1X8;
+	case V4L2_MBUS_FMT_GREY8_1X8:
+	case V4L2_MBUS_FMT_YVYU8_2X8:
+	case V4L2_MBUS_FMT_YUYV8_2X8:
+	case V4L2_MBUS_FMT_VYUY8_2X8:
+	case V4L2_MBUS_FMT_UYVY8_2X8:
+		mf->colorspace = V4L2_COLORSPACE_JPEG;
+		break;
+	default:
+		mf->code = V4L2_MBUS_FMT_SBGGR8_1X8;
+	case V4L2_MBUS_FMT_SBGGR8_1X8:
+		mf->colorspace = V4L2_COLORSPACE_SRGB;
+		break;
+	}
+
+	return 0;
+}
+
+static int ov6650_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+			   enum v4l2_mbus_pixelcode *code)
+{
+	if (index >= ARRAY_SIZE(ov6650_codes))
+		return -EINVAL;
+
+	*code = ov6650_codes[index];
+	return 0;
+}
+
+static int ov6650_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov6650 *priv = to_ov6650(client);
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	memset(cp, 0, sizeof(*cp));
+	cp->capability = V4L2_CAP_TIMEPERFRAME;
+	cp->timeperframe.numerator = GET_CLKRC_DIV(to_clkrc(&priv->tpf,
+			priv->pclk_limit, priv->pclk_max));
+	cp->timeperframe.denominator = FRAME_RATE_MAX;
+
+	dev_dbg(&client->dev, "Frame interval: %u/%u s\n",
+		cp->timeperframe.numerator, cp->timeperframe.denominator);
+
+	return 0;
+}
+
+static int ov6650_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov6650 *priv = to_ov6650(client);
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+	struct v4l2_fract *tpf = &cp->timeperframe;
+	int div, ret;
+	u8 clkrc;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	if (cp->extendedmode != 0)
+		return -EINVAL;
+
+	if (tpf->numerator == 0 || tpf->denominator == 0)
+		div = 1;  /* Reset to full rate */
+	else
+		div = (tpf->numerator * FRAME_RATE_MAX) / tpf->denominator;
+
+	if (div == 0)
+		div = 1;
+	else if (div > GET_CLKRC_DIV(CLKRC_DIV_MASK))
+		div = GET_CLKRC_DIV(CLKRC_DIV_MASK);
+
+	/*
+	 * Keep result to be used as tpf limit
+	 * for subseqent clock divider calculations
+	 */
+	priv->tpf.numerator = div;
+	priv->tpf.denominator = FRAME_RATE_MAX;
+
+	clkrc = to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max);
+
+	ret = ov6650_reg_rmw(client, REG_CLKRC, clkrc, CLKRC_DIV_MASK);
+	if (!ret) {
+		tpf->numerator = GET_CLKRC_DIV(clkrc);
+		tpf->denominator = FRAME_RATE_MAX;
+	}
+
+	return ret;
+}
+
+/* Soft reset the camera. This has nothing to do with the RESET pin! */
+static int ov6650_reset(struct i2c_client *client)
+{
+	int ret;
+
+	dev_dbg(&client->dev, "reset\n");
+
+	ret = ov6650_reg_rmw(client, REG_COMA, COMA_RESET, 0);
+	if (ret)
+		dev_err(&client->dev,
+			"An error occured while entering soft reset!\n");
+
+	return ret;
+}
+
+/* program default register values */
+static int ov6650_prog_dflt(struct i2c_client *client)
+{
+	int ret;
+
+	dev_dbg(&client->dev, "initializing\n");
+
+	ret = ov6650_reg_write(client, REG_COMA, 0);	/* ~COMA_RESET */
+	if (!ret)
+		ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_BAND_FILTER);
+
+	return ret;
+}
+
+static int ov6650_video_probe(struct soc_camera_device *icd,
+				struct i2c_client *client)
+{
+	u8		pidh, pidl, midh, midl;
+	int		ret = 0;
+
+	/*
+	 * check and show product ID and manufacturer ID
+	 */
+	ret = ov6650_reg_read(client, REG_PIDH, &pidh);
+	if (!ret)
+		ret = ov6650_reg_read(client, REG_PIDL, &pidl);
+	if (!ret)
+		ret = ov6650_reg_read(client, REG_MIDH, &midh);
+	if (!ret)
+		ret = ov6650_reg_read(client, REG_MIDL, &midl);
+
+	if (ret)
+		return ret;
+
+	if ((pidh != OV6650_PIDH) || (pidl != OV6650_PIDL)) {
+		dev_err(&client->dev, "Product ID error 0x%02x:0x%02x\n",
+				pidh, pidl);
+		return -ENODEV;
+	}
+
+	dev_info(&client->dev,
+		"ov6650 Product ID 0x%02x:0x%02x Manufacturer ID 0x%02x:0x%02x\n",
+		pidh, pidl, midh, midl);
+
+	ret = ov6650_reset(client);
+	if (!ret)
+		ret = ov6650_prog_dflt(client);
+
+	return ret;
+}
+
+static struct soc_camera_ops ov6650_ops = {
+	.set_bus_param		= ov6650_set_bus_param,
+	.query_bus_param	= ov6650_query_bus_param,
+	.controls		= ov6650_controls,
+	.num_controls		= ARRAY_SIZE(ov6650_controls),
+};
+
+static struct v4l2_subdev_core_ops ov6650_core_ops = {
+	.g_ctrl			= ov6650_g_ctrl,
+	.s_ctrl			= ov6650_s_ctrl,
+	.g_chip_ident		= ov6650_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register		= ov6650_get_register,
+	.s_register		= ov6650_set_register,
+#endif
+};
+
+static struct v4l2_subdev_video_ops ov6650_video_ops = {
+	.s_stream	= ov6650_s_stream,
+	.g_mbus_fmt	= ov6650_g_fmt,
+	.s_mbus_fmt	= ov6650_s_fmt,
+	.try_mbus_fmt	= ov6650_try_fmt,
+	.enum_mbus_fmt	= ov6650_enum_fmt,
+	.cropcap	= ov6650_cropcap,
+	.g_crop		= ov6650_g_crop,
+	.s_crop		= ov6650_s_crop,
+	.g_parm		= ov6650_g_parm,
+	.s_parm		= ov6650_s_parm,
+};
+
+static struct v4l2_subdev_ops ov6650_subdev_ops = {
+	.core	= &ov6650_core_ops,
+	.video	= &ov6650_video_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+static int ov6650_probe(struct i2c_client *client,
+			const struct i2c_device_id *did)
+{
+	struct ov6650 *priv;
+	struct soc_camera_device *icd = client->dev.platform_data;
+	struct soc_camera_link *icl;
+	int ret;
+
+	if (!icd) {
+		dev_err(&client->dev, "Missing soc-camera data!\n");
+		return -EINVAL;
+	}
+
+	icl = to_soc_camera_link(icd);
+	if (!icl) {
+		dev_err(&client->dev, "Missing platform_data for driver\n");
+		return -EINVAL;
+	}
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&client->dev,
+			"Failed to allocate memory for private data!\n");
+		return -ENOMEM;
+	}
+
+	v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops);
+
+	icd->ops = &ov6650_ops;
+
+	priv->rect.left	  = DEF_HSTRT << 1;
+	priv->rect.top	  = DEF_VSTRT << 1;
+	priv->rect.width  = W_CIF;
+	priv->rect.height = H_CIF;
+	priv->half_scale  = false;
+	priv->code	  = V4L2_MBUS_FMT_YUYV8_2X8;
+	priv->colorspace  = V4L2_COLORSPACE_JPEG;
+
+	ret = ov6650_video_probe(icd, client);
+
+	if (ret) {
+		icd->ops = NULL;
+		i2c_set_clientdata(client, NULL);
+		kfree(priv);
+	}
+
+	return ret;
+}
+
+static int ov6650_remove(struct i2c_client *client)
+{
+	struct ov6650 *priv = to_ov6650(client);
+
+	i2c_set_clientdata(client, NULL);
+	kfree(priv);
+	return 0;
+}
+
+static const struct i2c_device_id ov6650_id[] = {
+	{ "ov6650", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov6650_id);
+
+static struct i2c_driver ov6650_i2c_driver = {
+	.driver = {
+		.name = "ov6650",
+	},
+	.probe    = ov6650_probe,
+	.remove   = ov6650_remove,
+	.id_table = ov6650_id,
+};
+
+static int __init ov6650_module_init(void)
+{
+	return i2c_add_driver(&ov6650_i2c_driver);
+}
+
+static void __exit ov6650_module_exit(void)
+{
+	i2c_del_driver(&ov6650_i2c_driver);
+}
+
+module_init(ov6650_module_init);
+module_exit(ov6650_module_exit);
+
+MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV6650");
+MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
index 91c886a..c881a64 100644
--- a/drivers/media/video/ov7670.c
+++ b/drivers/media/video/ov7670.c
@@ -18,8 +18,9 @@
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
+#include <media/v4l2-mediabus.h>
 
+#include "ov7670.h"
 
 MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
 MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
@@ -43,11 +44,6 @@
 #define	QCIF_HEIGHT	144
 
 /*
- * Our nominal (default) frame rate.
- */
-#define OV7670_FRAME_RATE 30
-
-/*
  * The 7670 sits on i2c with ID 0x42
  */
 #define OV7670_I2C_ADDR 0x42
@@ -198,7 +194,11 @@
 	struct ov7670_format_struct *fmt;  /* Current format */
 	unsigned char sat;		/* Saturation value */
 	int hue;			/* Hue value */
+	int min_width;			/* Filter out smaller sizes */
+	int min_height;			/* Filter out smaller sizes */
+	int clock_speed;		/* External clock speed (MHz) */
 	u8 clkrc;			/* Clock divider value */
+	bool use_smbus;			/* Use smbus I/O instead of I2C */
 };
 
 static inline struct ov7670_info *to_state(struct v4l2_subdev *sd)
@@ -415,8 +415,7 @@
  * ov7670 is not really an SMBUS device, though, so the communication
  * is not always entirely reliable.
  */
-#ifdef CONFIG_OLPC_XO_1
-static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
+static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg,
 		unsigned char *value)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -431,7 +430,7 @@
 }
 
 
-static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg,
+static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg,
 		unsigned char value)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -442,11 +441,10 @@
 	return ret;
 }
 
-#else /* ! CONFIG_OLPC_XO_1 */
 /*
  * On most platforms, we'd rather do straight i2c I/O.
  */
-static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
+static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg,
 		unsigned char *value)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -479,7 +477,7 @@
 }
 
 
-static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg,
+static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg,
 		unsigned char value)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -498,8 +496,26 @@
 		msleep(5);  /* Wait for reset to run */
 	return ret;
 }
-#endif /* CONFIG_OLPC_XO_1 */
 
+static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
+		unsigned char *value)
+{
+	struct ov7670_info *info = to_state(sd);
+	if (info->use_smbus)
+		return ov7670_read_smbus(sd, reg, value);
+	else
+		return ov7670_read_i2c(sd, reg, value);
+}
+
+static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg,
+		unsigned char value)
+{
+	struct ov7670_info *info = to_state(sd);
+	if (info->use_smbus)
+		return ov7670_write_smbus(sd, reg, value);
+	else
+		return ov7670_write_i2c(sd, reg, value);
+}
 
 /*
  * Write a list of register settings; ff/ff stops the process.
@@ -572,42 +588,37 @@
 /*
  * Store information about the video data format.  The color matrix
  * is deeply tied into the format, so keep the relevant values here.
- * The magic matrix nubmers come from OmniVision.
+ * The magic matrix numbers come from OmniVision.
  */
 static struct ov7670_format_struct {
-	__u8 *desc;
-	__u32 pixelformat;
+	enum v4l2_mbus_pixelcode mbus_code;
+	enum v4l2_colorspace colorspace;
 	struct regval_list *regs;
 	int cmatrix[CMATRIX_LEN];
-	int bpp;   /* Bytes per pixel */
 } ov7670_formats[] = {
 	{
-		.desc		= "YUYV 4:2:2",
-		.pixelformat	= V4L2_PIX_FMT_YUYV,
+		.mbus_code	= V4L2_MBUS_FMT_YUYV8_2X8,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
 		.regs 		= ov7670_fmt_yuv422,
 		.cmatrix	= { 128, -128, 0, -34, -94, 128 },
-		.bpp		= 2,
 	},
 	{
-		.desc		= "RGB 444",
-		.pixelformat	= V4L2_PIX_FMT_RGB444,
+		.mbus_code	= V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
 		.regs		= ov7670_fmt_rgb444,
 		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
-		.bpp		= 2,
 	},
 	{
-		.desc		= "RGB 565",
-		.pixelformat	= V4L2_PIX_FMT_RGB565,
+		.mbus_code	= V4L2_MBUS_FMT_RGB565_2X8_LE,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
 		.regs		= ov7670_fmt_rgb565,
 		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
-		.bpp		= 2,
 	},
 	{
-		.desc		= "Raw RGB Bayer",
-		.pixelformat	= V4L2_PIX_FMT_SBGGR8,
+		.mbus_code	= V4L2_MBUS_FMT_SBGGR8_1X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
 		.regs 		= ov7670_fmt_raw,
 		.cmatrix	= { 0, 0, 0, 0, 0, 0 },
-		.bpp		= 1
 	},
 };
 #define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats)
@@ -680,10 +691,10 @@
 		.width		= QVGA_WIDTH,
 		.height		= QVGA_HEIGHT,
 		.com7_bit	= COM7_FMT_QVGA,
-		.hstart		= 164,		/* Empirically determined */
-		.hstop		=  20,
-		.vstart		=  14,
-		.vstop		= 494,
+		.hstart		= 168,		/* Empirically determined */
+		.hstop		=  24,
+		.vstart		=  12,
+		.vstop		= 492,
 		.regs 		= NULL,
 	},
 	/* QCIF */
@@ -734,51 +745,45 @@
 }
 
 
-static int ov7670_enum_fmt(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmt)
+static int ov7670_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+					enum v4l2_mbus_pixelcode *code)
 {
-	struct ov7670_format_struct *ofmt;
-
-	if (fmt->index >= N_OV7670_FMTS)
+	if (index >= N_OV7670_FMTS)
 		return -EINVAL;
 
-	ofmt = ov7670_formats + fmt->index;
-	fmt->flags = 0;
-	strcpy(fmt->description, ofmt->desc);
-	fmt->pixelformat = ofmt->pixelformat;
+	*code = ov7670_formats[index].mbus_code;
 	return 0;
 }
 
-
 static int ov7670_try_fmt_internal(struct v4l2_subdev *sd,
-		struct v4l2_format *fmt,
+		struct v4l2_mbus_framefmt *fmt,
 		struct ov7670_format_struct **ret_fmt,
 		struct ov7670_win_size **ret_wsize)
 {
 	int index;
 	struct ov7670_win_size *wsize;
-	struct v4l2_pix_format *pix = &fmt->fmt.pix;
 
 	for (index = 0; index < N_OV7670_FMTS; index++)
-		if (ov7670_formats[index].pixelformat == pix->pixelformat)
+		if (ov7670_formats[index].mbus_code == fmt->code)
 			break;
 	if (index >= N_OV7670_FMTS) {
 		/* default to first format */
 		index = 0;
-		pix->pixelformat = ov7670_formats[0].pixelformat;
+		fmt->code = ov7670_formats[0].mbus_code;
 	}
 	if (ret_fmt != NULL)
 		*ret_fmt = ov7670_formats + index;
 	/*
 	 * Fields: the OV devices claim to be progressive.
 	 */
-	pix->field = V4L2_FIELD_NONE;
+	fmt->field = V4L2_FIELD_NONE;
 	/*
 	 * Round requested image size down to the nearest
 	 * we support, but not below the smallest.
 	 */
 	for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES;
 	     wsize++)
-		if (pix->width >= wsize->width && pix->height >= wsize->height)
+		if (fmt->width >= wsize->width && fmt->height >= wsize->height)
 			break;
 	if (wsize >= ov7670_win_sizes + N_WIN_SIZES)
 		wsize--;   /* Take the smallest one */
@@ -787,14 +792,14 @@
 	/*
 	 * Note the size we'll actually handle.
 	 */
-	pix->width = wsize->width;
-	pix->height = wsize->height;
-	pix->bytesperline = pix->width*ov7670_formats[index].bpp;
-	pix->sizeimage = pix->height*pix->bytesperline;
+	fmt->width = wsize->width;
+	fmt->height = wsize->height;
+	fmt->colorspace = ov7670_formats[index].colorspace;
 	return 0;
 }
 
-static int ov7670_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+static int ov7670_try_mbus_fmt(struct v4l2_subdev *sd,
+			    struct v4l2_mbus_framefmt *fmt)
 {
 	return ov7670_try_fmt_internal(sd, fmt, NULL, NULL);
 }
@@ -802,15 +807,17 @@
 /*
  * Set a format.
  */
-static int ov7670_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+static int ov7670_s_mbus_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_mbus_framefmt *fmt)
 {
-	int ret;
 	struct ov7670_format_struct *ovfmt;
 	struct ov7670_win_size *wsize;
 	struct ov7670_info *info = to_state(sd);
 	unsigned char com7;
+	int ret;
 
 	ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize);
+
 	if (ret)
 		return ret;
 	/*
@@ -845,7 +852,7 @@
 	 */
 	if (ret == 0)
 		ret = ov7670_write(sd, REG_CLKRC, info->clkrc);
-	return ret;
+	return 0;
 }
 
 /*
@@ -863,7 +870,7 @@
 	memset(cp, 0, sizeof(struct v4l2_captureparm));
 	cp->capability = V4L2_CAP_TIMEPERFRAME;
 	cp->timeperframe.numerator = 1;
-	cp->timeperframe.denominator = OV7670_FRAME_RATE;
+	cp->timeperframe.denominator = info->clock_speed;
 	if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1)
 		cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE);
 	return 0;
@@ -884,27 +891,73 @@
 	if (tpf->numerator == 0 || tpf->denominator == 0)
 		div = 1;  /* Reset to full rate */
 	else
-		div = (tpf->numerator*OV7670_FRAME_RATE)/tpf->denominator;
+		div = (tpf->numerator * info->clock_speed) / tpf->denominator;
 	if (div == 0)
 		div = 1;
 	else if (div > CLK_SCALE)
 		div = CLK_SCALE;
 	info->clkrc = (info->clkrc & 0x80) | div;
 	tpf->numerator = 1;
-	tpf->denominator = OV7670_FRAME_RATE/div;
+	tpf->denominator = info->clock_speed / div;
 	return ov7670_write(sd, REG_CLKRC, info->clkrc);
 }
 
 
+/*
+ * Frame intervals.  Since frame rates are controlled with the clock
+ * divider, we can only do 30/n for integer n values.  So no continuous
+ * or stepwise options.  Here we just pick a handful of logical values.
+ */
+
+static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 };
+
+static int ov7670_enum_frameintervals(struct v4l2_subdev *sd,
+		struct v4l2_frmivalenum *interval)
+{
+	if (interval->index >= ARRAY_SIZE(ov7670_frame_rates))
+		return -EINVAL;
+	interval->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+	interval->discrete.numerator = 1;
+	interval->discrete.denominator = ov7670_frame_rates[interval->index];
+	return 0;
+}
+
+/*
+ * Frame size enumeration
+ */
+static int ov7670_enum_framesizes(struct v4l2_subdev *sd,
+		struct v4l2_frmsizeenum *fsize)
+{
+	struct ov7670_info *info = to_state(sd);
+	int i;
+	int num_valid = -1;
+	__u32 index = fsize->index;
+
+	/*
+	 * If a minimum width/height was requested, filter out the capture
+	 * windows that fall outside that.
+	 */
+	for (i = 0; i < N_WIN_SIZES; i++) {
+		struct ov7670_win_size *win = &ov7670_win_sizes[index];
+		if (info->min_width && win->width < info->min_width)
+			continue;
+		if (info->min_height && win->height < info->min_height)
+			continue;
+		if (index == ++num_valid) {
+			fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+			fsize->discrete.width = win->width;
+			fsize->discrete.height = win->height;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
 
 /*
  * Code for dealing with controls.
  */
 
-
-
-
-
 static int ov7670_store_cmatrix(struct v4l2_subdev *sd,
 		int matrix[CMATRIX_LEN])
 {
@@ -1396,6 +1449,47 @@
 	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0);
 }
 
+static int ov7670_s_config(struct v4l2_subdev *sd, int dumb, void *data)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov7670_config *config = data;
+	struct ov7670_info *info = to_state(sd);
+	int ret;
+
+	info->clock_speed = 30; /* default: a guess */
+
+	/*
+	 * Must apply configuration before initializing device, because it
+	 * selects I/O method.
+	 */
+	if (config) {
+		info->min_width = config->min_width;
+		info->min_height = config->min_height;
+		info->use_smbus = config->use_smbus;
+
+		if (config->clock_speed)
+			info->clock_speed = config->clock_speed;
+	}
+
+	/* Make sure it's an ov7670 */
+	ret = ov7670_detect(sd);
+	if (ret) {
+		v4l_dbg(1, debug, client,
+			"chip found @ 0x%x (%s) is not an ov7670 chip.\n",
+			client->addr << 1, client->adapter->name);
+		kfree(info);
+		return ret;
+	}
+	v4l_info(client, "chip found @ 0x%02x (%s)\n",
+			client->addr << 1, client->adapter->name);
+
+	info->fmt = &ov7670_formats[0];
+	info->sat = 128;	/* Review this */
+	info->clkrc = info->clock_speed / 30;
+
+	return 0;
+}
+
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
 {
@@ -1434,6 +1528,7 @@
 	.s_ctrl = ov7670_s_ctrl,
 	.queryctrl = ov7670_queryctrl,
 	.reset = ov7670_reset,
+	.s_config = ov7670_s_config,
 	.init = ov7670_init,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.g_register = ov7670_g_register,
@@ -1442,11 +1537,13 @@
 };
 
 static const struct v4l2_subdev_video_ops ov7670_video_ops = {
-	.enum_fmt = ov7670_enum_fmt,
-	.try_fmt = ov7670_try_fmt,
-	.s_fmt = ov7670_s_fmt,
+	.enum_mbus_fmt = ov7670_enum_mbus_fmt,
+	.try_mbus_fmt = ov7670_try_mbus_fmt,
+	.s_mbus_fmt = ov7670_s_mbus_fmt,
 	.s_parm = ov7670_s_parm,
 	.g_parm = ov7670_g_parm,
+	.enum_frameintervals = ov7670_enum_frameintervals,
+	.enum_framesizes = ov7670_enum_framesizes,
 };
 
 static const struct v4l2_subdev_ops ov7670_ops = {
@@ -1461,7 +1558,6 @@
 {
 	struct v4l2_subdev *sd;
 	struct ov7670_info *info;
-	int ret;
 
 	info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL);
 	if (info == NULL)
@@ -1469,22 +1565,6 @@
 	sd = &info->sd;
 	v4l2_i2c_subdev_init(sd, client, &ov7670_ops);
 
-	/* Make sure it's an ov7670 */
-	ret = ov7670_detect(sd);
-	if (ret) {
-		v4l_dbg(1, debug, client,
-			"chip found @ 0x%x (%s) is not an ov7670 chip.\n",
-			client->addr << 1, client->adapter->name);
-		kfree(info);
-		return ret;
-	}
-	v4l_info(client, "chip found @ 0x%02x (%s)\n",
-			client->addr << 1, client->adapter->name);
-
-	info->fmt = &ov7670_formats[0];
-	info->sat = 128;	/* Review this */
-	info->clkrc = 1;	/* 30fps */
-
 	return 0;
 }
 
@@ -1504,9 +1584,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, ov7670_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "ov7670",
-	.probe = ov7670_probe,
-	.remove = ov7670_remove,
-	.id_table = ov7670_id,
+static struct i2c_driver ov7670_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "ov7670",
+	},
+	.probe		= ov7670_probe,
+	.remove		= ov7670_remove,
+	.id_table	= ov7670_id,
 };
+
+static __init int init_ov7670(void)
+{
+	return i2c_add_driver(&ov7670_driver);
+}
+
+static __exit void exit_ov7670(void)
+{
+	i2c_del_driver(&ov7670_driver);
+}
+
+module_init(init_ov7670);
+module_exit(exit_ov7670);
diff --git a/drivers/media/video/ov7670.h b/drivers/media/video/ov7670.h
new file mode 100644
index 0000000..b133bc1
--- /dev/null
+++ b/drivers/media/video/ov7670.h
@@ -0,0 +1,20 @@
+/*
+ * A V4L2 driver for OmniVision OV7670 cameras.
+ *
+ * Copyright 2010 One Laptop Per Child
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+
+#ifndef __OV7670_H
+#define __OV7670_H
+
+struct ov7670_config {
+	int min_width;			/* Filter out smaller sizes */
+	int min_height;			/* Filter out smaller sizes */
+	int clock_speed;		/* External clock speed (MHz) */
+	bool use_smbus;			/* Use smbus I/O instead of I2C */
+};
+
+#endif
diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
index 25eb5d6..a84b770 100644
--- a/drivers/media/video/ov772x.c
+++ b/drivers/media/video/ov772x.c
@@ -599,7 +599,7 @@
 
 static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct ov772x_priv *priv = to_ov772x(client);
 
 	if (!enable) {
@@ -645,7 +645,7 @@
 
 static int ov772x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct ov772x_priv *priv = to_ov772x(client);
 
 	switch (ctrl->id) {
@@ -664,7 +664,7 @@
 
 static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct ov772x_priv *priv = to_ov772x(client);
 	int ret = 0;
 	u8 val;
@@ -715,7 +715,7 @@
 static int ov772x_g_chip_ident(struct v4l2_subdev *sd,
 			       struct v4l2_dbg_chip_ident *id)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct ov772x_priv *priv = to_ov772x(client);
 
 	id->ident    = priv->model;
@@ -728,7 +728,7 @@
 static int ov772x_g_register(struct v4l2_subdev *sd,
 			     struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	int ret;
 
 	reg->size = 1;
@@ -747,7 +747,7 @@
 static int ov772x_s_register(struct v4l2_subdev *sd,
 			     struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (reg->reg > 0xff ||
 	    reg->val > 0xff)
@@ -954,7 +954,7 @@
 static int ov772x_g_fmt(struct v4l2_subdev *sd,
 			struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct ov772x_priv *priv = to_ov772x(client);
 
 	if (!priv->win || !priv->cfmt) {
@@ -977,7 +977,7 @@
 static int ov772x_s_fmt(struct v4l2_subdev *sd,
 			struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct ov772x_priv *priv = to_ov772x(client);
 	int ret = ov772x_set_params(client, &mf->width, &mf->height,
 				    mf->code);
@@ -991,7 +991,7 @@
 static int ov772x_try_fmt(struct v4l2_subdev *sd,
 			  struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct ov772x_priv *priv = to_ov772x(client);
 	const struct ov772x_win_size *win;
 	int i;
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
index 40cdfab..99e9e1d 100644
--- a/drivers/media/video/ov9640.c
+++ b/drivers/media/video/ov9640.c
@@ -308,7 +308,7 @@
 /* Get status of additional camera capabilities */
 static int ov9640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct ov9640_priv *priv = container_of(i2c_get_clientdata(client),
 					struct ov9640_priv, subdev);
 
@@ -326,7 +326,7 @@
 /* Set status of additional camera capabilities */
 static int ov9640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct ov9640_priv *priv = container_of(i2c_get_clientdata(client),
 					struct ov9640_priv, subdev);
 
@@ -360,7 +360,7 @@
 static int ov9640_g_chip_ident(struct v4l2_subdev *sd,
 				struct v4l2_dbg_chip_ident *id)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct ov9640_priv *priv = container_of(i2c_get_clientdata(client),
 					struct ov9640_priv, subdev);
 
@@ -374,7 +374,7 @@
 static int ov9640_get_register(struct v4l2_subdev *sd,
 				struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	int ret;
 	u8 val;
 
@@ -395,7 +395,7 @@
 static int ov9640_set_register(struct v4l2_subdev *sd,
 				struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (reg->reg & ~0xff || reg->val & ~0xff)
 		return -EINVAL;
@@ -558,7 +558,7 @@
 static int ov9640_s_fmt(struct v4l2_subdev *sd,
 			struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct ov9640_reg_alt alts = {0};
 	enum v4l2_colorspace cspace;
 	enum v4l2_mbus_pixelcode code = mf->code;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 70ea578..bef2027 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -2082,20 +2082,13 @@
 		return -EINVAL;
 	}
 
-	/* Note how the 2nd and 3rd arguments are the same for
-	 * v4l2_i2c_new_subdev().  Why?
-	 * Well the 2nd argument is the module name to load, while the 3rd
-	 * argument is documented in the framework as being the "chipid" -
-	 * and every other place where I can find examples of this, the
-	 * "chipid" appears to just be the module name again.  So here we
-	 * just do the same thing. */
 	if (i2ccnt == 1) {
 		pvr2_trace(PVR2_TRACE_INIT,
 			   "Module ID %u:"
 			   " Setting up with specified i2c address 0x%x",
 			   mid, i2caddr[0]);
 		sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap,
-					 fname, fname,
+					 NULL, fname,
 					 i2caddr[0], NULL);
 	} else {
 		pvr2_trace(PVR2_TRACE_INIT,
@@ -2103,7 +2096,7 @@
 			   " Setting up with address probe list",
 			   mid);
 		sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap,
-						fname, fname,
+						NULL, fname,
 						0, i2caddr);
 	}
 
diff --git a/drivers/media/video/pwc/Kconfig b/drivers/media/video/pwc/Kconfig
index 11980db..8da42e4 100644
--- a/drivers/media/video/pwc/Kconfig
+++ b/drivers/media/video/pwc/Kconfig
@@ -1,6 +1,6 @@
 config USB_PWC
 	tristate "USB Philips Cameras"
-	depends on VIDEO_V4L1
+	depends on VIDEO_V4L2
 	---help---
 	  Say Y or M here if you want to use one of these Philips & OEM
 	  webcams:
diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c
index f7f7e04..6b8fbdd 100644
--- a/drivers/media/video/pwc/pwc-ctrl.c
+++ b/drivers/media/video/pwc/pwc-ctrl.c
@@ -261,7 +261,7 @@
 		PWC_DEBUG_MODULE("Failed to send video command... %d\n", ret);
 		return ret;
 	}
-	if (pEntry->compressed && pdev->vpalette != VIDEO_PALETTE_RAW)
+	if (pEntry->compressed && pdev->pixfmt == V4L2_PIX_FMT_YUV420)
 		pwc_dec1_init(pdev->type, pdev->release, buf, pdev->decompress_data);
 
 	pdev->cmd_len = 3;
@@ -321,7 +321,7 @@
 	if (ret < 0)
 		return ret;
 
-	if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW)
+	if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420)
 		pwc_dec23_init(pdev, pdev->type, buf);
 
 	pdev->cmd_len = 13;
@@ -356,7 +356,7 @@
 	fps = (frames / 5) - 1;
 
 	/* special case: VGA @ 5 fps and snapshot is raw bayer mode */
-	if (size == PSZ_VGA && frames == 5 && snapshot && pdev->vpalette == VIDEO_PALETTE_RAW)
+	if (size == PSZ_VGA && frames == 5 && snapshot && pdev->pixfmt != V4L2_PIX_FMT_YUV420)
 	{
 		/* Only available in case the raw palette is selected or
 		   we have the decompressor available. This mode is
@@ -394,7 +394,7 @@
 	if (ret < 0)
 		return ret;
 
-	if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW)
+	if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420)
 		pwc_dec23_init(pdev, pdev->type, buf);
 
 	pdev->cmd_len = 12;
@@ -429,7 +429,7 @@
 {
 	int ret, size;
 
-	PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette);
+	PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", width, height, frames, pdev->pixfmt);
 	size = pwc_decode_size(pdev, width, height);
 	if (size < 0) {
 		PWC_DEBUG_MODULE("Could not find suitable size.\n");
@@ -519,13 +519,13 @@
 {
 	int i, factor = 0;
 
-	/* for PALETTE_YUV420P */
-	switch(pdev->vpalette)
-	{
-	case VIDEO_PALETTE_YUV420P:
+	/* for V4L2_PIX_FMT_YUV420 */
+	switch (pdev->pixfmt) {
+	case V4L2_PIX_FMT_YUV420:
 		factor = 6;
 		break;
-	case VIDEO_PALETTE_RAW:
+	case V4L2_PIX_FMT_PWC1:
+	case V4L2_PIX_FMT_PWC2:
 		factor = 6; /* can be uncompressed YUV420P */
 		break;
 	}
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index aea7e22..e62beb4 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -163,7 +163,7 @@
 	.read =		pwc_video_read,
 	.poll =		pwc_video_poll,
 	.mmap =		pwc_video_mmap,
-	.ioctl =        pwc_video_ioctl,
+	.unlocked_ioctl = pwc_video_ioctl,
 };
 static struct video_device pwc_template = {
 	.name =		"Philips Webcam",	/* Filled in later */
@@ -1247,8 +1247,8 @@
 
 	PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev);
 
-	lock_kernel();
 	pdev = video_get_drvdata(vdev);
+	mutex_lock(&pdev->modlock);
 	if (pdev->vopen == 0)
 		PWC_DEBUG_MODULE("video_close() called on closed device?\n");
 
@@ -1286,7 +1286,7 @@
 			if (device_hint[hint].pdev == pdev)
 				device_hint[hint].pdev = NULL;
 	}
-	unlock_kernel();
+	mutex_unlock(&pdev->modlock);
 
 	return 0;
 }
@@ -1365,7 +1365,7 @@
 	}
 
 	PWC_DEBUG_READ("Copying data to user space.\n");
-	if (pdev->vpalette == VIDEO_PALETTE_RAW)
+	if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
 		bytes_to_read = pdev->frame_size + sizeof(struct pwc_raw_frame);
 	else
 		bytes_to_read = pdev->view.size;
@@ -1800,13 +1800,6 @@
 	}
 
 	pdev->vdev->release = video_device_release;
-	rc = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr);
-	if (rc < 0) {
-		PWC_ERROR("Failed to register as video device (%d).\n", rc);
-		goto err_video_release;
-	}
-
-	PWC_INFO("Registered as %s.\n", video_device_node_name(pdev->vdev));
 
 	/* occupy slot */
 	if (hint < MAX_DEV_HINTS)
@@ -1814,14 +1807,22 @@
 
 	PWC_DEBUG_PROBE("probe() function returning struct at 0x%p.\n", pdev);
 	usb_set_intfdata(intf, pdev);
-	rc = pwc_create_sysfs_files(pdev->vdev);
-	if (rc)
-		goto err_video_unreg;
 
 	/* Set the leds off */
 	pwc_set_leds(pdev, 0, 0);
 	pwc_camera_power(pdev, 0);
 
+	rc = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr);
+	if (rc < 0) {
+		PWC_ERROR("Failed to register as video device (%d).\n", rc);
+		goto err_video_release;
+	}
+	rc = pwc_create_sysfs_files(pdev->vdev);
+	if (rc)
+		goto err_video_unreg;
+
+	PWC_INFO("Registered as %s.\n", video_device_node_name(pdev->vdev));
+
 #ifdef CONFIG_USB_PWC_INPUT_EVDEV
 	/* register webcam snapshot button input device */
 	pdev->button_dev = input_allocate_device();
@@ -1871,8 +1872,8 @@
 	struct pwc_device *pdev;
 	int hint;
 
-	lock_kernel();
 	pdev = usb_get_intfdata (intf);
+	mutex_lock(&pdev->modlock);
 	usb_set_intfdata (intf, NULL);
 	if (pdev == NULL) {
 		PWC_ERROR("pwc_disconnect() Called without private pointer.\n");
@@ -1897,9 +1898,7 @@
 	wake_up_interruptible(&pdev->frameq);
 	/* Wait until device is closed */
 	if (pdev->vopen) {
-		mutex_lock(&pdev->modlock);
 		pdev->unplugged = 1;
-		mutex_unlock(&pdev->modlock);
 		pwc_iso_stop(pdev);
 	} else {
 		/* Device is closed, so we can safely unregister it */
@@ -1913,7 +1912,7 @@
 				device_hint[hint].pdev = NULL;
 	}
 
-	unlock_kernel();
+	mutex_unlock(&pdev->modlock);
 }
 
 
diff --git a/drivers/media/video/pwc/pwc-misc.c b/drivers/media/video/pwc/pwc-misc.c
index 589c687..6af5bb5 100644
--- a/drivers/media/video/pwc/pwc-misc.c
+++ b/drivers/media/video/pwc/pwc-misc.c
@@ -47,7 +47,7 @@
 	   you don't have the decompressor loaded or use RAW mode,
 	   the maximum viewable size is smaller.
 	*/
-	if (pdev->vpalette == VIDEO_PALETTE_RAW)
+	if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
 	{
 		if (width > pdev->abs_max.x || height > pdev->abs_max.y)
 		{
@@ -123,7 +123,7 @@
 		pdev->frame_header_size = 0;
 		pdev->frame_trailer_size = 0;
 	}
-	pdev->vpalette = VIDEO_PALETTE_YUV420P; /* default */
+	pdev->pixfmt = V4L2_PIX_FMT_YUV420; /* default */
 	pdev->view_min.size = pdev->view_min.x * pdev->view_min.y;
 	pdev->view_max.size = pdev->view_max.x * pdev->view_max.y;
 	/* length of image, in YUV format; always allocate enough memory. */
diff --git a/drivers/media/video/pwc/pwc-uncompress.c b/drivers/media/video/pwc/pwc-uncompress.c
index 5d82028..3b73f29 100644
--- a/drivers/media/video/pwc/pwc-uncompress.c
+++ b/drivers/media/video/pwc/pwc-uncompress.c
@@ -54,7 +54,7 @@
 	yuv = fbuf->data + pdev->frame_header_size;  /* Skip header */
 
 	/* Raw format; that's easy... */
-	if (pdev->vpalette == VIDEO_PALETTE_RAW)
+	if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
 	{
 		struct pwc_raw_frame *raw_frame = image;
 		raw_frame->type = cpu_to_le16(pdev->type);
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index 62d89b3..7061a03f 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -216,7 +216,7 @@
 	f->fmt.pix.width        = pdev->view.x;
 	f->fmt.pix.height       = pdev->view.y;
 	f->fmt.pix.field        = V4L2_FIELD_NONE;
-	if (pdev->vpalette == VIDEO_PALETTE_YUV420P) {
+	if (pdev->pixfmt == V4L2_PIX_FMT_YUV420) {
 		f->fmt.pix.pixelformat  = V4L2_PIX_FMT_YUV420;
 		f->fmt.pix.bytesperline = (f->fmt.pix.width * 3)/2;
 		f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
@@ -304,10 +304,10 @@
 			fps = pdev->vframes;
 	}
 
-	if (pixelformat == V4L2_PIX_FMT_YUV420)
-		pdev->vpalette = VIDEO_PALETTE_YUV420P;
-	else
-		pdev->vpalette = VIDEO_PALETTE_RAW;
+	if (pixelformat != V4L2_PIX_FMT_YUV420 &&
+	    pixelformat != V4L2_PIX_FMT_PWC1 &&
+	    pixelformat != V4L2_PIX_FMT_PWC2)
+		return -EINVAL;
 
 	PWC_DEBUG_IOCTL("Try to change format to: width=%d height=%d fps=%d "
 			"compression=%d snapshot=%d format=%c%c%c%c\n",
@@ -330,6 +330,8 @@
 	if (ret)
 		return ret;
 
+	pdev->pixfmt = pixelformat;
+
 	pwc_vidioc_fill_fmt(pdev, f);
 
 	return 0;
@@ -357,152 +359,7 @@
 
 
 	switch (cmd) {
-		/* Query cabapilities */
-		case VIDIOCGCAP:
-		{
-			struct video_capability *caps = arg;
-
-			strcpy(caps->name, vdev->name);
-			caps->type = VID_TYPE_CAPTURE;
-			caps->channels = 1;
-			caps->audios = 1;
-			caps->minwidth  = pdev->view_min.x;
-			caps->minheight = pdev->view_min.y;
-			caps->maxwidth  = pdev->view_max.x;
-			caps->maxheight = pdev->view_max.y;
-			break;
-		}
-
-		/* Channel functions (simulate 1 channel) */
-		case VIDIOCGCHAN:
-		{
-			struct video_channel *v = arg;
-
-			if (v->channel != 0)
-				return -EINVAL;
-			v->flags = 0;
-			v->tuners = 0;
-			v->type = VIDEO_TYPE_CAMERA;
-			strcpy(v->name, "Webcam");
-			return 0;
-		}
-
-		case VIDIOCSCHAN:
-		{
-			/* The spec says the argument is an integer, but
-			   the bttv driver uses a video_channel arg, which
-			   makes sense becasue it also has the norm flag.
-			 */
-			struct video_channel *v = arg;
-			if (v->channel != 0)
-				return -EINVAL;
-			return 0;
-		}
-
-
-		/* Picture functions; contrast etc. */
-		case VIDIOCGPICT:
-		{
-			struct video_picture *p = arg;
-			int val;
-
-			val = pwc_get_brightness(pdev);
-			if (val >= 0)
-				p->brightness = (val<<9);
-			else
-				p->brightness = 0xffff;
-			val = pwc_get_contrast(pdev);
-			if (val >= 0)
-				p->contrast = (val<<10);
-			else
-				p->contrast = 0xffff;
-			/* Gamma, Whiteness, what's the difference? :) */
-			val = pwc_get_gamma(pdev);
-			if (val >= 0)
-				p->whiteness = (val<<11);
-			else
-				p->whiteness = 0xffff;
-			if (pwc_get_saturation(pdev, &val)<0)
-				p->colour = 0xffff;
-			else
-				p->colour = 32768 + val * 327;
-			p->depth = 24;
-			p->palette = pdev->vpalette;
-			p->hue = 0xFFFF; /* N/A */
-			break;
-		}
-
-		case VIDIOCSPICT:
-		{
-			struct video_picture *p = arg;
-			/*
-			 *	FIXME:	Suppose we are mid read
-				ANSWER: No problem: the firmware of the camera
-					can handle brightness/contrast/etc
-					changes at _any_ time, and the palette
-					is used exactly once in the uncompress
-					routine.
-			 */
-			pwc_set_brightness(pdev, p->brightness);
-			pwc_set_contrast(pdev, p->contrast);
-			pwc_set_gamma(pdev, p->whiteness);
-			pwc_set_saturation(pdev, (p->colour-32768)/327);
-			if (p->palette && p->palette != pdev->vpalette) {
-				switch (p->palette) {
-					case VIDEO_PALETTE_YUV420P:
-					case VIDEO_PALETTE_RAW:
-						pdev->vpalette = p->palette;
-						return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
-						break;
-					default:
-						return -EINVAL;
-						break;
-				}
-			}
-			break;
-		}
-
-		/* Window/size parameters */
-		case VIDIOCGWIN:
-		{
-			struct video_window *vw = arg;
-
-			vw->x = 0;
-			vw->y = 0;
-			vw->width = pdev->view.x;
-			vw->height = pdev->view.y;
-			vw->chromakey = 0;
-			vw->flags = (pdev->vframes << PWC_FPS_SHIFT) |
-				   (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0);
-			break;
-		}
-
-		case VIDIOCSWIN:
-		{
-			struct video_window *vw = arg;
-			int fps, snapshot, ret;
-
-			fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT;
-			snapshot = vw->flags & PWC_FPS_SNAPSHOT;
-			if (fps == 0)
-				fps = pdev->vframes;
-			if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot)
-				return 0;
-			ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot);
-			if (ret)
-				return ret;
-			break;
-		}
-
-		/* We don't have overlay support (yet) */
-		case VIDIOCGFBUF:
-		{
-			struct video_buffer *vb = arg;
-
-			memset(vb,0,sizeof(*vb));
-			break;
-		}
-
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
 		/* mmap() functions */
 		case VIDIOCGMBUF:
 		{
@@ -517,164 +374,7 @@
 				vm->offsets[i] = i * pdev->len_per_image;
 			break;
 		}
-
-		case VIDIOCMCAPTURE:
-		{
-			/* Start capture into a given image buffer (called 'frame' in video_mmap structure) */
-			struct video_mmap *vm = arg;
-
-			PWC_DEBUG_READ("VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format);
-			if (vm->frame < 0 || vm->frame >= pwc_mbufs)
-				return -EINVAL;
-
-			/* xawtv is nasty. It probes the available palettes
-			   by setting a very small image size and trying
-			   various palettes... The driver doesn't support
-			   such small images, so I'm working around it.
-			 */
-			if (vm->format)
-			{
-				switch (vm->format)
-				{
-					case VIDEO_PALETTE_YUV420P:
-					case VIDEO_PALETTE_RAW:
-						break;
-					default:
-						return -EINVAL;
-						break;
-				}
-			}
-
-			if ((vm->width != pdev->view.x || vm->height != pdev->view.y) &&
-			    (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) {
-				int ret;
-
-				PWC_DEBUG_OPEN("VIDIOCMCAPTURE: changing size to please xawtv :-(.\n");
-				ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
-				if (ret)
-					return ret;
-			} /* ... size mismatch */
-
-			/* FIXME: should we lock here? */
-			if (pdev->image_used[vm->frame])
-				return -EBUSY;	/* buffer wasn't available. Bummer */
-			pdev->image_used[vm->frame] = 1;
-
-			/* Okay, we're done here. In the SYNC call we wait until a
-			   frame comes available, then expand image into the given
-			   buffer.
-			   In contrast to the CPiA cam the Philips cams deliver a
-			   constant stream, almost like a grabber card. Also,
-			   we have separate buffers for the rawdata and the image,
-			   meaning we can nearly always expand into the requested buffer.
-			 */
-			PWC_DEBUG_READ("VIDIOCMCAPTURE done.\n");
-			break;
-		}
-
-		case VIDIOCSYNC:
-		{
-			/* The doc says: "Whenever a buffer is used it should
-			   call VIDIOCSYNC to free this frame up and continue."
-
-			   The only odd thing about this whole procedure is
-			   that MCAPTURE flags the buffer as "in use", and
-			   SYNC immediately unmarks it, while it isn't
-			   after SYNC that you know that the buffer actually
-			   got filled! So you better not start a CAPTURE in
-			   the same frame immediately (use double buffering).
-			   This is not a problem for this cam, since it has
-			   extra intermediate buffers, but a hardware
-			   grabber card will then overwrite the buffer
-			   you're working on.
-			 */
-			int *mbuf = arg;
-			int ret;
-
-			PWC_DEBUG_READ("VIDIOCSYNC called (%d).\n", *mbuf);
-
-			/* bounds check */
-			if (*mbuf < 0 || *mbuf >= pwc_mbufs)
-				return -EINVAL;
-			/* check if this buffer was requested anyway */
-			if (pdev->image_used[*mbuf] == 0)
-				return -EINVAL;
-
-			/* Add ourselves to the frame wait-queue.
-
-			   FIXME: needs auditing for safety.
-			   QUESTION: In what respect? I think that using the
-				     frameq is safe now.
-			 */
-			add_wait_queue(&pdev->frameq, &wait);
-			while (pdev->full_frames == NULL) {
-				/* Check for unplugged/etc. here */
-				if (pdev->error_status) {
-					remove_wait_queue(&pdev->frameq, &wait);
-					set_current_state(TASK_RUNNING);
-					return -pdev->error_status;
-				}
-
-				if (signal_pending(current)) {
-					remove_wait_queue(&pdev->frameq, &wait);
-					set_current_state(TASK_RUNNING);
-					return -ERESTARTSYS;
-				}
-				schedule();
-				set_current_state(TASK_INTERRUPTIBLE);
-			}
-			remove_wait_queue(&pdev->frameq, &wait);
-			set_current_state(TASK_RUNNING);
-
-			/* The frame is ready. Expand in the image buffer
-			   requested by the user. I don't care if you
-			   mmap() 5 buffers and request data in this order:
-			   buffer 4 2 3 0 1 2 3 0 4 3 1 . . .
-			   Grabber hardware may not be so forgiving.
-			 */
-			PWC_DEBUG_READ("VIDIOCSYNC: frame ready.\n");
-			pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */
-			/* Decompress, etc */
-			ret = pwc_handle_frame(pdev);
-			pdev->image_used[*mbuf] = 0;
-			if (ret)
-				return -EFAULT;
-			break;
-		}
-
-		case VIDIOCGAUDIO:
-		{
-			struct video_audio *v = arg;
-
-			strcpy(v->name, "Microphone");
-			v->audio = -1; /* unknown audio minor */
-			v->flags = 0;
-			v->mode = VIDEO_SOUND_MONO;
-			v->volume = 0;
-			v->bass = 0;
-			v->treble = 0;
-			v->balance = 0x8000;
-			v->step = 1;
-			break;
-		}
-
-		case VIDIOCSAUDIO:
-		{
-			/* Dummy: nothing can be set */
-			break;
-		}
-
-		case VIDIOCGUNIT:
-		{
-			struct video_unit *vu = arg;
-
-			vu->video = pdev->vdev->minor & 0x3F;
-			vu->audio = -1; /* not known yet */
-			vu->vbi = -1;
-			vu->radio = -1;
-			vu->teletext = -1;
-			break;
-		}
+#endif
 
 		/* V4L2 Layer */
 		case VIDIOC_QUERYCAP:
@@ -1081,7 +781,7 @@
 			buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 			buf->index = index;
 			buf->m.offset = index * pdev->len_per_image;
-			if (pdev->vpalette == VIDEO_PALETTE_RAW)
+			if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
 				buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
 			else
 				buf->bytesused = pdev->view.size;
@@ -1158,7 +858,7 @@
 			PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n");
 
 			buf->index = pdev->fill_image;
-			if (pdev->vpalette == VIDEO_PALETTE_RAW)
+			if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
 				buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
 			else
 				buf->bytesused = pdev->view.size;
diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
index f1b2066..36a9c83 100644
--- a/drivers/media/video/pwc/pwc.h
+++ b/drivers/media/video/pwc/pwc.h
@@ -34,7 +34,7 @@
 #include <linux/mm.h>
 #include <linux/slab.h>
 #include <asm/errno.h>
-#include <linux/videodev.h>
+#include <linux/videodev2.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 #ifdef CONFIG_USB_PWC_INPUT_EVDEV
@@ -49,7 +49,7 @@
 #define PWC_MINOR	0
 #define PWC_EXTRAMINOR	12
 #define PWC_VERSION_CODE KERNEL_VERSION(PWC_MAJOR,PWC_MINOR,PWC_EXTRAMINOR)
-#define PWC_VERSION 	"10.0.13"
+#define PWC_VERSION	"10.0.14"
 #define PWC_NAME 	"pwc"
 #define PFX		PWC_NAME ": "
 
@@ -180,7 +180,7 @@
    int vcinterface;		/* video control interface */
    int valternate;		/* alternate interface needed */
    int vframes, vsize;		/* frames-per-second & size (see PSZ_*) */
-   int vpalette;		/* palette: 420P, RAW or RGBBAYER */
+   int pixfmt;			/* pixelformat: V4L2_PIX_FMT_YUV420 or raw: _PWC1, _PWC2 */
    int vframe_count;		/* received frames */
    int vframes_dumped; 		/* counter for dumped frames */
    int vframes_error;		/* frames received in error */
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
index 9de7d59..c143ed0 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/video/pxa_camera.c
@@ -275,7 +275,7 @@
 	 * This waits until this buffer is out of danger, i.e., until it is no
 	 * longer in STATE_QUEUED or STATE_ACTIVE
 	 */
-	videobuf_waiton(&buf->vb, 0, 0);
+	videobuf_waiton(vq, &buf->vb, 0, 0);
 	videobuf_dma_unmap(vq->dev, dma);
 	videobuf_dma_free(dma);
 
@@ -852,7 +852,7 @@
 	 */
 	videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock,
 				V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
-				sizeof(struct pxa_buffer), icd);
+				sizeof(struct pxa_buffer), icd, NULL);
 }
 
 static u32 mclk_get_divisor(struct platform_device *pdev,
@@ -1539,7 +1539,7 @@
 	return ret;
 }
 
-static int pxa_camera_reqbufs(struct soc_camera_file *icf,
+static int pxa_camera_reqbufs(struct soc_camera_device *icd,
 			      struct v4l2_requestbuffers *p)
 {
 	int i;
@@ -1551,7 +1551,7 @@
 	 * it hadn't triggered
 	 */
 	for (i = 0; i < p->count; i++) {
-		struct pxa_buffer *buf = container_of(icf->vb_vidq.bufs[i],
+		struct pxa_buffer *buf = container_of(icd->vb_vidq.bufs[i],
 						      struct pxa_buffer, vb);
 		buf->inwork = 0;
 		INIT_LIST_HEAD(&buf->vb.queue);
@@ -1562,10 +1562,10 @@
 
 static unsigned int pxa_camera_poll(struct file *file, poll_table *pt)
 {
-	struct soc_camera_file *icf = file->private_data;
+	struct soc_camera_device *icd = file->private_data;
 	struct pxa_buffer *buf;
 
-	buf = list_entry(icf->vb_vidq.stream.next, struct pxa_buffer,
+	buf = list_entry(icd->vb_vidq.stream.next, struct pxa_buffer,
 			 vb.stream);
 
 	poll_wait(file, &buf->vb.done, pt);
diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c
index ce78fff..d2fa2d4 100644
--- a/drivers/media/video/rj54n1cb0c.c
+++ b/drivers/media/video/rj54n1cb0c.c
@@ -493,7 +493,7 @@
 
 static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	/* Switch between preview and still shot modes */
 	return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80);
@@ -503,7 +503,7 @@
 				unsigned long flags)
 {
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	/* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
 
 	if (flags & SOCAM_PCLK_SAMPLE_RISING)
@@ -560,7 +560,7 @@
 
 static int rj54n1_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct rj54n1 *rj54n1 = to_rj54n1(client);
 	struct v4l2_rect *rect = &a->c;
 	int dummy = 0, output_w, output_h,
@@ -595,7 +595,7 @@
 
 static int rj54n1_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct rj54n1 *rj54n1 = to_rj54n1(client);
 
 	a->c	= rj54n1->rect;
@@ -621,7 +621,7 @@
 static int rj54n1_g_fmt(struct v4l2_subdev *sd,
 			struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct rj54n1 *rj54n1 = to_rj54n1(client);
 
 	mf->code	= rj54n1->fmt->code;
@@ -641,7 +641,7 @@
 static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h,
 			       s32 *out_w, s32 *out_h)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct rj54n1 *rj54n1 = to_rj54n1(client);
 	unsigned int skip, resize, input_w = *in_w, input_h = *in_h,
 		output_w = *out_w, output_h = *out_h;
@@ -983,7 +983,7 @@
 static int rj54n1_try_fmt(struct v4l2_subdev *sd,
 			  struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct rj54n1 *rj54n1 = to_rj54n1(client);
 	const struct rj54n1_datafmt *fmt;
 	int align = mf->code == V4L2_MBUS_FMT_SBGGR10_1X10 ||
@@ -1014,7 +1014,7 @@
 static int rj54n1_s_fmt(struct v4l2_subdev *sd,
 			struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct rj54n1 *rj54n1 = to_rj54n1(client);
 	const struct rj54n1_datafmt *fmt;
 	int output_w, output_h, max_w, max_h,
@@ -1145,7 +1145,7 @@
 static int rj54n1_g_chip_ident(struct v4l2_subdev *sd,
 			       struct v4l2_dbg_chip_ident *id)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
 		return -EINVAL;
@@ -1163,7 +1163,7 @@
 static int rj54n1_g_register(struct v4l2_subdev *sd,
 			     struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR ||
 	    reg->reg < 0x400 || reg->reg > 0x1fff)
@@ -1185,7 +1185,7 @@
 static int rj54n1_s_register(struct v4l2_subdev *sd,
 			     struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR ||
 	    reg->reg < 0x400 || reg->reg > 0x1fff)
@@ -1248,7 +1248,7 @@
 
 static int rj54n1_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct rj54n1 *rj54n1 = to_rj54n1(client);
 	int data;
 
@@ -1283,7 +1283,7 @@
 static int rj54n1_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 {
 	int data;
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct rj54n1 *rj54n1 = to_rj54n1(client);
 	const struct v4l2_queryctrl *qctrl;
 
diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c
index 8ec7c9a..f5a46c4 100644
--- a/drivers/media/video/s2255drv.c
+++ b/drivers/media/video/s2255drv.c
@@ -600,7 +600,7 @@
 	dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i);
 unlock:
 	spin_unlock_irqrestore(&dev->slock, flags);
-	return 0;
+	return rc;
 }
 
 static const struct s2255_fmt *format_by_fourcc(int fourcc)
@@ -1817,7 +1817,7 @@
 				    NULL, &dev->slock,
 				    fh->type,
 				    V4L2_FIELD_INTERLACED,
-				    sizeof(struct s2255_buffer), fh);
+				    sizeof(struct s2255_buffer), fh, NULL);
 	return 0;
 }
 
diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile
index 0d9d541..7ea1b14 100644
--- a/drivers/media/video/s5p-fimc/Makefile
+++ b/drivers/media/video/s5p-fimc/Makefile
@@ -1,3 +1,3 @@
 
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) := s5p-fimc.o
-s5p-fimc-y := fimc-core.o fimc-reg.o
+s5p-fimc-y := fimc-core.o fimc-reg.o fimc-capture.o
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c
new file mode 100644
index 0000000..e8f13d3
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-capture.c
@@ -0,0 +1,819 @@
+/*
+ * Samsung S5P SoC series camera interface (camera capture) driver
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd
+ * Author: Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf-core.h>
+#include <media/videobuf-dma-contig.h>
+
+#include "fimc-core.h"
+
+static struct v4l2_subdev *fimc_subdev_register(struct fimc_dev *fimc,
+					    struct s3c_fimc_isp_info *isp_info)
+{
+	struct i2c_adapter *i2c_adap;
+	struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+	struct v4l2_subdev *sd = NULL;
+
+	i2c_adap = i2c_get_adapter(isp_info->i2c_bus_num);
+	if (!i2c_adap)
+		return ERR_PTR(-ENOMEM);
+
+	sd = v4l2_i2c_new_subdev_board(&vid_cap->v4l2_dev, i2c_adap,
+				       MODULE_NAME, isp_info->board_info, NULL);
+	if (!sd) {
+		v4l2_err(&vid_cap->v4l2_dev, "failed to acquire subdev\n");
+		return NULL;
+	}
+
+	v4l2_info(&vid_cap->v4l2_dev, "subdevice %s registered successfuly\n",
+		isp_info->board_info->type);
+
+	return sd;
+}
+
+static void fimc_subdev_unregister(struct fimc_dev *fimc)
+{
+	struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+	struct i2c_client *client;
+
+	if (vid_cap->input_index < 0)
+		return;	/* Subdevice already released or not registered. */
+
+	if (vid_cap->sd) {
+		v4l2_device_unregister_subdev(vid_cap->sd);
+		client = v4l2_get_subdevdata(vid_cap->sd);
+		i2c_unregister_device(client);
+		i2c_put_adapter(client->adapter);
+		vid_cap->sd = NULL;
+	}
+
+	vid_cap->input_index = -1;
+}
+
+/**
+ * fimc_subdev_attach - attach v4l2_subdev to camera host interface
+ *
+ * @fimc: FIMC device information
+ * @index: index to the array of available subdevices,
+ *	   -1 for full array search or non negative value
+ *	   to select specific subdevice
+ */
+static int fimc_subdev_attach(struct fimc_dev *fimc, int index)
+{
+	struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+	struct s3c_platform_fimc *pdata = fimc->pdata;
+	struct s3c_fimc_isp_info *isp_info;
+	struct v4l2_subdev *sd;
+	int i;
+
+	for (i = 0; i < FIMC_MAX_CAMIF_CLIENTS; ++i) {
+		isp_info = pdata->isp_info[i];
+
+		if (!isp_info || (index >= 0 && i != index))
+			continue;
+
+		sd = fimc_subdev_register(fimc, isp_info);
+		if (sd) {
+			vid_cap->sd = sd;
+			vid_cap->input_index = i;
+
+			return 0;
+		}
+	}
+
+	vid_cap->input_index = -1;
+	vid_cap->sd = NULL;
+	v4l2_err(&vid_cap->v4l2_dev, "fimc%d: sensor attach failed\n",
+		 fimc->id);
+	return -ENODEV;
+}
+
+static int fimc_isp_subdev_init(struct fimc_dev *fimc, int index)
+{
+	struct s3c_fimc_isp_info *isp_info;
+	int ret;
+
+	ret = fimc_subdev_attach(fimc, index);
+	if (ret)
+		return ret;
+
+	isp_info = fimc->pdata->isp_info[fimc->vid_cap.input_index];
+	ret = fimc_hw_set_camera_polarity(fimc, isp_info);
+	if (!ret) {
+		ret = v4l2_subdev_call(fimc->vid_cap.sd, core,
+				       s_power, 1);
+		if (!ret)
+			return ret;
+	}
+
+	fimc_subdev_unregister(fimc);
+	err("ISP initialization failed: %d", ret);
+	return ret;
+}
+
+/*
+ * At least one buffer on the pending_buf_q queue is required.
+ * Locking: The caller holds fimc->slock spinlock.
+ */
+int fimc_vid_cap_buf_queue(struct fimc_dev *fimc,
+			     struct fimc_vid_buffer *fimc_vb)
+{
+	struct fimc_vid_cap *cap = &fimc->vid_cap;
+	struct fimc_ctx *ctx = cap->ctx;
+	int ret = 0;
+
+	BUG_ON(!fimc || !fimc_vb);
+
+	ret = fimc_prepare_addr(ctx, fimc_vb, &ctx->d_frame,
+				&fimc_vb->paddr);
+	if (ret)
+		return ret;
+
+	if (test_bit(ST_CAPT_STREAM, &fimc->state)) {
+		fimc_pending_queue_add(cap, fimc_vb);
+	} else {
+		/* Setup the buffer directly for processing. */
+		int buf_id = (cap->reqbufs_count == 1) ? -1 : cap->buf_index;
+		fimc_hw_set_output_addr(fimc, &fimc_vb->paddr, buf_id);
+
+		fimc_vb->index = cap->buf_index;
+		active_queue_add(cap, fimc_vb);
+
+		if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
+			cap->buf_index = 0;
+	}
+	return ret;
+}
+
+static int fimc_stop_capture(struct fimc_dev *fimc)
+{
+	unsigned long flags;
+	struct fimc_vid_cap *cap;
+	int ret;
+
+	cap = &fimc->vid_cap;
+
+	if (!fimc_capture_active(fimc))
+		return 0;
+
+	spin_lock_irqsave(&fimc->slock, flags);
+	set_bit(ST_CAPT_SHUT, &fimc->state);
+	fimc_deactivate_capture(fimc);
+	spin_unlock_irqrestore(&fimc->slock, flags);
+
+	wait_event_timeout(fimc->irq_queue,
+			   test_bit(ST_CAPT_SHUT, &fimc->state),
+			   FIMC_SHUTDOWN_TIMEOUT);
+
+	ret = v4l2_subdev_call(cap->sd, video, s_stream, 0);
+	if (ret)
+		v4l2_err(&fimc->vid_cap.v4l2_dev, "s_stream(0) failed\n");
+
+	spin_lock_irqsave(&fimc->slock, flags);
+	fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_PEND |
+			1 << ST_CAPT_STREAM);
+
+	fimc->vid_cap.active_buf_cnt = 0;
+	spin_unlock_irqrestore(&fimc->slock, flags);
+
+	dbg("state: 0x%lx", fimc->state);
+	return 0;
+}
+
+static int fimc_capture_open(struct file *file)
+{
+	struct fimc_dev *fimc = video_drvdata(file);
+	int ret = 0;
+
+	dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
+
+	/* Return if the corresponding video mem2mem node is already opened. */
+	if (fimc_m2m_active(fimc))
+		return -EBUSY;
+
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
+	if (++fimc->vid_cap.refcnt == 1) {
+		ret = fimc_isp_subdev_init(fimc, -1);
+		if (ret) {
+			fimc->vid_cap.refcnt--;
+			ret = -EIO;
+		}
+	}
+
+	file->private_data = fimc->vid_cap.ctx;
+
+	mutex_unlock(&fimc->lock);
+	return ret;
+}
+
+static int fimc_capture_close(struct file *file)
+{
+	struct fimc_dev *fimc = video_drvdata(file);
+
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
+	dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
+
+	if (--fimc->vid_cap.refcnt == 0) {
+		fimc_stop_capture(fimc);
+
+		videobuf_stop(&fimc->vid_cap.vbq);
+		videobuf_mmap_free(&fimc->vid_cap.vbq);
+
+		v4l2_err(&fimc->vid_cap.v4l2_dev, "releasing ISP\n");
+		v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0);
+		fimc_subdev_unregister(fimc);
+	}
+
+	mutex_unlock(&fimc->lock);
+	return 0;
+}
+
+static unsigned int fimc_capture_poll(struct file *file,
+				      struct poll_table_struct *wait)
+{
+	struct fimc_ctx *ctx = file->private_data;
+	struct fimc_dev *fimc = ctx->fimc_dev;
+	struct fimc_vid_cap *cap = &fimc->vid_cap;
+	int ret;
+
+	if (mutex_lock_interruptible(&fimc->lock))
+		return POLLERR;
+
+	ret = videobuf_poll_stream(file, &cap->vbq, wait);
+	mutex_unlock(&fimc->lock);
+
+	return ret;
+}
+
+static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct fimc_ctx *ctx = file->private_data;
+	struct fimc_dev *fimc = ctx->fimc_dev;
+	struct fimc_vid_cap *cap = &fimc->vid_cap;
+	int ret;
+
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
+	ret = videobuf_mmap_mapper(&cap->vbq, vma);
+	mutex_unlock(&fimc->lock);
+
+	return ret;
+}
+
+/* video device file operations */
+static const struct v4l2_file_operations fimc_capture_fops = {
+	.owner		= THIS_MODULE,
+	.open		= fimc_capture_open,
+	.release	= fimc_capture_close,
+	.poll		= fimc_capture_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= fimc_capture_mmap,
+};
+
+static int fimc_vidioc_querycap_capture(struct file *file, void *priv,
+					struct v4l2_capability *cap)
+{
+	struct fimc_ctx *ctx = file->private_data;
+	struct fimc_dev *fimc = ctx->fimc_dev;
+
+	strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
+	strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
+	cap->bus_info[0] = 0;
+	cap->version = KERNEL_VERSION(1, 0, 0);
+	cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
+
+	return 0;
+}
+
+/* Synchronize formats of the camera interface input and attached  sensor. */
+static int sync_capture_fmt(struct fimc_ctx *ctx)
+{
+	struct fimc_frame *frame = &ctx->s_frame;
+	struct fimc_dev *fimc = ctx->fimc_dev;
+	struct v4l2_mbus_framefmt *fmt = &fimc->vid_cap.fmt;
+	int ret;
+
+	fmt->width  = ctx->d_frame.o_width;
+	fmt->height = ctx->d_frame.o_height;
+
+	ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_mbus_fmt, fmt);
+	if (ret == -ENOIOCTLCMD) {
+		err("s_mbus_fmt failed");
+		return ret;
+	}
+	dbg("w: %d, h: %d, code= %d", fmt->width, fmt->height, fmt->code);
+
+	frame->fmt = find_mbus_format(fmt, FMT_FLAGS_CAM);
+	if (!frame->fmt) {
+		err("fimc source format not found\n");
+		return -EINVAL;
+	}
+
+	frame->f_width	= fmt->width;
+	frame->f_height = fmt->height;
+	frame->width	= fmt->width;
+	frame->height	= fmt->height;
+	frame->o_width	= fmt->width;
+	frame->o_height = fmt->height;
+	frame->offs_h	= 0;
+	frame->offs_v	= 0;
+
+	return 0;
+}
+
+static int fimc_cap_s_fmt(struct file *file, void *priv,
+			     struct v4l2_format *f)
+{
+	struct fimc_ctx *ctx = priv;
+	struct fimc_dev *fimc = ctx->fimc_dev;
+	struct fimc_frame *frame;
+	struct v4l2_pix_format *pix;
+	int ret;
+
+	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	ret = fimc_vidioc_try_fmt(file, priv, f);
+	if (ret)
+		return ret;
+
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
+	if (fimc_capture_active(fimc)) {
+		ret = -EBUSY;
+		goto sf_unlock;
+	}
+
+	frame = &ctx->d_frame;
+
+	pix = &f->fmt.pix;
+	frame->fmt = find_format(f, FMT_FLAGS_M2M | FMT_FLAGS_CAM);
+	if (!frame->fmt) {
+		err("fimc target format not found\n");
+		ret = -EINVAL;
+		goto sf_unlock;
+	}
+
+	/* Output DMA frame pixel size and offsets. */
+	frame->f_width	= pix->bytesperline * 8 / frame->fmt->depth;
+	frame->f_height = pix->height;
+	frame->width	= pix->width;
+	frame->height	= pix->height;
+	frame->o_width	= pix->width;
+	frame->o_height = pix->height;
+	frame->size	= (pix->width * pix->height * frame->fmt->depth) >> 3;
+	frame->offs_h	= 0;
+	frame->offs_v	= 0;
+
+	ret = sync_capture_fmt(ctx);
+
+	ctx->state |= (FIMC_PARAMS | FIMC_DST_FMT);
+
+sf_unlock:
+	mutex_unlock(&fimc->lock);
+	return ret;
+}
+
+static int fimc_cap_enum_input(struct file *file, void *priv,
+				     struct v4l2_input *i)
+{
+	struct fimc_ctx *ctx = priv;
+	struct s3c_platform_fimc *pldata = ctx->fimc_dev->pdata;
+	struct s3c_fimc_isp_info *isp_info;
+
+	if (i->index >= FIMC_MAX_CAMIF_CLIENTS)
+		return -EINVAL;
+
+	isp_info = pldata->isp_info[i->index];
+	if (isp_info == NULL)
+		return -EINVAL;
+
+	i->type = V4L2_INPUT_TYPE_CAMERA;
+	strncpy(i->name, isp_info->board_info->type, 32);
+	return 0;
+}
+
+static int fimc_cap_s_input(struct file *file, void *priv,
+				  unsigned int i)
+{
+	struct fimc_ctx *ctx = priv;
+	struct fimc_dev *fimc = ctx->fimc_dev;
+	struct s3c_platform_fimc *pdata = fimc->pdata;
+	int ret;
+
+	if (fimc_capture_active(ctx->fimc_dev))
+		return -EBUSY;
+
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
+	if (i >= FIMC_MAX_CAMIF_CLIENTS || !pdata->isp_info[i]) {
+		ret = -EINVAL;
+		goto si_unlock;
+	}
+
+	if (fimc->vid_cap.sd) {
+		ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 0);
+		if (ret)
+			err("s_power failed: %d", ret);
+	}
+
+	/* Release the attached sensor subdevice. */
+	fimc_subdev_unregister(fimc);
+
+	ret = fimc_isp_subdev_init(fimc, i);
+
+si_unlock:
+	mutex_unlock(&fimc->lock);
+	return ret;
+}
+
+static int fimc_cap_g_input(struct file *file, void *priv,
+				       unsigned int *i)
+{
+	struct fimc_ctx *ctx = priv;
+	struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap;
+
+	*i = cap->input_index;
+	return 0;
+}
+
+static int fimc_cap_streamon(struct file *file, void *priv,
+			   enum v4l2_buf_type type)
+{
+	struct s3c_fimc_isp_info *isp_info;
+	struct fimc_ctx *ctx = priv;
+	struct fimc_dev *fimc = ctx->fimc_dev;
+	int ret = -EBUSY;
+
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
+	if (fimc_capture_active(fimc) || !fimc->vid_cap.sd)
+		goto s_unlock;
+
+	if (!(ctx->state & FIMC_DST_FMT)) {
+		v4l2_err(&fimc->vid_cap.v4l2_dev, "Format is not set\n");
+		ret = -EINVAL;
+		goto s_unlock;
+	}
+
+	ret = v4l2_subdev_call(fimc->vid_cap.sd, video, s_stream, 1);
+	if (ret && ret != -ENOIOCTLCMD)
+		goto s_unlock;
+
+	ret = fimc_prepare_config(ctx, ctx->state);
+	if (ret)
+		goto s_unlock;
+
+	isp_info = fimc->pdata->isp_info[fimc->vid_cap.input_index];
+	fimc_hw_set_camera_type(fimc, isp_info);
+	fimc_hw_set_camera_source(fimc, isp_info);
+	fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
+
+	if (ctx->state & FIMC_PARAMS) {
+		ret = fimc_set_scaler_info(ctx);
+		if (ret) {
+			err("Scaler setup error");
+			goto s_unlock;
+		}
+		fimc_hw_set_input_path(ctx);
+		fimc_hw_set_scaler(ctx);
+		fimc_hw_set_target_format(ctx);
+		fimc_hw_set_rotation(ctx);
+		fimc_hw_set_effect(ctx);
+	}
+
+	fimc_hw_set_output_path(ctx);
+	fimc_hw_set_out_dma(ctx);
+
+	INIT_LIST_HEAD(&fimc->vid_cap.pending_buf_q);
+	INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
+	fimc->vid_cap.active_buf_cnt = 0;
+	fimc->vid_cap.frame_count = 0;
+
+	set_bit(ST_CAPT_PEND, &fimc->state);
+	ret = videobuf_streamon(&fimc->vid_cap.vbq);
+
+s_unlock:
+	mutex_unlock(&fimc->lock);
+	return ret;
+}
+
+static int fimc_cap_streamoff(struct file *file, void *priv,
+			    enum v4l2_buf_type type)
+{
+	struct fimc_ctx *ctx = priv;
+	struct fimc_dev *fimc = ctx->fimc_dev;
+	struct fimc_vid_cap *cap = &fimc->vid_cap;
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&fimc->slock, flags);
+	if (!fimc_capture_running(fimc) && !fimc_capture_pending(fimc)) {
+		spin_unlock_irqrestore(&fimc->slock, flags);
+		dbg("state: 0x%lx", fimc->state);
+		return -EINVAL;
+	}
+	spin_unlock_irqrestore(&fimc->slock, flags);
+
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
+	fimc_stop_capture(fimc);
+	ret = videobuf_streamoff(&cap->vbq);
+	mutex_unlock(&fimc->lock);
+	return ret;
+}
+
+static int fimc_cap_reqbufs(struct file *file, void *priv,
+			  struct v4l2_requestbuffers *reqbufs)
+{
+	struct fimc_ctx *ctx = priv;
+	struct fimc_dev *fimc = ctx->fimc_dev;
+	struct fimc_vid_cap *cap = &fimc->vid_cap;
+	int ret;
+
+	if (fimc_capture_active(ctx->fimc_dev))
+		return -EBUSY;
+
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
+	ret = videobuf_reqbufs(&cap->vbq, reqbufs);
+	if (!ret)
+		cap->reqbufs_count = reqbufs->count;
+
+	mutex_unlock(&fimc->lock);
+	return ret;
+}
+
+static int fimc_cap_querybuf(struct file *file, void *priv,
+			   struct v4l2_buffer *buf)
+{
+	struct fimc_ctx *ctx = priv;
+	struct fimc_vid_cap *cap = &ctx->fimc_dev->vid_cap;
+
+	if (fimc_capture_active(ctx->fimc_dev))
+		return -EBUSY;
+
+	return videobuf_querybuf(&cap->vbq, buf);
+}
+
+static int fimc_cap_qbuf(struct file *file, void *priv,
+			  struct v4l2_buffer *buf)
+{
+	struct fimc_ctx *ctx = priv;
+	struct fimc_dev *fimc = ctx->fimc_dev;
+	struct fimc_vid_cap *cap = &fimc->vid_cap;
+	int ret;
+
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
+	ret = videobuf_qbuf(&cap->vbq, buf);
+
+	mutex_unlock(&fimc->lock);
+	return ret;
+}
+
+static int fimc_cap_dqbuf(struct file *file, void *priv,
+			   struct v4l2_buffer *buf)
+{
+	struct fimc_ctx *ctx = priv;
+	int ret;
+
+	if (mutex_lock_interruptible(&ctx->fimc_dev->lock))
+		return -ERESTARTSYS;
+
+	ret = videobuf_dqbuf(&ctx->fimc_dev->vid_cap.vbq, buf,
+		file->f_flags & O_NONBLOCK);
+
+	mutex_unlock(&ctx->fimc_dev->lock);
+	return ret;
+}
+
+static int fimc_cap_s_ctrl(struct file *file, void *priv,
+			 struct v4l2_control *ctrl)
+{
+	struct fimc_ctx *ctx = priv;
+	int ret = -EINVAL;
+
+	if (mutex_lock_interruptible(&ctx->fimc_dev->lock))
+		return -ERESTARTSYS;
+
+	/* Allow any controls but 90/270 rotation while streaming */
+	if (!fimc_capture_active(ctx->fimc_dev) ||
+	    ctrl->id != V4L2_CID_ROTATE ||
+	    (ctrl->value != 90 && ctrl->value != 270)) {
+		ret = check_ctrl_val(ctx, ctrl);
+		if (!ret) {
+			ret = fimc_s_ctrl(ctx, ctrl);
+			if (!ret)
+				ctx->state |= FIMC_PARAMS;
+		}
+	}
+	if (ret == -EINVAL)
+		ret = v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd,
+				       core, s_ctrl, ctrl);
+
+	mutex_unlock(&ctx->fimc_dev->lock);
+	return ret;
+}
+
+static int fimc_cap_s_crop(struct file *file, void *fh,
+			       struct v4l2_crop *cr)
+{
+	struct fimc_frame *f;
+	struct fimc_ctx *ctx = file->private_data;
+	struct fimc_dev *fimc = ctx->fimc_dev;
+	int ret = -EINVAL;
+
+	if (fimc_capture_active(fimc))
+		return -EBUSY;
+
+	ret = fimc_try_crop(ctx, cr);
+	if (ret)
+		return ret;
+
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
+	if (!(ctx->state & FIMC_DST_FMT)) {
+		v4l2_err(&fimc->vid_cap.v4l2_dev,
+			 "Capture color format not set\n");
+		goto sc_unlock;
+	}
+
+	f = &ctx->s_frame;
+	/* Check for the pixel scaling ratio when cropping input image. */
+	ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame);
+	if (ret) {
+		v4l2_err(&fimc->vid_cap.v4l2_dev, "Out of the scaler range");
+	} else {
+		ret = 0;
+		f->offs_h = cr->c.left;
+		f->offs_v = cr->c.top;
+		f->width  = cr->c.width;
+		f->height = cr->c.height;
+	}
+
+sc_unlock:
+	mutex_unlock(&fimc->lock);
+	return ret;
+}
+
+
+static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
+	.vidioc_querycap		= fimc_vidioc_querycap_capture,
+
+	.vidioc_enum_fmt_vid_cap	= fimc_vidioc_enum_fmt,
+	.vidioc_try_fmt_vid_cap		= fimc_vidioc_try_fmt,
+	.vidioc_s_fmt_vid_cap		= fimc_cap_s_fmt,
+	.vidioc_g_fmt_vid_cap		= fimc_vidioc_g_fmt,
+
+	.vidioc_reqbufs			= fimc_cap_reqbufs,
+	.vidioc_querybuf		= fimc_cap_querybuf,
+
+	.vidioc_qbuf			= fimc_cap_qbuf,
+	.vidioc_dqbuf			= fimc_cap_dqbuf,
+
+	.vidioc_streamon		= fimc_cap_streamon,
+	.vidioc_streamoff		= fimc_cap_streamoff,
+
+	.vidioc_queryctrl		= fimc_vidioc_queryctrl,
+	.vidioc_g_ctrl			= fimc_vidioc_g_ctrl,
+	.vidioc_s_ctrl			= fimc_cap_s_ctrl,
+
+	.vidioc_g_crop			= fimc_vidioc_g_crop,
+	.vidioc_s_crop			= fimc_cap_s_crop,
+	.vidioc_cropcap			= fimc_vidioc_cropcap,
+
+	.vidioc_enum_input		= fimc_cap_enum_input,
+	.vidioc_s_input			= fimc_cap_s_input,
+	.vidioc_g_input			= fimc_cap_g_input,
+};
+
+int fimc_register_capture_device(struct fimc_dev *fimc)
+{
+	struct v4l2_device *v4l2_dev = &fimc->vid_cap.v4l2_dev;
+	struct video_device *vfd;
+	struct fimc_vid_cap *vid_cap;
+	struct fimc_ctx *ctx;
+	struct v4l2_format f;
+	int ret;
+
+	ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->fimc_dev	 = fimc;
+	ctx->in_path	 = FIMC_CAMERA;
+	ctx->out_path	 = FIMC_DMA;
+	ctx->state	 = FIMC_CTX_CAP;
+
+	f.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
+	ctx->d_frame.fmt = find_format(&f, FMT_FLAGS_M2M);
+
+	if (!v4l2_dev->name[0])
+		snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
+			 "%s.capture", dev_name(&fimc->pdev->dev));
+
+	ret = v4l2_device_register(NULL, v4l2_dev);
+	if (ret)
+		goto err_info;
+
+	vfd = video_device_alloc();
+	if (!vfd) {
+		v4l2_err(v4l2_dev, "Failed to allocate video device\n");
+		goto err_v4l2_reg;
+	}
+
+	snprintf(vfd->name, sizeof(vfd->name), "%s:cap",
+		 dev_name(&fimc->pdev->dev));
+
+	vfd->fops	= &fimc_capture_fops;
+	vfd->ioctl_ops	= &fimc_capture_ioctl_ops;
+	vfd->minor	= -1;
+	vfd->release	= video_device_release;
+	video_set_drvdata(vfd, fimc);
+
+	vid_cap = &fimc->vid_cap;
+	vid_cap->vfd = vfd;
+	vid_cap->active_buf_cnt = 0;
+	vid_cap->reqbufs_count  = 0;
+	vid_cap->refcnt = 0;
+	/* The default color format for image sensor. */
+	vid_cap->fmt.code = V4L2_MBUS_FMT_YUYV8_2X8;
+
+	INIT_LIST_HEAD(&vid_cap->pending_buf_q);
+	INIT_LIST_HEAD(&vid_cap->active_buf_q);
+	spin_lock_init(&ctx->slock);
+	vid_cap->ctx = ctx;
+
+	videobuf_queue_dma_contig_init(&vid_cap->vbq, &fimc_qops,
+		vid_cap->v4l2_dev.dev, &fimc->irqlock,
+		V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+		sizeof(struct fimc_vid_buffer), (void *)ctx);
+
+	ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+	if (ret) {
+		v4l2_err(v4l2_dev, "Failed to register video device\n");
+		goto err_vd_reg;
+	}
+
+	v4l2_info(v4l2_dev,
+		  "FIMC capture driver registered as /dev/video%d\n",
+		  vfd->num);
+
+	return 0;
+
+err_vd_reg:
+	video_device_release(vfd);
+err_v4l2_reg:
+	v4l2_device_unregister(v4l2_dev);
+err_info:
+	dev_err(&fimc->pdev->dev, "failed to install\n");
+	return ret;
+}
+
+void fimc_unregister_capture_device(struct fimc_dev *fimc)
+{
+	struct fimc_vid_cap *capture = &fimc->vid_cap;
+
+	if (capture->vfd)
+		video_unregister_device(capture->vfd);
+
+	kfree(capture->ctx);
+}
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
index 6961c55..2e7c547 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -1,7 +1,7 @@
 /*
  * S5P camera interface (video postprocessor) driver
  *
- * Copyright (c) 2010 Samsung Electronics
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd
  *
  * Sylwester Nawrocki, <s.nawrocki@samsung.com>
  *
@@ -38,86 +38,103 @@
 		.depth	= 16,
 		.color	= S5P_FIMC_RGB565,
 		.buff_cnt = 1,
-		.planes_cnt = 1
+		.planes_cnt = 1,
+		.mbus_code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+		.flags = FMT_FLAGS_M2M,
 	}, {
 		.name	= "BGR666",
 		.fourcc	= V4L2_PIX_FMT_BGR666,
 		.depth	= 32,
 		.color	= S5P_FIMC_RGB666,
 		.buff_cnt = 1,
-		.planes_cnt = 1
+		.planes_cnt = 1,
+		.flags = FMT_FLAGS_M2M,
 	}, {
 		.name = "XRGB-8-8-8-8, 24 bpp",
 		.fourcc	= V4L2_PIX_FMT_RGB24,
 		.depth = 32,
 		.color	= S5P_FIMC_RGB888,
 		.buff_cnt = 1,
-		.planes_cnt = 1
+		.planes_cnt = 1,
+		.flags = FMT_FLAGS_M2M,
 	}, {
 		.name	= "YUV 4:2:2 packed, YCbYCr",
 		.fourcc	= V4L2_PIX_FMT_YUYV,
 		.depth	= 16,
 		.color	= S5P_FIMC_YCBYCR422,
 		.buff_cnt = 1,
-		.planes_cnt = 1
-		}, {
+		.planes_cnt = 1,
+		.mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+		.flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
+	}, {
 		.name	= "YUV 4:2:2 packed, CbYCrY",
 		.fourcc	= V4L2_PIX_FMT_UYVY,
 		.depth	= 16,
 		.color	= S5P_FIMC_CBYCRY422,
 		.buff_cnt = 1,
-		.planes_cnt = 1
+		.planes_cnt = 1,
+		.mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+		.flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
 	}, {
 		.name	= "YUV 4:2:2 packed, CrYCbY",
 		.fourcc	= V4L2_PIX_FMT_VYUY,
 		.depth	= 16,
 		.color	= S5P_FIMC_CRYCBY422,
 		.buff_cnt = 1,
-		.planes_cnt = 1
+		.planes_cnt = 1,
+		.mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,
+		.flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
 	}, {
 		.name	= "YUV 4:2:2 packed, YCrYCb",
 		.fourcc	= V4L2_PIX_FMT_YVYU,
 		.depth	= 16,
 		.color	= S5P_FIMC_YCRYCB422,
 		.buff_cnt = 1,
-		.planes_cnt = 1
+		.planes_cnt = 1,
+		.mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
+		.flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
 	}, {
 		.name	= "YUV 4:2:2 planar, Y/Cb/Cr",
 		.fourcc	= V4L2_PIX_FMT_YUV422P,
 		.depth	= 12,
 		.color	= S5P_FIMC_YCBCR422,
 		.buff_cnt = 1,
-		.planes_cnt = 3
+		.planes_cnt = 3,
+		.flags = FMT_FLAGS_M2M,
 	}, {
 		.name	= "YUV 4:2:2 planar, Y/CbCr",
 		.fourcc	= V4L2_PIX_FMT_NV16,
 		.depth	= 16,
 		.color	= S5P_FIMC_YCBCR422,
 		.buff_cnt = 1,
-		.planes_cnt = 2
+		.planes_cnt = 2,
+		.flags = FMT_FLAGS_M2M,
 	}, {
 		.name	= "YUV 4:2:2 planar, Y/CrCb",
 		.fourcc	= V4L2_PIX_FMT_NV61,
 		.depth	= 16,
 		.color	= S5P_FIMC_RGB565,
 		.buff_cnt = 1,
-		.planes_cnt = 2
+		.planes_cnt = 2,
+		.flags = FMT_FLAGS_M2M,
 	}, {
 		.name	= "YUV 4:2:0 planar, YCbCr",
 		.fourcc	= V4L2_PIX_FMT_YUV420,
 		.depth	= 12,
 		.color	= S5P_FIMC_YCBCR420,
 		.buff_cnt = 1,
-		.planes_cnt = 3
+		.planes_cnt = 3,
+		.flags = FMT_FLAGS_M2M,
 	}, {
 		.name	= "YUV 4:2:0 planar, Y/CbCr",
 		.fourcc	= V4L2_PIX_FMT_NV12,
 		.depth	= 12,
 		.color	= S5P_FIMC_YCBCR420,
 		.buff_cnt = 1,
-		.planes_cnt = 2
-	}
- };
+		.planes_cnt = 2,
+		.flags = FMT_FLAGS_M2M,
+	},
+};
 
 static struct v4l2_queryctrl fimc_ctrls[] = {
 	{
@@ -127,16 +144,14 @@
 		.minimum	= 0,
 		.maximum	= 1,
 		.default_value	= 0,
-	},
-	{
+	}, {
 		.id		= V4L2_CID_VFLIP,
 		.type		= V4L2_CTRL_TYPE_BOOLEAN,
 		.name		= "Vertical flip",
 		.minimum	= 0,
 		.maximum	= 1,
 		.default_value	= 0,
-	},
-	{
+	}, {
 		.id		= V4L2_CID_ROTATE,
 		.type		= V4L2_CTRL_TYPE_INTEGER,
 		.name		= "Rotation (CCW)",
@@ -158,7 +173,7 @@
 	return NULL;
 }
 
-static int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f)
+int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f)
 {
 	if (r->width > f->width) {
 		if (f->width > (r->width * SCALER_MAX_HRATIO))
@@ -181,32 +196,27 @@
 
 static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
 {
-	if (src >= tar * 64) {
+	u32 sh = 6;
+
+	if (src >= 64 * tar)
 		return -EINVAL;
-	} else if (src >= tar * 32) {
-		*ratio = 32;
-		*shift = 5;
-	} else if (src >= tar * 16) {
-		*ratio = 16;
-		*shift = 4;
-	} else if (src >= tar * 8) {
-		*ratio = 8;
-		*shift = 3;
-	} else if (src >= tar * 4) {
-		*ratio = 4;
-		*shift = 2;
-	} else if (src >= tar * 2) {
-		*ratio = 2;
-		*shift = 1;
-	} else {
-		*ratio = 1;
-		*shift = 0;
+
+	while (sh--) {
+		u32 tmp = 1 << sh;
+		if (src >= tar * tmp) {
+			*shift = sh, *ratio = tmp;
+			return 0;
+		}
 	}
 
+	*shift = 0, *ratio = 1;
+
+	dbg("s: %d, t: %d, shift: %d, ratio: %d",
+	    src, tar, *shift, *ratio);
 	return 0;
 }
 
-static int fimc_set_scaler_info(struct fimc_ctx *ctx)
+int fimc_set_scaler_info(struct fimc_ctx *ctx)
 {
 	struct fimc_scaler *sc = &ctx->scaler;
 	struct fimc_frame *s_frame = &ctx->s_frame;
@@ -214,8 +224,13 @@
 	int tx, ty, sx, sy;
 	int ret;
 
-	tx = d_frame->width;
-	ty = d_frame->height;
+	if (ctx->rotation == 90 || ctx->rotation == 270) {
+		ty = d_frame->width;
+		tx = d_frame->height;
+	} else {
+		tx = d_frame->width;
+		ty = d_frame->height;
+	}
 	if (tx <= 0 || ty <= 0) {
 		v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
 			"invalid target size: %d x %d", tx, ty);
@@ -261,12 +276,57 @@
 	return 0;
 }
 
+static void fimc_capture_handler(struct fimc_dev *fimc)
+{
+	struct fimc_vid_cap *cap = &fimc->vid_cap;
+	struct fimc_vid_buffer *v_buf = NULL;
+
+	if (!list_empty(&cap->active_buf_q)) {
+		v_buf = active_queue_pop(cap);
+		fimc_buf_finish(fimc, v_buf);
+	}
+
+	if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) {
+		wake_up(&fimc->irq_queue);
+		return;
+	}
+
+	if (!list_empty(&cap->pending_buf_q)) {
+
+		v_buf = pending_queue_pop(cap);
+		fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index);
+		v_buf->index = cap->buf_index;
+
+		dbg("hw ptr: %d, sw ptr: %d",
+		    fimc_hw_get_frame_index(fimc), cap->buf_index);
+
+		spin_lock(&fimc->irqlock);
+		v_buf->vb.state = VIDEOBUF_ACTIVE;
+		spin_unlock(&fimc->irqlock);
+
+		/* Move the buffer to the capture active queue */
+		active_queue_add(cap, v_buf);
+
+		dbg("next frame: %d, done frame: %d",
+		    fimc_hw_get_frame_index(fimc), v_buf->index);
+
+		if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
+			cap->buf_index = 0;
+
+	} else if (test_and_clear_bit(ST_CAPT_STREAM, &fimc->state) &&
+		   cap->active_buf_cnt <= 1) {
+		fimc_deactivate_capture(fimc);
+	}
+
+	dbg("frame: %d, active_buf_cnt= %d",
+	    fimc_hw_get_frame_index(fimc), cap->active_buf_cnt);
+}
 
 static irqreturn_t fimc_isr(int irq, void *priv)
 {
 	struct fimc_vid_buffer *src_buf, *dst_buf;
-	struct fimc_dev *fimc = (struct fimc_dev *)priv;
 	struct fimc_ctx *ctx;
+	struct fimc_dev *fimc = priv;
 
 	BUG_ON(!fimc);
 	fimc_hw_clear_irq(fimc);
@@ -281,12 +341,22 @@
 		dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
 		if (src_buf && dst_buf) {
 			spin_lock(&fimc->irqlock);
-			src_buf->vb.state = dst_buf->vb.state =  VIDEOBUF_DONE;
+			src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE;
 			wake_up(&src_buf->vb.done);
 			wake_up(&dst_buf->vb.done);
 			spin_unlock(&fimc->irqlock);
 			v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx);
 		}
+		goto isr_unlock;
+
+	}
+
+	if (test_bit(ST_CAPT_RUN, &fimc->state))
+		fimc_capture_handler(fimc);
+
+	if (test_and_clear_bit(ST_CAPT_PEND, &fimc->state)) {
+		set_bit(ST_CAPT_RUN, &fimc->state);
+		wake_up(&fimc->irq_queue);
 	}
 
 isr_unlock:
@@ -295,20 +365,13 @@
 }
 
 /* The color format (planes_cnt, buff_cnt) must be already configured. */
-static int fimc_prepare_addr(struct fimc_ctx *ctx,
-		struct fimc_vid_buffer *buf, enum v4l2_buf_type type)
+int fimc_prepare_addr(struct fimc_ctx *ctx, struct fimc_vid_buffer *buf,
+		      struct fimc_frame *frame, struct fimc_addr *paddr)
 {
-	struct fimc_frame *frame;
-	struct fimc_addr *paddr;
-	u32 pix_size;
 	int ret = 0;
+	u32 pix_size;
 
-	frame = ctx_m2m_get_frame(ctx, type);
-	if (IS_ERR(frame))
-		return PTR_ERR(frame);
-	paddr = &frame->paddr;
-
-	if (!buf)
+	if (buf == NULL || frame == NULL)
 		return -EINVAL;
 
 	pix_size = frame->width * frame->height;
@@ -344,8 +407,8 @@
 		}
 	}
 
-	dbg("PHYS_ADDR: type= %d, y= 0x%X  cb= 0x%X cr= 0x%X ret= %d",
-	type, paddr->y, paddr->cb, paddr->cr, ret);
+	dbg("PHYS_ADDR: y= 0x%X  cb= 0x%X cr= 0x%X ret= %d",
+	    paddr->y, paddr->cb, paddr->cr, ret);
 
 	return ret;
 }
@@ -433,7 +496,7 @@
  *
  * Return: 0 if dimensions are valid or non zero otherwise.
  */
-static int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags)
+int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags)
 {
 	struct fimc_frame *s_frame, *d_frame;
 	struct fimc_vid_buffer *buf = NULL;
@@ -443,12 +506,6 @@
 	d_frame = &ctx->d_frame;
 
 	if (flags & FIMC_PARAMS) {
-		if ((ctx->out_path == FIMC_DMA) &&
-			(ctx->rotation == 90 || ctx->rotation == 270)) {
-			swap(d_frame->f_width, d_frame->f_height);
-			swap(d_frame->width, d_frame->height);
-		}
-
 		/* Prepare the DMA offset ratios for scaler. */
 		fimc_prepare_dma_offset(ctx, &ctx->s_frame);
 		fimc_prepare_dma_offset(ctx, &ctx->d_frame);
@@ -466,16 +523,14 @@
 
 	if (flags & FIMC_SRC_ADDR) {
 		buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
-		ret = fimc_prepare_addr(ctx, buf,
-			V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		ret = fimc_prepare_addr(ctx, buf, s_frame, &s_frame->paddr);
 		if (ret)
 			return ret;
 	}
 
 	if (flags & FIMC_DST_ADDR) {
 		buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
-		ret = fimc_prepare_addr(ctx, buf,
-			V4L2_BUF_TYPE_VIDEO_CAPTURE);
+		ret = fimc_prepare_addr(ctx, buf, d_frame, &d_frame->paddr);
 	}
 
 	return ret;
@@ -499,12 +554,14 @@
 	ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR);
 	ret = fimc_prepare_config(ctx, ctx->state);
 	if (ret) {
-		err("general configuration error");
+		err("Wrong parameters");
 		goto dma_unlock;
 	}
-
-	if (fimc->m2m.ctx != ctx)
+	/* Reconfigure hardware if the context has changed. */
+	if (fimc->m2m.ctx != ctx) {
 		ctx->state |= FIMC_PARAMS;
+		fimc->m2m.ctx = ctx;
+	}
 
 	fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr);
 
@@ -512,10 +569,9 @@
 		fimc_hw_set_input_path(ctx);
 		fimc_hw_set_in_dma(ctx);
 		if (fimc_set_scaler_info(ctx)) {
-			err("scaler configuration error");
+			err("Scaler setup error");
 			goto dma_unlock;
 		}
-		fimc_hw_set_prescaler(ctx);
 		fimc_hw_set_scaler(ctx);
 		fimc_hw_set_target_format(ctx);
 		fimc_hw_set_rotation(ctx);
@@ -524,19 +580,15 @@
 
 	fimc_hw_set_output_path(ctx);
 	if (ctx->state & (FIMC_DST_ADDR | FIMC_PARAMS))
-		fimc_hw_set_output_addr(fimc, &ctx->d_frame.paddr);
+		fimc_hw_set_output_addr(fimc, &ctx->d_frame.paddr, -1);
 
 	if (ctx->state & FIMC_PARAMS)
 		fimc_hw_set_out_dma(ctx);
 
-	if (ctx->scaler.enabled)
-		fimc_hw_start_scaler(fimc);
-	fimc_hw_en_capture(ctx);
+	fimc_activate_capture(ctx);
 
-	ctx->state = 0;
-	fimc_hw_start_in_dma(fimc);
-
-	fimc->m2m.ctx = ctx;
+	ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP);
+	fimc_hw_activate_input_dma(fimc, true);
 
 dma_unlock:
 	spin_unlock_irqrestore(&ctx->slock, flags);
@@ -560,7 +612,7 @@
 	struct fimc_ctx *ctx = vq->priv_data;
 	struct fimc_frame *frame;
 
-	frame = ctx_m2m_get_frame(ctx, vq->type);
+	frame = ctx_get_frame(ctx, vq->type);
 	if (IS_ERR(frame))
 		return PTR_ERR(frame);
 
@@ -578,7 +630,7 @@
 	struct fimc_frame *frame;
 	int ret;
 
-	frame = ctx_m2m_get_frame(ctx, vq->type);
+	frame = ctx_get_frame(ctx, vq->type);
 	if (IS_ERR(frame))
 		return PTR_ERR(frame);
 
@@ -618,10 +670,31 @@
 				  struct videobuf_buffer *vb)
 {
 	struct fimc_ctx *ctx = vq->priv_data;
-	v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb);
+	struct fimc_dev *fimc = ctx->fimc_dev;
+	struct fimc_vid_cap *cap = &fimc->vid_cap;
+	unsigned long flags;
+
+	dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
+
+	if ((ctx->state & FIMC_CTX_M2M) && ctx->m2m_ctx) {
+		v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb);
+	} else if (ctx->state & FIMC_CTX_CAP) {
+		spin_lock_irqsave(&fimc->slock, flags);
+		fimc_vid_cap_buf_queue(fimc, (struct fimc_vid_buffer *)vb);
+
+		dbg("fimc->cap.active_buf_cnt: %d",
+		    fimc->vid_cap.active_buf_cnt);
+
+		if (cap->active_buf_cnt >= cap->reqbufs_count ||
+		   cap->active_buf_cnt >= FIMC_MAX_OUT_BUFS) {
+			if (!test_and_set_bit(ST_CAPT_STREAM, &fimc->state))
+				fimc_activate_capture(ctx);
+		}
+		spin_unlock_irqrestore(&fimc->slock, flags);
+	}
 }
 
-static struct videobuf_queue_ops fimc_qops = {
+struct videobuf_queue_ops fimc_qops = {
 	.buf_setup	= fimc_buf_setup,
 	.buf_prepare	= fimc_buf_prepare,
 	.buf_queue	= fimc_buf_queue,
@@ -644,7 +717,7 @@
 	return 0;
 }
 
-static int fimc_m2m_enum_fmt(struct file *file, void *priv,
+int fimc_vidioc_enum_fmt(struct file *file, void *priv,
 				struct v4l2_fmtdesc *f)
 {
 	struct fimc_fmt *fmt;
@@ -655,189 +728,210 @@
 	fmt = &fimc_formats[f->index];
 	strncpy(f->description, fmt->name, sizeof(f->description) - 1);
 	f->pixelformat = fmt->fourcc;
+
 	return 0;
 }
 
-static int fimc_m2m_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+int fimc_vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
 {
 	struct fimc_ctx *ctx = priv;
+	struct fimc_dev *fimc = ctx->fimc_dev;
 	struct fimc_frame *frame;
 
-	frame = ctx_m2m_get_frame(ctx, f->type);
+	frame = ctx_get_frame(ctx, f->type);
 	if (IS_ERR(frame))
 		return PTR_ERR(frame);
 
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
 	f->fmt.pix.width	= frame->width;
 	f->fmt.pix.height	= frame->height;
 	f->fmt.pix.field	= V4L2_FIELD_NONE;
 	f->fmt.pix.pixelformat	= frame->fmt->fourcc;
 
+	mutex_unlock(&fimc->lock);
 	return 0;
 }
 
-static struct fimc_fmt *find_format(struct v4l2_format *f)
+struct fimc_fmt *find_format(struct v4l2_format *f, unsigned int mask)
 {
 	struct fimc_fmt *fmt;
 	unsigned int i;
 
 	for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) {
 		fmt = &fimc_formats[i];
-		if (fmt->fourcc == f->fmt.pix.pixelformat)
+		if (fmt->fourcc == f->fmt.pix.pixelformat &&
+		   (fmt->flags & mask))
 			break;
 	}
-	if (i == ARRAY_SIZE(fimc_formats))
-		return NULL;
 
-	return fmt;
+	return (i == ARRAY_SIZE(fimc_formats)) ? NULL : fmt;
 }
 
-static int fimc_m2m_try_fmt(struct file *file, void *priv,
-			     struct v4l2_format *f)
+struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f,
+				  unsigned int mask)
 {
 	struct fimc_fmt *fmt;
-	u32 max_width, max_height, mod_x, mod_y;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) {
+		fmt = &fimc_formats[i];
+		if (fmt->mbus_code == f->code && (fmt->flags & mask))
+			break;
+	}
+
+	return (i == ARRAY_SIZE(fimc_formats)) ? NULL : fmt;
+}
+
+
+int fimc_vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
 	struct fimc_ctx *ctx = priv;
 	struct fimc_dev *fimc = ctx->fimc_dev;
-	struct v4l2_pix_format *pix = &f->fmt.pix;
 	struct samsung_fimc_variant *variant = fimc->variant;
+	struct v4l2_pix_format *pix = &f->fmt.pix;
+	struct fimc_fmt *fmt;
+	u32 max_width, mod_x, mod_y, mask;
+	int ret = -EINVAL, is_output = 0;
 
-	fmt = find_format(f);
-	if (!fmt) {
-		v4l2_err(&fimc->m2m.v4l2_dev,
-			 "Fourcc format (0x%X) invalid.\n",  pix->pixelformat);
+	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+		if (ctx->state & FIMC_CTX_CAP)
+			return -EINVAL;
+		is_output = 1;
+	} else if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
 		return -EINVAL;
 	}
 
+	dbg("w: %d, h: %d, bpl: %d",
+	    pix->width, pix->height, pix->bytesperline);
+
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
+	mask = is_output ? FMT_FLAGS_M2M : FMT_FLAGS_M2M | FMT_FLAGS_CAM;
+	fmt = find_format(f, mask);
+	if (!fmt) {
+		v4l2_err(&fimc->m2m.v4l2_dev, "Fourcc format (0x%X) invalid.\n",
+			 pix->pixelformat);
+		goto tf_out;
+	}
+
 	if (pix->field == V4L2_FIELD_ANY)
 		pix->field = V4L2_FIELD_NONE;
 	else if (V4L2_FIELD_NONE != pix->field)
-		return -EINVAL;
+		goto tf_out;
 
-	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
-		max_width = variant->scaler_dis_w;
-		max_height = variant->scaler_dis_w;
-		mod_x = variant->min_inp_pixsize;
-		mod_y = variant->min_inp_pixsize;
-	} else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-		max_width = variant->out_rot_dis_w;
-		max_height = variant->out_rot_dis_w;
-		mod_x = variant->min_out_pixsize;
-		mod_y = variant->min_out_pixsize;
+	if (is_output) {
+		max_width = variant->pix_limit->scaler_dis_w;
+		mod_x = ffs(variant->min_inp_pixsize) - 1;
 	} else {
-		err("Wrong stream type (%d)", f->type);
-		return -EINVAL;
+		max_width = variant->pix_limit->out_rot_dis_w;
+		mod_x = ffs(variant->min_out_pixsize) - 1;
 	}
 
-	dbg("max_w= %d, max_h= %d", max_width, max_height);
-
-	if (pix->height > max_height)
-		pix->height = max_height;
-	if (pix->width > max_width)
-		pix->width = max_width;
-
 	if (tiled_fmt(fmt)) {
-		mod_x = 64; /* 64x32 tile */
-		mod_y = 32;
+		mod_x = 6; /* 64 x 32 pixels tile */
+		mod_y = 5;
+	} else {
+		if (fimc->id == 1 && fimc->variant->pix_hoff)
+			mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1;
+		else
+			mod_y = mod_x;
 	}
 
-	dbg("mod_x= 0x%X, mod_y= 0x%X", mod_x, mod_y);
+	dbg("mod_x: %d, mod_y: %d, max_w: %d", mod_x, mod_y, max_width);
 
-	pix->width = (pix->width == 0) ? mod_x : ALIGN(pix->width, mod_x);
-	pix->height = (pix->height == 0) ? mod_y : ALIGN(pix->height, mod_y);
+	v4l_bound_align_image(&pix->width, 16, max_width, mod_x,
+		&pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0);
 
 	if (pix->bytesperline == 0 ||
-	    pix->bytesperline * 8 / fmt->depth > pix->width)
+	    (pix->bytesperline * 8 / fmt->depth) > pix->width)
 		pix->bytesperline = (pix->width * fmt->depth) >> 3;
 
 	if (pix->sizeimage == 0)
 		pix->sizeimage = pix->height * pix->bytesperline;
 
-	dbg("pix->bytesperline= %d, fmt->depth= %d",
-	    pix->bytesperline, fmt->depth);
+	dbg("w: %d, h: %d, bpl: %d, depth: %d",
+	    pix->width, pix->height, pix->bytesperline, fmt->depth);
 
-	return 0;
+	ret = 0;
+
+tf_out:
+	mutex_unlock(&fimc->lock);
+	return ret;
 }
 
-
 static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
 {
 	struct fimc_ctx *ctx = priv;
-	struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev;
-	struct videobuf_queue *src_vq, *dst_vq;
+	struct fimc_dev *fimc = ctx->fimc_dev;
+	struct v4l2_device *v4l2_dev = &fimc->m2m.v4l2_dev;
+	struct videobuf_queue *vq;
 	struct fimc_frame *frame;
 	struct v4l2_pix_format *pix;
 	unsigned long flags;
 	int ret = 0;
 
-	BUG_ON(!ctx);
-
-	ret = fimc_m2m_try_fmt(file, priv, f);
+	ret = fimc_vidioc_try_fmt(file, priv, f);
 	if (ret)
 		return ret;
 
-	mutex_lock(&ctx->fimc_dev->lock);
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
 
-	src_vq = v4l2_m2m_get_src_vq(ctx->m2m_ctx);
-	dst_vq = v4l2_m2m_get_dst_vq(ctx->m2m_ctx);
+	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+	mutex_lock(&vq->vb_lock);
 
-	mutex_lock(&src_vq->vb_lock);
-	mutex_lock(&dst_vq->vb_lock);
+	if (videobuf_queue_is_busy(vq)) {
+		v4l2_err(v4l2_dev, "%s: queue (%d) busy\n", __func__, f->type);
+		ret = -EBUSY;
+		goto sf_out;
+	}
 
+	spin_lock_irqsave(&ctx->slock, flags);
 	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
-		if (videobuf_queue_is_busy(src_vq)) {
-			v4l2_err(v4l2_dev, "%s queue busy\n", __func__);
-			ret = -EBUSY;
-			goto s_fmt_out;
-		}
 		frame = &ctx->s_frame;
-		spin_lock_irqsave(&ctx->slock, flags);
 		ctx->state |= FIMC_SRC_FMT;
-		spin_unlock_irqrestore(&ctx->slock, flags);
-
 	} else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-		if (videobuf_queue_is_busy(dst_vq)) {
-			v4l2_err(v4l2_dev, "%s queue busy\n", __func__);
-			ret = -EBUSY;
-			goto s_fmt_out;
-		}
 		frame = &ctx->d_frame;
-		spin_lock_irqsave(&ctx->slock, flags);
 		ctx->state |= FIMC_DST_FMT;
-		spin_unlock_irqrestore(&ctx->slock, flags);
 	} else {
+		spin_unlock_irqrestore(&ctx->slock, flags);
 		v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
 			 "Wrong buffer/video queue type (%d)\n", f->type);
 		ret = -EINVAL;
-		goto s_fmt_out;
+		goto sf_out;
 	}
+	spin_unlock_irqrestore(&ctx->slock, flags);
 
 	pix = &f->fmt.pix;
-	frame->fmt = find_format(f);
+	frame->fmt = find_format(f, FMT_FLAGS_M2M);
 	if (!frame->fmt) {
 		ret = -EINVAL;
-		goto s_fmt_out;
+		goto sf_out;
 	}
 
-	frame->f_width = pix->bytesperline * 8 / frame->fmt->depth;
-	frame->f_height = pix->sizeimage/pix->bytesperline;
-	frame->width = pix->width;
-	frame->height = pix->height;
-	frame->o_width = pix->width;
+	frame->f_width	= pix->bytesperline * 8 / frame->fmt->depth;
+	frame->f_height	= pix->height;
+	frame->width	= pix->width;
+	frame->height	= pix->height;
+	frame->o_width	= pix->width;
 	frame->o_height = pix->height;
-	frame->offs_h = 0;
-	frame->offs_v = 0;
-	frame->size = (pix->width * pix->height * frame->fmt->depth) >> 3;
-	src_vq->field = dst_vq->field = pix->field;
+	frame->offs_h	= 0;
+	frame->offs_v	= 0;
+	frame->size	= (pix->width * pix->height * frame->fmt->depth) >> 3;
+	vq->field	= pix->field;
+
 	spin_lock_irqsave(&ctx->slock, flags);
 	ctx->state |= FIMC_PARAMS;
 	spin_unlock_irqrestore(&ctx->slock, flags);
 
-	dbg("f_width= %d, f_height= %d", frame->f_width, frame->f_height);
+	dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
 
-s_fmt_out:
-	mutex_unlock(&dst_vq->vb_lock);
-	mutex_unlock(&src_vq->vb_lock);
-	mutex_unlock(&ctx->fimc_dev->lock);
+sf_out:
+	mutex_unlock(&vq->vb_lock);
+	mutex_unlock(&fimc->lock);
 	return ret;
 }
 
@@ -884,21 +978,33 @@
 	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
 }
 
-int fimc_m2m_queryctrl(struct file *file, void *priv,
+int fimc_vidioc_queryctrl(struct file *file, void *priv,
 			    struct v4l2_queryctrl *qc)
 {
+	struct fimc_ctx *ctx = priv;
 	struct v4l2_queryctrl *c;
+
 	c = get_ctrl(qc->id);
-	if (!c)
-		return -EINVAL;
-	*qc = *c;
-	return 0;
+	if (c) {
+		*qc = *c;
+		return 0;
+	}
+
+	if (ctx->state & FIMC_CTX_CAP)
+		return v4l2_subdev_call(ctx->fimc_dev->vid_cap.sd,
+					core, queryctrl, qc);
+	return -EINVAL;
 }
 
-int fimc_m2m_g_ctrl(struct file *file, void *priv,
+int fimc_vidioc_g_ctrl(struct file *file, void *priv,
 			 struct v4l2_control *ctrl)
 {
 	struct fimc_ctx *ctx = priv;
+	struct fimc_dev *fimc = ctx->fimc_dev;
+	int ret = 0;
+
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
 
 	switch (ctrl->id) {
 	case V4L2_CID_HFLIP:
@@ -911,15 +1017,22 @@
 		ctrl->value = ctx->rotation;
 		break;
 	default:
-		v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, "Invalid control\n");
-		return -EINVAL;
+		if (ctx->state & FIMC_CTX_CAP) {
+			ret = v4l2_subdev_call(fimc->vid_cap.sd, core,
+				       g_ctrl, ctrl);
+		} else {
+			v4l2_err(&fimc->m2m.v4l2_dev,
+				 "Invalid control\n");
+			ret = -EINVAL;
+		}
 	}
 	dbg("ctrl->value= %d", ctrl->value);
-	return 0;
+
+	mutex_unlock(&fimc->lock);
+	return ret;
 }
 
-static int check_ctrl_val(struct fimc_ctx *ctx,
-			  struct v4l2_control *ctrl)
+int check_ctrl_val(struct fimc_ctx *ctx,  struct v4l2_control *ctrl)
 {
 	struct v4l2_queryctrl *c;
 	c = get_ctrl(ctrl->id);
@@ -936,22 +1049,23 @@
 	return 0;
 }
 
-int fimc_m2m_s_ctrl(struct file *file, void *priv,
-			 struct v4l2_control *ctrl)
+int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl)
 {
-	struct fimc_ctx *ctx = priv;
 	struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+	struct fimc_dev *fimc = ctx->fimc_dev;
 	unsigned long flags;
-	int ret = 0;
 
-	ret = check_ctrl_val(ctx, ctrl);
-	if (ret)
-		return ret;
+	if (ctx->rotation != 0 &&
+	    (ctrl->id == V4L2_CID_HFLIP || ctrl->id == V4L2_CID_VFLIP)) {
+		v4l2_err(&fimc->m2m.v4l2_dev,
+			 "Simultaneous flip and rotation is not supported\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&ctx->slock, flags);
 
 	switch (ctrl->id) {
 	case V4L2_CID_HFLIP:
-		if (ctx->rotation != 0)
-			return 0;
 		if (ctrl->value)
 			ctx->flip |= FLIP_X_AXIS;
 		else
@@ -959,8 +1073,6 @@
 		break;
 
 	case V4L2_CID_VFLIP:
-		if (ctx->rotation != 0)
-			return 0;
 		if (ctrl->value)
 			ctx->flip |= FLIP_Y_AXIS;
 		else
@@ -968,77 +1080,95 @@
 		break;
 
 	case V4L2_CID_ROTATE:
-		if (ctrl->value == 90 || ctrl->value == 270) {
-			if (ctx->out_path == FIMC_LCDFIFO &&
-			    !variant->has_inp_rot) {
-				return -EINVAL;
-			} else if (ctx->in_path == FIMC_DMA &&
-				   !variant->has_out_rot) {
-				return -EINVAL;
-			}
+		/* Check for the output rotator availability */
+		if ((ctrl->value == 90 || ctrl->value == 270) &&
+		    (ctx->in_path == FIMC_DMA && !variant->has_out_rot)) {
+			spin_unlock_irqrestore(&ctx->slock, flags);
+			return -EINVAL;
+		} else {
+			ctx->rotation = ctrl->value;
 		}
-		ctx->rotation = ctrl->value;
-		if (ctrl->value == 180)
-			ctx->flip = FLIP_XY_AXIS;
 		break;
 
 	default:
-		v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, "Invalid control\n");
+		spin_unlock_irqrestore(&ctx->slock, flags);
+		v4l2_err(&fimc->m2m.v4l2_dev, "Invalid control\n");
 		return -EINVAL;
 	}
-	spin_lock_irqsave(&ctx->slock, flags);
 	ctx->state |= FIMC_PARAMS;
 	spin_unlock_irqrestore(&ctx->slock, flags);
+
 	return 0;
 }
 
+static int fimc_m2m_s_ctrl(struct file *file, void *priv,
+			 struct v4l2_control *ctrl)
+{
+	struct fimc_ctx *ctx = priv;
+	int ret = 0;
 
-static int fimc_m2m_cropcap(struct file *file, void *fh,
-			     struct v4l2_cropcap *cr)
+	ret = check_ctrl_val(ctx, ctrl);
+	if (ret)
+		return ret;
+
+	ret = fimc_s_ctrl(ctx, ctrl);
+	return 0;
+}
+
+int fimc_vidioc_cropcap(struct file *file, void *fh,
+			struct v4l2_cropcap *cr)
 {
 	struct fimc_frame *frame;
 	struct fimc_ctx *ctx = fh;
+	struct fimc_dev *fimc = ctx->fimc_dev;
 
-	frame = ctx_m2m_get_frame(ctx, cr->type);
+	frame = ctx_get_frame(ctx, cr->type);
 	if (IS_ERR(frame))
 		return PTR_ERR(frame);
 
-	cr->bounds.left = 0;
-	cr->bounds.top = 0;
-	cr->bounds.width = frame->f_width;
-	cr->bounds.height = frame->f_height;
-	cr->defrect.left = frame->offs_h;
-	cr->defrect.top = frame->offs_v;
-	cr->defrect.width = frame->o_width;
-	cr->defrect.height = frame->o_height;
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
+	cr->bounds.left		= 0;
+	cr->bounds.top		= 0;
+	cr->bounds.width	= frame->f_width;
+	cr->bounds.height	= frame->f_height;
+	cr->defrect		= cr->bounds;
+
+	mutex_unlock(&fimc->lock);
 	return 0;
 }
 
-static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+int fimc_vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
 {
 	struct fimc_frame *frame;
 	struct fimc_ctx *ctx = file->private_data;
+	struct fimc_dev *fimc = ctx->fimc_dev;
 
-	frame = ctx_m2m_get_frame(ctx, cr->type);
+	frame = ctx_get_frame(ctx, cr->type);
 	if (IS_ERR(frame))
 		return PTR_ERR(frame);
 
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
 	cr->c.left = frame->offs_h;
 	cr->c.top = frame->offs_v;
 	cr->c.width = frame->width;
 	cr->c.height = frame->height;
 
+	mutex_unlock(&fimc->lock);
 	return 0;
 }
 
-static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
 {
-	struct fimc_ctx *ctx = file->private_data;
 	struct fimc_dev *fimc = ctx->fimc_dev;
-	unsigned long flags;
 	struct fimc_frame *f;
-	u32 min_size;
-	int ret = 0;
+	u32 min_size, halign;
+
+	f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ?
+		&ctx->s_frame : &ctx->d_frame;
 
 	if (cr->c.top < 0 || cr->c.left < 0) {
 		v4l2_err(&fimc->m2m.v4l2_dev,
@@ -1046,66 +1176,98 @@
 		return -EINVAL;
 	}
 
-	if (cr->c.width  <= 0 || cr->c.height <= 0) {
-		v4l2_err(&fimc->m2m.v4l2_dev,
-			"crop width and height must be greater than 0\n");
-		return -EINVAL;
-	}
-
-	f = ctx_m2m_get_frame(ctx, cr->type);
+	f = ctx_get_frame(ctx, cr->type);
 	if (IS_ERR(f))
 		return PTR_ERR(f);
 
-	/* Adjust to required pixel boundary. */
-	min_size = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ?
-		fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
+	min_size = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+		? fimc->variant->min_inp_pixsize
+		: fimc->variant->min_out_pixsize;
 
-	cr->c.width = round_down(cr->c.width, min_size);
-	cr->c.height = round_down(cr->c.height, min_size);
-	cr->c.left = round_down(cr->c.left + 1, min_size);
-	cr->c.top = round_down(cr->c.top + 1, min_size);
-
-	if ((cr->c.left + cr->c.width > f->o_width)
-		|| (cr->c.top + cr->c.height > f->o_height)) {
-		v4l2_err(&fimc->m2m.v4l2_dev, "Error in S_CROP params\n");
-		return -EINVAL;
+	if (ctx->state & FIMC_CTX_M2M) {
+		if (fimc->id == 1 && fimc->variant->pix_hoff)
+			halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1;
+		else
+			halign = ffs(min_size) - 1;
+	/* there are more strict aligment requirements at camera interface */
+	} else {
+		min_size = 16;
+		halign = 4;
 	}
 
+	v4l_bound_align_image(&cr->c.width, min_size, f->o_width,
+			      ffs(min_size) - 1,
+			      &cr->c.height, min_size, f->o_height,
+			      halign, 64/(ALIGN(f->fmt->depth, 8)));
+
+	/* adjust left/top if cropping rectangle is out of bounds */
+	if (cr->c.left + cr->c.width > f->o_width)
+		cr->c.left = f->o_width - cr->c.width;
+	if (cr->c.top + cr->c.height > f->o_height)
+		cr->c.top = f->o_height - cr->c.height;
+
+	cr->c.left = round_down(cr->c.left, min_size);
+	cr->c.top  = round_down(cr->c.top,
+				ctx->state & FIMC_CTX_M2M ? 8 : 16);
+
+	dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
+	    cr->c.left, cr->c.top, cr->c.width, cr->c.height,
+	    f->f_width, f->f_height);
+
+	return 0;
+}
+
+
+static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+{
+	struct fimc_ctx *ctx = file->private_data;
+	struct fimc_dev *fimc = ctx->fimc_dev;
+	unsigned long flags;
+	struct fimc_frame *f;
+	int ret;
+
+	ret = fimc_try_crop(ctx, cr);
+	if (ret)
+		return ret;
+
+	f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ?
+		&ctx->s_frame : &ctx->d_frame;
+
 	spin_lock_irqsave(&ctx->slock, flags);
-	if ((ctx->state & FIMC_SRC_FMT) && (ctx->state & FIMC_DST_FMT)) {
-		/* Check for the pixel scaling ratio when cropping input img. */
+	if (~ctx->state & (FIMC_SRC_FMT | FIMC_DST_FMT)) {
+		/* Check to see if scaling ratio is within supported range */
 		if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
 			ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame);
-		else if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		else
 			ret = fimc_check_scaler_ratio(&cr->c, &ctx->s_frame);
-
 		if (ret) {
 			spin_unlock_irqrestore(&ctx->slock, flags);
-			v4l2_err(&fimc->m2m.v4l2_dev,  "Out of scaler range");
+			v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range");
 			return -EINVAL;
 		}
 	}
 	ctx->state |= FIMC_PARAMS;
-	spin_unlock_irqrestore(&ctx->slock, flags);
 
 	f->offs_h = cr->c.left;
 	f->offs_v = cr->c.top;
-	f->width = cr->c.width;
+	f->width  = cr->c.width;
 	f->height = cr->c.height;
+
+	spin_unlock_irqrestore(&ctx->slock, flags);
 	return 0;
 }
 
 static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
 	.vidioc_querycap		= fimc_m2m_querycap,
 
-	.vidioc_enum_fmt_vid_cap	= fimc_m2m_enum_fmt,
-	.vidioc_enum_fmt_vid_out	= fimc_m2m_enum_fmt,
+	.vidioc_enum_fmt_vid_cap	= fimc_vidioc_enum_fmt,
+	.vidioc_enum_fmt_vid_out	= fimc_vidioc_enum_fmt,
 
-	.vidioc_g_fmt_vid_cap		= fimc_m2m_g_fmt,
-	.vidioc_g_fmt_vid_out		= fimc_m2m_g_fmt,
+	.vidioc_g_fmt_vid_cap		= fimc_vidioc_g_fmt,
+	.vidioc_g_fmt_vid_out		= fimc_vidioc_g_fmt,
 
-	.vidioc_try_fmt_vid_cap		= fimc_m2m_try_fmt,
-	.vidioc_try_fmt_vid_out		= fimc_m2m_try_fmt,
+	.vidioc_try_fmt_vid_cap		= fimc_vidioc_try_fmt,
+	.vidioc_try_fmt_vid_out		= fimc_vidioc_try_fmt,
 
 	.vidioc_s_fmt_vid_cap		= fimc_m2m_s_fmt,
 	.vidioc_s_fmt_vid_out		= fimc_m2m_s_fmt,
@@ -1119,13 +1281,13 @@
 	.vidioc_streamon		= fimc_m2m_streamon,
 	.vidioc_streamoff		= fimc_m2m_streamoff,
 
-	.vidioc_queryctrl		= fimc_m2m_queryctrl,
-	.vidioc_g_ctrl			= fimc_m2m_g_ctrl,
+	.vidioc_queryctrl		= fimc_vidioc_queryctrl,
+	.vidioc_g_ctrl			= fimc_vidioc_g_ctrl,
 	.vidioc_s_ctrl			= fimc_m2m_s_ctrl,
 
-	.vidioc_g_crop			= fimc_m2m_g_crop,
+	.vidioc_g_crop			= fimc_vidioc_g_crop,
 	.vidioc_s_crop			= fimc_m2m_s_crop,
-	.vidioc_cropcap			= fimc_m2m_cropcap
+	.vidioc_cropcap			= fimc_vidioc_cropcap
 
 };
 
@@ -1136,9 +1298,9 @@
 	struct fimc_dev *fimc = ctx->fimc_dev;
 
 	videobuf_queue_dma_contig_init(vq, &fimc_qops,
-		fimc->m2m.v4l2_dev.dev,
+		&fimc->pdev->dev,
 		&fimc->irqlock, type, V4L2_FIELD_NONE,
-		sizeof(struct fimc_vid_buffer), priv);
+		sizeof(struct fimc_vid_buffer), priv, NULL);
 }
 
 static int fimc_m2m_open(struct file *file)
@@ -1147,25 +1309,38 @@
 	struct fimc_ctx *ctx = NULL;
 	int err = 0;
 
-	mutex_lock(&fimc->lock);
+	if (mutex_lock_interruptible(&fimc->lock))
+		return -ERESTARTSYS;
+
+	dbg("pid: %d, state: 0x%lx, refcnt: %d",
+		task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt);
+
+	/*
+	 * Return if the corresponding video capture node
+	 * is already opened.
+	 */
+	if (fimc->vid_cap.refcnt > 0) {
+		err = -EBUSY;
+		goto err_unlock;
+	}
+
 	fimc->m2m.refcnt++;
 	set_bit(ST_OUTDMA_RUN, &fimc->state);
-	mutex_unlock(&fimc->lock);
-
 
 	ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
-	if (!ctx)
-		return -ENOMEM;
+	if (!ctx) {
+		err = -ENOMEM;
+		goto err_unlock;
+	}
 
 	file->private_data = ctx;
 	ctx->fimc_dev = fimc;
-	/* default format */
+	/* Default color format */
 	ctx->s_frame.fmt = &fimc_formats[0];
 	ctx->d_frame.fmt = &fimc_formats[0];
-	/* per user process device context initialization */
-	ctx->state = 0;
+	/* Setup the device context for mem2mem mode. */
+	ctx->state = FIMC_CTX_M2M;
 	ctx->flags = 0;
-	ctx->effect.type = S5P_FIMC_EFFECT_ORIGINAL;
 	ctx->in_path = FIMC_DMA;
 	ctx->out_path = FIMC_DMA;
 	spin_lock_init(&ctx->slock);
@@ -1175,6 +1350,9 @@
 		err = PTR_ERR(ctx->m2m_ctx);
 		kfree(ctx);
 	}
+
+err_unlock:
+	mutex_unlock(&fimc->lock);
 	return err;
 }
 
@@ -1183,11 +1361,16 @@
 	struct fimc_ctx *ctx = file->private_data;
 	struct fimc_dev *fimc = ctx->fimc_dev;
 
+	mutex_lock(&fimc->lock);
+
+	dbg("pid: %d, state: 0x%lx, refcnt= %d",
+		task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
+
 	v4l2_m2m_ctx_release(ctx->m2m_ctx);
 	kfree(ctx);
-	mutex_lock(&fimc->lock);
 	if (--fimc->m2m.refcnt <= 0)
 		clear_bit(ST_OUTDMA_RUN, &fimc->state);
+
 	mutex_unlock(&fimc->lock);
 	return 0;
 }
@@ -1196,6 +1379,7 @@
 				     struct poll_table_struct *wait)
 {
 	struct fimc_ctx *ctx = file->private_data;
+
 	return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
 }
 
@@ -1203,6 +1387,7 @@
 static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct fimc_ctx *ctx = file->private_data;
+
 	return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
 }
 
@@ -1241,7 +1426,7 @@
 
 	ret = v4l2_device_register(&pdev->dev, v4l2_dev);
 	if (ret)
-		return ret;;
+		goto err_m2m_r1;
 
 	vfd = video_device_alloc();
 	if (!vfd) {
@@ -1293,7 +1478,7 @@
 	if (fimc) {
 		v4l2_m2m_release(fimc->m2m.m2m_dev);
 		video_unregister_device(fimc->m2m.vfd);
-		video_device_release(fimc->m2m.vfd);
+
 		v4l2_device_unregister(&fimc->m2m.v4l2_dev);
 	}
 }
@@ -1337,7 +1522,7 @@
 	drv_data = (struct samsung_fimc_driverdata *)
 		platform_get_device_id(pdev)->driver_data;
 
-	if (pdev->id >= drv_data->devs_cnt) {
+	if (pdev->id >= drv_data->num_entities) {
 		dev_err(&pdev->dev, "Invalid platform device id: %d\n",
 			pdev->id);
 		return -EINVAL;
@@ -1350,9 +1535,11 @@
 	fimc->id = pdev->id;
 	fimc->variant = drv_data->variant[fimc->id];
 	fimc->pdev = pdev;
+	fimc->pdata = pdev->dev.platform_data;
 	fimc->state = ST_IDLE;
 
 	spin_lock_init(&fimc->irqlock);
+	init_waitqueue_head(&fimc->irq_queue);
 	spin_lock_init(&fimc->slock);
 
 	mutex_init(&fimc->lock);
@@ -1382,6 +1569,7 @@
 	ret = fimc_clk_get(fimc);
 	if (ret)
 		goto err_regs_unmap;
+	clk_set_rate(fimc->clock[0], drv_data->lclk_frequency);
 
 	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 	if (!res) {
@@ -1399,25 +1587,38 @@
 		goto err_clk;
 	}
 
-	fimc->work_queue = create_workqueue(dev_name(&fimc->pdev->dev));
-	if (!fimc->work_queue) {
-		ret = -ENOMEM;
-		goto err_irq;
-	}
-
 	ret = fimc_register_m2m_device(fimc);
 	if (ret)
-		goto err_wq;
+		goto err_irq;
 
-	fimc_hw_en_lastirq(fimc, true);
+	/* At least one camera sensor is required to register capture node */
+	if (fimc->pdata) {
+		int i;
+		for (i = 0; i < FIMC_MAX_CAMIF_CLIENTS; ++i)
+			if (fimc->pdata->isp_info[i])
+				break;
+
+		if (i < FIMC_MAX_CAMIF_CLIENTS) {
+			ret = fimc_register_capture_device(fimc);
+			if (ret)
+				goto err_m2m;
+		}
+	}
+
+	/*
+	 * Exclude the additional output DMA address registers by masking
+	 * them out on HW revisions that provide extended capabilites.
+	 */
+	if (fimc->variant->out_buf_count > 4)
+		fimc_hw_set_dma_seq(fimc, 0xF);
 
 	dev_dbg(&pdev->dev, "%s(): fimc-%d registered successfully\n",
 		__func__, fimc->id);
 
 	return 0;
 
-err_wq:
-	destroy_workqueue(fimc->work_queue);
+err_m2m:
+	fimc_unregister_m2m_device(fimc);
 err_irq:
 	free_irq(fimc->irq, fimc);
 err_clk:
@@ -1429,7 +1630,7 @@
 	kfree(fimc->regs_res);
 err_info:
 	kfree(fimc);
-	dev_err(&pdev->dev, "failed to install\n");
+
 	return ret;
 }
 
@@ -1438,91 +1639,151 @@
 	struct fimc_dev *fimc =
 		(struct fimc_dev *)platform_get_drvdata(pdev);
 
-	v4l2_info(&fimc->m2m.v4l2_dev, "Removing %s\n", pdev->name);
-
 	free_irq(fimc->irq, fimc);
-
 	fimc_hw_reset(fimc);
 
 	fimc_unregister_m2m_device(fimc);
+	fimc_unregister_capture_device(fimc);
+
 	fimc_clk_release(fimc);
 	iounmap(fimc->regs);
 	release_resource(fimc->regs_res);
 	kfree(fimc->regs_res);
 	kfree(fimc);
+
+	dev_info(&pdev->dev, "%s driver unloaded\n", pdev->name);
 	return 0;
 }
 
-static struct samsung_fimc_variant fimc01_variant_s5p = {
-	.has_inp_rot	= 1,
-	.has_out_rot	= 1,
+/* Image pixel limits, similar across several FIMC HW revisions. */
+static struct fimc_pix_limit s5p_pix_limit[3] = {
+	[0] = {
+		.scaler_en_w	= 3264,
+		.scaler_dis_w	= 8192,
+		.in_rot_en_h	= 1920,
+		.in_rot_dis_w	= 8192,
+		.out_rot_en_w	= 1920,
+		.out_rot_dis_w	= 4224,
+	},
+	[1] = {
+		.scaler_en_w	= 4224,
+		.scaler_dis_w	= 8192,
+		.in_rot_en_h	= 1920,
+		.in_rot_dis_w	= 8192,
+		.out_rot_en_w	= 1920,
+		.out_rot_dis_w	= 4224,
+	},
+	[2] = {
+		.scaler_en_w	= 1920,
+		.scaler_dis_w	= 8192,
+		.in_rot_en_h	= 1280,
+		.in_rot_dis_w	= 8192,
+		.out_rot_en_w	= 1280,
+		.out_rot_dis_w	= 1920,
+	},
+};
+
+static struct samsung_fimc_variant fimc0_variant_s5p = {
+	.has_inp_rot	 = 1,
+	.has_out_rot	 = 1,
 	.min_inp_pixsize = 16,
 	.min_out_pixsize = 16,
-
-	.scaler_en_w	= 3264,
-	.scaler_dis_w	= 8192,
-	.in_rot_en_h	= 1920,
-	.in_rot_dis_w	= 8192,
-	.out_rot_en_w	= 1920,
-	.out_rot_dis_w	= 4224,
+	.hor_offs_align	 = 8,
+	.out_buf_count	 = 4,
+	.pix_limit	 = &s5p_pix_limit[0],
 };
 
 static struct samsung_fimc_variant fimc2_variant_s5p = {
 	.min_inp_pixsize = 16,
 	.min_out_pixsize = 16,
-
-	.scaler_en_w	= 4224,
-	.scaler_dis_w	= 8192,
-	.in_rot_en_h	= 1920,
-	.in_rot_dis_w	= 8192,
-	.out_rot_en_w	= 1920,
-	.out_rot_dis_w	= 4224,
+	.hor_offs_align	 = 8,
+	.out_buf_count	 = 4,
+	.pix_limit = &s5p_pix_limit[1],
 };
 
-static struct samsung_fimc_variant fimc01_variant_s5pv210 = {
-	.pix_hoff	= 1,
-	.has_inp_rot	= 1,
-	.has_out_rot	= 1,
+static struct samsung_fimc_variant fimc0_variant_s5pv210 = {
+	.pix_hoff	 = 1,
+	.has_inp_rot	 = 1,
+	.has_out_rot	 = 1,
 	.min_inp_pixsize = 16,
-	.min_out_pixsize = 32,
+	.min_out_pixsize = 16,
+	.hor_offs_align	 = 8,
+	.out_buf_count	 = 4,
+	.pix_limit	 = &s5p_pix_limit[1],
+};
 
-	.scaler_en_w	= 4224,
-	.scaler_dis_w	= 8192,
-	.in_rot_en_h	= 1920,
-	.in_rot_dis_w	= 8192,
-	.out_rot_en_w	= 1920,
-	.out_rot_dis_w	= 4224,
+static struct samsung_fimc_variant fimc1_variant_s5pv210 = {
+	.pix_hoff	 = 1,
+	.has_inp_rot	 = 1,
+	.has_out_rot	 = 1,
+	.min_inp_pixsize = 16,
+	.min_out_pixsize = 16,
+	.hor_offs_align	 = 1,
+	.out_buf_count	 = 4,
+	.pix_limit	 = &s5p_pix_limit[2],
 };
 
 static struct samsung_fimc_variant fimc2_variant_s5pv210 = {
 	.pix_hoff	 = 1,
 	.min_inp_pixsize = 16,
-	.min_out_pixsize = 32,
-
-	.scaler_en_w	= 1920,
-	.scaler_dis_w	= 8192,
-	.in_rot_en_h	= 1280,
-	.in_rot_dis_w	= 8192,
-	.out_rot_en_w	= 1280,
-	.out_rot_dis_w	= 1920,
+	.min_out_pixsize = 16,
+	.hor_offs_align	 = 8,
+	.out_buf_count	 = 4,
+	.pix_limit	 = &s5p_pix_limit[2],
 };
 
+static struct samsung_fimc_variant fimc0_variant_s5pv310 = {
+	.pix_hoff	 = 1,
+	.has_inp_rot	 = 1,
+	.has_out_rot	 = 1,
+	.min_inp_pixsize = 16,
+	.min_out_pixsize = 16,
+	.hor_offs_align	 = 1,
+	.out_buf_count	 = 32,
+	.pix_limit	 = &s5p_pix_limit[1],
+};
+
+static struct samsung_fimc_variant fimc2_variant_s5pv310 = {
+	.pix_hoff	 = 1,
+	.min_inp_pixsize = 16,
+	.min_out_pixsize = 16,
+	.hor_offs_align	 = 1,
+	.out_buf_count	 = 32,
+	.pix_limit	 = &s5p_pix_limit[2],
+};
+
+/* S5PC100 */
 static struct samsung_fimc_driverdata fimc_drvdata_s5p = {
 	.variant = {
-		[0] = &fimc01_variant_s5p,
-		[1] = &fimc01_variant_s5p,
+		[0] = &fimc0_variant_s5p,
+		[1] = &fimc0_variant_s5p,
 		[2] = &fimc2_variant_s5p,
 	},
-	.devs_cnt = 3
+	.num_entities = 3,
+	.lclk_frequency = 133000000UL,
 };
 
+/* S5PV210, S5PC110 */
 static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = {
 	.variant = {
-		[0] = &fimc01_variant_s5pv210,
-		[1] = &fimc01_variant_s5pv210,
+		[0] = &fimc0_variant_s5pv210,
+		[1] = &fimc1_variant_s5pv210,
 		[2] = &fimc2_variant_s5pv210,
 	},
-	.devs_cnt = 3
+	.num_entities = 3,
+	.lclk_frequency = 166000000UL,
+};
+
+/* S5PV310, S5PC210 */
+static struct samsung_fimc_driverdata fimc_drvdata_s5pv310 = {
+	.variant = {
+		[0] = &fimc0_variant_s5pv310,
+		[1] = &fimc0_variant_s5pv310,
+		[2] = &fimc0_variant_s5pv310,
+		[3] = &fimc2_variant_s5pv310,
+	},
+	.num_entities = 4,
+	.lclk_frequency = 166000000UL,
 };
 
 static struct platform_device_id fimc_driver_ids[] = {
@@ -1532,6 +1793,9 @@
 	}, {
 		.name		= "s5pv210-fimc",
 		.driver_data	= (unsigned long)&fimc_drvdata_s5pv210,
+	}, {
+		.name		= "s5pv310-fimc",
+		.driver_data	= (unsigned long)&fimc_drvdata_s5pv310,
 	},
 	{},
 };
@@ -1547,20 +1811,12 @@
 	}
 };
 
-static char banner[] __initdata = KERN_INFO
-	"S5PC Camera Interface V4L2 Driver, (c) 2010 Samsung Electronics\n";
-
 static int __init fimc_init(void)
 {
-	u32 ret;
-	printk(banner);
-
-	ret = platform_driver_register(&fimc_driver);
-	if (ret) {
-		printk(KERN_ERR "FIMC platform driver register failed\n");
-		return -1;
-	}
-	return 0;
+	int ret = platform_driver_register(&fimc_driver);
+	if (ret)
+		err("platform_driver_register failed: %d\n", ret);
+	return ret;
 }
 
 static void __exit fimc_exit(void)
@@ -1571,6 +1827,6 @@
 module_init(fimc_init);
 module_exit(fimc_exit);
 
-MODULE_AUTHOR("Sylwester Nawrocki, s.nawrocki@samsung.com");
-MODULE_DESCRIPTION("S3C/S5P FIMC (video postprocessor) driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h
index 6b3e0cd..3e10785 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.h
+++ b/drivers/media/video/s5p-fimc/fimc-core.h
@@ -11,10 +11,14 @@
 #ifndef FIMC_CORE_H_
 #define FIMC_CORE_H_
 
+/*#define DEBUG*/
+
 #include <linux/types.h>
 #include <media/videobuf-core.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-mem2mem.h>
+#include <media/v4l2-mediabus.h>
+#include <media/s3c_fimc.h>
 #include <linux/videodev2.h>
 #include "regs-fimc.h"
 
@@ -28,47 +32,72 @@
 #define dbg(fmt, args...)
 #endif
 
+/* Time to wait for next frame VSYNC interrupt while stopping operation. */
+#define FIMC_SHUTDOWN_TIMEOUT	((100*HZ)/1000)
 #define NUM_FIMC_CLOCKS		2
 #define MODULE_NAME		"s5p-fimc"
-#define FIMC_MAX_DEVS		3
+#define FIMC_MAX_DEVS		4
 #define FIMC_MAX_OUT_BUFS	4
 #define SCALER_MAX_HRATIO	64
 #define SCALER_MAX_VRATIO	64
+#define DMA_MIN_SIZE		8
 
-enum {
+/* FIMC device state flags */
+enum fimc_dev_flags {
+	/* for m2m node */
 	ST_IDLE,
 	ST_OUTDMA_RUN,
 	ST_M2M_PEND,
+	/* for capture node */
+	ST_CAPT_PEND,
+	ST_CAPT_RUN,
+	ST_CAPT_STREAM,
+	ST_CAPT_SHUT,
 };
 
 #define fimc_m2m_active(dev) test_bit(ST_OUTDMA_RUN, &(dev)->state)
 #define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)
 
+#define fimc_capture_running(dev) test_bit(ST_CAPT_RUN, &(dev)->state)
+#define fimc_capture_pending(dev) test_bit(ST_CAPT_PEND, &(dev)->state)
+
+#define fimc_capture_active(dev) \
+	(test_bit(ST_CAPT_RUN, &(dev)->state) || \
+	 test_bit(ST_CAPT_PEND, &(dev)->state))
+
+#define fimc_capture_streaming(dev) \
+	test_bit(ST_CAPT_STREAM, &(dev)->state)
+
+#define fimc_buf_finish(dev, vid_buf) do { \
+	spin_lock(&(dev)->irqlock); \
+	(vid_buf)->vb.state = VIDEOBUF_DONE; \
+	spin_unlock(&(dev)->irqlock); \
+	wake_up(&(vid_buf)->vb.done); \
+} while (0)
+
 enum fimc_datapath {
-	FIMC_ITU_CAM_A,
-	FIMC_ITU_CAM_B,
-	FIMC_MIPI_CAM,
+	FIMC_CAMERA,
 	FIMC_DMA,
 	FIMC_LCDFIFO,
 	FIMC_WRITEBACK
 };
 
 enum fimc_color_fmt {
-	S5P_FIMC_RGB565,
+	S5P_FIMC_RGB565 = 0x10,
 	S5P_FIMC_RGB666,
 	S5P_FIMC_RGB888,
-	S5P_FIMC_YCBCR420,
+	S5P_FIMC_RGB30_LOCAL,
+	S5P_FIMC_YCBCR420 = 0x20,
 	S5P_FIMC_YCBCR422,
 	S5P_FIMC_YCBYCR422,
 	S5P_FIMC_YCRYCB422,
 	S5P_FIMC_CBYCRY422,
 	S5P_FIMC_CRYCBY422,
-	S5P_FIMC_RGB30_LOCAL,
 	S5P_FIMC_YCBCR444_LOCAL,
-	S5P_FIMC_MAX_COLOR = S5P_FIMC_YCBCR444_LOCAL,
-	S5P_FIMC_COLOR_MASK = 0x0F,
 };
 
+#define fimc_fmt_is_rgb(x) ((x) & 0x10)
+
 /* Y/Cb/Cr components order at DMA output for 1 plane YCbCr 4:2:2 formats. */
 #define	S5P_FIMC_OUT_CRYCBY	S5P_CIOCTRL_ORDER422_CRYCBY
 #define	S5P_FIMC_OUT_CBYCRY	S5P_CIOCTRL_ORDER422_YCRYCB
@@ -93,11 +122,13 @@
 #define	S5P_FIMC_EFFECT_SIKHOUETTE	S5P_CIIMGEFF_FIN_SILHOUETTE
 
 /* The hardware context state. */
-#define	FIMC_PARAMS			(1 << 0)
-#define	FIMC_SRC_ADDR			(1 << 1)
-#define	FIMC_DST_ADDR			(1 << 2)
-#define	FIMC_SRC_FMT			(1 << 3)
-#define	FIMC_DST_FMT			(1 << 4)
+#define	FIMC_PARAMS		(1 << 0)
+#define	FIMC_SRC_ADDR		(1 << 1)
+#define	FIMC_DST_ADDR		(1 << 2)
+#define	FIMC_SRC_FMT		(1 << 3)
+#define	FIMC_DST_FMT		(1 << 4)
+#define	FIMC_CTX_M2M		(1 << 5)
+#define	FIMC_CTX_CAP		(1 << 6)
 
 /* Image conversion flags */
 #define	FIMC_IN_DMA_ACCESS_TILED	(1 << 0)
@@ -106,7 +137,9 @@
 #define	FIMC_OUT_DMA_ACCESS_LINEAR	(0 << 1)
 #define	FIMC_SCAN_MODE_PROGRESSIVE	(0 << 2)
 #define	FIMC_SCAN_MODE_INTERLACED	(1 << 2)
-/* YCbCr data dynamic range for RGB-YUV color conversion. Y/Cb/Cr: (0 ~ 255) */
+/*
+ * YCbCr data dynamic range for RGB-YUV color conversion.
+ * Y/Cb/Cr: (0 ~ 255) */
 #define	FIMC_COLOR_RANGE_WIDE		(0 << 3)
 /* Y (16 ~ 235), Cb/Cr (16 ~ 240) */
 #define	FIMC_COLOR_RANGE_NARROW		(1 << 3)
@@ -118,20 +151,25 @@
 
 /**
  * struct fimc_fmt - the driver's internal color format data
+ * @mbus_code: Media Bus pixel code, -1 if not applicable
  * @name: format description
- * @fourcc: the fourcc code for this format
+ * @fourcc: the fourcc code for this format, 0 if not applicable
  * @color: the corresponding fimc_color_fmt
- * @depth: number of bits per pixel
+ * @depth: driver's private 'number of bits per pixel'
  * @buff_cnt: number of physically non-contiguous data planes
  * @planes_cnt: number of physically contiguous data planes
  */
 struct fimc_fmt {
+	enum v4l2_mbus_pixelcode mbus_code;
 	char	*name;
 	u32	fourcc;
 	u32	color;
-	u32	depth;
 	u16	buff_cnt;
 	u16	planes_cnt;
+	u16	depth;
+	u16	flags;
+#define FMT_FLAGS_CAM	(1 << 0)
+#define FMT_FLAGS_M2M	(1 << 1)
 };
 
 /**
@@ -167,37 +205,37 @@
 /**
  * struct fimc_scaler - the configuration data for FIMC inetrnal scaler
  *
- * @enabled:		the flag set when the scaler is used
+ * @scaleup_h:		flag indicating scaling up horizontally
+ * @scaleup_v:		flag indicating scaling up vertically
+ * @copy_mode:		flag indicating transparent DMA transfer (no scaling
+ *			and color format conversion)
+ * @enabled:		flag indicating if the scaler is used
  * @hfactor:		horizontal shift factor
  * @vfactor:		vertical shift factor
  * @pre_hratio:		horizontal ratio of the prescaler
  * @pre_vratio:		vertical ratio of the prescaler
  * @pre_dst_width:	the prescaler's destination width
  * @pre_dst_height:	the prescaler's destination height
- * @scaleup_h:		flag indicating scaling up horizontally
- * @scaleup_v:		flag indicating scaling up vertically
  * @main_hratio:	the main scaler's horizontal ratio
  * @main_vratio:	the main scaler's vertical ratio
- * @real_width:		source width - offset
- * @real_height:	source height - offset
- * @copy_mode:		flag set if one-to-one mode is used, i.e. no scaling
- *			and color format conversion
+ * @real_width:		source pixel (width - offset)
+ * @real_height:	source pixel (height - offset)
  */
 struct fimc_scaler {
-	u32	enabled;
+	unsigned int scaleup_h:1;
+	unsigned int scaleup_v:1;
+	unsigned int copy_mode:1;
+	unsigned int enabled:1;
 	u32	hfactor;
 	u32	vfactor;
 	u32	pre_hratio;
 	u32	pre_vratio;
 	u32	pre_dst_width;
 	u32	pre_dst_height;
-	u32	scaleup_h;
-	u32	scaleup_v;
 	u32	main_hratio;
 	u32	main_vratio;
 	u32	real_width;
 	u32	real_height;
-	u32	copy_mode;
 };
 
 /**
@@ -215,15 +253,18 @@
 
 /**
  * struct fimc_vid_buffer - the driver's video buffer
- * @vb:	v4l videobuf buffer
+ * @vb:    v4l videobuf buffer
+ * @paddr: precalculated physical address set
+ * @index: buffer index for the output DMA engine
  */
 struct fimc_vid_buffer {
 	struct videobuf_buffer	vb;
+	struct fimc_addr	paddr;
+	int			index;
 };
 
 /**
- * struct fimc_frame - input/output frame format properties
- *
+ * struct fimc_frame - source/target frame properties
  * @f_width:	image full width (virtual screen size)
  * @f_height:	image full height (virtual screen size)
  * @o_width:	original image width as set by S_FMT
@@ -270,67 +311,119 @@
 };
 
 /**
+ * struct fimc_vid_cap - camera capture device information
+ * @ctx: hardware context data
+ * @vfd: video device node for camera capture mode
+ * @v4l2_dev: v4l2_device struct to manage subdevs
+ * @sd: pointer to camera sensor subdevice currently in use
+ * @fmt: Media Bus format configured at selected image sensor
+ * @pending_buf_q: the pending buffer queue head
+ * @active_buf_q: the queue head of buffers scheduled in hardware
+ * @vbq: the capture am video buffer queue
+ * @active_buf_cnt: number of video buffers scheduled in hardware
+ * @buf_index: index for managing the output DMA buffers
+ * @frame_count: the frame counter for statistics
+ * @reqbufs_count: the number of buffers requested in REQBUFS ioctl
+ * @input_index: input (camera sensor) index
+ * @refcnt: driver's private reference counter
+ */
+struct fimc_vid_cap {
+	struct fimc_ctx			*ctx;
+	struct video_device		*vfd;
+	struct v4l2_device		v4l2_dev;
+	struct v4l2_subdev		*sd;
+	struct v4l2_mbus_framefmt	fmt;
+	struct list_head		pending_buf_q;
+	struct list_head		active_buf_q;
+	struct videobuf_queue		vbq;
+	int				active_buf_cnt;
+	int				buf_index;
+	unsigned int			frame_count;
+	unsigned int			reqbufs_count;
+	int				input_index;
+	int				refcnt;
+};
+
+/**
+ *  struct fimc_pix_limit - image pixel size limits in various IP configurations
+ *
+ *  @scaler_en_w: max input pixel width when the scaler is enabled
+ *  @scaler_dis_w: max input pixel width when the scaler is disabled
+ *  @in_rot_en_h: max input width with the input rotator is on
+ *  @in_rot_dis_w: max input width with the input rotator is off
+ *  @out_rot_en_w: max output width with the output rotator on
+ *  @out_rot_dis_w: max output width with the output rotator off
+ */
+struct fimc_pix_limit {
+	u16 scaler_en_w;
+	u16 scaler_dis_w;
+	u16 in_rot_en_h;
+	u16 in_rot_dis_w;
+	u16 out_rot_en_w;
+	u16 out_rot_dis_w;
+};
+
+/**
  * struct samsung_fimc_variant - camera interface variant information
  *
  * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes
  * @has_inp_rot: set if has input rotator
  * @has_out_rot: set if has output rotator
+ * @pix_limit: pixel size constraints for the scaler
  * @min_inp_pixsize: minimum input pixel size
  * @min_out_pixsize: minimum output pixel size
- * @scaler_en_w: maximum input pixel width when the scaler is enabled
- * @scaler_dis_w: maximum input pixel width when the scaler is disabled
- * @in_rot_en_h: maximum input width when the input rotator is used
- * @in_rot_dis_w: maximum input width when the input rotator is used
- * @out_rot_en_w: maximum output width for the output rotator enabled
- * @out_rot_dis_w: maximum output width for the output rotator enabled
+ * @hor_offs_align: horizontal pixel offset aligment
+ * @out_buf_count: the number of buffers in output DMA sequence
  */
 struct samsung_fimc_variant {
 	unsigned int	pix_hoff:1;
 	unsigned int	has_inp_rot:1;
 	unsigned int	has_out_rot:1;
-
+	struct fimc_pix_limit *pix_limit;
 	u16		min_inp_pixsize;
 	u16		min_out_pixsize;
-	u16		scaler_en_w;
-	u16		scaler_dis_w;
-	u16		in_rot_en_h;
-	u16		in_rot_dis_w;
-	u16		out_rot_en_w;
-	u16		out_rot_dis_w;
+	u16		hor_offs_align;
+	u16		out_buf_count;
 };
 
 /**
- * struct samsung_fimc_driverdata - per-device type driver data for init time.
+ * struct samsung_fimc_driverdata - per device type driver data for init time.
  *
  * @variant: the variant information for this driver.
  * @dev_cnt: number of fimc sub-devices available in SoC
+ * @lclk_frequency: fimc bus clock frequency
  */
 struct samsung_fimc_driverdata {
 	struct samsung_fimc_variant *variant[FIMC_MAX_DEVS];
-	int	devs_cnt;
+	unsigned long	lclk_frequency;
+	int		num_entities;
 };
 
 struct fimc_ctx;
 
 /**
- * struct fimc_subdev - abstraction for a FIMC entity
+ * struct fimc_dev - abstraction for FIMC entity
  *
  * @slock:	the spinlock protecting this data structure
  * @lock:	the mutex protecting this data structure
  * @pdev:	pointer to the FIMC platform device
+ * @pdata:	pointer to the device platform data
  * @id:		FIMC device index (0..2)
  * @clock[]:	the clocks required for FIMC operation
  * @regs:	the mapped hardware registers
  * @regs_res:	the resource claimed for IO registers
  * @irq:	interrupt number of the FIMC subdevice
- * @irqlock:	spinlock protecting videbuffer queue
+ * @irqlock:	spinlock protecting videobuffer queue
+ * @irq_queue:
  * @m2m:	memory-to-memory V4L2 device information
- * @state:	the FIMC device state flags
+ * @vid_cap:	camera capture device information
+ * @state:	flags used to synchronize m2m and capture mode operation
  */
 struct fimc_dev {
 	spinlock_t			slock;
 	struct mutex			lock;
 	struct platform_device		*pdev;
+	struct s3c_platform_fimc	*pdata;
 	struct samsung_fimc_variant	*variant;
 	int				id;
 	struct clk			*clock[NUM_FIMC_CLOCKS];
@@ -338,8 +431,9 @@
 	struct resource			*regs_res;
 	int				irq;
 	spinlock_t			irqlock;
-	struct workqueue_struct		*work_queue;
+	wait_queue_head_t		irq_queue;
 	struct fimc_m2m_device		m2m;
+	struct fimc_vid_cap		vid_cap;
 	unsigned long			state;
 };
 
@@ -359,7 +453,7 @@
  * @effect:		image effect
  * @rotation:		image clockwise rotation in degrees
  * @flip:		image flip mode
- * @flags:		an additional flags for image conversion
+ * @flags:		additional flags for image conversion
  * @state:		flags to keep track of user configuration
  * @fimc_dev:		the FIMC device this context applies to
  * @m2m_ctx:		memory-to-memory device context
@@ -384,6 +478,7 @@
 	struct v4l2_m2m_ctx	*m2m_ctx;
 };
 
+extern struct videobuf_queue_ops fimc_qops;
 
 static inline int tiled_fmt(struct fimc_fmt *fmt)
 {
@@ -397,18 +492,24 @@
 	writel(cfg, dev->regs + S5P_CIGCTRL);
 }
 
-static inline void fimc_hw_start_scaler(struct fimc_dev *dev)
+static inline void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on)
 {
 	u32 cfg = readl(dev->regs + S5P_CISCCTRL);
-	cfg |= S5P_CISCCTRL_SCALERSTART;
+	if (on)
+		cfg |= S5P_CISCCTRL_SCALERSTART;
+	else
+		cfg &= ~S5P_CISCCTRL_SCALERSTART;
 	writel(cfg, dev->regs + S5P_CISCCTRL);
 }
 
-static inline void fimc_hw_stop_scaler(struct fimc_dev *dev)
+static inline void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on)
 {
-	u32 cfg = readl(dev->regs + S5P_CISCCTRL);
-	cfg &= ~S5P_CISCCTRL_SCALERSTART;
-	writel(cfg, dev->regs + S5P_CISCCTRL);
+	u32 cfg = readl(dev->regs + S5P_MSCTRL);
+	if (on)
+		cfg |= S5P_MSCTRL_ENVID;
+	else
+		cfg &= ~S5P_MSCTRL_ENVID;
+	writel(cfg, dev->regs + S5P_MSCTRL);
 }
 
 static inline void fimc_hw_dis_capture(struct fimc_dev *dev)
@@ -418,27 +519,30 @@
 	writel(cfg, dev->regs + S5P_CIIMGCPT);
 }
 
-static inline void fimc_hw_start_in_dma(struct fimc_dev *dev)
+/**
+ * fimc_hw_set_dma_seq - configure output DMA buffer sequence
+ * @mask: each bit corresponds to one of 32 output buffer registers set
+ *	  1 to include buffer in the sequence, 0 to disable
+ *
+ * This function mask output DMA ring buffers, i.e. it allows to configure
+ * which of the output buffer address registers will be used by the DMA
+ * engine.
+ */
+static inline void fimc_hw_set_dma_seq(struct fimc_dev *dev, u32 mask)
 {
-	u32 cfg = readl(dev->regs + S5P_MSCTRL);
-	cfg |= S5P_MSCTRL_ENVID;
-	writel(cfg, dev->regs + S5P_MSCTRL);
+	writel(mask, dev->regs + S5P_CIFCNTSEQ);
 }
 
-static inline void fimc_hw_stop_in_dma(struct fimc_dev *dev)
-{
-	u32 cfg = readl(dev->regs + S5P_MSCTRL);
-	cfg &= ~S5P_MSCTRL_ENVID;
-	writel(cfg, dev->regs + S5P_MSCTRL);
-}
-
-static inline struct fimc_frame *ctx_m2m_get_frame(struct fimc_ctx *ctx,
-						   enum v4l2_buf_type type)
+static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx,
+					       enum v4l2_buf_type type)
 {
 	struct fimc_frame *frame;
 
 	if (V4L2_BUF_TYPE_VIDEO_OUTPUT == type) {
-		frame = &ctx->s_frame;
+		if (ctx->state & FIMC_CTX_M2M)
+			frame = &ctx->s_frame;
+		else
+			return ERR_PTR(-EINVAL);
 	} else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == type) {
 		frame = &ctx->d_frame;
 	} else {
@@ -450,22 +554,137 @@
 	return frame;
 }
 
+static inline u32 fimc_hw_get_frame_index(struct fimc_dev *dev)
+{
+	u32 reg = readl(dev->regs + S5P_CISTATUS);
+	return (reg & S5P_CISTATUS_FRAMECNT_MASK) >>
+		S5P_CISTATUS_FRAMECNT_SHIFT;
+}
+
 /* -----------------------------------------------------*/
 /* fimc-reg.c						*/
-void fimc_hw_reset(struct fimc_dev *dev);
+void fimc_hw_reset(struct fimc_dev *fimc);
 void fimc_hw_set_rotation(struct fimc_ctx *ctx);
 void fimc_hw_set_target_format(struct fimc_ctx *ctx);
 void fimc_hw_set_out_dma(struct fimc_ctx *ctx);
-void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable);
-void fimc_hw_en_irq(struct fimc_dev *dev, int enable);
-void fimc_hw_set_prescaler(struct fimc_ctx *ctx);
+void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable);
+void fimc_hw_en_irq(struct fimc_dev *fimc, int enable);
 void fimc_hw_set_scaler(struct fimc_ctx *ctx);
 void fimc_hw_en_capture(struct fimc_ctx *ctx);
 void fimc_hw_set_effect(struct fimc_ctx *ctx);
 void fimc_hw_set_in_dma(struct fimc_ctx *ctx);
 void fimc_hw_set_input_path(struct fimc_ctx *ctx);
 void fimc_hw_set_output_path(struct fimc_ctx *ctx);
-void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr);
-void fimc_hw_set_output_addr(struct fimc_dev *dev, struct fimc_addr *paddr);
+void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr);
+void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr,
+			      int index);
+int fimc_hw_set_camera_source(struct fimc_dev *fimc,
+			      struct s3c_fimc_isp_info *cam);
+int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f);
+int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
+				struct s3c_fimc_isp_info *cam);
+int fimc_hw_set_camera_type(struct fimc_dev *fimc,
+			    struct s3c_fimc_isp_info *cam);
+
+/* -----------------------------------------------------*/
+/* fimc-core.c */
+int fimc_vidioc_enum_fmt(struct file *file, void *priv,
+		      struct v4l2_fmtdesc *f);
+int fimc_vidioc_g_fmt(struct file *file, void *priv,
+		      struct v4l2_format *f);
+int fimc_vidioc_try_fmt(struct file *file, void *priv,
+			struct v4l2_format *f);
+int fimc_vidioc_g_crop(struct file *file, void *fh,
+		       struct v4l2_crop *cr);
+int fimc_vidioc_cropcap(struct file *file, void *fh,
+			struct v4l2_cropcap *cr);
+int fimc_vidioc_queryctrl(struct file *file, void *priv,
+			  struct v4l2_queryctrl *qc);
+int fimc_vidioc_g_ctrl(struct file *file, void *priv,
+		       struct v4l2_control *ctrl);
+
+int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr);
+int check_ctrl_val(struct fimc_ctx *ctx,  struct v4l2_control *ctrl);
+int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl);
+
+struct fimc_fmt *find_format(struct v4l2_format *f, unsigned int mask);
+struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f,
+				  unsigned int mask);
+
+int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f);
+int fimc_set_scaler_info(struct fimc_ctx *ctx);
+int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags);
+int fimc_prepare_addr(struct fimc_ctx *ctx, struct fimc_vid_buffer *buf,
+		      struct fimc_frame *frame, struct fimc_addr *paddr);
+
+/* -----------------------------------------------------*/
+/* fimc-capture.c					*/
+int fimc_register_capture_device(struct fimc_dev *fimc);
+void fimc_unregister_capture_device(struct fimc_dev *fimc);
+int fimc_sensor_sd_init(struct fimc_dev *fimc, int index);
+int fimc_vid_cap_buf_queue(struct fimc_dev *fimc,
+			     struct fimc_vid_buffer *fimc_vb);
+
+/* Locking: the caller holds fimc->slock */
+static inline void fimc_activate_capture(struct fimc_ctx *ctx)
+{
+	fimc_hw_enable_scaler(ctx->fimc_dev, ctx->scaler.enabled);
+	fimc_hw_en_capture(ctx);
+}
+
+static inline void fimc_deactivate_capture(struct fimc_dev *fimc)
+{
+	fimc_hw_en_lastirq(fimc, true);
+	fimc_hw_dis_capture(fimc);
+	fimc_hw_enable_scaler(fimc, false);
+	fimc_hw_en_lastirq(fimc, false);
+}
+
+/*
+ * Add video buffer to the active buffers queue.
+ * The caller holds irqlock spinlock.
+ */
+static inline void active_queue_add(struct fimc_vid_cap *vid_cap,
+					 struct fimc_vid_buffer *buf)
+{
+	buf->vb.state = VIDEOBUF_ACTIVE;
+	list_add_tail(&buf->vb.queue, &vid_cap->active_buf_q);
+	vid_cap->active_buf_cnt++;
+}
+
+/*
+ * Pop a video buffer from the capture active buffers queue
+ * Locking: Need to be called with dev->slock held.
+ */
+static inline struct fimc_vid_buffer *
+active_queue_pop(struct fimc_vid_cap *vid_cap)
+{
+	struct fimc_vid_buffer *buf;
+	buf = list_entry(vid_cap->active_buf_q.next,
+			 struct fimc_vid_buffer, vb.queue);
+	list_del(&buf->vb.queue);
+	vid_cap->active_buf_cnt--;
+	return buf;
+}
+
+/* Add video buffer to the capture pending buffers queue */
+static inline void fimc_pending_queue_add(struct fimc_vid_cap *vid_cap,
+					  struct fimc_vid_buffer *buf)
+{
+	buf->vb.state = VIDEOBUF_QUEUED;
+	list_add_tail(&buf->vb.queue, &vid_cap->pending_buf_q);
+}
+
+/* Add video buffer to the capture pending buffers queue */
+static inline struct fimc_vid_buffer *
+pending_queue_pop(struct fimc_vid_cap *vid_cap)
+{
+	struct fimc_vid_buffer *buf;
+	buf = list_entry(vid_cap->pending_buf_q.next,
+			struct fimc_vid_buffer, vb.queue);
+	list_del(&buf->vb.queue);
+	return buf;
+}
+
 
 #endif /* FIMC_CORE_H_ */
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c
index 5570f1c..511631a 100644
--- a/drivers/media/video/s5p-fimc/fimc-reg.c
+++ b/drivers/media/video/s5p-fimc/fimc-reg.c
@@ -13,6 +13,7 @@
 #include <linux/io.h>
 #include <linux/delay.h>
 #include <mach/map.h>
+#include <media/s3c_fimc.h>
 
 #include "fimc-core.h"
 
@@ -29,49 +30,11 @@
 	cfg = readl(dev->regs + S5P_CIGCTRL);
 	cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL);
 	writel(cfg, dev->regs + S5P_CIGCTRL);
-	msleep(1);
+	udelay(1000);
 
 	cfg = readl(dev->regs + S5P_CIGCTRL);
 	cfg &= ~S5P_CIGCTRL_SWRST;
 	writel(cfg, dev->regs + S5P_CIGCTRL);
-
-}
-
-void fimc_hw_set_rotation(struct fimc_ctx *ctx)
-{
-	u32 cfg, flip;
-	struct fimc_dev *dev = ctx->fimc_dev;
-
-	cfg = readl(dev->regs + S5P_CITRGFMT);
-	cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90);
-
-	flip = readl(dev->regs + S5P_MSCTRL);
-	flip &= ~S5P_MSCTRL_FLIP_MASK;
-
-	/*
-	 * The input and output rotator cannot work simultaneously.
-	 * Use the output rotator in output DMA mode or the input rotator
-	 * in direct fifo output mode.
-	 */
-	if (ctx->rotation == 90 || ctx->rotation == 270) {
-		if (ctx->out_path == FIMC_LCDFIFO) {
-			cfg |= S5P_CITRGFMT_INROT90;
-			if (ctx->rotation == 270)
-				flip |= S5P_MSCTRL_FLIP_180;
-		} else {
-			cfg |= S5P_CITRGFMT_OUTROT90;
-			if (ctx->rotation == 270)
-				cfg |= S5P_CITRGFMT_FLIP_180;
-		}
-	} else if (ctx->rotation == 180) {
-		if (ctx->out_path == FIMC_LCDFIFO)
-			flip |= S5P_MSCTRL_FLIP_180;
-		else
-			cfg |= S5P_CITRGFMT_FLIP_180;
-	}
-	if (ctx->rotation == 180 || ctx->rotation == 270)
-		writel(flip, dev->regs + S5P_MSCTRL);
-	writel(cfg, dev->regs + S5P_CITRGFMT);
 }
 
 static u32 fimc_hw_get_in_flip(u32 ctx_flip)
@@ -114,6 +77,46 @@
 	return flip;
 }
 
+void fimc_hw_set_rotation(struct fimc_ctx *ctx)
+{
+	u32 cfg, flip;
+	struct fimc_dev *dev = ctx->fimc_dev;
+
+	cfg = readl(dev->regs + S5P_CITRGFMT);
+	cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90 |
+		  S5P_CITRGFMT_FLIP_180);
+
+	flip = readl(dev->regs + S5P_MSCTRL);
+	flip &= ~S5P_MSCTRL_FLIP_MASK;
+
+	/*
+	 * The input and output rotator cannot work simultaneously.
+	 * Use the output rotator in output DMA mode or the input rotator
+	 * in direct fifo output mode.
+	 */
+	if (ctx->rotation == 90 || ctx->rotation == 270) {
+		if (ctx->out_path == FIMC_LCDFIFO) {
+			cfg |= S5P_CITRGFMT_INROT90;
+			if (ctx->rotation == 270)
+				flip |= S5P_MSCTRL_FLIP_180;
+		} else {
+			cfg |= S5P_CITRGFMT_OUTROT90;
+			if (ctx->rotation == 270)
+				cfg |= S5P_CITRGFMT_FLIP_180;
+		}
+	} else if (ctx->rotation == 180) {
+		if (ctx->out_path == FIMC_LCDFIFO)
+			flip |= S5P_MSCTRL_FLIP_180;
+		else
+			cfg |= S5P_CITRGFMT_FLIP_180;
+	}
+	if (ctx->rotation == 180 || ctx->rotation == 270)
+		writel(flip, dev->regs + S5P_MSCTRL);
+
+	cfg |= fimc_hw_get_target_flip(ctx->flip);
+	writel(cfg, dev->regs + S5P_CITRGFMT);
+}
+
 void fimc_hw_set_target_format(struct fimc_ctx *ctx)
 {
 	u32 cfg;
@@ -149,13 +152,15 @@
 		break;
 	}
 
-	cfg |= S5P_CITRGFMT_HSIZE(frame->width);
-	cfg |= S5P_CITRGFMT_VSIZE(frame->height);
+	if (ctx->rotation == 90 || ctx->rotation == 270) {
+		cfg |= S5P_CITRGFMT_HSIZE(frame->height);
+		cfg |= S5P_CITRGFMT_VSIZE(frame->width);
+	} else {
 
-	if (ctx->rotation == 0) {
-		cfg &= ~S5P_CITRGFMT_FLIP_MASK;
-		cfg |= fimc_hw_get_target_flip(ctx->flip);
+		cfg |= S5P_CITRGFMT_HSIZE(frame->width);
+		cfg |= S5P_CITRGFMT_VSIZE(frame->height);
 	}
+
 	writel(cfg, dev->regs + S5P_CITRGFMT);
 
 	cfg = readl(dev->regs + S5P_CITAREA) & ~S5P_CITAREA_MASK;
@@ -167,16 +172,20 @@
 {
 	struct fimc_dev *dev = ctx->fimc_dev;
 	struct fimc_frame *frame = &ctx->d_frame;
-	u32 cfg = 0;
+	u32 cfg;
 
-	if (ctx->rotation == 90 || ctx->rotation == 270) {
-		cfg |= S5P_ORIG_SIZE_HOR(frame->f_height);
-		cfg |= S5P_ORIG_SIZE_VER(frame->f_width);
-	} else {
-		cfg |= S5P_ORIG_SIZE_HOR(frame->f_width);
-		cfg |= S5P_ORIG_SIZE_VER(frame->f_height);
-	}
+	cfg = S5P_ORIG_SIZE_HOR(frame->f_width);
+	cfg |= S5P_ORIG_SIZE_VER(frame->f_height);
 	writel(cfg, dev->regs + S5P_ORGOSIZE);
+
+	/* Select color space conversion equation (HD/SD size).*/
+	cfg = readl(dev->regs + S5P_CIGCTRL);
+	if (frame->f_width >= 1280) /* HD */
+		cfg |= S5P_CIGCTRL_CSC_ITU601_709;
+	else	/* SD */
+		cfg &= ~S5P_CIGCTRL_CSC_ITU601_709;
+	writel(cfg, dev->regs + S5P_CIGCTRL);
+
 }
 
 void fimc_hw_set_out_dma(struct fimc_ctx *ctx)
@@ -232,36 +241,28 @@
 
 void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable)
 {
-	unsigned long flags;
-	u32 cfg;
-
-	spin_lock_irqsave(&dev->slock, flags);
-
-	cfg = readl(dev->regs + S5P_CIOCTRL);
+	u32 cfg = readl(dev->regs + S5P_CIOCTRL);
 	if (enable)
 		cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE;
 	else
 		cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE;
 	writel(cfg, dev->regs + S5P_CIOCTRL);
-
-	spin_unlock_irqrestore(&dev->slock, flags);
 }
 
-void fimc_hw_set_prescaler(struct fimc_ctx *ctx)
+static void fimc_hw_set_prescaler(struct fimc_ctx *ctx)
 {
 	struct fimc_dev *dev =  ctx->fimc_dev;
 	struct fimc_scaler *sc = &ctx->scaler;
-	u32 cfg = 0, shfactor;
+	u32 cfg, shfactor;
 
 	shfactor = 10 - (sc->hfactor + sc->vfactor);
 
-	cfg |= S5P_CISCPRERATIO_SHFACTOR(shfactor);
+	cfg = S5P_CISCPRERATIO_SHFACTOR(shfactor);
 	cfg |= S5P_CISCPRERATIO_HOR(sc->pre_hratio);
 	cfg |= S5P_CISCPRERATIO_VER(sc->pre_vratio);
 	writel(cfg, dev->regs + S5P_CISCPRERATIO);
 
-	cfg = 0;
-	cfg |= S5P_CISCPREDST_WIDTH(sc->pre_dst_width);
+	cfg = S5P_CISCPREDST_WIDTH(sc->pre_dst_width);
 	cfg |= S5P_CISCPREDST_HEIGHT(sc->pre_dst_height);
 	writel(cfg, dev->regs + S5P_CISCPREDST);
 }
@@ -274,6 +275,8 @@
 	struct fimc_frame *dst_frame = &ctx->d_frame;
 	u32 cfg = 0;
 
+	fimc_hw_set_prescaler(ctx);
+
 	if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW))
 		cfg |= (S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE);
 
@@ -325,14 +328,18 @@
 void fimc_hw_en_capture(struct fimc_ctx *ctx)
 {
 	struct fimc_dev *dev = ctx->fimc_dev;
-	u32 cfg;
 
-	cfg = readl(dev->regs + S5P_CIIMGCPT);
-	/* One shot mode for output DMA or freerun for FIFO. */
-	if (ctx->out_path == FIMC_DMA)
-		cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE;
-	else
-		cfg &= ~S5P_CIIMGCPT_CPT_FREN_ENABLE;
+	u32 cfg = readl(dev->regs + S5P_CIIMGCPT);
+
+	if (ctx->out_path == FIMC_DMA) {
+		/* one shot mode */
+		cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE | S5P_CIIMGCPT_IMGCPTEN;
+	} else {
+		/* Continous frame capture mode (freerun). */
+		cfg &= ~(S5P_CIIMGCPT_CPT_FREN_ENABLE |
+			 S5P_CIIMGCPT_CPT_FRMOD_CNT);
+		cfg |= S5P_CIIMGCPT_IMGCPTEN;
+	}
 
 	if (ctx->scaler.enabled)
 		cfg |= S5P_CIIMGCPT_IMGCPTEN_SC;
@@ -364,7 +371,7 @@
 	u32 cfg_r = 0;
 
 	if (FIMC_LCDFIFO == ctx->out_path)
-		cfg_r |=  S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+		cfg_r |= S5P_CIREAL_ISIZE_AUTOLOAD_EN;
 
 	cfg_o |= S5P_ORIG_SIZE_HOR(frame->f_width);
 	cfg_o |= S5P_ORIG_SIZE_VER(frame->f_height);
@@ -380,27 +387,25 @@
 	struct fimc_dev *dev = ctx->fimc_dev;
 	struct fimc_frame *frame = &ctx->s_frame;
 	struct fimc_dma_offset *offset = &frame->dma_offset;
-	u32 cfg = 0;
+	u32 cfg;
 
 	/* Set the pixel offsets. */
-	cfg |= S5P_CIO_OFFS_HOR(offset->y_h);
+	cfg = S5P_CIO_OFFS_HOR(offset->y_h);
 	cfg |= S5P_CIO_OFFS_VER(offset->y_v);
 	writel(cfg, dev->regs + S5P_CIIYOFF);
 
-	cfg = 0;
-	cfg |= S5P_CIO_OFFS_HOR(offset->cb_h);
+	cfg = S5P_CIO_OFFS_HOR(offset->cb_h);
 	cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
 	writel(cfg, dev->regs + S5P_CIICBOFF);
 
-	cfg = 0;
-	cfg |= S5P_CIO_OFFS_HOR(offset->cr_h);
+	cfg = S5P_CIO_OFFS_HOR(offset->cr_h);
 	cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
 	writel(cfg, dev->regs + S5P_CIICROFF);
 
 	/* Input original and real size. */
 	fimc_hw_set_in_dma_size(ctx);
 
-	/* Autoload is used currently only in FIFO mode. */
+	/* Use DMA autoload only in FIFO mode. */
 	fimc_hw_en_autoload(dev, ctx->out_path == FIMC_LCDFIFO);
 
 	/* Set the input DMA to process single frame only. */
@@ -501,27 +506,163 @@
 
 void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr)
 {
-	u32 cfg = 0;
-
-	cfg = readl(dev->regs + S5P_CIREAL_ISIZE);
+	u32 cfg = readl(dev->regs + S5P_CIREAL_ISIZE);
 	cfg |= S5P_CIREAL_ISIZE_ADDR_CH_DIS;
 	writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
 
-	writel(paddr->y, dev->regs + S5P_CIIYSA0);
-	writel(paddr->cb, dev->regs + S5P_CIICBSA0);
-	writel(paddr->cr, dev->regs + S5P_CIICRSA0);
+	writel(paddr->y, dev->regs + S5P_CIIYSA(0));
+	writel(paddr->cb, dev->regs + S5P_CIICBSA(0));
+	writel(paddr->cr, dev->regs + S5P_CIICRSA(0));
 
 	cfg &= ~S5P_CIREAL_ISIZE_ADDR_CH_DIS;
 	writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
 }
 
-void fimc_hw_set_output_addr(struct fimc_dev *dev, struct fimc_addr *paddr)
+void fimc_hw_set_output_addr(struct fimc_dev *dev,
+			     struct fimc_addr *paddr, int index)
 {
-	int i;
-	/* Set all the output register sets to point to single video buffer. */
-	for (i = 0; i < FIMC_MAX_OUT_BUFS; i++) {
+	int i = (index == -1) ? 0 : index;
+	do {
 		writel(paddr->y, dev->regs + S5P_CIOYSA(i));
 		writel(paddr->cb, dev->regs + S5P_CIOCBSA(i));
 		writel(paddr->cr, dev->regs + S5P_CIOCRSA(i));
+		dbg("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X",
+		    i, paddr->y, paddr->cb, paddr->cr);
+	} while (index == -1 && ++i < FIMC_MAX_OUT_BUFS);
+}
+
+int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
+				struct s3c_fimc_isp_info *cam)
+{
+	u32 cfg = readl(fimc->regs + S5P_CIGCTRL);
+
+	cfg &= ~(S5P_CIGCTRL_INVPOLPCLK | S5P_CIGCTRL_INVPOLVSYNC |
+		 S5P_CIGCTRL_INVPOLHREF | S5P_CIGCTRL_INVPOLHSYNC);
+
+	if (cam->flags & FIMC_CLK_INV_PCLK)
+		cfg |= S5P_CIGCTRL_INVPOLPCLK;
+
+	if (cam->flags & FIMC_CLK_INV_VSYNC)
+		cfg |= S5P_CIGCTRL_INVPOLVSYNC;
+
+	if (cam->flags & FIMC_CLK_INV_HREF)
+		cfg |= S5P_CIGCTRL_INVPOLHREF;
+
+	if (cam->flags & FIMC_CLK_INV_HSYNC)
+		cfg |= S5P_CIGCTRL_INVPOLHSYNC;
+
+	writel(cfg, fimc->regs + S5P_CIGCTRL);
+
+	return 0;
+}
+
+int fimc_hw_set_camera_source(struct fimc_dev *fimc,
+			      struct s3c_fimc_isp_info *cam)
+{
+	struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
+	u32 cfg = 0;
+
+	if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) {
+
+		switch (fimc->vid_cap.fmt.code) {
+		case V4L2_MBUS_FMT_YUYV8_2X8:
+			cfg = S5P_CISRCFMT_ORDER422_YCBYCR;
+			break;
+		case V4L2_MBUS_FMT_YVYU8_2X8:
+			cfg = S5P_CISRCFMT_ORDER422_YCRYCB;
+			break;
+		case V4L2_MBUS_FMT_VYUY8_2X8:
+			cfg = S5P_CISRCFMT_ORDER422_CRYCBY;
+			break;
+		case V4L2_MBUS_FMT_UYVY8_2X8:
+			cfg = S5P_CISRCFMT_ORDER422_CBYCRY;
+			break;
+		default:
+			err("camera image format not supported: %d",
+			    fimc->vid_cap.fmt.code);
+			return -EINVAL;
+		}
+
+		if (cam->bus_type == FIMC_ITU_601) {
+			if (cam->bus_width == 8) {
+				cfg |= S5P_CISRCFMT_ITU601_8BIT;
+			} else if (cam->bus_width == 16) {
+				cfg |= S5P_CISRCFMT_ITU601_16BIT;
+			} else {
+				err("invalid bus width: %d", cam->bus_width);
+				return -EINVAL;
+			}
+		} /* else defaults to ITU-R BT.656 8-bit */
 	}
+
+	cfg |= S5P_CISRCFMT_HSIZE(f->o_width) | S5P_CISRCFMT_VSIZE(f->o_height);
+	writel(cfg, fimc->regs + S5P_CISRCFMT);
+	return 0;
+}
+
+
+int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f)
+{
+	u32 hoff2, voff2;
+
+	u32 cfg = readl(fimc->regs + S5P_CIWDOFST);
+
+	cfg &= ~(S5P_CIWDOFST_HOROFF_MASK | S5P_CIWDOFST_VEROFF_MASK);
+	cfg |=  S5P_CIWDOFST_OFF_EN |
+		S5P_CIWDOFST_HOROFF(f->offs_h) |
+		S5P_CIWDOFST_VEROFF(f->offs_v);
+
+	writel(cfg, fimc->regs + S5P_CIWDOFST);
+
+	/* See CIWDOFSTn register description in the datasheet for details. */
+	hoff2 = f->o_width - f->width - f->offs_h;
+	voff2 = f->o_height - f->height - f->offs_v;
+	cfg = S5P_CIWDOFST2_HOROFF(hoff2) | S5P_CIWDOFST2_VEROFF(voff2);
+
+	writel(cfg, fimc->regs + S5P_CIWDOFST2);
+	return 0;
+}
+
+int fimc_hw_set_camera_type(struct fimc_dev *fimc,
+			    struct s3c_fimc_isp_info *cam)
+{
+	u32 cfg, tmp;
+	struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+
+	cfg = readl(fimc->regs + S5P_CIGCTRL);
+
+	/* Select ITU B interface, disable Writeback path and test pattern. */
+	cfg &= ~(S5P_CIGCTRL_TESTPAT_MASK | S5P_CIGCTRL_SELCAM_ITU_A |
+		S5P_CIGCTRL_SELCAM_MIPI | S5P_CIGCTRL_CAMIF_SELWB |
+		S5P_CIGCTRL_SELCAM_MIPI_A);
+
+	if (cam->bus_type == FIMC_MIPI_CSI2) {
+		cfg |= S5P_CIGCTRL_SELCAM_MIPI;
+
+		if (cam->mux_id == 0)
+			cfg |= S5P_CIGCTRL_SELCAM_MIPI_A;
+
+		/* TODO: add remaining supported formats. */
+		if (vid_cap->fmt.code == V4L2_MBUS_FMT_VYUY8_2X8) {
+			tmp = S5P_CSIIMGFMT_YCBCR422_8BIT;
+		} else {
+			err("camera image format not supported: %d",
+			    vid_cap->fmt.code);
+			return -EINVAL;
+		}
+		writel(tmp | (0x1 << 8), fimc->regs + S5P_CSIIMGFMT);
+
+	} else if (cam->bus_type == FIMC_ITU_601 ||
+		  cam->bus_type == FIMC_ITU_656) {
+		if (cam->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */
+			cfg |= S5P_CIGCTRL_SELCAM_ITU_A;
+	} else if (cam->bus_type == FIMC_LCD_WB) {
+		cfg |= S5P_CIGCTRL_CAMIF_SELWB;
+	} else {
+		err("invalid camera bus type selected\n");
+		return -EINVAL;
+	}
+	writel(cfg, fimc->regs + S5P_CIGCTRL);
+
+	return 0;
 }
diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h
index a3cfe82..a57daed 100644
--- a/drivers/media/video/s5p-fimc/regs-fimc.h
+++ b/drivers/media/video/s5p-fimc/regs-fimc.h
@@ -11,10 +11,6 @@
 #ifndef REGS_FIMC_H_
 #define REGS_FIMC_H_
 
-#define S5P_CIOYSA(__x)			(0x18 + (__x) * 4)
-#define S5P_CIOCBSA(__x)		(0x28 + (__x) * 4)
-#define S5P_CIOCRSA(__x)		(0x38 + (__x) * 4)
-
 /* Input source format */
 #define S5P_CISRCFMT			0x00
 #define S5P_CISRCFMT_ITU601_8BIT	(1 << 31)
@@ -28,22 +24,21 @@
 
 /* Window offset */
 #define S5P_CIWDOFST			0x04
-#define S5P_CIWDOFST_WINOFSEN		(1 << 31)
+#define S5P_CIWDOFST_OFF_EN		(1 << 31)
 #define S5P_CIWDOFST_CLROVFIY		(1 << 30)
 #define S5P_CIWDOFST_CLROVRLB		(1 << 29)
-#define S5P_CIWDOFST_WINHOROFST_MASK	(0x7ff << 16)
+#define S5P_CIWDOFST_HOROFF_MASK	(0x7ff << 16)
 #define S5P_CIWDOFST_CLROVFICB		(1 << 15)
 #define S5P_CIWDOFST_CLROVFICR		(1 << 14)
-#define S5P_CIWDOFST_WINHOROFST(x)	((x) << 16)
-#define S5P_CIWDOFST_WINVEROFST(x)	((x) << 0)
-#define S5P_CIWDOFST_WINVEROFST_MASK	(0xfff << 0)
+#define S5P_CIWDOFST_HOROFF(x)		((x) << 16)
+#define S5P_CIWDOFST_VEROFF(x)		((x) << 0)
+#define S5P_CIWDOFST_VEROFF_MASK	(0xfff << 0)
 
 /* Global control */
 #define S5P_CIGCTRL			0x08
 #define S5P_CIGCTRL_SWRST		(1 << 31)
 #define S5P_CIGCTRL_CAMRST_A		(1 << 30)
 #define S5P_CIGCTRL_SELCAM_ITU_A	(1 << 29)
-#define S5P_CIGCTRL_SELCAM_ITU_MASK	(1 << 29)
 #define S5P_CIGCTRL_TESTPAT_NORMAL	(0 << 27)
 #define S5P_CIGCTRL_TESTPAT_COLOR_BAR	(1 << 27)
 #define S5P_CIGCTRL_TESTPAT_HOR_INC	(2 << 27)
@@ -61,6 +56,8 @@
 #define S5P_CIGCTRL_SHDW_DISABLE	(1 << 12)
 #define S5P_CIGCTRL_SELCAM_MIPI_A	(1 << 7)
 #define S5P_CIGCTRL_CAMIF_SELWB		(1 << 6)
+/* 0 - ITU601; 1 - ITU709 */
+#define S5P_CIGCTRL_CSC_ITU601_709	(1 << 5)
 #define S5P_CIGCTRL_INVPOLHSYNC		(1 << 4)
 #define S5P_CIGCTRL_SELCAM_MIPI		(1 << 3)
 #define S5P_CIGCTRL_INTERLACE		(1 << 0)
@@ -72,23 +69,10 @@
 #define S5P_CIWDOFST2_HOROFF(x)		((x) << 16)
 #define S5P_CIWDOFST2_VEROFF(x)		((x) << 0)
 
-/* Output DMA Y plane start address */
-#define S5P_CIOYSA1			0x18
-#define S5P_CIOYSA2			0x1c
-#define S5P_CIOYSA3			0x20
-#define S5P_CIOYSA4			0x24
-
-/* Output DMA Cb plane start address */
-#define S5P_CIOCBSA1			0x28
-#define S5P_CIOCBSA2			0x2c
-#define S5P_CIOCBSA3			0x30
-#define S5P_CIOCBSA4			0x34
-
-/* Output DMA Cr plane start address */
-#define S5P_CIOCRSA1			0x38
-#define S5P_CIOCRSA2			0x3c
-#define S5P_CIOCRSA3			0x40
-#define S5P_CIOCRSA4			0x44
+/* Output DMA Y/Cb/Cr plane start addresses */
+#define S5P_CIOYSA(n)			(0x18 + (n) * 4)
+#define S5P_CIOCBSA(n)			(0x28 + (n) * 4)
+#define S5P_CIOCRSA(n)			(0x38 + (n) * 4)
 
 /* Target image format */
 #define S5P_CITRGFMT			0x48
@@ -168,6 +152,8 @@
 #define S5P_CISTATUS_OVFICB		(1 << 30)
 #define S5P_CISTATUS_OVFICR		(1 << 29)
 #define S5P_CISTATUS_VSYNC		(1 << 28)
+#define S5P_CISTATUS_FRAMECNT_MASK	(3 << 26)
+#define S5P_CISTATUS_FRAMECNT_SHIFT	26
 #define S5P_CISTATUS_WINOFF_EN		(1 << 25)
 #define S5P_CISTATUS_IMGCPT_EN		(1 << 22)
 #define S5P_CISTATUS_IMGCPT_SCEN	(1 << 21)
@@ -206,10 +192,10 @@
 #define S5P_CIIMGEFF_PAT_CB(x)		((x) << 13)
 #define S5P_CIIMGEFF_PAT_CR(x)		((x) << 0)
 
-/* Input DMA Y/Cb/Cr plane start address 0 */
-#define S5P_CIIYSA0			0xd4
-#define S5P_CIICBSA0			0xd8
-#define S5P_CIICRSA0			0xdc
+/* Input DMA Y/Cb/Cr plane start address 0/1 */
+#define S5P_CIIYSA(n)			(0xd4 + (n) * 0x70)
+#define S5P_CIICBSA(n)			(0xd8 + (n) * 0x70)
+#define S5P_CIICRSA(n)			(0xdc + (n) * 0x70)
 
 /* Real input DMA image size */
 #define S5P_CIREAL_ISIZE		0xf8
@@ -250,11 +236,6 @@
 #define S5P_MSCTRL_ENVID		(1 << 0)
 #define S5P_MSCTRL_FRAME_COUNT(x)	((x) << 24)
 
-/* Input DMA Y/Cb/Cr plane start address 1 */
-#define S5P_CIIYSA1			0x144
-#define S5P_CIICBSA1			0x148
-#define S5P_CIICRSA1			0x14c
-
 /* Output DMA Y/Cb/Cr offset */
 #define S5P_CIOYOFF			0x168
 #define S5P_CIOCBOFF			0x16c
@@ -289,5 +270,16 @@
 
 /* MIPI CSI image format */
 #define S5P_CSIIMGFMT			0x194
+#define S5P_CSIIMGFMT_YCBCR422_8BIT	0x1e
+#define S5P_CSIIMGFMT_RAW8		0x2a
+#define S5P_CSIIMGFMT_RAW10		0x2b
+#define S5P_CSIIMGFMT_RAW12		0x2c
+#define S5P_CSIIMGFMT_USER1		0x30
+#define S5P_CSIIMGFMT_USER2		0x31
+#define S5P_CSIIMGFMT_USER3		0x32
+#define S5P_CSIIMGFMT_USER4		0x33
+
+/* Output frame buffer sequence mask */
+#define S5P_CIFCNTSEQ			0x1FC
 
 #endif /* REGS_FIMC_H_ */
diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c
deleted file mode 100644
index 6b3b09e..0000000
--- a/drivers/media/video/saa5246a.c
+++ /dev/null
@@ -1,1123 +0,0 @@
-/*
- * Driver for the SAA5246A or SAA5281 Teletext (=Videotext) decoder chips from
- * Philips.
- *
- * Only capturing of Teletext pages is tested. The videotext chips also have a
- * TV output but my hardware doesn't use it. For this reason this driver does
- * not support changing any TV display settings.
- *
- * Copyright (C) 2004 Michael Geng <linux@MichaelGeng.de>
- *
- * Derived from
- *
- * saa5249 driver
- * Copyright (C) 1998 Richard Guenther
- * <richard.guenther@student.uni-tuebingen.de>
- *
- * with changes by
- * Alan Cox <alan@lxorguk.ukuu.org.uk>
- *
- * and
- *
- * vtx.c
- * Copyright (C) 1994-97 Martin Buck  <martin-2.buck@student.uni-ulm.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <linux/videotext.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-i2c-drv.h>
-
-MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>");
-MODULE_DESCRIPTION("Philips SAA5246A, SAA5281 Teletext decoder driver");
-MODULE_LICENSE("GPL");
-
-#define MAJOR_VERSION 1		/* driver major version number */
-#define MINOR_VERSION 8		/* driver minor version number */
-
-/* Number of DAUs = number of pages that can be searched at the same time. */
-#define NUM_DAUS 4
-
-#define NUM_ROWS_PER_PAGE 40
-
-/* first column is 0 (not 1) */
-#define POS_TIME_START 32
-#define POS_TIME_END 39
-
-#define POS_HEADER_START 7
-#define POS_HEADER_END 31
-
-/* Returns 'true' if the part of the videotext page described with req contains
-   (at least parts of) the time field */
-#define REQ_CONTAINS_TIME(p_req) \
-	((p_req)->start <= POS_TIME_END && \
-	 (p_req)->end   >= POS_TIME_START)
-
-/* Returns 'true' if the part of the videotext page described with req contains
-   (at least parts of) the page header */
-#define REQ_CONTAINS_HEADER(p_req) \
-	((p_req)->start <= POS_HEADER_END && \
-	 (p_req)->end   >= POS_HEADER_START)
-
-/*****************************************************************************/
-/* Mode register numbers of the SAA5246A				     */
-/*****************************************************************************/
-#define SAA5246A_REGISTER_R0    0
-#define SAA5246A_REGISTER_R1    1
-#define SAA5246A_REGISTER_R2    2
-#define SAA5246A_REGISTER_R3    3
-#define SAA5246A_REGISTER_R4    4
-#define SAA5246A_REGISTER_R5    5
-#define SAA5246A_REGISTER_R6    6
-#define SAA5246A_REGISTER_R7    7
-#define SAA5246A_REGISTER_R8    8
-#define SAA5246A_REGISTER_R9    9
-#define SAA5246A_REGISTER_R10  10
-#define SAA5246A_REGISTER_R11  11
-#define SAA5246A_REGISTER_R11B 11
-
-/* SAA5246A mode registers often autoincrement to the next register.
-   Therefore we use variable argument lists. The following macro indicates
-   the end of a command list. */
-#define COMMAND_END (-1)
-
-/*****************************************************************************/
-/* Contents of the mode registers of the SAA5246A			     */
-/*****************************************************************************/
-/* Register R0 (Advanced Control) */
-#define R0_SELECT_R11					   0x00
-#define R0_SELECT_R11B					   0x01
-
-#define R0_PLL_TIME_CONSTANT_LONG			   0x00
-#define R0_PLL_TIME_CONSTANT_SHORT			   0x02
-
-#define R0_ENABLE_nODD_EVEN_OUTPUT			   0x00
-#define R0_DISABLE_nODD_EVEN_OUTPUT			   0x04
-
-#define R0_ENABLE_HDR_POLL				   0x00
-#define R0_DISABLE_HDR_POLL				   0x10
-
-#define R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x00
-#define R0_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED	   0x20
-
-#define R0_NO_FREE_RUN_PLL				   0x00
-#define R0_FREE_RUN_PLL					   0x40
-
-#define R0_NO_AUTOMATIC_FASTEXT_PROMPT			   0x00
-#define R0_AUTOMATIC_FASTEXT_PROMPT			   0x80
-
-/* Register R1 (Mode) */
-#define R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES	   0x00
-#define R1_NON_INTERLACED_312_313_LINES			   0x01
-#define R1_NON_INTERLACED_312_312_LINES			   0x02
-#define R1_FFB_LEADING_EDGE_IN_FIRST_BROAD_PULSE	   0x03
-#define R1_FFB_LEADING_EDGE_IN_SECOND_BROAD_PULSE	   0x07
-
-#define R1_DEW						   0x00
-#define R1_FULL_FIELD					   0x08
-
-#define R1_EXTENDED_PACKET_DISABLE			   0x00
-#define R1_EXTENDED_PACKET_ENABLE			   0x10
-
-#define R1_DAUS_ALL_ON					   0x00
-#define R1_DAUS_ALL_OFF					   0x20
-
-#define R1_7_BITS_PLUS_PARITY				   0x00
-#define R1_8_BITS_NO_PARITY				   0x40
-
-#define R1_VCS_TO_SCS					   0x00
-#define R1_NO_VCS_TO_SCS				   0x80
-
-/* Register R2 (Page request address) */
-#define R2_IN_R3_SELECT_PAGE_HUNDREDS			   0x00
-#define R2_IN_R3_SELECT_PAGE_TENS			   0x01
-#define R2_IN_R3_SELECT_PAGE_UNITS			   0x02
-#define R2_IN_R3_SELECT_HOURS_TENS			   0x03
-#define R2_IN_R3_SELECT_HOURS_UNITS			   0x04
-#define R2_IN_R3_SELECT_MINUTES_TENS			   0x05
-#define R2_IN_R3_SELECT_MINUTES_UNITS			   0x06
-
-#define R2_DAU_0					   0x00
-#define R2_DAU_1					   0x10
-#define R2_DAU_2					   0x20
-#define R2_DAU_3					   0x30
-
-#define R2_BANK_0					   0x00
-#define R2_BANK 1					   0x40
-
-#define R2_HAMMING_CHECK_ON				   0x80
-#define R2_HAMMING_CHECK_OFF				   0x00
-
-/* Register R3 (Page request data) */
-#define R3_PAGE_HUNDREDS_0				   0x00
-#define R3_PAGE_HUNDREDS_1				   0x01
-#define R3_PAGE_HUNDREDS_2				   0x02
-#define R3_PAGE_HUNDREDS_3				   0x03
-#define R3_PAGE_HUNDREDS_4				   0x04
-#define R3_PAGE_HUNDREDS_5				   0x05
-#define R3_PAGE_HUNDREDS_6				   0x06
-#define R3_PAGE_HUNDREDS_7				   0x07
-
-#define R3_HOLD_PAGE					   0x00
-#define R3_UPDATE_PAGE					   0x08
-
-#define R3_PAGE_HUNDREDS_DO_NOT_CARE			   0x00
-#define R3_PAGE_HUNDREDS_DO_CARE			   0x10
-
-#define R3_PAGE_TENS_DO_NOT_CARE			   0x00
-#define R3_PAGE_TENS_DO_CARE				   0x10
-
-#define R3_PAGE_UNITS_DO_NOT_CARE			   0x00
-#define R3_PAGE_UNITS_DO_CARE				   0x10
-
-#define R3_HOURS_TENS_DO_NOT_CARE			   0x00
-#define R3_HOURS_TENS_DO_CARE				   0x10
-
-#define R3_HOURS_UNITS_DO_NOT_CARE			   0x00
-#define R3_HOURS_UNITS_DO_CARE				   0x10
-
-#define R3_MINUTES_TENS_DO_NOT_CARE			   0x00
-#define R3_MINUTES_TENS_DO_CARE				   0x10
-
-#define R3_MINUTES_UNITS_DO_NOT_CARE			   0x00
-#define R3_MINUTES_UNITS_DO_CARE			   0x10
-
-/* Register R4 (Display chapter) */
-#define R4_DISPLAY_PAGE_0				   0x00
-#define R4_DISPLAY_PAGE_1				   0x01
-#define R4_DISPLAY_PAGE_2				   0x02
-#define R4_DISPLAY_PAGE_3				   0x03
-#define R4_DISPLAY_PAGE_4				   0x04
-#define R4_DISPLAY_PAGE_5				   0x05
-#define R4_DISPLAY_PAGE_6				   0x06
-#define R4_DISPLAY_PAGE_7				   0x07
-
-/* Register R5 (Normal display control) */
-#define R5_PICTURE_INSIDE_BOXING_OFF			   0x00
-#define R5_PICTURE_INSIDE_BOXING_ON			   0x01
-
-#define R5_PICTURE_OUTSIDE_BOXING_OFF			   0x00
-#define R5_PICTURE_OUTSIDE_BOXING_ON			   0x02
-
-#define R5_TEXT_INSIDE_BOXING_OFF			   0x00
-#define R5_TEXT_INSIDE_BOXING_ON			   0x04
-
-#define R5_TEXT_OUTSIDE_BOXING_OFF			   0x00
-#define R5_TEXT_OUTSIDE_BOXING_ON			   0x08
-
-#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_OFF		   0x00
-#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_ON		   0x10
-
-#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF	   0x00
-#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON		   0x20
-
-#define R5_BACKGROUND_COLOR_INSIDE_BOXING_OFF		   0x00
-#define R5_BACKGROUND_COLOR_INSIDE_BOXING_ON		   0x40
-
-#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF		   0x00
-#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_ON		   0x80
-
-/* Register R6 (Newsflash display) */
-#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_OFF		   0x00
-#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_ON		   0x01
-
-#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_OFF		   0x00
-#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_ON		   0x02
-
-#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_OFF		   0x00
-#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_ON		   0x04
-
-#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_OFF		   0x00
-#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_ON		   0x08
-
-#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_OFF  0x00
-#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_ON   0x10
-
-#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00
-#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON  0x20
-
-#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_OFF    0x00
-#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_ON	   0x40
-
-#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF   0x00
-#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_ON    0x80
-
-/* Register R7 (Display mode) */
-#define R7_BOX_OFF_ROW_0				   0x00
-#define R7_BOX_ON_ROW_0					   0x01
-
-#define R7_BOX_OFF_ROW_1_TO_23				   0x00
-#define R7_BOX_ON_ROW_1_TO_23				   0x02
-
-#define R7_BOX_OFF_ROW_24				   0x00
-#define R7_BOX_ON_ROW_24				   0x04
-
-#define R7_SINGLE_HEIGHT				   0x00
-#define R7_DOUBLE_HEIGHT				   0x08
-
-#define R7_TOP_HALF					   0x00
-#define R7_BOTTOM_HALF					   0x10
-
-#define R7_REVEAL_OFF					   0x00
-#define R7_REVEAL_ON					   0x20
-
-#define R7_CURSER_OFF					   0x00
-#define R7_CURSER_ON					   0x40
-
-#define R7_STATUS_BOTTOM				   0x00
-#define R7_STATUS_TOP					   0x80
-
-/* Register R8 (Active chapter) */
-#define R8_ACTIVE_CHAPTER_0				   0x00
-#define R8_ACTIVE_CHAPTER_1				   0x01
-#define R8_ACTIVE_CHAPTER_2				   0x02
-#define R8_ACTIVE_CHAPTER_3				   0x03
-#define R8_ACTIVE_CHAPTER_4				   0x04
-#define R8_ACTIVE_CHAPTER_5				   0x05
-#define R8_ACTIVE_CHAPTER_6				   0x06
-#define R8_ACTIVE_CHAPTER_7				   0x07
-
-#define R8_CLEAR_MEMORY					   0x08
-#define R8_DO_NOT_CLEAR_MEMORY				   0x00
-
-/* Register R9 (Curser row) */
-#define R9_CURSER_ROW_0					   0x00
-#define R9_CURSER_ROW_1					   0x01
-#define R9_CURSER_ROW_2					   0x02
-#define R9_CURSER_ROW_25				   0x19
-
-/* Register R10 (Curser column) */
-#define R10_CURSER_COLUMN_0				   0x00
-#define R10_CURSER_COLUMN_6				   0x06
-#define R10_CURSER_COLUMN_8				   0x08
-
-/*****************************************************************************/
-/* Row 25 control data in column 0 to 9					     */
-/*****************************************************************************/
-#define ROW25_COLUMN0_PAGE_UNITS			   0x0F
-
-#define ROW25_COLUMN1_PAGE_TENS				   0x0F
-
-#define ROW25_COLUMN2_MINUTES_UNITS			   0x0F
-
-#define ROW25_COLUMN3_MINUTES_TENS			   0x07
-#define ROW25_COLUMN3_DELETE_PAGE			   0x08
-
-#define ROW25_COLUMN4_HOUR_UNITS			   0x0F
-
-#define ROW25_COLUMN5_HOUR_TENS				   0x03
-#define ROW25_COLUMN5_INSERT_HEADLINE			   0x04
-#define ROW25_COLUMN5_INSERT_SUBTITLE			   0x08
-
-#define ROW25_COLUMN6_SUPPRESS_HEADER			   0x01
-#define ROW25_COLUMN6_UPDATE_PAGE			   0x02
-#define ROW25_COLUMN6_INTERRUPTED_SEQUENCE		   0x04
-#define ROW25_COLUMN6_SUPPRESS_DISPLAY			   0x08
-
-#define ROW25_COLUMN7_SERIAL_MODE			   0x01
-#define ROW25_COLUMN7_CHARACTER_SET			   0x0E
-
-#define ROW25_COLUMN8_PAGE_HUNDREDS			   0x07
-#define ROW25_COLUMN8_PAGE_NOT_FOUND			   0x10
-
-#define ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR		   0x20
-
-#define ROW25_COLUMN0_TO_7_HAMMING_ERROR		   0x10
-
-/*****************************************************************************/
-/* Helper macros for extracting page, hour and minute digits		     */
-/*****************************************************************************/
-/* BYTE_POS  0 is at row 0, column 0,
-   BYTE_POS  1 is at row 0, column 1,
-   BYTE_POS 40 is at row 1, column 0, (with NUM_ROWS_PER_PAGE = 40)
-   BYTE_POS 41 is at row 1, column 1, (with NUM_ROWS_PER_PAGE = 40),
-   ... */
-#define ROW(BYTE_POS)    (BYTE_POS / NUM_ROWS_PER_PAGE)
-#define COLUMN(BYTE_POS) (BYTE_POS % NUM_ROWS_PER_PAGE)
-
-/*****************************************************************************/
-/* Helper macros for extracting page, hour and minute digits		     */
-/*****************************************************************************/
-/* Macros for extracting hundreds, tens and units of a page number which
-   must be in the range 0 ... 0x799.
-   Note that page is coded in hexadecimal, i.e. 0x123 means page 123.
-   page 0x.. means page 8.. */
-#define HUNDREDS_OF_PAGE(page) (((page) / 0x100) & 0x7)
-#define TENS_OF_PAGE(page)     (((page) / 0x10)  & 0xF)
-#define UNITS_OF_PAGE(page)     ((page) & 0xF)
-
-/* Macros for extracting tens and units of a hour information which
-   must be in the range 0 ... 0x24.
-   Note that hour is coded in hexadecimal, i.e. 0x12 means 12 hours */
-#define TENS_OF_HOUR(hour)  ((hour) / 0x10)
-#define UNITS_OF_HOUR(hour) ((hour) & 0xF)
-
-/* Macros for extracting tens and units of a minute information which
-   must be in the range 0 ... 0x59.
-   Note that minute is coded in hexadecimal, i.e. 0x12 means 12 minutes */
-#define TENS_OF_MINUTE(minute)  ((minute) / 0x10)
-#define UNITS_OF_MINUTE(minute) ((minute) & 0xF)
-
-#define HOUR_MAX   0x23
-#define MINUTE_MAX 0x59
-#define PAGE_MAX   0x8FF
-
-
-struct saa5246a_device
-{
-	struct v4l2_subdev sd;
-	struct video_device *vdev;
-	u8     pgbuf[NUM_DAUS][VTX_VIRTUALSIZE];
-	int    is_searching[NUM_DAUS];
-	unsigned long in_use;
-	struct mutex lock;
-};
-
-static inline struct saa5246a_device *to_dev(struct v4l2_subdev *sd)
-{
-	return container_of(sd, struct saa5246a_device, sd);
-}
-
-static struct video_device saa_template;	/* Declared near bottom */
-
-/*
- *	I2C interfaces
- */
-
-static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data)
-{
-	struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
-	char buf[64];
-
-	buf[0] = reg;
-	memcpy(buf+1, data, count);
-
-	if (i2c_master_send(client, buf, count + 1) == count + 1)
-		return 0;
-	return -1;
-}
-
-static int i2c_senddata(struct saa5246a_device *t, ...)
-{
-	unsigned char buf[64];
-	int v;
-	int ct = 0;
-	va_list argp;
-	va_start(argp, t);
-
-	while ((v = va_arg(argp, int)) != -1)
-		buf[ct++] = v;
-
-	va_end(argp);
-	return i2c_sendbuf(t, buf[0], ct-1, buf+1);
-}
-
-/* Get count number of bytes from I²C-device at address adr, store them in buf.
- * Start & stop handshaking is done by this routine, ack will be sent after the
- * last byte to inhibit further sending of data. If uaccess is 'true', data is
- * written to user-space with put_user. Returns -1 if I²C-device didn't send
- * acknowledge, 0 otherwise
- */
-static int i2c_getdata(struct saa5246a_device *t, int count, u8 *buf)
-{
-	struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
-
-	if (i2c_master_recv(client, buf, count) != count)
-		return -1;
-	return 0;
-}
-
-/* When a page is found then the not FOUND bit in one of the status registers
- * of the SAA5264A chip is cleared. Unfortunately this bit is not set
- * automatically when a new page is requested. Instead this function must be
- * called after a page has been requested.
- *
- * Return value: 0 if successful
- */
-static int saa5246a_clear_found_bit(struct saa5246a_device *t,
-	unsigned char dau_no)
-{
-	unsigned char row_25_column_8;
-
-	if (i2c_senddata(t, SAA5246A_REGISTER_R8,
-
-		dau_no |
-		R8_DO_NOT_CLEAR_MEMORY,
-
-		R9_CURSER_ROW_25,
-
-		R10_CURSER_COLUMN_8,
-
-		COMMAND_END) ||
-		i2c_getdata(t, 1, &row_25_column_8))
-	{
-		return -EIO;
-	}
-	row_25_column_8 |= ROW25_COLUMN8_PAGE_NOT_FOUND;
-	if (i2c_senddata(t, SAA5246A_REGISTER_R8,
-
-		dau_no |
-		R8_DO_NOT_CLEAR_MEMORY,
-
-		R9_CURSER_ROW_25,
-
-		R10_CURSER_COLUMN_8,
-
-		row_25_column_8,
-
-		COMMAND_END))
-	{
-		return -EIO;
-	}
-
-	return 0;
-}
-
-/* Requests one videotext page as described in req. The fields of req are
- * checked and an error is returned if something is invalid.
- *
- * Return value: 0 if successful
- */
-static int saa5246a_request_page(struct saa5246a_device *t,
-    vtx_pagereq_t *req)
-{
-	if (req->pagemask < 0 || req->pagemask >= PGMASK_MAX)
-		return -EINVAL;
-	if (req->pagemask & PGMASK_PAGE)
-		if (req->page < 0 || req->page > PAGE_MAX)
-			return -EINVAL;
-	if (req->pagemask & PGMASK_HOUR)
-		if (req->hour < 0 || req->hour > HOUR_MAX)
-			return -EINVAL;
-	if (req->pagemask & PGMASK_MINUTE)
-		if (req->minute < 0 || req->minute > MINUTE_MAX)
-			return -EINVAL;
-	if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
-		return -EINVAL;
-
-	if (i2c_senddata(t, SAA5246A_REGISTER_R2,
-
-		R2_IN_R3_SELECT_PAGE_HUNDREDS |
-		req->pgbuf << 4 |
-		R2_BANK_0 |
-		R2_HAMMING_CHECK_OFF,
-
-		HUNDREDS_OF_PAGE(req->page) |
-		R3_HOLD_PAGE |
-		(req->pagemask & PG_HUND ?
-			R3_PAGE_HUNDREDS_DO_CARE :
-			R3_PAGE_HUNDREDS_DO_NOT_CARE),
-
-		TENS_OF_PAGE(req->page) |
-		(req->pagemask & PG_TEN ?
-			R3_PAGE_TENS_DO_CARE :
-			R3_PAGE_TENS_DO_NOT_CARE),
-
-		UNITS_OF_PAGE(req->page) |
-		(req->pagemask & PG_UNIT ?
-			R3_PAGE_UNITS_DO_CARE :
-			R3_PAGE_UNITS_DO_NOT_CARE),
-
-		TENS_OF_HOUR(req->hour) |
-		(req->pagemask & HR_TEN ?
-			R3_HOURS_TENS_DO_CARE :
-			R3_HOURS_TENS_DO_NOT_CARE),
-
-		UNITS_OF_HOUR(req->hour) |
-		(req->pagemask & HR_UNIT ?
-			R3_HOURS_UNITS_DO_CARE :
-			R3_HOURS_UNITS_DO_NOT_CARE),
-
-		TENS_OF_MINUTE(req->minute) |
-		(req->pagemask & MIN_TEN ?
-			R3_MINUTES_TENS_DO_CARE :
-			R3_MINUTES_TENS_DO_NOT_CARE),
-
-		UNITS_OF_MINUTE(req->minute) |
-		(req->pagemask & MIN_UNIT ?
-			R3_MINUTES_UNITS_DO_CARE :
-			R3_MINUTES_UNITS_DO_NOT_CARE),
-
-		COMMAND_END) || i2c_senddata(t, SAA5246A_REGISTER_R2,
-
-		R2_IN_R3_SELECT_PAGE_HUNDREDS |
-		req->pgbuf << 4 |
-		R2_BANK_0 |
-		R2_HAMMING_CHECK_OFF,
-
-		HUNDREDS_OF_PAGE(req->page) |
-		R3_UPDATE_PAGE |
-		(req->pagemask & PG_HUND ?
-			R3_PAGE_HUNDREDS_DO_CARE :
-			R3_PAGE_HUNDREDS_DO_NOT_CARE),
-
-		COMMAND_END))
-	{
-		return -EIO;
-	}
-
-	t->is_searching[req->pgbuf] = true;
-	return 0;
-}
-
-/* This routine decodes the page number from the infobits contained in line 25.
- *
- * Parameters:
- * infobits: must be bits 0 to 9 of column 25
- *
- * Return value: page number coded in hexadecimal, i. e. page 123 is coded 0x123
- */
-static inline int saa5246a_extract_pagenum_from_infobits(
-    unsigned char infobits[10])
-{
-	int page_hundreds, page_tens, page_units;
-
-	page_units    = infobits[0] & ROW25_COLUMN0_PAGE_UNITS;
-	page_tens     = infobits[1] & ROW25_COLUMN1_PAGE_TENS;
-	page_hundreds = infobits[8] & ROW25_COLUMN8_PAGE_HUNDREDS;
-
-	/* page 0x.. means page 8.. */
-	if (page_hundreds == 0)
-		page_hundreds = 8;
-
-	return((page_hundreds << 8) | (page_tens << 4) | page_units);
-}
-
-/* Decodes the hour from the infobits contained in line 25.
- *
- * Parameters:
- * infobits: must be bits 0 to 9 of column 25
- *
- * Return: hour coded in hexadecimal, i. e. 12h is coded 0x12
- */
-static inline int saa5246a_extract_hour_from_infobits(
-    unsigned char infobits[10])
-{
-	int hour_tens, hour_units;
-
-	hour_units = infobits[4] & ROW25_COLUMN4_HOUR_UNITS;
-	hour_tens  = infobits[5] & ROW25_COLUMN5_HOUR_TENS;
-
-	return((hour_tens << 4) | hour_units);
-}
-
-/* Decodes the minutes from the infobits contained in line 25.
- *
- * Parameters:
- * infobits: must be bits 0 to 9 of column 25
- *
- * Return: minutes coded in hexadecimal, i. e. 10min is coded 0x10
- */
-static inline int saa5246a_extract_minutes_from_infobits(
-    unsigned char infobits[10])
-{
-	int minutes_tens, minutes_units;
-
-	minutes_units = infobits[2] & ROW25_COLUMN2_MINUTES_UNITS;
-	minutes_tens  = infobits[3] & ROW25_COLUMN3_MINUTES_TENS;
-
-	return((minutes_tens << 4) | minutes_units);
-}
-
-/* Reads the status bits contained in the first 10 columns of the first line
- * and extracts the information into info.
- *
- * Return value: 0 if successful
- */
-static inline int saa5246a_get_status(struct saa5246a_device *t,
-    vtx_pageinfo_t *info, unsigned char dau_no)
-{
-	unsigned char infobits[10];
-	int column;
-
-	if (dau_no >= NUM_DAUS)
-		return -EINVAL;
-
-	if (i2c_senddata(t, SAA5246A_REGISTER_R8,
-
-		dau_no |
-		R8_DO_NOT_CLEAR_MEMORY,
-
-		R9_CURSER_ROW_25,
-
-		R10_CURSER_COLUMN_0,
-
-		COMMAND_END) ||
-		i2c_getdata(t, 10, infobits))
-	{
-		return -EIO;
-	}
-
-	info->pagenum = saa5246a_extract_pagenum_from_infobits(infobits);
-	info->hour    = saa5246a_extract_hour_from_infobits(infobits);
-	info->minute  = saa5246a_extract_minutes_from_infobits(infobits);
-	info->charset = ((infobits[7] & ROW25_COLUMN7_CHARACTER_SET) >> 1);
-	info->delete = !!(infobits[3] & ROW25_COLUMN3_DELETE_PAGE);
-	info->headline = !!(infobits[5] & ROW25_COLUMN5_INSERT_HEADLINE);
-	info->subtitle = !!(infobits[5] & ROW25_COLUMN5_INSERT_SUBTITLE);
-	info->supp_header = !!(infobits[6] & ROW25_COLUMN6_SUPPRESS_HEADER);
-	info->update = !!(infobits[6] & ROW25_COLUMN6_UPDATE_PAGE);
-	info->inter_seq = !!(infobits[6] & ROW25_COLUMN6_INTERRUPTED_SEQUENCE);
-	info->dis_disp = !!(infobits[6] & ROW25_COLUMN6_SUPPRESS_DISPLAY);
-	info->serial = !!(infobits[7] & ROW25_COLUMN7_SERIAL_MODE);
-	info->notfound = !!(infobits[8] & ROW25_COLUMN8_PAGE_NOT_FOUND);
-	info->pblf = !!(infobits[9] & ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR);
-	info->hamming = 0;
-	for (column = 0; column <= 7; column++) {
-		if (infobits[column] & ROW25_COLUMN0_TO_7_HAMMING_ERROR) {
-			info->hamming = 1;
-			break;
-		}
-	}
-	if (!info->hamming && !info->notfound)
-		t->is_searching[dau_no] = false;
-	return 0;
-}
-
-/* Reads 1 videotext page buffer of the SAA5246A.
- *
- * req is used both as input and as output. It contains information which part
- * must be read. The videotext page is copied into req->buffer.
- *
- * Return value: 0 if successful
- */
-static inline int saa5246a_get_page(struct saa5246a_device *t,
-	vtx_pagereq_t *req)
-{
-	int start, end, size;
-	char *buf;
-	int err;
-
-	if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS ||
-	    req->start < 0 || req->start > req->end || req->end >= VTX_PAGESIZE)
-		return -EINVAL;
-
-	buf = kmalloc(VTX_PAGESIZE, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	/* Read "normal" part of page */
-	err = -EIO;
-
-	end = min(req->end, VTX_PAGESIZE - 1);
-	if (i2c_senddata(t, SAA5246A_REGISTER_R8,
-			req->pgbuf | R8_DO_NOT_CLEAR_MEMORY,
-			ROW(req->start), COLUMN(req->start), COMMAND_END))
-		goto out;
-	if (i2c_getdata(t, end - req->start + 1, buf))
-		goto out;
-	err = -EFAULT;
-	if (copy_to_user(req->buffer, buf, end - req->start + 1))
-		goto out;
-
-	/* Always get the time from buffer 4, since this stupid SAA5246A only
-	 * updates the currently displayed buffer...
-	 */
-	if (REQ_CONTAINS_TIME(req)) {
-		start = max(req->start, POS_TIME_START);
-		end   = min(req->end,   POS_TIME_END);
-		size = end - start + 1;
-		err = -EINVAL;
-		if (size < 0)
-			goto out;
-		err = -EIO;
-		if (i2c_senddata(t, SAA5246A_REGISTER_R8,
-				R8_ACTIVE_CHAPTER_4 | R8_DO_NOT_CLEAR_MEMORY,
-				R9_CURSER_ROW_0, start, COMMAND_END))
-			goto out;
-		if (i2c_getdata(t, size, buf))
-			goto out;
-		err = -EFAULT;
-		if (copy_to_user(req->buffer + start - req->start, buf, size))
-			goto out;
-	}
-	/* Insert the header from buffer 4 only, if acquisition circuit is still searching for a page */
-	if (REQ_CONTAINS_HEADER(req) && t->is_searching[req->pgbuf]) {
-		start = max(req->start, POS_HEADER_START);
-		end   = min(req->end,   POS_HEADER_END);
-		size = end - start + 1;
-		err = -EINVAL;
-		if (size < 0)
-			goto out;
-		err = -EIO;
-		if (i2c_senddata(t, SAA5246A_REGISTER_R8,
-				R8_ACTIVE_CHAPTER_4 | R8_DO_NOT_CLEAR_MEMORY,
-				R9_CURSER_ROW_0, start, COMMAND_END))
-			goto out;
-		if (i2c_getdata(t, end - start + 1, buf))
-			goto out;
-		err = -EFAULT;
-		if (copy_to_user(req->buffer + start - req->start, buf, size))
-			goto out;
-	}
-	err = 0;
-out:
-	kfree(buf);
-	return err;
-}
-
-/* Stops the acquisition circuit given in dau_no. The page buffer associated
- * with this acquisition circuit will no more be updated. The other daus are
- * not affected.
- *
- * Return value: 0 if successful
- */
-static inline int saa5246a_stop_dau(struct saa5246a_device *t,
-    unsigned char dau_no)
-{
-	if (dau_no >= NUM_DAUS)
-		return -EINVAL;
-	if (i2c_senddata(t, SAA5246A_REGISTER_R2,
-
-		R2_IN_R3_SELECT_PAGE_HUNDREDS |
-		dau_no << 4 |
-		R2_BANK_0 |
-		R2_HAMMING_CHECK_OFF,
-
-		R3_PAGE_HUNDREDS_0 |
-		R3_HOLD_PAGE |
-		R3_PAGE_HUNDREDS_DO_NOT_CARE,
-
-		COMMAND_END))
-	{
-		return -EIO;
-	}
-	t->is_searching[dau_no] = false;
-	return 0;
-}
-
-/*  Handles ioctls defined in videotext.h
- *
- *  Returns 0 if successful
- */
-static long do_saa5246a_ioctl(struct file *file, unsigned int cmd, void *arg)
-{
-	struct saa5246a_device *t = video_drvdata(file);
-
-	switch(cmd)
-	{
-		case VTXIOCGETINFO:
-		{
-			vtx_info_t *info = arg;
-
-			info->version_major = MAJOR_VERSION;
-			info->version_minor = MINOR_VERSION;
-			info->numpages = NUM_DAUS;
-			return 0;
-		}
-
-		case VTXIOCCLRPAGE:
-		{
-			vtx_pagereq_t *req = arg;
-
-			if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
-				return -EINVAL;
-			memset(t->pgbuf[req->pgbuf], ' ', sizeof(t->pgbuf[0]));
-			return 0;
-		}
-
-		case VTXIOCCLRFOUND:
-		{
-			vtx_pagereq_t *req = arg;
-
-			if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
-				return -EINVAL;
-			return(saa5246a_clear_found_bit(t, req->pgbuf));
-		}
-
-		case VTXIOCPAGEREQ:
-		{
-			vtx_pagereq_t *req = arg;
-
-			return(saa5246a_request_page(t, req));
-		}
-
-		case VTXIOCGETSTAT:
-		{
-			vtx_pagereq_t *req = arg;
-			vtx_pageinfo_t info;
-			int rval;
-
-			if ((rval = saa5246a_get_status(t, &info, req->pgbuf)))
-				return rval;
-			if(copy_to_user(req->buffer, &info,
-				sizeof(vtx_pageinfo_t)))
-				return -EFAULT;
-			return 0;
-		}
-
-		case VTXIOCGETPAGE:
-		{
-			vtx_pagereq_t *req = arg;
-
-			return(saa5246a_get_page(t, req));
-		}
-
-		case VTXIOCSTOPDAU:
-		{
-			vtx_pagereq_t *req = arg;
-
-			return(saa5246a_stop_dau(t, req->pgbuf));
-		}
-
-		case VTXIOCPUTPAGE:
-		case VTXIOCSETDISP:
-		case VTXIOCPUTSTAT:
-			return 0;
-
-		case VTXIOCCLRCACHE:
-		{
-			return 0;
-		}
-
-		case VTXIOCSETVIRT:
-		{
-			/* I do not know what "virtual mode" means */
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-/*
- * Translates old vtx IOCTLs to new ones
- *
- * This keeps new kernel versions compatible with old userspace programs.
- */
-static inline unsigned int vtx_fix_command(unsigned int cmd)
-{
-	switch (cmd) {
-	case VTXIOCGETINFO_OLD:
-		cmd = VTXIOCGETINFO;
-		break;
-	case VTXIOCCLRPAGE_OLD:
-		cmd = VTXIOCCLRPAGE;
-		break;
-	case VTXIOCCLRFOUND_OLD:
-		cmd = VTXIOCCLRFOUND;
-		break;
-	case VTXIOCPAGEREQ_OLD:
-		cmd = VTXIOCPAGEREQ;
-		break;
-	case VTXIOCGETSTAT_OLD:
-		cmd = VTXIOCGETSTAT;
-		break;
-	case VTXIOCGETPAGE_OLD:
-		cmd = VTXIOCGETPAGE;
-		break;
-	case VTXIOCSTOPDAU_OLD:
-		cmd = VTXIOCSTOPDAU;
-		break;
-	case VTXIOCPUTPAGE_OLD:
-		cmd = VTXIOCPUTPAGE;
-		break;
-	case VTXIOCSETDISP_OLD:
-		cmd = VTXIOCSETDISP;
-		break;
-	case VTXIOCPUTSTAT_OLD:
-		cmd = VTXIOCPUTSTAT;
-		break;
-	case VTXIOCCLRCACHE_OLD:
-		cmd = VTXIOCCLRCACHE;
-		break;
-	case VTXIOCSETVIRT_OLD:
-		cmd = VTXIOCSETVIRT;
-		break;
-	}
-	return cmd;
-}
-
-/*
- *	Handle the locking
- */
-static long saa5246a_ioctl(struct file *file,
-			 unsigned int cmd, unsigned long arg)
-{
-	struct saa5246a_device *t = video_drvdata(file);
-	long err;
-
-	cmd = vtx_fix_command(cmd);
-	mutex_lock(&t->lock);
-	err = video_usercopy(file, cmd, arg, do_saa5246a_ioctl);
-	mutex_unlock(&t->lock);
-	return err;
-}
-
-static int saa5246a_open(struct file *file)
-{
-	struct saa5246a_device *t = video_drvdata(file);
-
-	if (test_and_set_bit(0, &t->in_use))
-		return -EBUSY;
-
-	if (i2c_senddata(t, SAA5246A_REGISTER_R0,
-		R0_SELECT_R11 |
-		R0_PLL_TIME_CONSTANT_LONG |
-		R0_ENABLE_nODD_EVEN_OUTPUT |
-		R0_ENABLE_HDR_POLL |
-		R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED |
-		R0_NO_FREE_RUN_PLL |
-		R0_NO_AUTOMATIC_FASTEXT_PROMPT,
-
-		R1_NON_INTERLACED_312_312_LINES |
-		R1_DEW |
-		R1_EXTENDED_PACKET_DISABLE |
-		R1_DAUS_ALL_ON |
-		R1_8_BITS_NO_PARITY |
-		R1_VCS_TO_SCS,
-
-		COMMAND_END) ||
-		i2c_senddata(t, SAA5246A_REGISTER_R4,
-
-		/* We do not care much for the TV display but nevertheless we
-		 * need the currently displayed page later because only on that
-		 * page the time is updated. */
-		R4_DISPLAY_PAGE_4,
-
-		COMMAND_END))
-	{
-		clear_bit(0, &t->in_use);
-		return -EIO;
-	}
-	return 0;
-}
-
-static int saa5246a_release(struct file *file)
-{
-	struct saa5246a_device *t = video_drvdata(file);
-
-	/* Stop all acquisition circuits. */
-	i2c_senddata(t, SAA5246A_REGISTER_R1,
-
-		R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES |
-		R1_DEW |
-		R1_EXTENDED_PACKET_DISABLE |
-		R1_DAUS_ALL_OFF |
-		R1_8_BITS_NO_PARITY |
-		R1_VCS_TO_SCS,
-
-		COMMAND_END);
-	clear_bit(0, &t->in_use);
-	return 0;
-}
-
-static const struct v4l2_file_operations saa_fops = {
-	.owner	 = THIS_MODULE,
-	.open	 = saa5246a_open,
-	.release = saa5246a_release,
-	.ioctl	 = saa5246a_ioctl,
-};
-
-static struct video_device saa_template =
-{
-	.name	  = "saa5246a",
-	.fops	  = &saa_fops,
-	.release  = video_device_release,
-};
-
-static int saa5246a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-
-	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA5246A, 0);
-}
-
-static const struct v4l2_subdev_core_ops saa5246a_core_ops = {
-	.g_chip_ident = saa5246a_g_chip_ident,
-};
-
-static const struct v4l2_subdev_ops saa5246a_ops = {
-	.core = &saa5246a_core_ops,
-};
-
-
-static int saa5246a_probe(struct i2c_client *client,
-			const struct i2c_device_id *id)
-{
-	int pgbuf;
-	int err;
-	struct saa5246a_device *t;
-	struct v4l2_subdev *sd;
-
-	v4l_info(client, "chip found @ 0x%x (%s)\n",
-			client->addr << 1, client->adapter->name);
-	v4l_info(client, "VideoText version %d.%d\n",
-			MAJOR_VERSION, MINOR_VERSION);
-	t = kzalloc(sizeof(*t), GFP_KERNEL);
-	if (t == NULL)
-		return -ENOMEM;
-	sd = &t->sd;
-	v4l2_i2c_subdev_init(sd, client, &saa5246a_ops);
-	mutex_init(&t->lock);
-
-	/* Now create a video4linux device */
-	t->vdev = video_device_alloc();
-	if (t->vdev == NULL) {
-		kfree(t);
-		return -ENOMEM;
-	}
-	memcpy(t->vdev, &saa_template, sizeof(*t->vdev));
-
-	for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) {
-		memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0]));
-		t->is_searching[pgbuf] = false;
-	}
-	video_set_drvdata(t->vdev, t);
-
-	/* Register it */
-	err = video_register_device(t->vdev, VFL_TYPE_VTX, -1);
-	if (err < 0) {
-		video_device_release(t->vdev);
-		kfree(t);
-		return err;
-	}
-	return 0;
-}
-
-static int saa5246a_remove(struct i2c_client *client)
-{
-	struct v4l2_subdev *sd = i2c_get_clientdata(client);
-	struct saa5246a_device *t = to_dev(sd);
-
-	video_unregister_device(t->vdev);
-	v4l2_device_unregister_subdev(sd);
-	kfree(t);
-	return 0;
-}
-
-static const struct i2c_device_id saa5246a_id[] = {
-	{ "saa5246a", 0 },
-	{ }
-};
-MODULE_DEVICE_TABLE(i2c, saa5246a_id);
-
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "saa5246a",
-	.probe = saa5246a_probe,
-	.remove = saa5246a_remove,
-	.id_table = saa5246a_id,
-};
diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c
deleted file mode 100644
index 31ff27df..0000000
--- a/drivers/media/video/saa5249.c
+++ /dev/null
@@ -1,650 +0,0 @@
-/*
- * Modified in order to keep it compatible both with new and old videotext IOCTLs by
- * Michael Geng <linux@MichaelGeng.de>
- *
- *	Cleaned up to use existing videodev interface and allow the idea
- *	of multiple teletext decoders on the video4linux iface. Changed i2c
- *	to cover addressing clashes on device busses. It's also rebuilt so
- *	you can add arbitary multiple teletext devices to Linux video4linux
- *	now (well 32 anyway).
- *
- *	Alan Cox <alan@lxorguk.ukuu.org.uk>
- *
- *	The original driver was heavily modified to match the i2c interface
- *	It was truncated to use the WinTV boards, too.
- *
- *	Copyright (c) 1998 Richard Guenther <richard.guenther@student.uni-tuebingen.de>
- *
- *	Derived From
- *
- * vtx.c:
- * This is a loadable character-device-driver for videotext-interfaces
- * (aka teletext). Please check the Makefile/README for a list of supported
- * interfaces.
- *
- * Copyright (c) 1994-97 Martin Buck  <martin-2.buck@student.uni-ulm.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
-#include <linux/videotext.h>
-#include <linux/videodev2.h>
-#include <linux/slab.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-i2c-drv.h>
-
-MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>");
-MODULE_DESCRIPTION("Philips SAA5249 Teletext decoder driver");
-MODULE_LICENSE("GPL");
-
-
-#define VTX_VER_MAJ 1
-#define VTX_VER_MIN 8
-
-
-#define NUM_DAUS 4
-#define NUM_BUFS 8
-
-static const int disp_modes[8][3] =
-{
-	{ 0x46, 0x03, 0x03 },	/* DISPOFF */
-	{ 0x46, 0xcc, 0xcc },	/* DISPNORM */
-	{ 0x44, 0x0f, 0x0f },	/* DISPTRANS */
-	{ 0x46, 0xcc, 0x46 },	/* DISPINS */
-	{ 0x44, 0x03, 0x03 },	/* DISPOFF, interlaced */
-	{ 0x44, 0xcc, 0xcc },	/* DISPNORM, interlaced */
-	{ 0x44, 0x0f, 0x0f },	/* DISPTRANS, interlaced */
-	{ 0x44, 0xcc, 0x46 }	/* DISPINS, interlaced */
-};
-
-
-
-#define PAGE_WAIT    msecs_to_jiffies(300)	/* Time between requesting page and */
-						/* checking status bits */
-#define PGBUF_EXPIRE msecs_to_jiffies(15000)	/* Time to wait before retransmitting */
-						/* page regardless of infobits */
-typedef struct {
-	u8 pgbuf[VTX_VIRTUALSIZE];		/* Page-buffer */
-	u8 laststat[10];			/* Last value of infobits for DAU */
-	u8 sregs[7];				/* Page-request registers */
-	unsigned long expire;			/* Time when page will be expired */
-	unsigned clrfound : 1;			/* VTXIOCCLRFOUND has been called */
-	unsigned stopped : 1;			/* VTXIOCSTOPDAU has been called */
-} vdau_t;
-
-struct saa5249_device
-{
-	struct v4l2_subdev sd;
-	struct video_device *vdev;
-	vdau_t vdau[NUM_DAUS];			/* Data for virtual DAUs (the 5249 only has one */
-						/* real DAU, so we have to simulate some more) */
-	int vtx_use_count;
-	int is_searching[NUM_DAUS];
-	int disp_mode;
-	int virtual_mode;
-	unsigned long in_use;
-	struct mutex lock;
-};
-
-static inline struct saa5249_device *to_dev(struct v4l2_subdev *sd)
-{
-	return container_of(sd, struct saa5249_device, sd);
-}
-
-
-#define CCTWR 34		/* I²C write/read-address of vtx-chip */
-#define CCTRD 35
-#define NOACK_REPEAT 10		/* Retry access this many times on failure */
-#define CLEAR_DELAY   msecs_to_jiffies(50)	/* Time required to clear a page */
-#define READY_TIMEOUT msecs_to_jiffies(30)	/* Time to wait for ready signal of I2C-bus interface */
-#define INIT_DELAY 500		/* Time in usec to wait at initialization of CEA interface */
-#define START_DELAY 10		/* Time in usec to wait before starting write-cycle (CEA) */
-
-#define VTX_DEV_MINOR 0
-
-static struct video_device saa_template;	/* Declared near bottom */
-
-/*
- *	Wait the given number of jiffies (10ms). This calls the scheduler, so the actual
- *	delay may be longer.
- */
-
-static void jdelay(unsigned long delay)
-{
-	sigset_t oldblocked = current->blocked;
-
-	spin_lock_irq(&current->sighand->siglock);
-	sigfillset(&current->blocked);
-	recalc_sigpending();
-	spin_unlock_irq(&current->sighand->siglock);
-	msleep_interruptible(jiffies_to_msecs(delay));
-
-	spin_lock_irq(&current->sighand->siglock);
-	current->blocked = oldblocked;
-	recalc_sigpending();
-	spin_unlock_irq(&current->sighand->siglock);
-}
-
-
-/*
- *	I2C interfaces
- */
-
-static int i2c_sendbuf(struct saa5249_device *t, int reg, int count, u8 *data)
-{
-	struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
-	char buf[64];
-
-	buf[0] = reg;
-	memcpy(buf+1, data, count);
-
-	if (i2c_master_send(client, buf, count + 1) == count + 1)
-		return 0;
-	return -1;
-}
-
-static int i2c_senddata(struct saa5249_device *t, ...)
-{
-	unsigned char buf[64];
-	int v;
-	int ct = 0;
-	va_list argp;
-	va_start(argp,t);
-
-	while ((v = va_arg(argp, int)) != -1)
-		buf[ct++] = v;
-
-	va_end(argp);
-	return i2c_sendbuf(t, buf[0], ct-1, buf+1);
-}
-
-/* Get count number of bytes from I²C-device at address adr, store them in buf. Start & stop
- * handshaking is done by this routine, ack will be sent after the last byte to inhibit further
- * sending of data. If uaccess is 'true', data is written to user-space with put_user.
- * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise
- */
-
-static int i2c_getdata(struct saa5249_device *t, int count, u8 *buf)
-{
-	struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
-
-	if (i2c_master_recv(client, buf, count) != count)
-		return -1;
-	return 0;
-}
-
-
-/*
- *	Standard character-device-driver functions
- */
-
-static long do_saa5249_ioctl(struct file *file, unsigned int cmd, void *arg)
-{
-	static int virtual_mode = false;
-	struct saa5249_device *t = video_drvdata(file);
-
-	switch (cmd) {
-	case VTXIOCGETINFO:
-	{
-		vtx_info_t *info = arg;
-		info->version_major = VTX_VER_MAJ;
-		info->version_minor = VTX_VER_MIN;
-		info->numpages = NUM_DAUS;
-		/*info->cct_type = CCT_TYPE;*/
-		return 0;
-	}
-
-	case VTXIOCCLRPAGE:
-	{
-		vtx_pagereq_t *req = arg;
-
-		if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
-			return -EINVAL;
-		memset(t->vdau[req->pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
-		t->vdau[req->pgbuf].clrfound = true;
-		return 0;
-	}
-
-	case VTXIOCCLRFOUND:
-	{
-		vtx_pagereq_t *req = arg;
-
-		if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
-			return -EINVAL;
-		t->vdau[req->pgbuf].clrfound = true;
-		return 0;
-	}
-
-	case VTXIOCPAGEREQ:
-	{
-		vtx_pagereq_t *req = arg;
-		if (!(req->pagemask & PGMASK_PAGE))
-			req->page = 0;
-		if (!(req->pagemask & PGMASK_HOUR))
-			req->hour = 0;
-		if (!(req->pagemask & PGMASK_MINUTE))
-			req->minute = 0;
-		if (req->page < 0 || req->page > 0x8ff) /* 7FF ?? */
-			return -EINVAL;
-		req->page &= 0x7ff;
-		if (req->hour < 0 || req->hour > 0x3f || req->minute < 0 || req->minute > 0x7f ||
-			req->pagemask < 0 || req->pagemask >= PGMASK_MAX || req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
-			return -EINVAL;
-		t->vdau[req->pgbuf].sregs[0] = (req->pagemask & PG_HUND ? 0x10 : 0) | (req->page / 0x100);
-		t->vdau[req->pgbuf].sregs[1] = (req->pagemask & PG_TEN ? 0x10 : 0) | ((req->page / 0x10) & 0xf);
-		t->vdau[req->pgbuf].sregs[2] = (req->pagemask & PG_UNIT ? 0x10 : 0) | (req->page & 0xf);
-		t->vdau[req->pgbuf].sregs[3] = (req->pagemask & HR_TEN ? 0x10 : 0) | (req->hour / 0x10);
-		t->vdau[req->pgbuf].sregs[4] = (req->pagemask & HR_UNIT ? 0x10 : 0) | (req->hour & 0xf);
-		t->vdau[req->pgbuf].sregs[5] = (req->pagemask & MIN_TEN ? 0x10 : 0) | (req->minute / 0x10);
-		t->vdau[req->pgbuf].sregs[6] = (req->pagemask & MIN_UNIT ? 0x10 : 0) | (req->minute & 0xf);
-		t->vdau[req->pgbuf].stopped = false;
-		t->vdau[req->pgbuf].clrfound = true;
-		t->is_searching[req->pgbuf] = true;
-		return 0;
-	}
-
-	case VTXIOCGETSTAT:
-	{
-		vtx_pagereq_t *req = arg;
-		u8 infobits[10];
-		vtx_pageinfo_t info;
-		int a;
-
-		if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
-			return -EINVAL;
-		if (!t->vdau[req->pgbuf].stopped) {
-			if (i2c_senddata(t, 2, 0, -1) ||
-				i2c_sendbuf(t, 3, sizeof(t->vdau[0].sregs), t->vdau[req->pgbuf].sregs) ||
-				i2c_senddata(t, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', -1) ||
-				i2c_senddata(t, 2, 0, t->vdau[req->pgbuf].sregs[0] | 8, -1) ||
-				i2c_senddata(t, 8, 0, 25, 0, -1))
-				return -EIO;
-			jdelay(PAGE_WAIT);
-			if (i2c_getdata(t, 10, infobits))
-				return -EIO;
-
-			if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) &&	/* check FOUND-bit */
-				(memcmp(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)) ||
-				time_after_eq(jiffies, t->vdau[req->pgbuf].expire)))
-			{		/* check if new page arrived */
-				if (i2c_senddata(t, 8, 0, 0, 0, -1) ||
-					i2c_getdata(t, VTX_PAGESIZE, t->vdau[req->pgbuf].pgbuf))
-					return -EIO;
-				t->vdau[req->pgbuf].expire = jiffies + PGBUF_EXPIRE;
-				memset(t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE);
-				if (t->virtual_mode) {
-					/* Packet X/24 */
-					if (i2c_senddata(t, 8, 0, 0x20, 0, -1) ||
-						i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40))
-						return -EIO;
-					/* Packet X/27/0 */
-					if (i2c_senddata(t, 8, 0, 0x21, 0, -1) ||
-						i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40))
-						return -EIO;
-					/* Packet 8/30/0...8/30/15
-					 * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30,
-					 *        so we should undo this here.
-					 */
-					if (i2c_senddata(t, 8, 0, 0x22, 0, -1) ||
-						i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40))
-						return -EIO;
-				}
-				t->vdau[req->pgbuf].clrfound = false;
-				memcpy(t->vdau[req->pgbuf].laststat, infobits, sizeof(infobits));
-			} else {
-				memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits));
-			}
-		} else {
-			memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits));
-		}
-
-		info.pagenum = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f);
-		if (info.pagenum < 0x100)
-			info.pagenum += 0x800;
-		info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f);
-		info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f);
-		info.charset = ((infobits[7] >> 1) & 7);
-		info.delete = !!(infobits[3] & 8);
-		info.headline = !!(infobits[5] & 4);
-		info.subtitle = !!(infobits[5] & 8);
-		info.supp_header = !!(infobits[6] & 1);
-		info.update = !!(infobits[6] & 2);
-		info.inter_seq = !!(infobits[6] & 4);
-		info.dis_disp = !!(infobits[6] & 8);
-		info.serial = !!(infobits[7] & 1);
-		info.notfound = !!(infobits[8] & 0x10);
-		info.pblf = !!(infobits[9] & 0x20);
-		info.hamming = 0;
-		for (a = 0; a <= 7; a++) {
-			if (infobits[a] & 0xf0) {
-				info.hamming = 1;
-				break;
-			}
-		}
-		if (t->vdau[req->pgbuf].clrfound)
-			info.notfound = 1;
-		if (copy_to_user(req->buffer, &info, sizeof(vtx_pageinfo_t)))
-			return -EFAULT;
-		if (!info.hamming && !info.notfound)
-			t->is_searching[req->pgbuf] = false;
-		return 0;
-	}
-
-	case VTXIOCGETPAGE:
-	{
-		vtx_pagereq_t *req = arg;
-		int start, end;
-
-		if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || req->start < 0 ||
-			req->start > req->end || req->end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE))
-			return -EINVAL;
-		if (copy_to_user(req->buffer, &t->vdau[req->pgbuf].pgbuf[req->start], req->end - req->start + 1))
-			return -EFAULT;
-
-		 /*
-		  *	Always read the time directly from SAA5249
-		  */
-
-		if (req->start <= 39 && req->end >= 32) {
-			int len;
-			char buf[16];
-			start = max(req->start, 32);
-			end = min(req->end, 39);
-			len = end - start + 1;
-			if (i2c_senddata(t, 8, 0, 0, start, -1) ||
-				i2c_getdata(t, len, buf))
-				return -EIO;
-			if (copy_to_user(req->buffer + start - req->start, buf, len))
-				return -EFAULT;
-		}
-		/* Insert the current header if DAU is still searching for a page */
-		if (req->start <= 31 && req->end >= 7 && t->is_searching[req->pgbuf]) {
-			char buf[32];
-			int len;
-
-			start = max(req->start, 7);
-			end = min(req->end, 31);
-			len = end - start + 1;
-			if (i2c_senddata(t, 8, 0, 0, start, -1) ||
-				i2c_getdata(t, len, buf))
-				return -EIO;
-			if (copy_to_user(req->buffer + start - req->start, buf, len))
-				return -EFAULT;
-		}
-		return 0;
-	}
-
-	case VTXIOCSTOPDAU:
-	{
-		vtx_pagereq_t *req = arg;
-
-		if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
-			return -EINVAL;
-		t->vdau[req->pgbuf].stopped = true;
-		t->is_searching[req->pgbuf] = false;
-		return 0;
-	}
-
-	case VTXIOCPUTPAGE:
-	case VTXIOCSETDISP:
-	case VTXIOCPUTSTAT:
-		return 0;
-
-	case VTXIOCCLRCACHE:
-	{
-		if (i2c_senddata(t, 0, NUM_DAUS, 0, 8, -1) || i2c_senddata(t, 11,
-			' ', ' ', ' ', ' ', ' ', ' ',
-			' ', ' ', ' ', ' ', ' ', ' ',
-			' ', ' ', ' ', ' ', ' ', ' ',
-			' ', ' ', ' ', ' ', ' ', ' ',
-			-1))
-			return -EIO;
-		if (i2c_senddata(t, 3, 0x20, -1))
-			return -EIO;
-		jdelay(10 * CLEAR_DELAY);			/* I have no idea how long we have to wait here */
-		return 0;
-	}
-
-	case VTXIOCSETVIRT:
-	{
-		/* The SAA5249 has virtual-row reception turned on always */
-		t->virtual_mode = (int)(long)arg;
-		return 0;
-	}
-	}
-	return -EINVAL;
-}
-
-/*
- * Translates old vtx IOCTLs to new ones
- *
- * This keeps new kernel versions compatible with old userspace programs.
- */
-static inline unsigned int vtx_fix_command(unsigned int cmd)
-{
-	switch (cmd) {
-	case VTXIOCGETINFO_OLD:
-		cmd = VTXIOCGETINFO;
-		break;
-	case VTXIOCCLRPAGE_OLD:
-		cmd = VTXIOCCLRPAGE;
-		break;
-	case VTXIOCCLRFOUND_OLD:
-		cmd = VTXIOCCLRFOUND;
-		break;
-	case VTXIOCPAGEREQ_OLD:
-		cmd = VTXIOCPAGEREQ;
-		break;
-	case VTXIOCGETSTAT_OLD:
-		cmd = VTXIOCGETSTAT;
-		break;
-	case VTXIOCGETPAGE_OLD:
-		cmd = VTXIOCGETPAGE;
-		break;
-	case VTXIOCSTOPDAU_OLD:
-		cmd = VTXIOCSTOPDAU;
-		break;
-	case VTXIOCPUTPAGE_OLD:
-		cmd = VTXIOCPUTPAGE;
-		break;
-	case VTXIOCSETDISP_OLD:
-		cmd = VTXIOCSETDISP;
-		break;
-	case VTXIOCPUTSTAT_OLD:
-		cmd = VTXIOCPUTSTAT;
-		break;
-	case VTXIOCCLRCACHE_OLD:
-		cmd = VTXIOCCLRCACHE;
-		break;
-	case VTXIOCSETVIRT_OLD:
-		cmd = VTXIOCSETVIRT;
-		break;
-	}
-	return cmd;
-}
-
-/*
- *	Handle the locking
- */
-
-static long saa5249_ioctl(struct file *file,
-			 unsigned int cmd, unsigned long arg)
-{
-	struct saa5249_device *t = video_drvdata(file);
-	long err;
-
-	cmd = vtx_fix_command(cmd);
-	mutex_lock(&t->lock);
-	err = video_usercopy(file, cmd, arg, do_saa5249_ioctl);
-	mutex_unlock(&t->lock);
-	return err;
-}
-
-static int saa5249_open(struct file *file)
-{
-	struct saa5249_device *t = video_drvdata(file);
-	int pgbuf;
-
-	if (test_and_set_bit(0, &t->in_use))
-		return -EBUSY;
-
-	if (i2c_senddata(t, 0, 0, -1) || /* Select R11 */
-		/* Turn off parity checks (we do this ourselves) */
-		i2c_senddata(t, 1, disp_modes[t->disp_mode][0], 0, -1) ||
-		/* Display TV-picture, no virtual rows */
-		i2c_senddata(t, 4, NUM_DAUS, disp_modes[t->disp_mode][1], disp_modes[t->disp_mode][2], 7, -1))
-		/* Set display to page 4 */
-	{
-		clear_bit(0, &t->in_use);
-		return -EIO;
-	}
-
-	for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) {
-		memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
-		memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs));
-		memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat));
-		t->vdau[pgbuf].expire = 0;
-		t->vdau[pgbuf].clrfound = true;
-		t->vdau[pgbuf].stopped = true;
-		t->is_searching[pgbuf] = false;
-	}
-	t->virtual_mode = false;
-	return 0;
-}
-
-
-
-static int saa5249_release(struct file *file)
-{
-	struct saa5249_device *t = video_drvdata(file);
-
-	i2c_senddata(t, 1, 0x20, -1);		/* Turn off CCT */
-	i2c_senddata(t, 5, 3, 3, -1);		/* Turn off TV-display */
-	clear_bit(0, &t->in_use);
-	return 0;
-}
-
-static const struct v4l2_file_operations saa_fops = {
-	.owner		= THIS_MODULE,
-	.open		= saa5249_open,
-	.release       	= saa5249_release,
-	.ioctl          = saa5249_ioctl,
-};
-
-static struct video_device saa_template =
-{
-	.name		= "saa5249",
-	.fops           = &saa_fops,
-	.release 	= video_device_release,
-};
-
-static int saa5249_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-
-	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA5249, 0);
-}
-
-static const struct v4l2_subdev_core_ops saa5249_core_ops = {
-	.g_chip_ident = saa5249_g_chip_ident,
-};
-
-static const struct v4l2_subdev_ops saa5249_ops = {
-	.core = &saa5249_core_ops,
-};
-
-static int saa5249_probe(struct i2c_client *client,
-			const struct i2c_device_id *id)
-{
-	int pgbuf;
-	int err;
-	struct saa5249_device *t;
-	struct v4l2_subdev *sd;
-
-	v4l_info(client, "chip found @ 0x%x (%s)\n",
-			client->addr << 1, client->adapter->name);
-	v4l_info(client, "VideoText version %d.%d\n",
-			VTX_VER_MAJ, VTX_VER_MIN);
-	t = kzalloc(sizeof(*t), GFP_KERNEL);
-	if (t == NULL)
-		return -ENOMEM;
-	sd = &t->sd;
-	v4l2_i2c_subdev_init(sd, client, &saa5249_ops);
-	mutex_init(&t->lock);
-
-	/* Now create a video4linux device */
-	t->vdev = video_device_alloc();
-	if (t->vdev == NULL) {
-		kfree(t);
-		kfree(client);
-		return -ENOMEM;
-	}
-	memcpy(t->vdev, &saa_template, sizeof(*t->vdev));
-
-	for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) {
-		memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
-		memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs));
-		memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat));
-		t->vdau[pgbuf].expire = 0;
-		t->vdau[pgbuf].clrfound = true;
-		t->vdau[pgbuf].stopped = true;
-		t->is_searching[pgbuf] = false;
-	}
-	video_set_drvdata(t->vdev, t);
-
-	/* Register it */
-	err = video_register_device(t->vdev, VFL_TYPE_VTX, -1);
-	if (err < 0) {
-		video_device_release(t->vdev);
-		kfree(t);
-		return err;
-	}
-	return 0;
-}
-
-static int saa5249_remove(struct i2c_client *client)
-{
-	struct v4l2_subdev *sd = i2c_get_clientdata(client);
-	struct saa5249_device *t = to_dev(sd);
-
-	video_unregister_device(t->vdev);
-	v4l2_device_unregister_subdev(sd);
-	kfree(t);
-	return 0;
-}
-
-static const struct i2c_device_id saa5249_id[] = {
-	{ "saa5249", 0 },
-	{ }
-};
-MODULE_DEVICE_TABLE(i2c, saa5249_id);
-
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "saa5249",
-	.probe = saa5249_probe,
-	.remove = saa5249_remove,
-	.id_table = saa5249_id,
-};
diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c
index c3e96f0..984c0fe 100644
--- a/drivers/media/video/saa6588.c
+++ b/drivers/media/video/saa6588.c
@@ -34,7 +34,6 @@
 #include <media/rds.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 
 /* insmod options */
@@ -430,7 +429,7 @@
 {
 	struct saa6588 *s = to_saa6588(sd);
 
-	vt->capability |= V4L2_TUNER_CAP_RDS;
+	vt->capability |= V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO;
 	if (s->sync)
 		vt->rxsubchans |= V4L2_TUNER_SUB_RDS;
 	return 0;
@@ -530,9 +529,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, saa6588_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "saa6588",
-	.probe = saa6588_probe,
-	.remove = saa6588_remove,
-	.id_table = saa6588_id,
+static struct i2c_driver saa6588_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "saa6588",
+	},
+	.probe		= saa6588_probe,
+	.remove		= saa6588_remove,
+	.id_table	= saa6588_id,
 };
+
+static __init int init_saa6588(void)
+{
+	return i2c_add_driver(&saa6588_driver);
+}
+
+static __exit void exit_saa6588(void)
+{
+	i2c_del_driver(&saa6588_driver);
+}
+
+module_init(init_saa6588);
+module_exit(exit_saa6588);
diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c
index 3bca744..7913f93 100644
--- a/drivers/media/video/saa7110.c
+++ b/drivers/media/video/saa7110.c
@@ -36,7 +36,6 @@
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("Philips SAA7110 video decoder driver");
 MODULE_AUTHOR("Pauline Middelink");
@@ -505,9 +504,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, saa7110_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "saa7110",
-	.probe = saa7110_probe,
-	.remove = saa7110_remove,
-	.id_table = saa7110_id,
+static struct i2c_driver saa7110_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "saa7110",
+	},
+	.probe		= saa7110_probe,
+	.remove		= saa7110_remove,
+	.id_table	= saa7110_id,
 };
+
+static __init int init_saa7110(void)
+{
+	return i2c_add_driver(&saa7110_driver);
+}
+
+static __exit void exit_saa7110(void)
+{
+	i2c_del_driver(&saa7110_driver);
+}
+
+module_init(init_saa7110);
+module_exit(exit_saa7110);
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index ee963f4..301c62b 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -47,7 +47,6 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 #include <media/saa7115.h>
 #include <asm/div64.h>
 
@@ -1676,7 +1675,7 @@
 	return 0;
 }
 
-static const struct i2c_device_id saa7115_id[] = {
+static const struct i2c_device_id saa711x_id[] = {
 	{ "saa7115_auto", 1 }, /* autodetect */
 	{ "saa7111", 0 },
 	{ "saa7113", 0 },
@@ -1685,11 +1684,27 @@
 	{ "saa7118", 0 },
 	{ }
 };
-MODULE_DEVICE_TABLE(i2c, saa7115_id);
+MODULE_DEVICE_TABLE(i2c, saa711x_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "saa7115",
-	.probe = saa711x_probe,
-	.remove = saa711x_remove,
-	.id_table = saa7115_id,
+static struct i2c_driver saa711x_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "saa7115",
+	},
+	.probe		= saa711x_probe,
+	.remove		= saa711x_remove,
+	.id_table	= saa711x_id,
 };
+
+static __init int init_saa711x(void)
+{
+	return i2c_add_driver(&saa711x_driver);
+}
+
+static __exit void exit_saa711x(void)
+{
+	i2c_del_driver(&saa711x_driver);
+}
+
+module_init(init_saa711x);
+module_exit(exit_saa711x);
diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c
index 79fffcf..ad96461 100644
--- a/drivers/media/video/saa7127.c
+++ b/drivers/media/video/saa7127.c
@@ -55,7 +55,6 @@
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 #include <media/saa7127.h>
 
 static int debug;
@@ -843,9 +842,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, saa7127_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "saa7127",
-	.probe = saa7127_probe,
-	.remove = saa7127_remove,
-	.id_table = saa7127_id,
+static struct i2c_driver saa7127_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "saa7127",
+	},
+	.probe		= saa7127_probe,
+	.remove		= saa7127_remove,
+	.id_table	= saa7127_id,
 };
+
+static __init int init_saa7127(void)
+{
+	return i2c_add_driver(&saa7127_driver);
+}
+
+static __exit void exit_saa7127(void)
+{
+	i2c_del_driver(&saa7127_driver);
+}
+
+module_init(init_saa7127);
+module_exit(exit_saa7127);
diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig
index fda005e..3fe71be 100644
--- a/drivers/media/video/saa7134/Kconfig
+++ b/drivers/media/video/saa7134/Kconfig
@@ -1,8 +1,7 @@
 config VIDEO_SAA7134
 	tristate "Philips SAA7134 support"
-	depends on VIDEO_DEV && PCI && I2C && INPUT
+	depends on VIDEO_DEV && PCI && I2C
 	select VIDEOBUF_DMA_SG
-	depends on VIDEO_IR
 	select VIDEO_TUNER
 	select VIDEO_TVEEPROM
 	select CRC32
@@ -25,6 +24,14 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called saa7134-alsa.
 
+config VIDEO_SAA7134_RC
+	bool "Philips SAA7134 Remote Controller support"
+	depends on VIDEO_IR
+	depends on VIDEO_SAA7134
+	default y
+	---help---
+	  Enables Remote Controller support on saa7134 driver.
+
 config VIDEO_SAA7134_DVB
 	tristate "DVB/ATSC Support for saa7134 based TV cards"
 	depends on VIDEO_SAA7134 && DVB_CORE
diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile
index 604158a8..8a5ff4d 100644
--- a/drivers/media/video/saa7134/Makefile
+++ b/drivers/media/video/saa7134/Makefile
@@ -1,7 +1,8 @@
 
-saa7134-objs :=	saa7134-cards.o saa7134-core.o saa7134-i2c.o	\
-		saa7134-ts.o saa7134-tvaudio.o saa7134-vbi.o    \
-		saa7134-video.o saa7134-input.o
+saa7134-y :=	saa7134-cards.o saa7134-core.o saa7134-i2c.o
+saa7134-y +=	saa7134-ts.o saa7134-tvaudio.o saa7134-vbi.o
+saa7134-y +=	saa7134-video.o
+saa7134-$(CONFIG_VIDEO_SAA7134_RC) += saa7134-input.o
 
 obj-$(CONFIG_VIDEO_SAA7134) +=  saa6752hs.o saa7134.o saa7134-empress.o
 
diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c
index 40fd31c..f9f29cc 100644
--- a/drivers/media/video/saa7134/saa6752hs.c
+++ b/drivers/media/video/saa7134/saa6752hs.c
@@ -36,7 +36,6 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 #include <linux/init.h>
 #include <linux/crc32.h>
 
@@ -992,13 +991,29 @@
 };
 MODULE_DEVICE_TABLE(i2c, saa6752hs_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "saa6752hs",
-	.probe = saa6752hs_probe,
-	.remove = saa6752hs_remove,
-	.id_table = saa6752hs_id,
+static struct i2c_driver saa6752hs_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "saa6752hs",
+	},
+	.probe		= saa6752hs_probe,
+	.remove		= saa6752hs_remove,
+	.id_table	= saa6752hs_id,
 };
 
+static __init int init_saa6752hs(void)
+{
+	return i2c_add_driver(&saa6752hs_driver);
+}
+
+static __exit void exit_saa6752hs(void)
+{
+	i2c_del_driver(&saa6752hs_driver);
+}
+
+module_init(init_saa6752hs);
+module_exit(exit_saa6752hs);
+
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * ---------------------------------------------------------------------------
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index bb8d83d..10a6cbf 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -7551,22 +7551,22 @@
 		   so we do not need to probe for a radio tuner device. */
 		if (dev->radio_type != UNSET)
 			v4l2_i2c_new_subdev(&dev->v4l2_dev,
-				&dev->i2c_adap, "tuner", "tuner",
+				&dev->i2c_adap, NULL, "tuner",
 				dev->radio_addr, NULL);
 		if (has_demod)
 			v4l2_i2c_new_subdev(&dev->v4l2_dev,
-				&dev->i2c_adap, "tuner", "tuner",
+				&dev->i2c_adap, NULL, "tuner",
 				0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
 		if (dev->tuner_addr == ADDR_UNSET) {
 			enum v4l2_i2c_tuner_type type =
 				has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
 
 			v4l2_i2c_new_subdev(&dev->v4l2_dev,
-				&dev->i2c_adap, "tuner", "tuner",
+				&dev->i2c_adap, NULL, "tuner",
 				0, v4l2_i2c_tuner_addrs(type));
 		} else {
 			v4l2_i2c_new_subdev(&dev->v4l2_dev,
-				&dev->i2c_adap, "tuner", "tuner",
+				&dev->i2c_adap, NULL, "tuner",
 				dev->tuner_addr, NULL);
 		}
 	}
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index 40bc635..764d7d2 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -255,7 +255,7 @@
 	struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
 	BUG_ON(in_interrupt());
 
-	videobuf_waiton(&buf->vb,0,0);
+	videobuf_waiton(q, &buf->vb, 0, 0);
 	videobuf_dma_unmap(q->dev, dma);
 	videobuf_dma_free(dma);
 	buf->vb.state = VIDEOBUF_NEEDS_INIT;
@@ -991,7 +991,7 @@
 	if (card_is_empress(dev)) {
 		struct v4l2_subdev *sd =
 			v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
-				"saa6752hs", "saa6752hs",
+				NULL, "saa6752hs",
 				saa7134_boards[dev->board].empress_addr, NULL);
 
 		if (sd)
@@ -1002,7 +1002,7 @@
 		struct v4l2_subdev *sd;
 
 		sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
-				&dev->i2c_adap,	"saa6588", "saa6588",
+				&dev->i2c_adap, NULL, "saa6588",
 				0, I2C_ADDRS(saa7134_boards[dev->board].rds_addr));
 		if (sd) {
 			printk(KERN_INFO "%s: found RDS decoder\n", dev->name);
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index f26fe76..beb95e2 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -1111,7 +1111,7 @@
 			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
 			    V4L2_FIELD_ALTERNATE,
 			    sizeof(struct saa7134_buf),
-			    dev);
+			    dev, NULL);
 
 	switch (dev->board) {
 	case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c
index e763f9f..1467a30 100644
--- a/drivers/media/video/saa7134/saa7134-empress.c
+++ b/drivers/media/video/saa7134/saa7134-empress.c
@@ -542,7 +542,7 @@
 			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
 			    V4L2_FIELD_ALTERNATE,
 			    sizeof(struct saa7134_buf),
-			    dev);
+			    dev, NULL);
 
 	empress_signal_update(&dev->empress_workqueue);
 	return 0;
diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c
index da41b6b..2d3f6d2 100644
--- a/drivers/media/video/saa7134/saa7134-i2c.c
+++ b/drivers/media/video/saa7134/saa7134-i2c.c
@@ -328,7 +328,6 @@
 static struct i2c_adapter saa7134_adap_template = {
 	.owner         = THIS_MODULE,
 	.name          = "saa7134",
-	.id            = I2C_HW_SAA7134,
 	.algo          = &saa7134_algo,
 };
 
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index 0b336ca..46d31df 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -429,7 +429,7 @@
 	mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
 }
 
-void ir_raw_decode_timer_end(unsigned long data)
+static void ir_raw_decode_timer_end(unsigned long data)
 {
 	struct saa7134_dev *dev = (struct saa7134_dev *)data;
 	struct card_ir *ir = dev->remote;
@@ -550,7 +550,7 @@
 }
 
 
-int saa7134_ir_change_protocol(void *priv, u64 ir_type)
+static int saa7134_ir_change_protocol(void *priv, u64 ir_type)
 {
 	struct saa7134_dev *dev = priv;
 	struct card_ir *ir = dev->remote;
@@ -772,8 +772,10 @@
 	case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
 	case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
 		ir_codes     = RC_MAP_ASUS_PC39;
-		mask_keydown = 0x0040000;
-		rc5_gpio = 1;
+		mask_keydown = 0x0040000;	/* Enable GPIO18 line on both edges */
+		mask_keyup   = 0x0040000;
+		mask_keycode = 0xffff;
+		raw_decode   = 1;
 		break;
 	case SAA7134_BOARD_ENCORE_ENLTV:
 	case SAA7134_BOARD_ENCORE_ENLTV_FM:
@@ -959,6 +961,11 @@
 		dev->init_data.name = "MSI TV@nywhere Plus";
 		dev->init_data.get_key = get_key_msi_tvanywhere_plus;
 		dev->init_data.ir_codes = RC_MAP_MSI_TVANYWHERE_PLUS;
+		/*
+		 * MSI TV@nyware Plus requires more frequent polling
+		 * otherwise it will miss some keypresses
+		 */
+		dev->init_data.polling_interval = 50;
 		info.addr = 0x30;
 		/* MSI TV@nywhere Plus controller doesn't seem to
 		   respond to probes unless we read something from
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
index 45f0ac8..f0b1573 100644
--- a/drivers/media/video/saa7134/saa7134-video.c
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -1366,13 +1366,13 @@
 			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
 			    V4L2_FIELD_INTERLACED,
 			    sizeof(struct saa7134_buf),
-			    fh);
+			    fh, NULL);
 	videobuf_queue_sg_init(&fh->vbi, &saa7134_vbi_qops,
 			    &dev->pci->dev, &dev->slock,
 			    V4L2_BUF_TYPE_VBI_CAPTURE,
 			    V4L2_FIELD_SEQ_TB,
 			    sizeof(struct saa7134_buf),
-			    fh);
+			    fh, NULL);
 	saa7134_pgtable_alloc(dev->pci,&fh->pt_cap);
 	saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi);
 
@@ -1825,7 +1825,7 @@
 
 	if ((tuner_type == TUNER_ABSENT) || (tuner_type == UNSET))
 		cap->capabilities &= ~V4L2_CAP_TUNER;
-		return 0;
+	return 0;
 }
 
 int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id)
@@ -1871,9 +1871,12 @@
 			else
 				fixup = V4L2_STD_SECAM;
 		}
-		for (i = 0; i < TVNORMS; i++)
+		for (i = 0; i < TVNORMS; i++) {
 			if (fixup == tvnorms[i].id)
 				break;
+		}
+		if (i == TVNORMS)
+			return -EINVAL;
 	}
 
 	*id = tvnorms[i].id;
@@ -1997,9 +2000,12 @@
 	if (0 != t->index)
 		return -EINVAL;
 	memset(t, 0, sizeof(*t));
-	for (n = 0; n < SAA7134_INPUT_MAX; n++)
+	for (n = 0; n < SAA7134_INPUT_MAX; n++) {
 		if (card_in(dev, n).tv)
 			break;
+	}
+	if (n == SAA7134_INPUT_MAX)
+		return -EINVAL;
 	if (NULL != card_in(dev, n).name) {
 		strcpy(t->name, "Television");
 		t->type = V4L2_TUNER_ANALOG_TV;
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index c040a18..d3b6a19 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -810,16 +810,18 @@
 /* ----------------------------------------------------------- */
 /* saa7134-input.c                                             */
 
+#if defined(CONFIG_VIDEO_SAA7134_RC)
 int  saa7134_input_init1(struct saa7134_dev *dev);
 void saa7134_input_fini(struct saa7134_dev *dev);
 void saa7134_input_irq(struct saa7134_dev *dev);
 void saa7134_probe_i2c_ir(struct saa7134_dev *dev);
 int saa7134_ir_start(struct saa7134_dev *dev);
 void saa7134_ir_stop(struct saa7134_dev *dev);
-
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+#else
+#define saa7134_input_init1(dev)	(0)
+#define saa7134_input_fini(dev)		(0)
+#define saa7134_input_irq(dev)		(0)
+#define saa7134_probe_i2c_ir(dev)	(0)
+#define saa7134_ir_start(dev)		(0)
+#define saa7134_ir_stop(dev)		(0)
+#endif
diff --git a/drivers/media/video/saa7164/Makefile b/drivers/media/video/saa7164/Makefile
index 4b329fd..6303a8e 100644
--- a/drivers/media/video/saa7164/Makefile
+++ b/drivers/media/video/saa7164/Makefile
@@ -1,6 +1,6 @@
 saa7164-objs	:= saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \
 			saa7164-fw.o saa7164-bus.o saa7164-cmd.o saa7164-api.o \
-			saa7164-buffer.o
+			saa7164-buffer.o saa7164-encoder.o saa7164-vbi.o
 
 obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o
 
diff --git a/drivers/media/video/saa7164/saa7164-api.c b/drivers/media/video/saa7164/saa7164-api.c
index 3f1262b..ad3bc41 100644
--- a/drivers/media/video/saa7164/saa7164-api.c
+++ b/drivers/media/video/saa7164/saa7164-api.c
@@ -1,7 +1,7 @@
 /*
  *  Driver for the NXP SAA7164 PCIe bridge
  *
- *  Copyright (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,14 +24,750 @@
 
 #include "saa7164.h"
 
-int saa7164_api_transition_port(struct saa7164_tsport *port, u8 mode)
+int saa7164_api_get_load_info(struct saa7164_dev *dev, struct tmFwInfoStruct *i)
 {
 	int ret;
 
+	if (!(saa_debug & DBGLVL_CPU))
+		return 0;
+
+	dprintk(DBGLVL_API, "%s()\n", __func__);
+
+	i->deviceinst = 0;
+	i->devicespec = 0;
+	i->mode = 0;
+	i->status = 0;
+
+	ret = saa7164_cmd_send(dev, 0, GET_CUR,
+		GET_FW_STATUS_CONTROL, sizeof(struct tmFwInfoStruct), i);
+	if (ret != SAA_OK) {
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+	}
+
+	printk(KERN_INFO "saa7164[%d]-CPU: %d percent", dev->nr, i->CPULoad);
+
+	return ret;
+}
+
+int saa7164_api_collect_debug(struct saa7164_dev *dev)
+{
+	struct tmComResDebugGetData d;
+	u8 more = 255;
+	int ret;
+
+	dprintk(DBGLVL_API, "%s()\n", __func__);
+
+	while (more--) {
+
+		memset(&d, 0, sizeof(d));
+
+		ret = saa7164_cmd_send(dev, 0, GET_CUR,
+			GET_DEBUG_DATA_CONTROL, sizeof(d), &d);
+		if (ret != SAA_OK) {
+			printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+		}
+
+		if (d.dwResult != SAA_OK)
+			break;
+
+		printk(KERN_INFO "saa7164[%d]-FWMSG: %s", dev->nr, d.ucDebugData);
+	}
+
+	return 0;
+}
+
+int saa7164_api_set_debug(struct saa7164_dev *dev, u8 level)
+{
+	struct tmComResDebugSetLevel lvl;
+	int ret;
+
+	dprintk(DBGLVL_API, "%s(level=%d)\n", __func__, level);
+
+	/* Retrieve current state */
+	ret = saa7164_cmd_send(dev, 0, GET_CUR,
+		SET_DEBUG_LEVEL_CONTROL, sizeof(lvl), &lvl);
+	if (ret != SAA_OK) {
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+	}
+	dprintk(DBGLVL_API, "%s() Was %d\n", __func__, lvl.dwDebugLevel);
+
+	lvl.dwDebugLevel = level;
+
+	/* set new state */
+	ret = saa7164_cmd_send(dev, 0, SET_CUR,
+		SET_DEBUG_LEVEL_CONTROL, sizeof(lvl), &lvl);
+	if (ret != SAA_OK) {
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+	}
+
+	return ret;
+}
+
+int saa7164_api_set_vbi_format(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct tmComResProbeCommit fmt, rsp;
+	int ret;
+
+	dprintk(DBGLVL_API, "%s(nr=%d, unitid=0x%x)\n", __func__,
+		port->nr, port->hwcfg.unitid);
+
+	fmt.bmHint = 0;
+	fmt.bFormatIndex = 1;
+	fmt.bFrameIndex = 1;
+
+	/* Probe, see if it can support this format */
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+		SET_CUR, SAA_PROBE_CONTROL, sizeof(fmt), &fmt);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() set error, ret = 0x%x\n", __func__, ret);
+
+	/* See of the format change was successful */
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+		GET_CUR, SAA_PROBE_CONTROL, sizeof(rsp), &rsp);
+	if (ret != SAA_OK) {
+		printk(KERN_ERR "%s() get error, ret = 0x%x\n", __func__, ret);
+	} else {
+		/* Compare requested vs received, should be same */
+		if (memcmp(&fmt, &rsp, sizeof(rsp)) == 0) {
+			dprintk(DBGLVL_API, "SET/PROBE Verified\n");
+
+			/* Ask the device to select the negotiated format */
+			ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+				SET_CUR, SAA_COMMIT_CONTROL, sizeof(fmt), &fmt);
+			if (ret != SAA_OK)
+				printk(KERN_ERR "%s() commit error, ret = 0x%x\n",
+					__func__, ret);
+
+			ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+				GET_CUR, SAA_COMMIT_CONTROL, sizeof(rsp), &rsp);
+			if (ret != SAA_OK)
+				printk(KERN_ERR "%s() GET commit error, ret = 0x%x\n",
+					__func__, ret);
+
+			if (memcmp(&fmt, &rsp, sizeof(rsp)) != 0) {
+				printk(KERN_ERR "%s() memcmp error, ret = 0x%x\n",
+					__func__, ret);
+			} else
+				dprintk(DBGLVL_API, "SET/COMMIT Verified\n");
+
+			dprintk(DBGLVL_API, "rsp.bmHint = 0x%x\n", rsp.bmHint);
+			dprintk(DBGLVL_API, "rsp.bFormatIndex = 0x%x\n", rsp.bFormatIndex);
+			dprintk(DBGLVL_API, "rsp.bFrameIndex = 0x%x\n", rsp.bFrameIndex);
+		} else
+			printk(KERN_ERR "%s() compare failed\n", __func__);
+	}
+
+	if (ret == SAA_OK)
+		dprintk(DBGLVL_API, "%s(nr=%d) Success\n", __func__, port->nr);
+
+	return ret;
+}
+
+int saa7164_api_set_gop_size(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct tmComResEncVideoGopStructure gs;
+	int ret;
+
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	gs.ucRefFrameDist = port->encoder_params.refdist;
+	gs.ucGOPSize = port->encoder_params.gop_size;
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+		EU_VIDEO_GOP_STRUCTURE_CONTROL,
+		sizeof(gs), &gs);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	return ret;
+}
+
+int saa7164_api_set_encoder(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct tmComResEncVideoBitRate vb;
+	struct tmComResEncAudioBitRate ab;
+	int ret;
+
+	dprintk(DBGLVL_ENC, "%s() unitid=0x%x\n", __func__,
+		port->hwcfg.sourceid);
+
+	if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS)
+		port->encoder_profile = EU_PROFILE_PS_DVD;
+	else
+		port->encoder_profile = EU_PROFILE_TS_HQ;
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+		EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Resolution */
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+		EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Establish video bitrates */
+	if (port->encoder_params.bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+		vb.ucVideoBitRateMode = EU_VIDEO_BIT_RATE_MODE_CONSTANT;
+	else
+		vb.ucVideoBitRateMode = EU_VIDEO_BIT_RATE_MODE_VARIABLE_PEAK;
+	vb.dwVideoBitRate = port->encoder_params.bitrate;
+	vb.dwVideoBitRatePeak = port->encoder_params.bitrate_peak;
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+		EU_VIDEO_BIT_RATE_CONTROL, sizeof(struct tmComResEncVideoBitRate), &vb);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Establish audio bitrates */
+	ab.ucAudioBitRateMode = 0;
+	ab.dwAudioBitRate = 384000;
+	ab.dwAudioBitRatePeak = ab.dwAudioBitRate;
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+		EU_AUDIO_BIT_RATE_CONTROL, sizeof(struct tmComResEncAudioBitRate), &ab);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	saa7164_api_set_aspect_ratio(port);
+	saa7164_api_set_gop_size(port);
+
+	return ret;
+}
+
+int saa7164_api_get_encoder(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct tmComResEncVideoBitRate v;
+	struct tmComResEncAudioBitRate a;
+	struct tmComResEncVideoInputAspectRatio ar;
+	int ret;
+
+	dprintk(DBGLVL_ENC, "%s() unitid=0x%x\n", __func__, port->hwcfg.sourceid);
+
+	port->encoder_profile = 0;
+	port->video_format = 0;
+	port->video_resolution = 0;
+	port->audio_format = 0;
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+		EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+		EU_VIDEO_RESOLUTION_CONTROL, sizeof(u8), &port->video_resolution);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+		EU_VIDEO_FORMAT_CONTROL, sizeof(u8), &port->video_format);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+		EU_VIDEO_BIT_RATE_CONTROL, sizeof(v), &v);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+		EU_AUDIO_FORMAT_CONTROL, sizeof(u8), &port->audio_format);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+		EU_AUDIO_BIT_RATE_CONTROL, sizeof(a), &a);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Aspect Ratio */
+	ar.width = 0;
+	ar.height = 0;
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+		EU_VIDEO_INPUT_ASPECT_CONTROL,
+		sizeof(struct tmComResEncVideoInputAspectRatio), &ar);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	dprintk(DBGLVL_ENC, "encoder_profile = %d\n", port->encoder_profile);
+	dprintk(DBGLVL_ENC, "video_format    = %d\n", port->video_format);
+	dprintk(DBGLVL_ENC, "audio_format    = %d\n", port->audio_format);
+	dprintk(DBGLVL_ENC, "video_resolution= %d\n", port->video_resolution);
+	dprintk(DBGLVL_ENC, "v.ucVideoBitRateMode = %d\n", v.ucVideoBitRateMode);
+	dprintk(DBGLVL_ENC, "v.dwVideoBitRate     = %d\n", v.dwVideoBitRate);
+	dprintk(DBGLVL_ENC, "v.dwVideoBitRatePeak = %d\n", v.dwVideoBitRatePeak);
+	dprintk(DBGLVL_ENC, "a.ucVideoBitRateMode = %d\n", a.ucAudioBitRateMode);
+	dprintk(DBGLVL_ENC, "a.dwVideoBitRate     = %d\n", a.dwAudioBitRate);
+	dprintk(DBGLVL_ENC, "a.dwVideoBitRatePeak = %d\n", a.dwAudioBitRatePeak);
+	dprintk(DBGLVL_ENC, "aspect.width / height = %d:%d\n", ar.width, ar.height);
+
+	return ret;
+}
+
+int saa7164_api_set_aspect_ratio(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct tmComResEncVideoInputAspectRatio ar;
+	int ret;
+
+	dprintk(DBGLVL_ENC, "%s(%d)\n", __func__,
+		port->encoder_params.ctl_aspect);
+
+	switch (port->encoder_params.ctl_aspect) {
+	case V4L2_MPEG_VIDEO_ASPECT_1x1:
+		ar.width = 1;
+		ar.height = 1;
+		break;
+	case V4L2_MPEG_VIDEO_ASPECT_4x3:
+		ar.width = 4;
+		ar.height = 3;
+		break;
+	case V4L2_MPEG_VIDEO_ASPECT_16x9:
+		ar.width = 16;
+		ar.height = 9;
+		break;
+	case V4L2_MPEG_VIDEO_ASPECT_221x100:
+		ar.width = 221;
+		ar.height = 100;
+		break;
+	default:
+		BUG();
+	}
+
+	dprintk(DBGLVL_ENC, "%s(%d) now %d:%d\n", __func__,
+		port->encoder_params.ctl_aspect,
+		ar.width, ar.height);
+
+	/* Aspect Ratio */
+	ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+		EU_VIDEO_INPUT_ASPECT_CONTROL,
+		sizeof(struct tmComResEncVideoInputAspectRatio), &ar);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	return ret;
+}
+
+int saa7164_api_set_usercontrol(struct saa7164_port *port, u8 ctl)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+	u16 val;
+
+	if (ctl == PU_BRIGHTNESS_CONTROL)
+		val = port->ctl_brightness;
+	else
+	if (ctl == PU_CONTRAST_CONTROL)
+		val = port->ctl_contrast;
+	else
+	if (ctl == PU_HUE_CONTROL)
+		val = port->ctl_hue;
+	else
+	if (ctl == PU_SATURATION_CONTROL)
+		val = port->ctl_saturation;
+	else
+	if (ctl == PU_SHARPNESS_CONTROL)
+		val = port->ctl_sharpness;
+	else
+		return -EINVAL;
+
+	dprintk(DBGLVL_ENC, "%s() unitid=0x%x ctl=%d, val=%d\n",
+		__func__, port->encunit.vsourceid, ctl, val);
+
+	ret = saa7164_cmd_send(port->dev, port->encunit.vsourceid, SET_CUR,
+		ctl, sizeof(u16), &val);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	return ret;
+}
+
+int saa7164_api_get_usercontrol(struct saa7164_port *port, u8 ctl)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+	u16 val;
+
+	ret = saa7164_cmd_send(port->dev, port->encunit.vsourceid, GET_CUR,
+		ctl, sizeof(u16), &val);
+	if (ret != SAA_OK) {
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+		return ret;
+	}
+
+	dprintk(DBGLVL_ENC, "%s() ctl=%d, val=%d\n",
+		__func__, ctl, val);
+
+	if (ctl == PU_BRIGHTNESS_CONTROL)
+		port->ctl_brightness = val;
+	else
+	if (ctl == PU_CONTRAST_CONTROL)
+		port->ctl_contrast = val;
+	else
+	if (ctl == PU_HUE_CONTROL)
+		port->ctl_hue = val;
+	else
+	if (ctl == PU_SATURATION_CONTROL)
+		port->ctl_saturation = val;
+	else
+	if (ctl == PU_SHARPNESS_CONTROL)
+		port->ctl_sharpness = val;
+
+	return ret;
+}
+
+int saa7164_api_set_videomux(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	u8 inputs[] = { 1, 2, 2, 2, 5, 5, 5 };
+	int ret;
+
+	dprintk(DBGLVL_ENC, "%s() v_mux=%d a_mux=%d\n",
+		__func__, port->mux_input, inputs[port->mux_input - 1]);
+
+	/* Audio Mute */
+	ret = saa7164_api_audio_mute(port, 1);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Video Mux */
+	ret = saa7164_cmd_send(port->dev, port->vidproc.sourceid, SET_CUR,
+		SU_INPUT_SELECT_CONTROL, sizeof(u8), &port->mux_input);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Audio Mux */
+	ret = saa7164_cmd_send(port->dev, port->audfeat.sourceid, SET_CUR,
+		SU_INPUT_SELECT_CONTROL, sizeof(u8), &inputs[port->mux_input - 1]);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Audio UnMute */
+	ret = saa7164_api_audio_mute(port, 0);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	return ret;
+}
+
+int saa7164_api_audio_mute(struct saa7164_port *port, int mute)
+{
+	struct saa7164_dev *dev = port->dev;
+	u8 v = mute;
+	int ret;
+
+	dprintk(DBGLVL_API, "%s(%d)\n", __func__, mute);
+
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+		MUTE_CONTROL, sizeof(u8), &v);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	return ret;
+}
+
+/* 0 = silence, 0xff = full */
+int saa7164_api_set_audio_volume(struct saa7164_port *port, s8 level)
+{
+	struct saa7164_dev *dev = port->dev;
+	s16 v, min, max;
+	int ret;
+
+	dprintk(DBGLVL_API, "%s(%d)\n", __func__, level);
+
+	/* Obtain the min/max ranges */
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_MIN,
+		VOLUME_CONTROL, sizeof(u16), &min);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_MAX,
+		VOLUME_CONTROL, sizeof(u16), &max);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_CUR,
+		(0x01 << 8) | VOLUME_CONTROL, sizeof(u16), &v);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	dprintk(DBGLVL_API, "%s(%d) min=%d max=%d cur=%d\n", __func__, level, min, max, v);
+
+	v = level;
+	if (v < min)
+		v = min;
+	if (v > max)
+		v = max;
+
+	/* Left */
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+		(0x01 << 8) | VOLUME_CONTROL, sizeof(s16), &v);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Right */
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+		(0x02 << 8) | VOLUME_CONTROL, sizeof(s16), &v);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_CUR,
+		(0x01 << 8) | VOLUME_CONTROL, sizeof(u16), &v);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	dprintk(DBGLVL_API, "%s(%d) min=%d max=%d cur=%d\n", __func__, level, min, max, v);
+
+	return ret;
+}
+
+int saa7164_api_set_audio_std(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct tmComResAudioDefaults lvl;
+	struct tmComResTunerStandard tvaudio;
+	int ret;
+
+	dprintk(DBGLVL_API, "%s()\n", __func__);
+
+	/* Establish default levels */
+	lvl.ucDecoderLevel = TMHW_LEV_ADJ_DECLEV_DEFAULT;
+	lvl.ucDecoderFM_Level = TMHW_LEV_ADJ_DECLEV_DEFAULT;
+	lvl.ucMonoLevel = TMHW_LEV_ADJ_MONOLEV_DEFAULT;
+	lvl.ucNICAM_Level = TMHW_LEV_ADJ_NICLEV_DEFAULT;
+	lvl.ucSAP_Level = TMHW_LEV_ADJ_SAPLEV_DEFAULT;
+	lvl.ucADC_Level = TMHW_LEV_ADJ_ADCLEV_DEFAULT;
+	ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+		AUDIO_DEFAULT_CONTROL, sizeof(struct tmComResAudioDefaults), &lvl);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	/* Manually select the appropriate TV audio standard */
+	if (port->encodernorm.id & V4L2_STD_NTSC) {
+		tvaudio.std = TU_STANDARD_NTSC_M;
+		tvaudio.country = 1;
+	} else {
+		tvaudio.std = TU_STANDARD_PAL_I;
+		tvaudio.country = 44;
+	}
+
+	ret = saa7164_cmd_send(port->dev, port->tunerunit.unitid, SET_CUR,
+		TU_STANDARD_CONTROL, sizeof(tvaudio), &tvaudio);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() TU_STANDARD_CONTROL error, ret = 0x%x\n", __func__, ret);
+	return ret;
+}
+
+int saa7164_api_set_audio_detection(struct saa7164_port *port, int autodetect)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct tmComResTunerStandardAuto p;
+	int ret;
+
+	dprintk(DBGLVL_API, "%s(%d)\n", __func__, autodetect);
+
+	/* Disable TV Audio autodetect if not already set (buggy) */
+	if (autodetect)
+		p.mode = TU_STANDARD_AUTO;
+	else
+		p.mode = TU_STANDARD_MANUAL;
+	ret = saa7164_cmd_send(port->dev, port->tunerunit.unitid, SET_CUR,
+		TU_STANDARD_AUTO_CONTROL, sizeof(p), &p);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() TU_STANDARD_AUTO_CONTROL error, ret = 0x%x\n", __func__, ret);
+
+	return ret;
+}
+
+int saa7164_api_get_videomux(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_cmd_send(port->dev, port->vidproc.sourceid, GET_CUR,
+		SU_INPUT_SELECT_CONTROL, sizeof(u8), &port->mux_input);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+	dprintk(DBGLVL_ENC, "%s() v_mux=%d\n",
+		__func__, port->mux_input);
+
+	return ret;
+}
+
+int saa7164_api_set_dif(struct saa7164_port *port, u8 reg, u8 val)
+{
+	struct saa7164_dev *dev = port->dev;
+
+	u16 len = 0;
+	u8 buf[256];
+	int ret;
+	u8 mas;
+
+	dprintk(DBGLVL_API, "%s(nr=%d type=%d val=%x)\n", __func__,
+		port->nr, port->type, val);
+
+	if (port->nr == 0)
+		mas = 0xd0;
+	else
+		mas = 0xe0;
+
+	memset(buf, 0, sizeof(buf));
+
+	buf[0x00] = 0x04;
+	buf[0x01] = 0x00;
+	buf[0x02] = 0x00;
+	buf[0x03] = 0x00;
+
+	buf[0x04] = 0x04;
+	buf[0x05] = 0x00;
+	buf[0x06] = 0x00;
+	buf[0x07] = 0x00;
+
+	buf[0x08] = reg;
+	buf[0x09] = 0x26;
+	buf[0x0a] = mas;
+	buf[0x0b] = 0xb0;
+
+	buf[0x0c] = val;
+	buf[0x0d] = 0x00;
+	buf[0x0e] = 0x00;
+	buf[0x0f] = 0x00;
+
+	ret = saa7164_cmd_send(dev, port->ifunit.unitid, GET_LEN,
+		EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
+	if (ret != SAA_OK) {
+		printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
+		return -EIO;
+	}
+
+	ret = saa7164_cmd_send(dev, port->ifunit.unitid, SET_CUR,
+		EXU_REGISTER_ACCESS_CONTROL, len, &buf);
+	if (ret != SAA_OK)
+		printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
+
+	//saa7164_dumphex16(dev, buf, 16);
+
+	return ret == SAA_OK ? 0 : -EIO;
+}
+
+/* Disable the IF block AGC controls */
+int saa7164_api_configure_dif(struct saa7164_port *port, u32 std)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret = 0;
+	u8 agc_disable;
+
+	dprintk(DBGLVL_API, "%s(nr=%d, 0x%x)\n", __func__, port->nr, std);
+
+	if (std & V4L2_STD_NTSC) {
+		dprintk(DBGLVL_API, " NTSC\n");
+		saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+		agc_disable = 0;
+	} else if (std & V4L2_STD_PAL_I) {
+		dprintk(DBGLVL_API, " PAL-I\n");
+		saa7164_api_set_dif(port, 0x00, 0x08); /* Video Standard */
+		agc_disable = 0;
+	} else if (std & V4L2_STD_PAL_M) {
+		dprintk(DBGLVL_API, " PAL-M\n");
+		saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+		agc_disable = 0;
+	} else if (std & V4L2_STD_PAL_N) {
+		dprintk(DBGLVL_API, " PAL-N\n");
+		saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+		agc_disable = 0;
+	} else if (std & V4L2_STD_PAL_Nc) {
+		dprintk(DBGLVL_API, " PAL-Nc\n");
+		saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+		agc_disable = 0;
+	} else if (std & V4L2_STD_PAL_B) {
+		dprintk(DBGLVL_API, " PAL-B\n");
+		saa7164_api_set_dif(port, 0x00, 0x02); /* Video Standard */
+		agc_disable = 0;
+	} else if (std & V4L2_STD_PAL_DK) {
+		dprintk(DBGLVL_API, " PAL-DK\n");
+		saa7164_api_set_dif(port, 0x00, 0x10); /* Video Standard */
+		agc_disable = 0;
+	} else if (std & V4L2_STD_SECAM_L) {
+		dprintk(DBGLVL_API, " SECAM-L\n");
+		saa7164_api_set_dif(port, 0x00, 0x20); /* Video Standard */
+		agc_disable = 0;
+	} else {
+		/* Unknown standard, assume DTV */
+		dprintk(DBGLVL_API, " Unknown (assuming DTV)\n");
+		saa7164_api_set_dif(port, 0x00, 0x80); /* Undefined Video Standard */
+		agc_disable = 1;
+	}
+
+	saa7164_api_set_dif(port, 0x48, 0xa0); /* AGC Functions 1 */
+	saa7164_api_set_dif(port, 0xc0, agc_disable); /* AGC Output Disable */
+	saa7164_api_set_dif(port, 0x7c, 0x04); /* CVBS EQ */
+	saa7164_api_set_dif(port, 0x04, 0x01); /* Active */
+	msleep(100);
+	saa7164_api_set_dif(port, 0x04, 0x00); /* Active (again) */
+	msleep(100);
+
+	return ret;
+}
+
+/* Ensure the dif is in the correct state for the operating mode
+ * (analog / dtv). We only configure the diff through the analog encoder
+ * so when we're in digital mode we need to find the appropriate encoder
+ * and use it to configure the DIF.
+ */
+int saa7164_api_initialize_dif(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_port *p = 0;
+	int ret = -EINVAL;
+	u32 std = 0;
+
+	dprintk(DBGLVL_API, "%s(nr=%d type=%d)\n", __func__,
+		port->nr, port->type);
+
+	if (port->type == SAA7164_MPEG_ENCODER) {
+		/* Pick any analog standard to init the diff.
+		 * we'll come back during encoder_init'
+		 * and set the correct standard if requried.
+		 */
+		std = V4L2_STD_NTSC;
+	} else
+	if (port->type == SAA7164_MPEG_DVB) {
+		if (port->nr == SAA7164_PORT_TS1)
+			p = &dev->ports[SAA7164_PORT_ENC1];
+		else
+			p = &dev->ports[SAA7164_PORT_ENC2];
+	} else
+	if (port->type == SAA7164_MPEG_VBI) {
+		std = V4L2_STD_NTSC;
+		if (port->nr == SAA7164_PORT_VBI1)
+			p = &dev->ports[SAA7164_PORT_ENC1];
+		else
+			p = &dev->ports[SAA7164_PORT_ENC2];
+	} else
+		BUG();
+
+	if (p)
+		ret = saa7164_api_configure_dif(p, std);
+
+	return ret;
+}
+
+int saa7164_api_transition_port(struct saa7164_port *port, u8 mode)
+{
+	struct saa7164_dev *dev = port->dev;
+
+	int ret;
+
+	dprintk(DBGLVL_API, "%s(nr=%d unitid=0x%x,%d)\n",
+		__func__, port->nr, port->hwcfg.unitid, mode);
+
 	ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, SET_CUR,
 		SAA_STATE_CONTROL, sizeof(mode), &mode);
 	if (ret != SAA_OK)
-		printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+		printk(KERN_ERR "%s(portnr %d unitid 0x%x) error, ret = 0x%x\n",
+			__func__, port->nr, port->hwcfg.unitid, ret);
 
 	return ret;
 }
@@ -61,10 +797,45 @@
 		&reg[0], 128, buf);
 }
 
+int saa7164_api_configure_port_vbi(struct saa7164_dev *dev,
+	struct saa7164_port *port)
+{
+	struct tmComResVBIFormatDescrHeader *fmt = &port->vbi_fmt_ntsc;
+
+	dprintk(DBGLVL_API, "    bFormatIndex  = 0x%x\n", fmt->bFormatIndex);
+	dprintk(DBGLVL_API, "    VideoStandard = 0x%x\n", fmt->VideoStandard);
+	dprintk(DBGLVL_API, "    StartLine     = %d\n", fmt->StartLine);
+	dprintk(DBGLVL_API, "    EndLine       = %d\n", fmt->EndLine);
+	dprintk(DBGLVL_API, "    FieldRate     = %d\n", fmt->FieldRate);
+	dprintk(DBGLVL_API, "    bNumLines     = %d\n", fmt->bNumLines);
+
+	/* Cache the hardware configuration in the port */
+
+	port->bufcounter = port->hwcfg.BARLocation;
+	port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
+	port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
+	port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
+	port->bufptr32l = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
+	port->bufptr32h = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount);
+	port->bufptr64 = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount);
+	dprintk(DBGLVL_API, "   = port->hwcfg.BARLocation = 0x%x\n",
+		port->hwcfg.BARLocation);
+
+	dprintk(DBGLVL_API, "   = VS_FORMAT_VBI (becomes dev->en[%d])\n",
+		port->nr);
+
+	return 0;
+}
 
 int saa7164_api_configure_port_mpeg2ts(struct saa7164_dev *dev,
-	struct saa7164_tsport *port,
-	tmComResTSFormatDescrHeader_t *tsfmt)
+	struct saa7164_port *port,
+	struct tmComResTSFormatDescrHeader *tsfmt)
 {
 	dprintk(DBGLVL_API, "    bFormatIndex = 0x%x\n", tsfmt->bFormatIndex);
 	dprintk(DBGLVL_API, "    bDataOffset  = 0x%x\n", tsfmt->bDataOffset);
@@ -96,27 +867,68 @@
 	return 0;
 }
 
+int saa7164_api_configure_port_mpeg2ps(struct saa7164_dev *dev,
+	struct saa7164_port *port,
+	struct tmComResPSFormatDescrHeader *fmt)
+{
+	dprintk(DBGLVL_API, "    bFormatIndex = 0x%x\n", fmt->bFormatIndex);
+	dprintk(DBGLVL_API, "    wPacketLength= 0x%x\n", fmt->wPacketLength);
+	dprintk(DBGLVL_API, "    wPackLength=   0x%x\n", fmt->wPackLength);
+	dprintk(DBGLVL_API, "    bPackDataType= 0x%x\n", fmt->bPackDataType);
+
+	/* Cache the hardware configuration in the port */
+	/* TODO: CHECK THIS in the port config */
+	port->bufcounter = port->hwcfg.BARLocation;
+	port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
+	port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
+	port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
+	port->bufptr32l = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
+	port->bufptr32h = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount);
+	port->bufptr64 = port->hwcfg.BARLocation +
+		(4 * sizeof(u32)) +
+		(sizeof(u32) * port->hwcfg.buffercount);
+	dprintk(DBGLVL_API, "   = port->hwcfg.BARLocation = 0x%x\n",
+		port->hwcfg.BARLocation);
+
+	dprintk(DBGLVL_API, "   = VS_FORMAT_MPEGPS (becomes dev->enc[%d])\n",
+		port->nr);
+
+	return 0;
+}
+
 int saa7164_api_dump_subdevs(struct saa7164_dev *dev, u8 *buf, int len)
 {
-	struct saa7164_tsport *port = 0;
+	struct saa7164_port *tsport = 0;
+	struct saa7164_port *encport = 0;
+	struct saa7164_port *vbiport = 0;
 	u32 idx, next_offset;
 	int i;
-	tmComResDescrHeader_t *hdr, *t;
-	tmComResExtDevDescrHeader_t *exthdr;
-	tmComResPathDescrHeader_t *pathhdr;
-	tmComResAntTermDescrHeader_t *anttermhdr;
-	tmComResTunerDescrHeader_t *tunerunithdr;
-	tmComResDMATermDescrHeader_t *vcoutputtermhdr;
-	tmComResTSFormatDescrHeader_t *tsfmt;
+	struct tmComResDescrHeader *hdr, *t;
+	struct tmComResExtDevDescrHeader *exthdr;
+	struct tmComResPathDescrHeader *pathhdr;
+	struct tmComResAntTermDescrHeader *anttermhdr;
+	struct tmComResTunerDescrHeader *tunerunithdr;
+	struct tmComResDMATermDescrHeader *vcoutputtermhdr;
+	struct tmComResTSFormatDescrHeader *tsfmt;
+	struct tmComResPSFormatDescrHeader *psfmt;
+	struct tmComResSelDescrHeader *psel;
+	struct tmComResProcDescrHeader *pdh;
+	struct tmComResAFeatureDescrHeader *afd;
+	struct tmComResEncoderDescrHeader *edh;
+	struct tmComResVBIFormatDescrHeader *vbifmt;
 	u32 currpath = 0;
 
 	dprintk(DBGLVL_API,
-		"%s(?,?,%d) sizeof(tmComResDescrHeader_t) = %d bytes\n",
-		__func__, len, (u32)sizeof(tmComResDescrHeader_t));
+		"%s(?,?,%d) sizeof(struct tmComResDescrHeader) = %d bytes\n",
+		__func__, len, (u32)sizeof(struct tmComResDescrHeader));
 
-	for (idx = 0; idx < (len - sizeof(tmComResDescrHeader_t)); ) {
+	for (idx = 0; idx < (len - sizeof(struct tmComResDescrHeader));) {
 
-		hdr = (tmComResDescrHeader_t *)(buf + idx);
+		hdr = (struct tmComResDescrHeader *)(buf + idx);
 
 		if (hdr->type != CS_INTERFACE)
 			return SAA_ERR_NOT_SUPPORTED;
@@ -128,7 +940,7 @@
 			break;
 		case VC_TUNER_PATH:
 			dprintk(DBGLVL_API, " VC_TUNER_PATH\n");
-			pathhdr = (tmComResPathDescrHeader_t *)(buf + idx);
+			pathhdr = (struct tmComResPathDescrHeader *)(buf + idx);
 			dprintk(DBGLVL_API, "  pathid = 0x%x\n",
 				pathhdr->pathid);
 			currpath = pathhdr->pathid;
@@ -136,7 +948,7 @@
 		case VC_INPUT_TERMINAL:
 			dprintk(DBGLVL_API, " VC_INPUT_TERMINAL\n");
 			anttermhdr =
-				(tmComResAntTermDescrHeader_t *)(buf + idx);
+				(struct tmComResAntTermDescrHeader *)(buf + idx);
 			dprintk(DBGLVL_API, "  terminalid   = 0x%x\n",
 				anttermhdr->terminalid);
 			dprintk(DBGLVL_API, "  terminaltype = 0x%x\n",
@@ -179,7 +991,7 @@
 		case VC_OUTPUT_TERMINAL:
 			dprintk(DBGLVL_API, " VC_OUTPUT_TERMINAL\n");
 			vcoutputtermhdr =
-				(tmComResDMATermDescrHeader_t *)(buf + idx);
+				(struct tmComResDMATermDescrHeader *)(buf + idx);
 			dprintk(DBGLVL_API, "  unitid = 0x%x\n",
 				vcoutputtermhdr->unitid);
 			dprintk(DBGLVL_API, "  terminaltype = 0x%x\n",
@@ -233,32 +1045,49 @@
 			dprintk(DBGLVL_API, "  numformats   = 0x%x\n",
 				vcoutputtermhdr->numformats);
 
-			t = (tmComResDescrHeader_t *)
-				((tmComResDMATermDescrHeader_t *)(buf + idx));
+			t = (struct tmComResDescrHeader *)
+				((struct tmComResDMATermDescrHeader *)(buf + idx));
 			next_offset = idx + (vcoutputtermhdr->len);
 			for (i = 0; i < vcoutputtermhdr->numformats; i++) {
-				t = (tmComResDescrHeader_t *)
+				t = (struct tmComResDescrHeader *)
 					(buf + next_offset);
 				switch (t->subtype) {
 				case VS_FORMAT_MPEG2TS:
 					tsfmt =
-					(tmComResTSFormatDescrHeader_t *)t;
+					(struct tmComResTSFormatDescrHeader *)t;
 					if (currpath == 1)
-						port = &dev->ts1;
+						tsport = &dev->ports[SAA7164_PORT_TS1];
 					else
-						port = &dev->ts2;
-					memcpy(&port->hwcfg, vcoutputtermhdr,
+						tsport = &dev->ports[SAA7164_PORT_TS2];
+					memcpy(&tsport->hwcfg, vcoutputtermhdr,
 						sizeof(*vcoutputtermhdr));
 					saa7164_api_configure_port_mpeg2ts(dev,
-						port, tsfmt);
+						tsport, tsfmt);
 					break;
 				case VS_FORMAT_MPEG2PS:
-					dprintk(DBGLVL_API,
-						"   = VS_FORMAT_MPEG2PS\n");
+					psfmt =
+					(struct tmComResPSFormatDescrHeader *)t;
+					if (currpath == 1)
+						encport = &dev->ports[SAA7164_PORT_ENC1];
+					else
+						encport = &dev->ports[SAA7164_PORT_ENC2];
+					memcpy(&encport->hwcfg, vcoutputtermhdr,
+						sizeof(*vcoutputtermhdr));
+					saa7164_api_configure_port_mpeg2ps(dev,
+						encport, psfmt);
 					break;
 				case VS_FORMAT_VBI:
-					dprintk(DBGLVL_API,
-						"   = VS_FORMAT_VBI\n");
+					vbifmt =
+					(struct tmComResVBIFormatDescrHeader *)t;
+					if (currpath == 1)
+						vbiport = &dev->ports[SAA7164_PORT_VBI1];
+					else
+						vbiport = &dev->ports[SAA7164_PORT_VBI2];
+					memcpy(&vbiport->hwcfg, vcoutputtermhdr,
+						sizeof(*vcoutputtermhdr));
+					memcpy(&vbiport->vbi_fmt_ntsc, vbifmt, sizeof(*vbifmt));
+					saa7164_api_configure_port_vbi(dev,
+						vbiport);
 					break;
 				case VS_FORMAT_RDS:
 					dprintk(DBGLVL_API,
@@ -284,7 +1113,7 @@
 		case TUNER_UNIT:
 			dprintk(DBGLVL_API, " TUNER_UNIT\n");
 			tunerunithdr =
-				(tmComResTunerDescrHeader_t *)(buf + idx);
+				(struct tmComResTunerDescrHeader *)(buf + idx);
 			dprintk(DBGLVL_API, "  unitid = 0x%x\n",
 				tunerunithdr->unitid);
 			dprintk(DBGLVL_API, "  sourceid = 0x%x\n",
@@ -297,22 +1126,84 @@
 				tunerunithdr->controlsize);
 			dprintk(DBGLVL_API, "  controls = 0x%x\n",
 				tunerunithdr->controls);
+
+			if (tunerunithdr->unitid == tunerunithdr->iunit) {
+				if (currpath == 1)
+					encport = &dev->ports[SAA7164_PORT_ENC1];
+				else
+					encport = &dev->ports[SAA7164_PORT_ENC2];
+				memcpy(&encport->tunerunit, tunerunithdr,
+					sizeof(struct tmComResTunerDescrHeader));
+				dprintk(DBGLVL_API, "  (becomes dev->enc[%d] tuner)\n", encport->nr);
+			}
 			break;
 		case VC_SELECTOR_UNIT:
+			psel = (struct tmComResSelDescrHeader *)(buf + idx);
 			dprintk(DBGLVL_API, " VC_SELECTOR_UNIT\n");
+			dprintk(DBGLVL_API, "  unitid = 0x%x\n",
+				psel->unitid);
+			dprintk(DBGLVL_API, "  nrinpins = 0x%x\n",
+				psel->nrinpins);
+			dprintk(DBGLVL_API, "  sourceid = 0x%x\n",
+				psel->sourceid);
 			break;
 		case VC_PROCESSING_UNIT:
+			pdh = (struct tmComResProcDescrHeader *)(buf + idx);
 			dprintk(DBGLVL_API, " VC_PROCESSING_UNIT\n");
+			dprintk(DBGLVL_API, "  unitid = 0x%x\n",
+				pdh->unitid);
+			dprintk(DBGLVL_API, "  sourceid = 0x%x\n",
+				pdh->sourceid);
+			dprintk(DBGLVL_API, "  controlsize = 0x%x\n",
+				pdh->controlsize);
+			if (pdh->controlsize == 0x04) {
+				if (currpath == 1)
+					encport = &dev->ports[SAA7164_PORT_ENC1];
+				else
+					encport = &dev->ports[SAA7164_PORT_ENC2];
+				memcpy(&encport->vidproc, pdh,
+					sizeof(struct tmComResProcDescrHeader));
+				dprintk(DBGLVL_API, "  (becomes dev->enc[%d])\n", encport->nr);
+			}
 			break;
 		case FEATURE_UNIT:
+			afd = (struct tmComResAFeatureDescrHeader *)(buf + idx);
 			dprintk(DBGLVL_API, " FEATURE_UNIT\n");
+			dprintk(DBGLVL_API, "  unitid = 0x%x\n",
+				afd->unitid);
+			dprintk(DBGLVL_API, "  sourceid = 0x%x\n",
+				afd->sourceid);
+			dprintk(DBGLVL_API, "  controlsize = 0x%x\n",
+				afd->controlsize);
+			if (currpath == 1)
+				encport = &dev->ports[SAA7164_PORT_ENC1];
+			else
+				encport = &dev->ports[SAA7164_PORT_ENC2];
+			memcpy(&encport->audfeat, afd,
+				sizeof(struct tmComResAFeatureDescrHeader));
+			dprintk(DBGLVL_API, "  (becomes dev->enc[%d])\n", encport->nr);
 			break;
 		case ENCODER_UNIT:
+			edh = (struct tmComResEncoderDescrHeader *)(buf + idx);
 			dprintk(DBGLVL_API, " ENCODER_UNIT\n");
+			dprintk(DBGLVL_API, "  subtype = 0x%x\n", edh->subtype);
+			dprintk(DBGLVL_API, "  unitid = 0x%x\n", edh->unitid);
+			dprintk(DBGLVL_API, "  vsourceid = 0x%x\n", edh->vsourceid);
+			dprintk(DBGLVL_API, "  asourceid = 0x%x\n", edh->asourceid);
+			dprintk(DBGLVL_API, "  iunit = 0x%x\n", edh->iunit);
+			if (edh->iunit == edh->unitid) {
+				if (currpath == 1)
+					encport = &dev->ports[SAA7164_PORT_ENC1];
+				else
+					encport = &dev->ports[SAA7164_PORT_ENC2];
+				memcpy(&encport->encunit, edh,
+					sizeof(struct tmComResEncoderDescrHeader));
+				dprintk(DBGLVL_API, "  (becomes dev->enc[%d])\n", encport->nr);
+			}
 			break;
 		case EXTENSION_UNIT:
 			dprintk(DBGLVL_API, " EXTENSION_UNIT\n");
-			exthdr = (tmComResExtDevDescrHeader_t *)(buf + idx);
+			exthdr = (struct tmComResExtDevDescrHeader *)(buf + idx);
 			dprintk(DBGLVL_API, "  unitid = 0x%x\n",
 				exthdr->unitid);
 			dprintk(DBGLVL_API, "  deviceid = 0x%x\n",
@@ -364,6 +1255,15 @@
 				exthdr->numgpiogroups);
 			dprintk(DBGLVL_API, "  controlsize = 0x%x\n",
 				exthdr->controlsize);
+			if (exthdr->devicetype & 0x80) {
+				if (currpath == 1)
+					encport = &dev->ports[SAA7164_PORT_ENC1];
+				else
+					encport = &dev->ports[SAA7164_PORT_ENC2];
+				memcpy(&encport->ifunit, exthdr,
+					sizeof(struct tmComResExtDevDescrHeader));
+				dprintk(DBGLVL_API, "  (becomes dev->enc[%d])\n", encport->nr);
+			}
 			break;
 		case PVC_INFRARED_UNIT:
 			dprintk(DBGLVL_API, " PVC_INFRARED_UNIT\n");
@@ -560,12 +1460,11 @@
 	return ret == SAA_OK ? 0 : -EIO;
 }
 
-
 int saa7164_api_modify_gpio(struct saa7164_dev *dev, u8 unitid,
 	u8 pin, u8 state)
 {
 	int ret;
-	tmComResGPIO_t t;
+	struct tmComResGPIO t;
 
 	dprintk(DBGLVL_API, "%s(0x%x, %d, %d)\n",
 		__func__, unitid, pin, state);
@@ -597,5 +1496,3 @@
 	return saa7164_api_modify_gpio(dev, unitid, pin, 0);
 }
 
-
-
diff --git a/drivers/media/video/saa7164/saa7164-buffer.c b/drivers/media/video/saa7164/saa7164-buffer.c
index ddd25d3..7230912 100644
--- a/drivers/media/video/saa7164/saa7164-buffer.c
+++ b/drivers/media/video/saa7164/saa7164-buffer.c
@@ -1,7 +1,7 @@
 /*
  *  Driver for the NXP SAA7164 PCIe bridge
  *
- *  Copyright (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -66,12 +66,33 @@
 						    | etc
  */
 
+void saa7164_buffer_display(struct saa7164_buffer *buf)
+{
+	struct saa7164_dev *dev = buf->port->dev;
+	int i;
+
+	dprintk(DBGLVL_BUF, "%s()   buffer @ 0x%p nr=%d\n",
+		__func__, buf, buf->idx);
+	dprintk(DBGLVL_BUF, "  pci_cpu @ 0x%p    dma @ 0x%08llx len = 0x%x\n",
+		buf->cpu, (long long)buf->dma, buf->pci_size);
+	dprintk(DBGLVL_BUF, "   pt_cpu @ 0x%p pt_dma @ 0x%08llx len = 0x%x\n",
+		buf->pt_cpu, (long long)buf->pt_dma, buf->pt_size);
+
+	/* Format the Page Table Entries to point into the data buffer */
+	for (i = 0 ; i < SAA7164_PT_ENTRIES; i++) {
+
+		dprintk(DBGLVL_BUF, "    pt[%02d] = 0x%p -> 0x%llx\n",
+			i, buf->pt_cpu, (u64)*(buf->pt_cpu));
+
+	}
+}
 /* Allocate a new buffer structure and associated PCI space in bytes.
  * len must be a multiple of sizeof(u64)
  */
-struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_tsport *port,
+struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_port *port,
 	u32 len)
 {
+	struct tmHWStreamParameters *params = &port->hw_streamingparams;
 	struct saa7164_buffer *buf = 0;
 	struct saa7164_dev *dev = port->dev;
 	int i;
@@ -87,8 +108,12 @@
 		goto ret;
 	}
 
+	buf->idx = -1;
 	buf->port = port;
 	buf->flags = SAA7164_BUFFER_FREE;
+	buf->pos = 0;
+	buf->actual_size = params->pitch * params->numberoflines;
+	buf->crc = 0;
 	/* TODO: arg len is being ignored */
 	buf->pci_size = SAA7164_PT_ENTRIES * 0x1000;
 	buf->pt_size = (SAA7164_PT_ENTRIES * sizeof(u64)) + 0x1000;
@@ -105,19 +130,23 @@
 		goto fail2;
 
 	/* init the buffers to a known pattern, easier during debugging */
-	memset(buf->cpu, 0xff, buf->pci_size);
-	memset(buf->pt_cpu, 0xff, buf->pt_size);
+	memset_io(buf->cpu, 0xff, buf->pci_size);
+	buf->crc = crc32(0, buf->cpu, buf->actual_size);
+	memset_io(buf->pt_cpu, 0xff, buf->pt_size);
 
-	dprintk(DBGLVL_BUF, "%s()   allocated buffer @ 0x%p\n", __func__, buf);
+	dprintk(DBGLVL_BUF, "%s()   allocated buffer @ 0x%p (%d pageptrs)\n",
+		__func__, buf, params->numpagetables);
 	dprintk(DBGLVL_BUF, "  pci_cpu @ 0x%p    dma @ 0x%08lx len = 0x%x\n",
 		buf->cpu, (long)buf->dma, buf->pci_size);
 	dprintk(DBGLVL_BUF, "   pt_cpu @ 0x%p pt_dma @ 0x%08lx len = 0x%x\n",
 		buf->pt_cpu, (long)buf->pt_dma, buf->pt_size);
 
 	/* Format the Page Table Entries to point into the data buffer */
-	for (i = 0 ; i < SAA7164_PT_ENTRIES; i++) {
+	for (i = 0 ; i < params->numpagetables; i++) {
 
 		*(buf->pt_cpu + i) = buf->dma + (i * 0x1000); /* TODO */
+		dprintk(DBGLVL_BUF, "    pt[%02d] = 0x%p -> 0x%llx\n",
+			i, buf->pt_cpu, (u64)*(buf->pt_cpu));
 
 	}
 
@@ -133,26 +162,163 @@
 	return buf;
 }
 
-int saa7164_buffer_dealloc(struct saa7164_tsport *port,
-	struct saa7164_buffer *buf)
+int saa7164_buffer_dealloc(struct saa7164_buffer *buf)
 {
 	struct saa7164_dev *dev;
 
-	if (!buf || !port)
+	if (!buf || !buf->port)
 		return SAA_ERR_BAD_PARAMETER;
-	dev = port->dev;
+	dev = buf->port->dev;
 
-	dprintk(DBGLVL_BUF, "%s() deallocating buffer @ 0x%p\n", __func__, buf);
+	dprintk(DBGLVL_BUF, "%s() deallocating buffer @ 0x%p\n",
+		__func__, buf);
 
 	if (buf->flags != SAA7164_BUFFER_FREE)
 		log_warn(" freeing a non-free buffer\n");
 
-	pci_free_consistent(port->dev->pci, buf->pci_size, buf->cpu, buf->dma);
-	pci_free_consistent(port->dev->pci, buf->pt_size, buf->pt_cpu,
-		buf->pt_dma);
+	pci_free_consistent(dev->pci, buf->pci_size, buf->cpu, buf->dma);
+	pci_free_consistent(dev->pci, buf->pt_size, buf->pt_cpu, buf->pt_dma);
 
 	kfree(buf);
 
 	return SAA_OK;
 }
 
+int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i)
+{
+	struct saa7164_dev *dev = port->dev;
+
+	if ((i < 0) || (i >= port->hwcfg.buffercount))
+		return -EINVAL;
+
+	dprintk(DBGLVL_BUF, "%s(idx = %d)\n", __func__, i);
+
+	saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0);
+
+	return 0;
+}
+
+/* Write a buffer into the hardware */
+int saa7164_buffer_activate(struct saa7164_buffer *buf, int i)
+{
+	struct saa7164_port *port = buf->port;
+	struct saa7164_dev *dev = port->dev;
+
+	if ((i < 0) || (i >= port->hwcfg.buffercount))
+		return -EINVAL;
+
+	dprintk(DBGLVL_BUF, "%s(idx = %d)\n", __func__, i);
+
+	buf->idx = i; /* Note of which buffer list index position we occupy */
+	buf->flags = SAA7164_BUFFER_BUSY;
+	buf->pos = 0;
+
+	/* TODO: Review this in light of 32v64 assignments */
+	saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0);
+	saa7164_writel(port->bufptr32h + ((sizeof(u32) * 2) * i), buf->pt_dma);
+	saa7164_writel(port->bufptr32l + ((sizeof(u32) * 2) * i), 0);
+
+	dprintk(DBGLVL_BUF, "   buf[%d] offset 0x%llx (0x%x) "
+		"buf 0x%llx/%llx (0x%x/%x) nr=%d\n",
+		buf->idx,
+		(u64)port->bufoffset + (i * sizeof(u32)),
+		saa7164_readl(port->bufoffset + (sizeof(u32) * i)),
+		(u64)port->bufptr32h + ((sizeof(u32) * 2) * i),
+		(u64)port->bufptr32l + ((sizeof(u32) * 2) * i),
+		saa7164_readl(port->bufptr32h + ((sizeof(u32) * i) * 2)),
+		saa7164_readl(port->bufptr32l + ((sizeof(u32) * i) * 2)),
+		buf->idx);
+
+	return 0;
+}
+
+int saa7164_buffer_cfg_port(struct saa7164_port *port)
+{
+	struct tmHWStreamParameters *params = &port->hw_streamingparams;
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct list_head *c, *n;
+	int i = 0;
+
+	dprintk(DBGLVL_BUF, "%s(port=%d)\n", __func__, port->nr);
+
+	saa7164_writel(port->bufcounter, 0);
+	saa7164_writel(port->pitch, params->pitch);
+	saa7164_writel(port->bufsize, params->pitch * params->numberoflines);
+
+	dprintk(DBGLVL_BUF, " configured:\n");
+	dprintk(DBGLVL_BUF, "   lmmio       0x%p\n", dev->lmmio);
+	dprintk(DBGLVL_BUF, "   bufcounter  0x%x = 0x%x\n", port->bufcounter,
+		saa7164_readl(port->bufcounter));
+
+	dprintk(DBGLVL_BUF, "   pitch       0x%x = %d\n", port->pitch,
+		saa7164_readl(port->pitch));
+
+	dprintk(DBGLVL_BUF, "   bufsize     0x%x = %d\n", port->bufsize,
+		saa7164_readl(port->bufsize));
+
+	dprintk(DBGLVL_BUF, "   buffercount = %d\n", port->hwcfg.buffercount);
+	dprintk(DBGLVL_BUF, "   bufoffset = 0x%x\n", port->bufoffset);
+	dprintk(DBGLVL_BUF, "   bufptr32h = 0x%x\n", port->bufptr32h);
+	dprintk(DBGLVL_BUF, "   bufptr32l = 0x%x\n", port->bufptr32l);
+
+	/* Poke the buffers and offsets into PCI space */
+	mutex_lock(&port->dmaqueue_lock);
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+		buf = list_entry(c, struct saa7164_buffer, list);
+
+		if (buf->flags != SAA7164_BUFFER_FREE)
+			BUG();
+
+		/* Place the buffer in the h/w queue */
+		saa7164_buffer_activate(buf, i);
+
+		/* Don't exceed the device maximum # bufs */
+		if (i++ > port->hwcfg.buffercount)
+			BUG();
+
+	}
+	mutex_unlock(&port->dmaqueue_lock);
+
+	return 0;
+}
+
+struct saa7164_user_buffer *saa7164_buffer_alloc_user(struct saa7164_dev *dev, u32 len)
+{
+	struct saa7164_user_buffer *buf;
+
+	buf = kzalloc(sizeof(struct saa7164_user_buffer), GFP_KERNEL);
+	if (buf == 0)
+		return 0;
+
+	buf->data = kzalloc(len, GFP_KERNEL);
+
+	if (buf->data == 0) {
+		kfree(buf);
+		return 0;
+	}
+
+	buf->actual_size = len;
+	buf->pos = 0;
+	buf->crc = 0;
+
+	dprintk(DBGLVL_BUF, "%s()   allocated user buffer @ 0x%p\n",
+		__func__, buf);
+
+	return buf;
+}
+
+void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf)
+{
+	if (!buf)
+		return;
+
+	if (buf->data) {
+		kfree(buf->data);
+		buf->data = 0;
+	}
+
+	if (buf)
+		kfree(buf);
+}
+
diff --git a/drivers/media/video/saa7164/saa7164-bus.c b/drivers/media/video/saa7164/saa7164-bus.c
index 83a0464..30d5283 100644
--- a/drivers/media/video/saa7164/saa7164-bus.c
+++ b/drivers/media/video/saa7164/saa7164-bus.c
@@ -1,7 +1,7 @@
 /*
  *  Driver for the NXP SAA7164 PCIe bridge
  *
- *  Copyright (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@
  */
 int saa7164_bus_setup(struct saa7164_dev *dev)
 {
-	tmComResBusInfo_t *b	= &dev->bus;
+	struct tmComResBusInfo *b	= &dev->bus;
 
 	mutex_init(&b->lock);
 
@@ -43,24 +43,18 @@
 
 	b->m_dwSizeGetRing	= SAA_DEVICE_BUFFERBLOCKSIZE;
 
-	b->m_pdwSetWritePos	= (u32 *)((u8 *)(dev->bmmio +
-		((u32)dev->intfdesc.BARLocation) + (2 * sizeof(u64))));
+	b->m_dwSetWritePos	= ((u32)dev->intfdesc.BARLocation) + (2 * sizeof(u64));
+	b->m_dwSetReadPos	= b->m_dwSetWritePos + (1 * sizeof(u32));
 
-	b->m_pdwSetReadPos	= (u32 *)((u8 *)b->m_pdwSetWritePos +
-		1 * sizeof(u32));
-
-	b->m_pdwGetWritePos	= (u32 *)((u8 *)b->m_pdwSetWritePos +
-		2 * sizeof(u32));
-
-	b->m_pdwGetReadPos	= (u32 *)((u8 *)b->m_pdwSetWritePos +
-		3 * sizeof(u32));
+	b->m_dwGetWritePos	= b->m_dwSetWritePos + (2 * sizeof(u32));
+	b->m_dwGetReadPos	= b->m_dwSetWritePos + (3 * sizeof(u32));
 
 	return 0;
 }
 
 void saa7164_bus_dump(struct saa7164_dev *dev)
 {
-	tmComResBusInfo_t *b = &dev->bus;
+	struct tmComResBusInfo *b = &dev->bus;
 
 	dprintk(DBGLVL_BUS, "Dumping the bus structure:\n");
 	dprintk(DBGLVL_BUS, " .type             = %d\n", b->Type);
@@ -71,20 +65,47 @@
 	dprintk(DBGLVL_BUS, " .m_pdwGetRing     = 0x%p\n", b->m_pdwGetRing);
 	dprintk(DBGLVL_BUS, " .m_dwSizeGetRing  = 0x%x\n", b->m_dwSizeGetRing);
 
-	dprintk(DBGLVL_BUS, " .m_pdwSetWritePos = 0x%p (0x%08x)\n",
-		b->m_pdwSetWritePos, *b->m_pdwSetWritePos);
+	dprintk(DBGLVL_BUS, " .m_dwSetReadPos   = 0x%x (0x%08x)\n",
+		b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos));
 
-	dprintk(DBGLVL_BUS, " .m_pdwSetReadPos  = 0x%p (0x%08x)\n",
-		b->m_pdwSetReadPos, *b->m_pdwSetReadPos);
+	dprintk(DBGLVL_BUS, " .m_dwSetWritePos  = 0x%x (0x%08x)\n",
+		b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos));
 
-	dprintk(DBGLVL_BUS, " .m_pdwGetWritePos = 0x%p (0x%08x)\n",
-		b->m_pdwGetWritePos, *b->m_pdwGetWritePos);
+	dprintk(DBGLVL_BUS, " .m_dwGetReadPos   = 0x%x (0x%08x)\n",
+		b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos));
 
-	dprintk(DBGLVL_BUS, " .m_pdwGetReadPos  = 0x%p (0x%08x)\n",
-		b->m_pdwGetReadPos, *b->m_pdwGetReadPos);
+	dprintk(DBGLVL_BUS, " .m_dwGetWritePos  = 0x%x (0x%08x)\n",
+		b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos));
+
 }
 
-void saa7164_bus_dumpmsg(struct saa7164_dev *dev, tmComResInfo_t* m, void *buf)
+/* Intensionally throw a BUG() if the state of the message bus looks corrupt */
+void saa7164_bus_verify(struct saa7164_dev *dev)
+{
+	struct tmComResBusInfo *b = &dev->bus;
+	int bug = 0;
+
+	if (saa7164_readl(b->m_dwSetReadPos) > b->m_dwSizeSetRing)
+		bug++;
+
+	if (saa7164_readl(b->m_dwSetWritePos) > b->m_dwSizeSetRing)
+		bug++;
+
+	if (saa7164_readl(b->m_dwGetReadPos) > b->m_dwSizeGetRing)
+		bug++;
+
+	if (saa7164_readl(b->m_dwGetWritePos) > b->m_dwSizeGetRing)
+		bug++;
+
+	if (bug) {
+		saa_debug = 0xffff; /* Ensure we get the bus dump */
+		saa7164_bus_dump(dev);
+		saa_debug = 1024; /* Ensure we get the bus dump */
+		BUG();
+	}
+}
+
+void saa7164_bus_dumpmsg(struct saa7164_dev *dev, struct tmComResInfo* m, void *buf)
 {
 	dprintk(DBGLVL_BUS, "Dumping msg structure:\n");
 	dprintk(DBGLVL_BUS, " .id               = %d\n",   m->id);
@@ -100,7 +121,7 @@
 /*
  * Places a command or a response on the bus. The implementation does not
  * know if it is a command or a response it just places the data on the
- * bus depending on the bus information given in the tmComResBusInfo_t
+ * bus depending on the bus information given in the struct tmComResBusInfo
  * structure. If the command or response does not fit into the bus ring
  * buffer it will be refused.
  *
@@ -108,10 +129,10 @@
  *  SAA_OK     The function executed successfully.
  *  < 0        One or more members are not initialized.
  */
-int saa7164_bus_set(struct saa7164_dev *dev, tmComResInfo_t* msg, void *buf)
+int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg, void *buf)
 {
-	tmComResBusInfo_t *bus = &dev->bus;
-	u32 bytes_to_write, read_distance, timeout, curr_srp, curr_swp;
+	struct tmComResBusInfo *bus = &dev->bus;
+	u32 bytes_to_write, free_write_space, timeout, curr_srp, curr_swp;
 	u32 new_swp, space_rem;
 	int ret = SAA_ERR_BAD_PARAMETER;
 
@@ -122,6 +143,8 @@
 
 	dprintk(DBGLVL_BUS, "%s()\n", __func__);
 
+	saa7164_bus_verify(dev);
+
 	msg->size = cpu_to_le16(msg->size);
 	msg->command = cpu_to_le16(msg->command);
 	msg->controlselector = cpu_to_le16(msg->controlselector);
@@ -141,30 +164,30 @@
 	mutex_lock(&bus->lock);
 
 	bytes_to_write = sizeof(*msg) + msg->size;
-	read_distance = 0;
+	free_write_space = 0;
 	timeout = SAA_BUS_TIMEOUT;
-	curr_srp = le32_to_cpu(*bus->m_pdwSetReadPos);
-	curr_swp = le32_to_cpu(*bus->m_pdwSetWritePos);
+	curr_srp = le32_to_cpu(saa7164_readl(bus->m_dwSetReadPos));
+	curr_swp = le32_to_cpu(saa7164_readl(bus->m_dwSetWritePos));
 
 	/* Deal with ring wrapping issues */
 	if (curr_srp > curr_swp)
-		/* The ring has not wrapped yet */
-		read_distance = curr_srp - curr_swp;
-	else
 		/* Deal with the wrapped ring */
-		read_distance = (curr_srp + bus->m_dwSizeSetRing) - curr_swp;
+		free_write_space = curr_srp - curr_swp;
+	else
+		/* The ring has not wrapped yet */
+		free_write_space = (curr_srp + bus->m_dwSizeSetRing) - curr_swp;
 
 	dprintk(DBGLVL_BUS, "%s() bytes_to_write = %d\n", __func__,
 		bytes_to_write);
 
-	dprintk(DBGLVL_BUS, "%s() read_distance = %d\n", __func__,
-		read_distance);
+	dprintk(DBGLVL_BUS, "%s() free_write_space = %d\n", __func__,
+		free_write_space);
 
 	dprintk(DBGLVL_BUS, "%s() curr_srp = %x\n", __func__, curr_srp);
 	dprintk(DBGLVL_BUS, "%s() curr_swp = %x\n", __func__, curr_swp);
 
 	/* Process the msg and write the content onto the bus */
-	while (bytes_to_write >= read_distance) {
+	while (bytes_to_write >= free_write_space) {
 
 		if (timeout-- == 0) {
 			printk(KERN_ERR "%s() bus timeout\n", __func__);
@@ -177,15 +200,15 @@
 		mdelay(1);
 
 		/* Check the space usage again */
-		curr_srp = le32_to_cpu(*bus->m_pdwSetReadPos);
+		curr_srp = le32_to_cpu(saa7164_readl(bus->m_dwSetReadPos));
 
 		/* Deal with ring wrapping issues */
 		if (curr_srp > curr_swp)
-			/* Read didn't wrap around the buffer */
-			read_distance = curr_srp - curr_swp;
-		else
 			/* Deal with the wrapped ring */
-			read_distance = (curr_srp + bus->m_dwSizeSetRing) -
+			free_write_space = curr_srp - curr_swp;
+		else
+			/* Read didn't wrap around the buffer */
+			free_write_space = (curr_srp + bus->m_dwSizeSetRing) -
 				curr_swp;
 
 	}
@@ -257,37 +280,37 @@
 
 	dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp);
 
-	/* TODO: Convert all of the direct PCI writes into
-	 * saa7164_writel/b calls for consistency.
-	 */
-
 	/* Update the bus write position */
-	*bus->m_pdwSetWritePos = cpu_to_le32(new_swp);
+	saa7164_writel(bus->m_dwSetWritePos, cpu_to_le32(new_swp));
 	ret = SAA_OK;
 
 out:
+	saa7164_bus_dump(dev);
 	mutex_unlock(&bus->lock);
+	saa7164_bus_verify(dev);
 	return ret;
 }
 
 /*
  * Receive a command or a response from the bus. The implementation does not
  * know if it is a command or a response it simply dequeues the data,
- * depending on the bus information given in the tmComResBusInfo_t structure.
+ * depending on the bus information given in the struct tmComResBusInfo structure.
  *
  * Return Value:
  *  0          The function executed successfully.
  *  < 0        One or more members are not initialized.
  */
-int saa7164_bus_get(struct saa7164_dev *dev, tmComResInfo_t* msg, void *buf,
+int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg, void *buf,
 	int peekonly)
 {
-	tmComResBusInfo_t *bus = &dev->bus;
+	struct tmComResBusInfo *bus = &dev->bus;
 	u32 bytes_to_read, write_distance, curr_grp, curr_gwp,
 		new_grp, buf_size, space_rem;
-	tmComResInfo_t msg_tmp;
+	struct tmComResInfo msg_tmp;
 	int ret = SAA_ERR_BAD_PARAMETER;
 
+	saa7164_bus_verify(dev);
+
 	if (msg == 0)
 		return ret;
 
@@ -309,11 +332,10 @@
 	/* Peek the bus to see if a msg exists, if it's not what we're expecting
 	 * then return cleanly else read the message from the bus.
 	 */
-	curr_gwp = le32_to_cpu(*bus->m_pdwGetWritePos);
-	curr_grp = le32_to_cpu(*bus->m_pdwGetReadPos);
+	curr_gwp = le32_to_cpu(saa7164_readl(bus->m_dwGetWritePos));
+	curr_grp = le32_to_cpu(saa7164_readl(bus->m_dwGetReadPos));
 
 	if (curr_gwp == curr_grp) {
-		dprintk(DBGLVL_BUS, "%s() No message on the bus\n", __func__);
 		ret = SAA_ERR_EMPTY;
 		goto out;
 	}
@@ -434,7 +456,7 @@
 	}
 
 	/* Update the read positions, adjusting the ring */
-	*bus->m_pdwGetReadPos = cpu_to_le32(new_grp);
+	saa7164_writel(bus->m_dwGetReadPos, cpu_to_le32(new_grp));
 
 peekout:
 	msg->size = le16_to_cpu(msg->size);
@@ -443,6 +465,7 @@
 	ret = SAA_OK;
 out:
 	mutex_unlock(&bus->lock);
+	saa7164_bus_verify(dev);
 	return ret;
 }
 
diff --git a/drivers/media/video/saa7164/saa7164-cards.c b/drivers/media/video/saa7164/saa7164-cards.c
index a3c2994..4cb634e 100644
--- a/drivers/media/video/saa7164/saa7164-cards.c
+++ b/drivers/media/video/saa7164/saa7164-cards.c
@@ -1,7 +1,7 @@
 /*
  *  Driver for the NXP SAA7164 PCIe bridge
  *
- *  Copyright (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -55,6 +55,10 @@
 		.name		= "Hauppauge WinTV-HVR2200",
 		.porta		= SAA7164_MPEG_DVB,
 		.portb		= SAA7164_MPEG_DVB,
+		.portc		= SAA7164_MPEG_ENCODER,
+		.portd		= SAA7164_MPEG_ENCODER,
+		.porte		= SAA7164_MPEG_VBI,
+		.portf		= SAA7164_MPEG_VBI,
 		.chiprev	= SAA7164_CHIP_REV3,
 		.unit		= {{
 			.id		= 0x1d,
@@ -97,6 +101,10 @@
 		.name		= "Hauppauge WinTV-HVR2200",
 		.porta		= SAA7164_MPEG_DVB,
 		.portb		= SAA7164_MPEG_DVB,
+		.portc		= SAA7164_MPEG_ENCODER,
+		.portd		= SAA7164_MPEG_ENCODER,
+		.porte		= SAA7164_MPEG_VBI,
+		.portf		= SAA7164_MPEG_VBI,
 		.chiprev	= SAA7164_CHIP_REV2,
 		.unit		= {{
 			.id		= 0x06,
@@ -139,6 +147,10 @@
 		.name		= "Hauppauge WinTV-HVR2200",
 		.porta		= SAA7164_MPEG_DVB,
 		.portb		= SAA7164_MPEG_DVB,
+		.portc		= SAA7164_MPEG_ENCODER,
+		.portd		= SAA7164_MPEG_ENCODER,
+		.porte		= SAA7164_MPEG_VBI,
+		.portf		= SAA7164_MPEG_VBI,
 		.chiprev	= SAA7164_CHIP_REV2,
 		.unit		= {{
 			.id		= 0x1d,
@@ -195,6 +207,12 @@
 		.name		= "Hauppauge WinTV-HVR2250",
 		.porta		= SAA7164_MPEG_DVB,
 		.portb		= SAA7164_MPEG_DVB,
+		.portc		= SAA7164_MPEG_ENCODER,
+		.portd		= SAA7164_MPEG_ENCODER,
+		.portc		= SAA7164_MPEG_ENCODER,
+		.portd		= SAA7164_MPEG_ENCODER,
+		.porte		= SAA7164_MPEG_VBI,
+		.portf		= SAA7164_MPEG_VBI,
 		.chiprev	= SAA7164_CHIP_REV3,
 		.unit		= {{
 			.id		= 0x22,
@@ -251,6 +269,12 @@
 		.name		= "Hauppauge WinTV-HVR2250",
 		.porta		= SAA7164_MPEG_DVB,
 		.portb		= SAA7164_MPEG_DVB,
+		.portc		= SAA7164_MPEG_ENCODER,
+		.portd		= SAA7164_MPEG_ENCODER,
+		.porte		= SAA7164_MPEG_VBI,
+		.portf		= SAA7164_MPEG_VBI,
+		.porte		= SAA7164_MPEG_VBI,
+		.portf		= SAA7164_MPEG_VBI,
 		.chiprev	= SAA7164_CHIP_REV3,
 		.unit		= {{
 			.id		= 0x28,
@@ -307,6 +331,10 @@
 		.name		= "Hauppauge WinTV-HVR2250",
 		.porta		= SAA7164_MPEG_DVB,
 		.portb		= SAA7164_MPEG_DVB,
+		.portc		= SAA7164_MPEG_ENCODER,
+		.portd		= SAA7164_MPEG_ENCODER,
+		.porte		= SAA7164_MPEG_VBI,
+		.portf		= SAA7164_MPEG_VBI,
 		.chiprev	= SAA7164_CHIP_REV3,
 		.unit		= {{
 			.id		= 0x26,
@@ -437,8 +465,6 @@
 
 void saa7164_gpio_setup(struct saa7164_dev *dev)
 {
-
-
 	switch (dev->board) {
 	case SAA7164_BOARD_HAUPPAUGE_HVR2200:
 	case SAA7164_BOARD_HAUPPAUGE_HVR2200_2:
@@ -462,7 +488,6 @@
 		saa7164_api_set_gpiobit(dev, PCIEBRIDGE_UNITID, 3);
 		break;
 	}
-
 }
 
 static void hauppauge_eeprom(struct saa7164_dev *dev, u8 *eeprom_data)
diff --git a/drivers/media/video/saa7164/saa7164-cmd.c b/drivers/media/video/saa7164/saa7164-cmd.c
index 9c1d3ac..301a9e3 100644
--- a/drivers/media/video/saa7164/saa7164-cmd.c
+++ b/drivers/media/video/saa7164/saa7164-cmd.c
@@ -1,7 +1,7 @@
 /*
  *  Driver for the NXP SAA7164 PCIe bridge
  *
- *  Copyright (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -82,16 +82,17 @@
  * -bus/c running buffer. */
 int saa7164_irq_dequeue(struct saa7164_dev *dev)
 {
-	int ret = SAA_OK;
+	int ret = SAA_OK, i = 0;
 	u32 timeout;
 	wait_queue_head_t *q = 0;
+	u8 tmp[512];
 	dprintk(DBGLVL_CMD, "%s()\n", __func__);
 
 	/* While any outstand message on the bus exists... */
 	do {
 
 		/* Peek the msg bus */
-		tmComResInfo_t tRsp = { 0, 0, 0, 0, 0, 0 };
+		struct tmComResInfo tRsp = { 0, 0, 0, 0, 0, 0 };
 		ret = saa7164_bus_get(dev, &tRsp, NULL, 1);
 		if (ret != SAA_OK)
 			break;
@@ -109,8 +110,22 @@
 			printk(KERN_ERR
 				"%s() found timed out command on the bus\n",
 					__func__);
+
+			/* Clean the bus */
+			ret = saa7164_bus_get(dev, &tRsp, &tmp, 0);
+			printk(KERN_ERR "%s() ret = %x\n", __func__, ret);
+			if (ret == SAA_ERR_EMPTY)
+				/* Someone else already fetched the response */
+				return SAA_OK;
+
+			if (ret != SAA_OK)
+				return ret;
 		}
-	} while (0);
+
+		/* It's unlikely to have more than 4 or 5 pending messages, ensure we exit
+		 * at some point regardles.
+		 */
+	} while (i++ < 32);
 
 	return ret;
 }
@@ -128,7 +143,7 @@
 
 	while (loop) {
 
-		tmComResInfo_t tRsp = { 0, 0, 0, 0, 0, 0 };
+		struct tmComResInfo tRsp = { 0, 0, 0, 0, 0, 0 };
 		ret = saa7164_bus_get(dev, &tRsp, NULL, 1);
 		if (ret == SAA_ERR_EMPTY)
 			return SAA_OK;
@@ -171,9 +186,9 @@
 	return SAA_OK;
 }
 
-int saa7164_cmd_set(struct saa7164_dev *dev, tmComResInfo_t* msg, void *buf)
+int saa7164_cmd_set(struct saa7164_dev *dev, struct tmComResInfo* msg, void *buf)
 {
-	tmComResBusInfo_t *bus = &dev->bus;
+	struct tmComResBusInfo *bus = &dev->bus;
 	u8 cmd_sent;
 	u16 size, idx;
 	u32 cmds;
@@ -324,11 +339,11 @@
 	mutex_unlock(&dev->lock);
 }
 
-int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, tmComResCmd_t command,
+int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, enum tmComResCmd command,
 	u16 controlselector, u16 size, void *buf)
 {
-	tmComResInfo_t command_t, *pcommand_t;
-	tmComResInfo_t response_t, *presponse_t;
+	struct tmComResInfo command_t, *pcommand_t;
+	struct tmComResInfo response_t, *presponse_t;
 	u8 errdata[256];
 	u16 resp_dsize;
 	u16 data_recd;
diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c
index e6aa0fb..e1bac50 100644
--- a/drivers/media/video/saa7164/saa7164-core.c
+++ b/drivers/media/video/saa7164/saa7164-core.c
@@ -1,7 +1,7 @@
 /*
  *  Driver for the NXP SAA7164 PCIe bridge
  *
- *  Copyright (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -30,6 +30,9 @@
 #include <linux/delay.h>
 #include <asm/div64.h>
 
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
 #include "saa7164.h"
 
 MODULE_DESCRIPTION("Driver for NXP SAA7164 based TV cards");
@@ -49,14 +52,38 @@
 module_param_named(debug, saa_debug, int, 0644);
 MODULE_PARM_DESC(debug, "enable debug messages");
 
+unsigned int fw_debug;
+module_param(fw_debug, int, 0644);
+MODULE_PARM_DESC(fw_debug, "Firware debug level def:2");
+
+unsigned int encoder_buffers = SAA7164_MAX_ENCODER_BUFFERS;
+module_param(encoder_buffers, int, 0644);
+MODULE_PARM_DESC(encoder_buffers, "Total buffers in read queue 16-512 def:64");
+
+unsigned int vbi_buffers = SAA7164_MAX_VBI_BUFFERS;
+module_param(vbi_buffers, int, 0644);
+MODULE_PARM_DESC(vbi_buffers, "Total buffers in read queue 16-512 def:64");
+
 unsigned int waitsecs = 10;
 module_param(waitsecs, int, 0644);
-MODULE_PARM_DESC(debug, "timeout on firmware messages");
+MODULE_PARM_DESC(waitsecs, "timeout on firmware messages");
 
 static unsigned int card[]  = {[0 ... (SAA7164_MAXBOARDS - 1)] = UNSET };
 module_param_array(card,  int, NULL, 0444);
 MODULE_PARM_DESC(card, "card type");
 
+unsigned int print_histogram = 64;
+module_param(print_histogram, int, 0644);
+MODULE_PARM_DESC(print_histogram, "print histogram values once");
+
+unsigned int crc_checking = 1;
+module_param(crc_checking, int, 0644);
+MODULE_PARM_DESC(crc_checking, "enable crc sanity checking on buffers");
+
+unsigned int guard_checking = 1;
+module_param(guard_checking, int, 0644);
+MODULE_PARM_DESC(guard_checking, "enable dma sanity checking for buffer overruns");
+
 static unsigned int saa7164_devcount;
 
 static DEFINE_MUTEX(devlist);
@@ -64,6 +91,444 @@
 
 #define INT_SIZE 16
 
+void saa7164_dumphex16FF(struct saa7164_dev *dev, u8 *buf, int len)
+{
+	int i;
+	u8 tmp[16];
+	memset(&tmp[0], 0xff, sizeof(tmp));
+
+	printk(KERN_INFO "--------------------> "
+		"00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+
+	for (i = 0; i < len; i += 16) {
+		if (memcmp(&tmp, buf + i, sizeof(tmp)) != 0) {
+			printk(KERN_INFO "         [0x%08x] "
+				"%02x %02x %02x %02x %02x %02x %02x %02x "
+				"%02x %02x %02x %02x %02x %02x %02x %02x\n", i,
+			*(buf+i+0), *(buf+i+1), *(buf+i+2), *(buf+i+3),
+			*(buf+i+4), *(buf+i+5), *(buf+i+6), *(buf+i+7),
+			*(buf+i+8), *(buf+i+9), *(buf+i+10), *(buf+i+11),
+			*(buf+i+12), *(buf+i+13), *(buf+i+14), *(buf+i+15));
+		}
+	}
+}
+
+static void saa7164_pack_verifier(struct saa7164_buffer *buf)
+{
+	u8 *p = (u8 *)buf->cpu;
+	int i;
+
+	for (i = 0; i < buf->actual_size; i += 2048) {
+
+		if ((*(p + i + 0) != 0x00) || (*(p + i + 1) != 0x00) ||
+			(*(p + i + 2) != 0x01) || (*(p + i + 3) != 0xBA)) {
+			printk(KERN_ERR "No pack at 0x%x\n", i);
+//			saa7164_dumphex16FF(buf->port->dev, (p + i), 32);
+		}
+	}
+}
+
+#define FIXED_VIDEO_PID 0xf1
+#define FIXED_AUDIO_PID 0xf2
+
+static void saa7164_ts_verifier(struct saa7164_buffer *buf)
+{
+	struct saa7164_port *port = buf->port;
+	u32 i;
+	u8 cc, a;
+	u16 pid;
+	u8 __iomem *bufcpu = (u8 *)buf->cpu;
+
+	port->sync_errors = 0;
+	port->v_cc_errors = 0;
+	port->a_cc_errors = 0;
+
+	for (i = 0; i < buf->actual_size; i += 188) {
+		if (*(bufcpu + i) != 0x47)
+			port->sync_errors++;
+
+		/* TODO: Query pid lower 8 bits, ignoring upper bits intensionally */
+		pid = ((*(bufcpu + i + 1) & 0x1f) << 8) | *(bufcpu + i + 2);
+		cc = *(bufcpu + i + 3) & 0x0f;
+
+		if (pid == FIXED_VIDEO_PID) {
+			a = ((port->last_v_cc + 1) & 0x0f);
+			if (a != cc) {
+				printk(KERN_ERR "video cc last = %x current = %x i = %d\n",
+					port->last_v_cc, cc, i);
+				port->v_cc_errors++;
+			}
+
+			port->last_v_cc = cc;
+		} else
+		if (pid == FIXED_AUDIO_PID) {
+			a = ((port->last_a_cc + 1) & 0x0f);
+			if (a != cc) {
+				printk(KERN_ERR "audio cc last = %x current = %x i = %d\n",
+					port->last_a_cc, cc, i);
+				port->a_cc_errors++;
+			}
+
+			port->last_a_cc = cc;
+		}
+
+	}
+
+	/* Only report errors if we've been through this function atleast
+	 * once already and the cached cc values are primed. First time through
+	 * always generates errors.
+	 */
+	if (port->v_cc_errors && (port->done_first_interrupt > 1))
+		printk(KERN_ERR "video pid cc, %d errors\n", port->v_cc_errors);
+
+	if (port->a_cc_errors && (port->done_first_interrupt > 1))
+		printk(KERN_ERR "audio pid cc, %d errors\n", port->a_cc_errors);
+
+	if (port->sync_errors && (port->done_first_interrupt > 1))
+		printk(KERN_ERR "sync_errors = %d\n", port->sync_errors);
+
+	if (port->done_first_interrupt == 1)
+		port->done_first_interrupt++;
+}
+
+static void saa7164_histogram_reset(struct saa7164_histogram *hg, char *name)
+{
+	int i;
+
+	memset(hg, 0, sizeof(struct saa7164_histogram));
+	strcpy(hg->name, name);
+
+	/* First 30ms x 1ms */
+	for (i = 0; i < 30; i++) {
+		hg->counter1[0 + i].val = i;
+	}
+
+	/* 30 - 200ms x 10ms  */
+	for (i = 0; i < 18; i++) {
+		hg->counter1[30 + i].val = 30 + (i * 10);
+	}
+
+	/* 200 - 2000ms x 100ms  */
+	for (i = 0; i < 15; i++) {
+		hg->counter1[48 + i].val = 200 + (i * 200);
+	}
+
+	/* Catch all massive value (2secs) */
+	hg->counter1[55].val = 2000;
+
+	/* Catch all massive value (4secs) */
+	hg->counter1[56].val = 4000;
+
+	/* Catch all massive value (8secs) */
+	hg->counter1[57].val = 8000;
+
+	/* Catch all massive value (15secs) */
+	hg->counter1[58].val = 15000;
+
+	/* Catch all massive value (30secs) */
+	hg->counter1[59].val = 30000;
+
+	/* Catch all massive value (60secs) */
+	hg->counter1[60].val = 60000;
+
+	/* Catch all massive value (5mins) */
+	hg->counter1[61].val = 300000;
+
+	/* Catch all massive value (15mins) */
+	hg->counter1[62].val = 900000;
+
+	/* Catch all massive values (1hr) */
+	hg->counter1[63].val = 3600000;
+}
+
+void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val)
+{
+	int i;
+	for (i = 0; i < 64; i++) {
+		if (val <= hg->counter1[i].val) {
+			hg->counter1[i].count++;
+			hg->counter1[i].update_time = jiffies;
+			break;
+		}
+	}
+}
+
+static void saa7164_histogram_print(struct saa7164_port *port,
+	struct saa7164_histogram *hg)
+{
+	u32 entries = 0;
+	int i;
+
+	printk(KERN_ERR "Histogram named %s (ms, count, last_update_jiffy)\n", hg->name);
+	for (i = 0; i < 64; i++) {
+		if (hg->counter1[i].count == 0)
+			continue;
+
+		printk(KERN_ERR " %4d %12d %Ld\n",
+			hg->counter1[i].val,
+			hg->counter1[i].count,
+			hg->counter1[i].update_time);
+
+		entries++;
+	}
+	printk(KERN_ERR "Total: %d\n", entries);
+}
+
+static void saa7164_work_enchandler_helper(struct saa7164_port *port, int bufnr)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf = 0;
+	struct saa7164_user_buffer *ubuf = 0;
+	struct list_head *c, *n;
+	int i = 0;
+	u8 __iomem *p;
+
+	mutex_lock(&port->dmaqueue_lock);
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+
+		buf = list_entry(c, struct saa7164_buffer, list);
+		if (i++ > port->hwcfg.buffercount) {
+			printk(KERN_ERR "%s() illegal i count %d\n",
+				__func__, i);
+			break;
+		}
+
+		if (buf->idx == bufnr) {
+
+			/* Found the buffer, deal with it */
+			dprintk(DBGLVL_IRQ, "%s() bufnr: %d\n", __func__, bufnr);
+
+			if (crc_checking) {
+				/* Throw a new checksum on the dma buffer */
+				buf->crc = crc32(0, buf->cpu, buf->actual_size);
+			}
+
+			if (guard_checking) {
+				p = (u8 *)buf->cpu;
+				if ((*(p + buf->actual_size + 0) != 0xff) ||
+					(*(p + buf->actual_size + 1) != 0xff) ||
+					(*(p + buf->actual_size + 2) != 0xff) ||
+					(*(p + buf->actual_size + 3) != 0xff) ||
+					(*(p + buf->actual_size + 0x10) != 0xff) ||
+					(*(p + buf->actual_size + 0x11) != 0xff) ||
+					(*(p + buf->actual_size + 0x12) != 0xff) ||
+					(*(p + buf->actual_size + 0x13) != 0xff)) {
+						printk(KERN_ERR "%s() buf %p guard buffer breach\n",
+							__func__, buf);
+//						saa7164_dumphex16FF(dev, (p + buf->actual_size) - 32 , 64);
+				}
+			}
+
+			if ((port->nr != SAA7164_PORT_VBI1) && (port->nr != SAA7164_PORT_VBI2)) {
+				/* Validate the incoming buffer content */
+				if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
+					saa7164_ts_verifier(buf);
+				else if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS)
+					saa7164_pack_verifier(buf);
+			}
+
+			/* find a free user buffer and clone to it */
+			if (!list_empty(&port->list_buf_free.list)) {
+
+				/* Pull the first buffer from the used list */
+				ubuf = list_first_entry(&port->list_buf_free.list,
+					struct saa7164_user_buffer, list);
+
+				if (buf->actual_size <= ubuf->actual_size) {
+
+					memcpy_fromio(ubuf->data, buf->cpu,
+						ubuf->actual_size);
+
+					if (crc_checking) {
+						/* Throw a new checksum on the read buffer */
+						ubuf->crc = crc32(0, ubuf->data, ubuf->actual_size);
+					}
+
+					/* Requeue the buffer on the free list */
+					ubuf->pos = 0;
+
+					list_move_tail(&ubuf->list,
+						&port->list_buf_used.list);
+
+					/* Flag any userland waiters */
+					wake_up_interruptible(&port->wait_read);
+
+				} else {
+					printk(KERN_ERR "buf %p bufsize fails match\n", buf);
+				}
+
+			} else
+				printk(KERN_ERR "encirq no free buffers, increase param encoder_buffers\n");
+
+			/* Ensure offset into buffer remains 0, fill buffer
+			 * with known bad data. We check for this data at a later point
+			 * in time. */
+			saa7164_buffer_zero_offsets(port, bufnr);
+			memset_io(buf->cpu, 0xff, buf->pci_size);
+			if (crc_checking) {
+				/* Throw yet aanother new checksum on the dma buffer */
+				buf->crc = crc32(0, buf->cpu, buf->actual_size);
+			}
+
+			break;
+		}
+	}
+	mutex_unlock(&port->dmaqueue_lock);
+}
+
+static void saa7164_work_enchandler(struct work_struct *w)
+{
+	struct saa7164_port *port =
+		container_of(w, struct saa7164_port, workenc);
+	struct saa7164_dev *dev = port->dev;
+
+	u32 wp, mcb, rp, cnt = 0;
+
+	port->last_svc_msecs_diff = port->last_svc_msecs;
+	port->last_svc_msecs = jiffies_to_msecs(jiffies);
+
+	port->last_svc_msecs_diff = port->last_svc_msecs -
+		port->last_svc_msecs_diff;
+
+	saa7164_histogram_update(&port->svc_interval,
+		port->last_svc_msecs_diff);
+
+	port->last_irq_svc_msecs_diff = port->last_svc_msecs -
+		port->last_irq_msecs;
+
+	saa7164_histogram_update(&port->irq_svc_interval,
+		port->last_irq_svc_msecs_diff);
+
+	dprintk(DBGLVL_IRQ,
+		"%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n",
+		__func__,
+		port->last_svc_msecs_diff,
+		port->last_irq_svc_msecs_diff,
+		port->last_svc_wp,
+		port->last_svc_rp
+		);
+
+	/* Current write position */
+	wp = saa7164_readl(port->bufcounter);
+	if (wp > (port->hwcfg.buffercount - 1)) {
+		printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp);
+		return;
+	}
+
+	/* Most current complete buffer */
+	if (wp == 0)
+		mcb = (port->hwcfg.buffercount - 1);
+	else
+		mcb = wp - 1;
+
+	while (1) {
+		if (port->done_first_interrupt == 0) {
+			port->done_first_interrupt++;
+			rp = mcb;
+		} else
+			rp = (port->last_svc_rp + 1) % 8;
+
+		if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) {
+			printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
+			break;
+		}
+
+		saa7164_work_enchandler_helper(port, rp);
+		port->last_svc_rp = rp;
+		cnt++;
+
+		if (rp == mcb)
+			break;
+	}
+
+	/* TODO: Convert this into a /proc/saa7164 style readable file */
+	if (print_histogram == port->nr) {
+		saa7164_histogram_print(port, &port->irq_interval);
+		saa7164_histogram_print(port, &port->svc_interval);
+		saa7164_histogram_print(port, &port->irq_svc_interval);
+		saa7164_histogram_print(port, &port->read_interval);
+		saa7164_histogram_print(port, &port->poll_interval);
+		/* TODO: fix this to preserve any previous state */
+		print_histogram = 64 + port->nr;
+	}
+}
+
+static void saa7164_work_vbihandler(struct work_struct *w)
+{
+	struct saa7164_port *port =
+		container_of(w, struct saa7164_port, workenc);
+	struct saa7164_dev *dev = port->dev;
+
+	u32 wp, mcb, rp, cnt = 0;
+
+	port->last_svc_msecs_diff = port->last_svc_msecs;
+	port->last_svc_msecs = jiffies_to_msecs(jiffies);
+	port->last_svc_msecs_diff = port->last_svc_msecs -
+		port->last_svc_msecs_diff;
+
+	saa7164_histogram_update(&port->svc_interval,
+		port->last_svc_msecs_diff);
+
+	port->last_irq_svc_msecs_diff = port->last_svc_msecs -
+		port->last_irq_msecs;
+
+	saa7164_histogram_update(&port->irq_svc_interval,
+		port->last_irq_svc_msecs_diff);
+
+	dprintk(DBGLVL_IRQ,
+		"%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n",
+		__func__,
+		port->last_svc_msecs_diff,
+		port->last_irq_svc_msecs_diff,
+		port->last_svc_wp,
+		port->last_svc_rp
+		);
+
+	/* Current write position */
+	wp = saa7164_readl(port->bufcounter);
+	if (wp > (port->hwcfg.buffercount - 1)) {
+		printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp);
+		return;
+	}
+
+	/* Most current complete buffer */
+	if (wp == 0)
+		mcb = (port->hwcfg.buffercount - 1);
+	else
+		mcb = wp - 1;
+
+	while (1) {
+		if (port->done_first_interrupt == 0) {
+			port->done_first_interrupt++;
+			rp = mcb;
+		} else
+			rp = (port->last_svc_rp + 1) % 8;
+
+		if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) {
+			printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
+			break;
+		}
+
+		saa7164_work_enchandler_helper(port, rp);
+		port->last_svc_rp = rp;
+		cnt++;
+
+		if (rp == mcb)
+			break;
+	}
+
+	/* TODO: Convert this into a /proc/saa7164 style readable file */
+	if (print_histogram == port->nr) {
+		saa7164_histogram_print(port, &port->irq_interval);
+		saa7164_histogram_print(port, &port->svc_interval);
+		saa7164_histogram_print(port, &port->irq_svc_interval);
+		saa7164_histogram_print(port, &port->read_interval);
+		saa7164_histogram_print(port, &port->poll_interval);
+		/* TODO: fix this to preserve any previous state */
+		print_histogram = 64 + port->nr;
+	}
+}
+
 static void saa7164_work_cmdhandler(struct work_struct *w)
 {
 	struct saa7164_dev *dev = container_of(w, struct saa7164_dev, workcmd);
@@ -74,7 +539,7 @@
 
 static void saa7164_buffer_deliver(struct saa7164_buffer *buf)
 {
-	struct saa7164_tsport *port = buf->port;
+	struct saa7164_port *port = buf->port;
 
 	/* Feed the transport payload into the kernel demux */
 	dvb_dmx_swfilter_packets(&port->dvb.demux, (u8 *)buf->cpu,
@@ -82,7 +547,56 @@
 
 }
 
-static irqreturn_t saa7164_irq_ts(struct saa7164_tsport *port)
+static irqreturn_t saa7164_irq_vbi(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+
+	/* Store old time */
+	port->last_irq_msecs_diff = port->last_irq_msecs;
+
+	/* Collect new stats */
+	port->last_irq_msecs = jiffies_to_msecs(jiffies);
+
+	/* Calculate stats */
+	port->last_irq_msecs_diff = port->last_irq_msecs -
+		port->last_irq_msecs_diff;
+
+	saa7164_histogram_update(&port->irq_interval,
+		port->last_irq_msecs_diff);
+
+	dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__,
+		port->last_irq_msecs_diff);
+
+	/* Tis calls the vbi irq handler */
+	schedule_work(&port->workenc);
+	return 0;
+}
+
+static irqreturn_t saa7164_irq_encoder(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+
+	/* Store old time */
+	port->last_irq_msecs_diff = port->last_irq_msecs;
+
+	/* Collect new stats */
+	port->last_irq_msecs = jiffies_to_msecs(jiffies);
+
+	/* Calculate stats */
+	port->last_irq_msecs_diff = port->last_irq_msecs -
+		port->last_irq_msecs_diff;
+
+	saa7164_histogram_update(&port->irq_interval,
+		port->last_irq_msecs_diff);
+
+	dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__,
+		port->last_irq_msecs_diff);
+
+	schedule_work(&port->workenc);
+	return 0;
+}
+
+static irqreturn_t saa7164_irq_ts(struct saa7164_port *port)
 {
 	struct saa7164_dev *dev = port->dev;
 	struct saa7164_buffer *buf;
@@ -96,7 +610,7 @@
 
 	/* Find the previous buffer to the current write point */
 	if (wp == 0)
-		rp = 7;
+		rp = (port->hwcfg.buffercount - 1);
 	else
 		rp = wp - 1;
 
@@ -107,7 +621,7 @@
 		if (i++ > port->hwcfg.buffercount)
 			BUG();
 
-		if (buf->nr == rp) {
+		if (buf->idx == rp) {
 			/* Found the buffer, deal with it */
 			dprintk(DBGLVL_IRQ, "%s() wp: %d processing: %d\n",
 				__func__, wp, rp);
@@ -123,6 +637,13 @@
 static irqreturn_t saa7164_irq(int irq, void *dev_id)
 {
 	struct saa7164_dev *dev = dev_id;
+	struct saa7164_port *porta = &dev->ports[SAA7164_PORT_TS1];
+	struct saa7164_port *portb = &dev->ports[SAA7164_PORT_TS2];
+	struct saa7164_port *portc = &dev->ports[SAA7164_PORT_ENC1];
+	struct saa7164_port *portd = &dev->ports[SAA7164_PORT_ENC2];
+	struct saa7164_port *porte = &dev->ports[SAA7164_PORT_VBI1];
+	struct saa7164_port *portf = &dev->ports[SAA7164_PORT_VBI2];
+
 	u32 intid, intstat[INT_SIZE/4];
 	int i, handled = 0, bit;
 
@@ -168,17 +689,35 @@
 				if (intid == dev->intfdesc.bInterruptId) {
 					/* A response to an cmd/api call */
 					schedule_work(&dev->workcmd);
-				} else if (intid ==
-					dev->ts1.hwcfg.interruptid) {
+				} else if (intid == porta->hwcfg.interruptid) {
 
 					/* Transport path 1 */
-					saa7164_irq_ts(&dev->ts1);
+					saa7164_irq_ts(porta);
 
-				} else if (intid ==
-					dev->ts2.hwcfg.interruptid) {
+				} else if (intid == portb->hwcfg.interruptid) {
 
 					/* Transport path 2 */
-					saa7164_irq_ts(&dev->ts2);
+					saa7164_irq_ts(portb);
+
+				} else if (intid == portc->hwcfg.interruptid) {
+
+					/* Encoder path 1 */
+					saa7164_irq_encoder(portc);
+
+				} else if (intid == portd->hwcfg.interruptid) {
+
+					/* Encoder path 2 */
+					saa7164_irq_encoder(portd);
+
+				} else if (intid == porte->hwcfg.interruptid) {
+
+					/* VBI path 1 */
+					saa7164_irq_vbi(porte);
+
+				} else if (intid == portf->hwcfg.interruptid) {
+
+					/* VBI path 2 */
+					saa7164_irq_vbi(portf);
 
 				} else {
 					/* Find the function */
@@ -286,8 +825,8 @@
 
 static void saa7164_dump_hwdesc(struct saa7164_dev *dev)
 {
-	dprintk(1, "@0x%p hwdesc sizeof(tmComResHWDescr_t) = %d bytes\n",
-		&dev->hwdesc, (u32)sizeof(tmComResHWDescr_t));
+	dprintk(1, "@0x%p hwdesc sizeof(struct tmComResHWDescr) = %d bytes\n",
+		&dev->hwdesc, (u32)sizeof(struct tmComResHWDescr));
 
 	dprintk(1, " .bLength = 0x%x\n", dev->hwdesc.bLength);
 	dprintk(1, " .bDescriptorType = 0x%x\n", dev->hwdesc.bDescriptorType);
@@ -317,8 +856,8 @@
 static void saa7164_dump_intfdesc(struct saa7164_dev *dev)
 {
 	dprintk(1, "@0x%p intfdesc "
-		"sizeof(tmComResInterfaceDescr_t) = %d bytes\n",
-		&dev->intfdesc, (u32)sizeof(tmComResInterfaceDescr_t));
+		"sizeof(struct tmComResInterfaceDescr) = %d bytes\n",
+		&dev->intfdesc, (u32)sizeof(struct tmComResInterfaceDescr));
 
 	dprintk(1, " .bLength = 0x%x\n", dev->intfdesc.bLength);
 	dprintk(1, " .bDescriptorType = 0x%x\n", dev->intfdesc.bDescriptorType);
@@ -338,8 +877,8 @@
 
 static void saa7164_dump_busdesc(struct saa7164_dev *dev)
 {
-	dprintk(1, "@0x%p busdesc sizeof(tmComResBusDescr_t) = %d bytes\n",
-		&dev->busdesc, (u32)sizeof(tmComResBusDescr_t));
+	dprintk(1, "@0x%p busdesc sizeof(struct tmComResBusDescr) = %d bytes\n",
+		&dev->busdesc, (u32)sizeof(struct tmComResBusDescr));
 
 	dprintk(1, " .CommandRing   = 0x%016Lx\n", dev->busdesc.CommandRing);
 	dprintk(1, " .ResponseRing  = 0x%016Lx\n", dev->busdesc.ResponseRing);
@@ -356,23 +895,23 @@
  */
 static void saa7164_get_descriptors(struct saa7164_dev *dev)
 {
-	memcpy(&dev->hwdesc, dev->bmmio, sizeof(tmComResHWDescr_t));
-	memcpy(&dev->intfdesc, dev->bmmio + sizeof(tmComResHWDescr_t),
-		sizeof(tmComResInterfaceDescr_t));
-	memcpy(&dev->busdesc, dev->bmmio + dev->intfdesc.BARLocation,
-		sizeof(tmComResBusDescr_t));
+	memcpy_fromio(&dev->hwdesc, dev->bmmio, sizeof(struct tmComResHWDescr));
+	memcpy_fromio(&dev->intfdesc, dev->bmmio + sizeof(struct tmComResHWDescr),
+		sizeof(struct tmComResInterfaceDescr));
+	memcpy_fromio(&dev->busdesc, dev->bmmio + dev->intfdesc.BARLocation,
+		sizeof(struct tmComResBusDescr));
 
-	if (dev->hwdesc.bLength != sizeof(tmComResHWDescr_t)) {
-		printk(KERN_ERR "Structure tmComResHWDescr_t is mangled\n");
+	if (dev->hwdesc.bLength != sizeof(struct tmComResHWDescr)) {
+		printk(KERN_ERR "Structure struct tmComResHWDescr is mangled\n");
 		printk(KERN_ERR "Need %x got %d\n", dev->hwdesc.bLength,
-			(u32)sizeof(tmComResHWDescr_t));
+			(u32)sizeof(struct tmComResHWDescr));
 	} else
 		saa7164_dump_hwdesc(dev);
 
-	if (dev->intfdesc.bLength != sizeof(tmComResInterfaceDescr_t)) {
-		printk(KERN_ERR "struct tmComResInterfaceDescr_t is mangled\n");
+	if (dev->intfdesc.bLength != sizeof(struct tmComResInterfaceDescr)) {
+		printk(KERN_ERR "struct struct tmComResInterfaceDescr is mangled\n");
 		printk(KERN_ERR "Need %x got %d\n", dev->intfdesc.bLength,
-			(u32)sizeof(tmComResInterfaceDescr_t));
+			(u32)sizeof(struct tmComResInterfaceDescr));
 	} else
 		saa7164_dump_intfdesc(dev);
 
@@ -402,6 +941,58 @@
 	return -EBUSY;
 }
 
+static int saa7164_port_init(struct saa7164_dev *dev, int portnr)
+{
+	struct saa7164_port *port = 0;
+
+	if ((portnr < 0) || (portnr >= SAA7164_MAX_PORTS))
+		BUG();
+
+	port = &dev->ports[portnr];
+
+	port->dev = dev;
+	port->nr = portnr;
+
+	if ((portnr == SAA7164_PORT_TS1) || (portnr == SAA7164_PORT_TS2))
+		port->type = SAA7164_MPEG_DVB;
+	else
+	if ((portnr == SAA7164_PORT_ENC1) || (portnr == SAA7164_PORT_ENC2)) {
+		port->type = SAA7164_MPEG_ENCODER;
+
+		/* We need a deferred interrupt handler for cmd handling */
+		INIT_WORK(&port->workenc, saa7164_work_enchandler);
+	}
+	else
+	if ((portnr == SAA7164_PORT_VBI1) || (portnr == SAA7164_PORT_VBI2)) {
+		port->type = SAA7164_MPEG_VBI;
+
+		/* We need a deferred interrupt handler for cmd handling */
+		INIT_WORK(&port->workenc, saa7164_work_vbihandler);
+	} else
+		BUG();
+
+	/* Init all the critical resources */
+	mutex_init(&port->dvb.lock);
+	INIT_LIST_HEAD(&port->dmaqueue.list);
+	mutex_init(&port->dmaqueue_lock);
+
+	INIT_LIST_HEAD(&port->list_buf_used.list);
+	INIT_LIST_HEAD(&port->list_buf_free.list);
+	init_waitqueue_head(&port->wait_read);
+
+
+	saa7164_histogram_reset(&port->irq_interval, "irq intervals");
+	saa7164_histogram_reset(&port->svc_interval, "deferred intervals");
+	saa7164_histogram_reset(&port->irq_svc_interval,
+		"irq to deferred intervals");
+	saa7164_histogram_reset(&port->read_interval,
+		"encoder/vbi read() intervals");
+	saa7164_histogram_reset(&port->poll_interval,
+		"encoder/vbi poll() intervals");
+
+	return 0;
+}
+
 static int saa7164_dev_setup(struct saa7164_dev *dev)
 {
 	int i;
@@ -443,23 +1034,13 @@
 	dev->i2c_bus[2].dev = dev;
 	dev->i2c_bus[2].nr = 2;
 
-	/* Transport port A Defaults / setup */
-	dev->ts1.dev = dev;
-	dev->ts1.nr = 0;
-	mutex_init(&dev->ts1.dvb.lock);
-	INIT_LIST_HEAD(&dev->ts1.dmaqueue.list);
-	INIT_LIST_HEAD(&dev->ts1.dummy_dmaqueue.list);
-	mutex_init(&dev->ts1.dmaqueue_lock);
-	mutex_init(&dev->ts1.dummy_dmaqueue_lock);
-
-	/* Transport port B Defaults / setup */
-	dev->ts2.dev = dev;
-	dev->ts2.nr = 1;
-	mutex_init(&dev->ts2.dvb.lock);
-	INIT_LIST_HEAD(&dev->ts2.dmaqueue.list);
-	INIT_LIST_HEAD(&dev->ts2.dummy_dmaqueue.list);
-	mutex_init(&dev->ts2.dmaqueue_lock);
-	mutex_init(&dev->ts2.dummy_dmaqueue_lock);
+	/* Transport + Encoder ports 1, 2, 3, 4 - Defaults / setup */
+	saa7164_port_init(dev, SAA7164_PORT_TS1);
+	saa7164_port_init(dev, SAA7164_PORT_TS2);
+	saa7164_port_init(dev, SAA7164_PORT_ENC1);
+	saa7164_port_init(dev, SAA7164_PORT_ENC2);
+	saa7164_port_init(dev, SAA7164_PORT_VBI1);
+	saa7164_port_init(dev, SAA7164_PORT_VBI2);
 
 	if (get_resources(dev) < 0) {
 		printk(KERN_ERR "CORE %s No more PCIe resources for "
@@ -516,6 +1097,132 @@
 	return;
 }
 
+#ifdef CONFIG_PROC_FS
+static int saa7164_proc_show(struct seq_file *m, void *v)
+{
+	struct saa7164_dev *dev;
+	struct tmComResBusInfo *b;
+	struct list_head *list;
+	int i, c;
+
+	if (saa7164_devcount == 0)
+		return 0;
+
+	list_for_each(list, &saa7164_devlist) {
+		dev = list_entry(list, struct saa7164_dev, devlist);
+		seq_printf(m, "%s = %p\n", dev->name, dev);
+
+		/* Lock the bus from any other access */
+		b = &dev->bus;
+		mutex_lock(&b->lock);
+
+		seq_printf(m, " .m_pdwSetWritePos = 0x%x (0x%08x)\n",
+			b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos));
+
+		seq_printf(m, " .m_pdwSetReadPos  = 0x%x (0x%08x)\n",
+			b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos));
+
+		seq_printf(m, " .m_pdwGetWritePos = 0x%x (0x%08x)\n",
+			b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos));
+
+		seq_printf(m, " .m_pdwGetReadPos  = 0x%x (0x%08x)\n",
+			b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos));
+		c = 0;
+		seq_printf(m, "\n  Set Ring:\n");
+		seq_printf(m, "\n addr  00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+		for (i = 0; i < b->m_dwSizeSetRing; i++) {
+			if (c == 0)
+				seq_printf(m, " %04x:", i);
+
+			seq_printf(m, " %02x", *(b->m_pdwSetRing + i));
+
+			if (++c == 16) {
+				seq_printf(m, "\n");
+				c = 0;
+			}
+		}
+
+		c = 0;
+		seq_printf(m, "\n  Get Ring:\n");
+		seq_printf(m, "\n addr  00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+		for (i = 0; i < b->m_dwSizeGetRing; i++) {
+			if (c == 0)
+				seq_printf(m, " %04x:", i);
+
+			seq_printf(m, " %02x", *(b->m_pdwGetRing + i));
+
+			if (++c == 16) {
+				seq_printf(m, "\n");
+				c = 0;
+			}
+		}
+
+		mutex_unlock(&b->lock);
+
+	}
+
+	return 0;
+}
+
+static int saa7164_proc_open(struct inode *inode, struct file *filp)
+{
+	return single_open(filp, saa7164_proc_show, NULL);
+}
+
+static struct file_operations saa7164_proc_fops = {
+	.open		= saa7164_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int saa7164_proc_create(void)
+{
+	struct proc_dir_entry *pe;
+
+	pe = proc_create("saa7164", S_IRUGO, NULL, &saa7164_proc_fops);
+	if (!pe)
+		return -ENOMEM;
+
+	return 0;
+}
+#endif
+
+static int saa7164_thread_function(void *data)
+{
+	struct saa7164_dev *dev = data;
+	struct tmFwInfoStruct fwinfo;
+	u64 last_poll_time = 0;
+
+	dprintk(DBGLVL_THR, "thread started\n");
+
+	set_freezable();
+
+	while (1) {
+		msleep_interruptible(100);
+		if (kthread_should_stop())
+			break;
+		try_to_freeze();
+
+		dprintk(DBGLVL_THR, "thread running\n");
+
+		/* Dump the firmware debug message to console */
+		/* Polling this costs us 1-2% of the arm CPU */
+		/* convert this into a respnde to interrupt 0x7a */
+		saa7164_api_collect_debug(dev);
+
+		/* Monitor CPU load every 1 second */
+		if ((last_poll_time + 1000 /* ms */) < jiffies_to_msecs(jiffies)) {
+			saa7164_api_get_load_info(dev, &fwinfo);
+			last_poll_time = jiffies_to_msecs(jiffies);
+		}
+
+	}
+
+	dprintk(DBGLVL_THR, "thread exiting\n");
+	return 0;
+}
+
 static int __devinit saa7164_initdev(struct pci_dev *pci_dev,
 				     const struct pci_device_id *pci_id)
 {
@@ -622,7 +1329,6 @@
 		saa7164_gpio_setup(dev);
 		saa7164_card_setup(dev);
 
-
 		/* Parse the dynamic device configuration, find various
 		 * media endpoints (MPEG, WMV, PS, TS) and cache their
 		 * configuration details into the driver, so we can
@@ -633,7 +1339,7 @@
 
 		/* Begin to create the video sub-systems and register funcs */
 		if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) {
-			if (saa7164_dvb_register(&dev->ts1) < 0) {
+			if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS1]) < 0) {
 				printk(KERN_ERR "%s() Failed to register "
 					"dvb adapters on porta\n",
 					__func__);
@@ -641,13 +1347,50 @@
 		}
 
 		if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) {
-			if (saa7164_dvb_register(&dev->ts2) < 0) {
+			if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS2]) < 0) {
 				printk(KERN_ERR"%s() Failed to register "
 					"dvb adapters on portb\n",
 					__func__);
 			}
 		}
 
+		if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER) {
+			if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC1]) < 0) {
+				printk(KERN_ERR"%s() Failed to register "
+					"mpeg encoder\n", __func__);
+			}
+		}
+
+		if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER) {
+			if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC2]) < 0) {
+				printk(KERN_ERR"%s() Failed to register "
+					"mpeg encoder\n", __func__);
+			}
+		}
+
+		if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI) {
+			if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI1]) < 0) {
+				printk(KERN_ERR"%s() Failed to register "
+					"vbi device\n", __func__);
+			}
+		}
+
+		if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI) {
+			if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI2]) < 0) {
+				printk(KERN_ERR"%s() Failed to register "
+					"vbi device\n", __func__);
+			}
+		}
+		saa7164_api_set_debug(dev, fw_debug);
+
+		if (fw_debug) {
+			dev->kthread = kthread_run(saa7164_thread_function, dev,
+				"saa7164 debug");
+			if (!dev->kthread)
+				printk(KERN_ERR "%s() Failed to create "
+					"debug kernel thread\n", __func__);
+		}
+
 	} /* != BOARD_UNKNOWN */
 	else
 		printk(KERN_ERR "%s() Unsupported board detected, "
@@ -675,13 +1418,49 @@
 {
 	struct saa7164_dev *dev = pci_get_drvdata(pci_dev);
 
+	if (dev->board != SAA7164_BOARD_UNKNOWN) {
+		if (fw_debug && dev->kthread) {
+			kthread_stop(dev->kthread);
+			dev->kthread = NULL;
+		}
+		if (dev->firmwareloaded)
+			saa7164_api_set_debug(dev, 0x00);
+	}
+
+	saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+		&dev->ports[SAA7164_PORT_ENC1].irq_interval);
+	saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+		&dev->ports[SAA7164_PORT_ENC1].svc_interval);
+	saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+		&dev->ports[SAA7164_PORT_ENC1].irq_svc_interval);
+	saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+		&dev->ports[SAA7164_PORT_ENC1].read_interval);
+	saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+		&dev->ports[SAA7164_PORT_ENC1].poll_interval);
+	saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI1],
+		&dev->ports[SAA7164_PORT_VBI1].read_interval);
+	saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI2],
+		&dev->ports[SAA7164_PORT_VBI2].poll_interval);
+
 	saa7164_shutdown(dev);
 
 	if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB)
-		saa7164_dvb_unregister(&dev->ts1);
+		saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS1]);
 
 	if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB)
-		saa7164_dvb_unregister(&dev->ts2);
+		saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS2]);
+
+	if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER)
+		saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC1]);
+
+	if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER)
+		saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC2]);
+
+	if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI)
+		saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI1]);
+
+	if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI)
+		saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI2]);
 
 	saa7164_i2c_unregister(&dev->i2c_bus[0]);
 	saa7164_i2c_unregister(&dev->i2c_bus[1]);
@@ -727,11 +1506,18 @@
 static int __init saa7164_init(void)
 {
 	printk(KERN_INFO "saa7164 driver loaded\n");
+
+#ifdef CONFIG_PROC_FS
+	saa7164_proc_create();
+#endif
 	return pci_register_driver(&saa7164_pci_driver);
 }
 
 static void __exit saa7164_fini(void)
 {
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("saa7164", NULL);
+#endif
 	pci_unregister_driver(&saa7164_pci_driver);
 }
 
diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/video/saa7164/saa7164-dvb.c
index cf099c5..b305a01 100644
--- a/drivers/media/video/saa7164/saa7164-dvb.c
+++ b/drivers/media/video/saa7164/saa7164-dvb.c
@@ -1,7 +1,7 @@
 /*
  *  Driver for the NXP SAA7164 PCIe bridge
  *
- *  Copyright (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -82,7 +82,7 @@
 	.mpeg_timing   = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
 };
 
-static int saa7164_dvb_stop_tsport(struct saa7164_tsport *port)
+static int saa7164_dvb_stop_port(struct saa7164_port *port)
 {
 	struct saa7164_dev *dev = port->dev;
 	int ret;
@@ -100,7 +100,7 @@
 	return ret;
 }
 
-static int saa7164_dvb_acquire_tsport(struct saa7164_tsport *port)
+static int saa7164_dvb_acquire_port(struct saa7164_port *port)
 {
 	struct saa7164_dev *dev = port->dev;
 	int ret;
@@ -118,7 +118,7 @@
 	return ret;
 }
 
-static int saa7164_dvb_pause_tsport(struct saa7164_tsport *port)
+static int saa7164_dvb_pause_port(struct saa7164_port *port)
 {
 	struct saa7164_dev *dev = port->dev;
 	int ret;
@@ -140,90 +140,38 @@
  * the part through AVStream / KS Windows stages, forwards or backwards.
  * States are: stopped, acquired (h/w), paused, started.
  */
-static int saa7164_dvb_stop_streaming(struct saa7164_tsport *port)
+static int saa7164_dvb_stop_streaming(struct saa7164_port *port)
 {
 	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct list_head *p, *q;
 	int ret;
 
 	dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
 
-	ret = saa7164_dvb_pause_tsport(port);
-	ret = saa7164_dvb_acquire_tsport(port);
-	ret = saa7164_dvb_stop_tsport(port);
+	ret = saa7164_dvb_pause_port(port);
+	ret = saa7164_dvb_acquire_port(port);
+	ret = saa7164_dvb_stop_port(port);
+
+	/* Mark the hardware buffers as free */
+	mutex_lock(&port->dmaqueue_lock);
+	list_for_each_safe(p, q, &port->dmaqueue.list) {
+		buf = list_entry(p, struct saa7164_buffer, list);
+		buf->flags = SAA7164_BUFFER_FREE;
+	}
+	mutex_unlock(&port->dmaqueue_lock);
 
 	return ret;
 }
 
-static int saa7164_dvb_cfg_tsport(struct saa7164_tsport *port)
-{
-	tmHWStreamParameters_t *params = &port->hw_streamingparams;
-	struct saa7164_dev *dev = port->dev;
-	struct saa7164_buffer *buf;
-	struct list_head *c, *n;
-	int i = 0;
-
-	dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
-
-	saa7164_writel(port->pitch, params->pitch);
-	saa7164_writel(port->bufsize, params->pitch * params->numberoflines);
-
-	dprintk(DBGLVL_DVB, " configured:\n");
-	dprintk(DBGLVL_DVB, "   lmmio       0x%p\n", dev->lmmio);
-	dprintk(DBGLVL_DVB, "   bufcounter  0x%x = 0x%x\n", port->bufcounter,
-		saa7164_readl(port->bufcounter));
-
-	dprintk(DBGLVL_DVB, "   pitch       0x%x = %d\n", port->pitch,
-		saa7164_readl(port->pitch));
-
-	dprintk(DBGLVL_DVB, "   bufsize     0x%x = %d\n", port->bufsize,
-		saa7164_readl(port->bufsize));
-
-	dprintk(DBGLVL_DVB, "   buffercount = %d\n", port->hwcfg.buffercount);
-	dprintk(DBGLVL_DVB, "   bufoffset = 0x%x\n", port->bufoffset);
-	dprintk(DBGLVL_DVB, "   bufptr32h = 0x%x\n", port->bufptr32h);
-	dprintk(DBGLVL_DVB, "   bufptr32l = 0x%x\n", port->bufptr32l);
-
-	/* Poke the buffers and offsets into PCI space */
-	mutex_lock(&port->dmaqueue_lock);
-	list_for_each_safe(c, n, &port->dmaqueue.list) {
-		buf = list_entry(c, struct saa7164_buffer, list);
-
-		/* TODO: Review this in light of 32v64 assignments */
-		saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0);
-		saa7164_writel(port->bufptr32h + ((sizeof(u32) * 2) * i),
-			buf->pt_dma);
-		saa7164_writel(port->bufptr32l + ((sizeof(u32) * 2) * i), 0);
-
-		dprintk(DBGLVL_DVB,
-			"   buf[%d] offset 0x%llx (0x%x) "
-			"buf 0x%llx/%llx (0x%x/%x)\n",
-			i,
-			(u64)port->bufoffset + (i * sizeof(u32)),
-			saa7164_readl(port->bufoffset + (sizeof(u32) * i)),
-			(u64)port->bufptr32h + ((sizeof(u32) * 2) * i),
-			(u64)port->bufptr32l + ((sizeof(u32) * 2) * i),
-			saa7164_readl(port->bufptr32h + ((sizeof(u32) * i)
-				* 2)),
-			saa7164_readl(port->bufptr32l + ((sizeof(u32) * i)
-				* 2)));
-
-		if (i++ > port->hwcfg.buffercount)
-			BUG();
-
-	}
-	mutex_unlock(&port->dmaqueue_lock);
-
-	return 0;
-}
-
-static int saa7164_dvb_start_tsport(struct saa7164_tsport *port)
+static int saa7164_dvb_start_port(struct saa7164_port *port)
 {
 	struct saa7164_dev *dev = port->dev;
 	int ret = 0, result;
 
 	dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
 
-	saa7164_dvb_cfg_tsport(port);
+	saa7164_buffer_cfg_port(port);
 
 	/* Acquire the hardware */
 	result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
@@ -284,7 +232,7 @@
 static int saa7164_dvb_start_feed(struct dvb_demux_feed *feed)
 {
 	struct dvb_demux *demux = feed->demux;
-	struct saa7164_tsport *port = (struct saa7164_tsport *) demux->priv;
+	struct saa7164_port *port = (struct saa7164_port *) demux->priv;
 	struct saa7164_dvb *dvb = &port->dvb;
 	struct saa7164_dev *dev = port->dev;
 	int ret = 0;
@@ -298,7 +246,7 @@
 		mutex_lock(&dvb->lock);
 		if (dvb->feeding++ == 0) {
 			/* Start transport */
-			ret = saa7164_dvb_start_tsport(port);
+			ret = saa7164_dvb_start_port(port);
 		}
 		mutex_unlock(&dvb->lock);
 		dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n",
@@ -311,7 +259,7 @@
 static int saa7164_dvb_stop_feed(struct dvb_demux_feed *feed)
 {
 	struct dvb_demux *demux = feed->demux;
-	struct saa7164_tsport *port = (struct saa7164_tsport *) demux->priv;
+	struct saa7164_port *port = (struct saa7164_port *) demux->priv;
 	struct saa7164_dvb *dvb = &port->dvb;
 	struct saa7164_dev *dev = port->dev;
 	int ret = 0;
@@ -332,7 +280,7 @@
 	return ret;
 }
 
-static int dvb_register(struct saa7164_tsport *port)
+static int dvb_register(struct saa7164_port *port)
 {
 	struct saa7164_dvb *dvb = &port->dvb;
 	struct saa7164_dev *dev = port->dev;
@@ -341,6 +289,9 @@
 
 	dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
 
+	if (port->type != SAA7164_MPEG_DVB)
+		BUG();
+
 	/* Sanity check that the PCI configuration space is active */
 	if (port->hwcfg.BARLocation == 0) {
 		result = -ENOMEM;
@@ -378,7 +329,6 @@
 				DRIVER_NAME, result);
 			goto fail_adapter;
 		}
-		buf->nr = i;
 
 		mutex_lock(&port->dmaqueue_lock);
 		list_add_tail(&buf->list, &port->dmaqueue.list);
@@ -473,7 +423,7 @@
 	return result;
 }
 
-int saa7164_dvb_unregister(struct saa7164_tsport *port)
+int saa7164_dvb_unregister(struct saa7164_port *port)
 {
 	struct saa7164_dvb *dvb = &port->dvb;
 	struct saa7164_dev *dev = port->dev;
@@ -482,12 +432,15 @@
 
 	dprintk(DBGLVL_DVB, "%s()\n", __func__);
 
+	if (port->type != SAA7164_MPEG_DVB)
+		BUG();
+
 	/* Remove any allocated buffers */
 	mutex_lock(&port->dmaqueue_lock);
 	list_for_each_safe(c, n, &port->dmaqueue.list) {
 		b = list_entry(c, struct saa7164_buffer, list);
 		list_del(c);
-		saa7164_buffer_dealloc(port, b);
+		saa7164_buffer_dealloc(b);
 	}
 	mutex_unlock(&port->dmaqueue_lock);
 
@@ -508,7 +461,7 @@
 /* All the DVB attach calls go here, this function get's modified
  * for each new card.
  */
-int saa7164_dvb_register(struct saa7164_tsport *port)
+int saa7164_dvb_register(struct saa7164_port *port)
 {
 	struct saa7164_dev *dev = port->dev;
 	struct saa7164_dvb *dvb = &port->dvb;
@@ -588,8 +541,6 @@
 		return -1;
 	}
 
-	/* Put the analog decoder in standby to keep it quiet */
-
 	/* register everything */
 	ret = dvb_register(port);
 	if (ret < 0) {
diff --git a/drivers/media/video/saa7164/saa7164-encoder.c b/drivers/media/video/saa7164/saa7164-encoder.c
new file mode 100644
index 0000000..cbb53d0
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-encoder.c
@@ -0,0 +1,1503 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "saa7164.h"
+
+#define ENCODER_MAX_BITRATE 6500000
+#define ENCODER_MIN_BITRATE 1000000
+#define ENCODER_DEF_BITRATE 5000000
+
+static struct saa7164_tvnorm saa7164_tvnorms[] = {
+	{
+		.name      = "NTSC-M",
+		.id        = V4L2_STD_NTSC_M,
+	}, {
+		.name      = "NTSC-JP",
+		.id        = V4L2_STD_NTSC_M_JP,
+	}
+};
+
+static const u32 saa7164_v4l2_ctrls[] = {
+	V4L2_CID_BRIGHTNESS,
+	V4L2_CID_CONTRAST,
+	V4L2_CID_SATURATION,
+	V4L2_CID_HUE,
+	V4L2_CID_AUDIO_VOLUME,
+	V4L2_CID_SHARPNESS,
+	V4L2_CID_MPEG_STREAM_TYPE,
+	V4L2_CID_MPEG_VIDEO_ASPECT,
+	V4L2_CID_MPEG_VIDEO_B_FRAMES,
+	V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+	V4L2_CID_MPEG_AUDIO_MUTE,
+	V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+	V4L2_CID_MPEG_VIDEO_BITRATE,
+	V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+	0
+};
+
+/* Take the encoder configuration form the port struct and
+ * flush it to the hardware.
+ */
+static void saa7164_encoder_configure(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	port->encoder_params.width = port->width;
+	port->encoder_params.height = port->height;
+	port->encoder_params.is_50hz =
+		(port->encodernorm.id & V4L2_STD_625_50) != 0;
+
+	/* Set up the DIF (enable it) for analog mode by default */
+	saa7164_api_initialize_dif(port);
+
+	/* Configure the correct video standard */
+	saa7164_api_configure_dif(port, port->encodernorm.id);
+
+	/* Ensure the audio decoder is correct configured */
+	saa7164_api_set_audio_std(port);
+}
+
+static int saa7164_encoder_buffers_dealloc(struct saa7164_port *port)
+{
+	struct list_head *c, *n, *p, *q, *l, *v;
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+
+	/* Remove any allocated buffers */
+	mutex_lock(&port->dmaqueue_lock);
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) dmaqueue\n", __func__, port->nr);
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+		buf = list_entry(c, struct saa7164_buffer, list);
+		list_del(c);
+		saa7164_buffer_dealloc(buf);
+	}
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) used\n", __func__, port->nr);
+	list_for_each_safe(p, q, &port->list_buf_used.list) {
+		ubuf = list_entry(p, struct saa7164_user_buffer, list);
+		list_del(p);
+		saa7164_buffer_dealloc_user(ubuf);
+	}
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) free\n", __func__, port->nr);
+	list_for_each_safe(l, v, &port->list_buf_free.list) {
+		ubuf = list_entry(l, struct saa7164_user_buffer, list);
+		list_del(l);
+		saa7164_buffer_dealloc_user(ubuf);
+	}
+
+	mutex_unlock(&port->dmaqueue_lock);
+	dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr);
+
+	return 0;
+}
+
+/* Dynamic buffer switch at encoder start time */
+static int saa7164_encoder_buffers_alloc(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+	struct tmHWStreamParameters *params = &port->hw_streamingparams;
+	int result = -ENODEV, i;
+	int len = 0;
+
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) {
+		dprintk(DBGLVL_ENC, "%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_PS\n", __func__);
+		params->samplesperline = 128;
+		params->numberoflines = 256;
+		params->pitch = 128;
+		params->numpagetables = 2 +
+			((SAA7164_PS_NUMBER_OF_LINES * 128) / PAGE_SIZE);
+	} else
+	if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_TS) {
+		dprintk(DBGLVL_ENC, "%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_TS\n", __func__);
+		params->samplesperline = 188;
+		params->numberoflines = 312;
+		params->pitch = 188;
+		params->numpagetables = 2 +
+			((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE);
+	} else
+		BUG();
+
+	/* Init and establish defaults */
+	params->bitspersample = 8;
+	params->linethreshold = 0;
+	params->pagetablelistvirt = 0;
+	params->pagetablelistphys = 0;
+	params->numpagetableentries = port->hwcfg.buffercount;
+
+	/* Allocate the PCI resources, buffers (hard) */
+	for (i = 0; i < port->hwcfg.buffercount; i++) {
+		buf = saa7164_buffer_alloc(port,
+			params->numberoflines *
+			params->pitch);
+
+		if (!buf) {
+			printk(KERN_ERR "%s() failed "
+			       "(errno = %d), unable to allocate buffer\n",
+				__func__, result);
+			result = -ENOMEM;
+			goto failed;
+		} else {
+
+			mutex_lock(&port->dmaqueue_lock);
+			list_add_tail(&buf->list, &port->dmaqueue.list);
+			mutex_unlock(&port->dmaqueue_lock);
+
+		}
+	}
+
+	/* Allocate some kenrel kernel buffers for copying
+	 * to userpsace.
+	 */
+	len = params->numberoflines * params->pitch;
+
+	if (encoder_buffers < 16)
+		encoder_buffers = 16;
+	if (encoder_buffers > 512)
+		encoder_buffers = 512;
+
+	for (i = 0; i < encoder_buffers; i++) {
+
+		ubuf = saa7164_buffer_alloc_user(dev, len);
+		if (ubuf) {
+			mutex_lock(&port->dmaqueue_lock);
+			list_add_tail(&ubuf->list, &port->list_buf_free.list);
+			mutex_unlock(&port->dmaqueue_lock);
+		}
+
+	}
+
+	result = 0;
+
+failed:
+	return result;
+}
+
+static int saa7164_encoder_initialize(struct saa7164_port *port)
+{
+	saa7164_encoder_configure(port);
+	return 0;
+}
+
+/* -- V4L2 --------------------------------------------------------- */
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+	unsigned int i;
+
+	dprintk(DBGLVL_ENC, "%s(id=0x%x)\n", __func__, (u32)*id);
+
+	for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) {
+		if (*id & saa7164_tvnorms[i].id)
+			break;
+	}
+	if (i == ARRAY_SIZE(saa7164_tvnorms))
+		return -EINVAL;
+
+	port->encodernorm = saa7164_tvnorms[i];
+
+	/* Update the audio decoder while is not running in
+	 * auto detect mode.
+	 */
+	saa7164_api_set_audio_std(port);
+
+	dprintk(DBGLVL_ENC, "%s(id=0x%x) OK\n", __func__, (u32)*id);
+
+	return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+	struct v4l2_input *i)
+{
+	int n;
+
+	char *inputs[] = { "tuner", "composite", "svideo", "aux",
+		"composite 2", "svideo 2", "aux 2" };
+
+	if (i->index >= 7)
+		return -EINVAL;
+
+	strcpy(i->name, inputs[i->index]);
+
+	if (i->index == 0)
+		i->type = V4L2_INPUT_TYPE_TUNER;
+	else
+		i->type  = V4L2_INPUT_TYPE_CAMERA;
+
+	for (n = 0; n < ARRAY_SIZE(saa7164_tvnorms); n++)
+		i->std |= saa7164_tvnorms[n].id;
+
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	if (saa7164_api_get_videomux(port) != SAA_OK)
+		return -EIO;
+
+	*i = (port->mux_input - 1);
+
+	dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, *i);
+
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, i);
+
+	if (i >= 7)
+		return -EINVAL;
+
+	port->mux_input = i + 1;
+
+	if (saa7164_api_set_videomux(port) != SAA_OK)
+		return -EIO;
+
+	return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+	struct v4l2_tuner *t)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	if (0 != t->index)
+		return -EINVAL;
+
+	strcpy(t->name, "tuner");
+	t->type = V4L2_TUNER_ANALOG_TV;
+	t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO;
+
+	dprintk(DBGLVL_ENC, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
+
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+	struct v4l2_tuner *t)
+{
+	/* Update the A/V core */
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+	struct v4l2_frequency *f)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+
+	f->type = V4L2_TUNER_ANALOG_TV;
+	f->frequency = port->freq;
+
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+	struct v4l2_frequency *f)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_port *tsport;
+	struct dvb_frontend *fe;
+
+	/* TODO: Pull this for the std */
+	struct analog_parameters params = {
+		.mode      = V4L2_TUNER_ANALOG_TV,
+		.audmode   = V4L2_TUNER_MODE_STEREO,
+		.std       = port->encodernorm.id,
+		.frequency = f->frequency
+	};
+
+	/* Stop the encoder */
+	dprintk(DBGLVL_ENC, "%s() frequency=%d tuner=%d\n", __func__,
+		f->frequency, f->tuner);
+
+	if (f->tuner != 0)
+		return -EINVAL;
+
+	if (f->type != V4L2_TUNER_ANALOG_TV)
+		return -EINVAL;
+
+	port->freq = f->frequency;
+
+	/* Update the hardware */
+	if (port->nr == SAA7164_PORT_ENC1)
+		tsport = &dev->ports[SAA7164_PORT_TS1];
+	else
+	if (port->nr == SAA7164_PORT_ENC2)
+		tsport = &dev->ports[SAA7164_PORT_TS2];
+	else
+		BUG();
+
+	fe = tsport->dvb.frontend;
+
+	if (fe && fe->ops.tuner_ops.set_analog_params)
+		fe->ops.tuner_ops.set_analog_params(fe, &params);
+	else
+		printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__);
+
+	saa7164_encoder_initialize(port);
+
+	return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+	struct v4l2_control *ctl)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_ENC, "%s(id=%d, value=%d)\n", __func__,
+		ctl->id, ctl->value);
+
+	switch (ctl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		ctl->value = port->ctl_brightness;
+		break;
+	case V4L2_CID_CONTRAST:
+		ctl->value = port->ctl_contrast;
+		break;
+	case V4L2_CID_SATURATION:
+		ctl->value = port->ctl_saturation;
+		break;
+	case V4L2_CID_HUE:
+		ctl->value = port->ctl_hue;
+		break;
+	case V4L2_CID_SHARPNESS:
+		ctl->value = port->ctl_sharpness;
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+		ctl->value = port->ctl_volume;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+	struct v4l2_control *ctl)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+	int ret = 0;
+
+	dprintk(DBGLVL_ENC, "%s(id=%d, value=%d)\n", __func__,
+		ctl->id, ctl->value);
+
+	switch (ctl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_brightness = ctl->value;
+			saa7164_api_set_usercontrol(port,
+				PU_BRIGHTNESS_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_CONTRAST:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_contrast = ctl->value;
+			saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_SATURATION:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_saturation = ctl->value;
+			saa7164_api_set_usercontrol(port,
+				PU_SATURATION_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_HUE:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_hue = ctl->value;
+			saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_SHARPNESS:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_sharpness = ctl->value;
+			saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+		if ((ctl->value >= -83) && (ctl->value <= 24)) {
+			port->ctl_volume = ctl->value;
+			saa7164_api_set_audio_volume(port, port->ctl_volume);
+		} else
+			ret = -EINVAL;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int saa7164_get_ctrl(struct saa7164_port *port,
+	struct v4l2_ext_control *ctrl)
+{
+	struct saa7164_encoder_params *params = &port->encoder_params;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		ctrl->value = params->bitrate;
+		break;
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		ctrl->value = params->stream_type;
+		break;
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		ctrl->value = params->ctl_mute;
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		ctrl->value = params->ctl_aspect;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+		ctrl->value = params->bitrate_mode;
+		break;
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		ctrl->value = params->refdist;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+		ctrl->value = params->bitrate_peak;
+		break;
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		ctrl->value = params->gop_size;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+	struct v4l2_ext_controls *ctrls)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	int i, err = 0;
+
+	if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+		for (i = 0; i < ctrls->count; i++) {
+			struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+			err = saa7164_get_ctrl(port, ctrl);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+		}
+		return err;
+
+	}
+
+	return -EINVAL;
+}
+
+static int saa7164_try_ctrl(struct v4l2_ext_control *ctrl, int ac3)
+{
+	int ret = -EINVAL;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		if ((ctrl->value >= ENCODER_MIN_BITRATE) &&
+			(ctrl->value <= ENCODER_MAX_BITRATE))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		if ((ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) ||
+			(ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		if ((ctrl->value >= 0) &&
+			(ctrl->value <= 1))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		if ((ctrl->value >= V4L2_MPEG_VIDEO_ASPECT_1x1) &&
+			(ctrl->value <= V4L2_MPEG_VIDEO_ASPECT_221x100))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		if ((ctrl->value >= 0) &&
+			(ctrl->value <= 255))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+		if ((ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) ||
+			(ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		if ((ctrl->value >= 1) &&
+			(ctrl->value <= 3))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+		if ((ctrl->value >= ENCODER_MIN_BITRATE) &&
+			(ctrl->value <= ENCODER_MAX_BITRATE))
+			ret = 0;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+	struct v4l2_ext_controls *ctrls)
+{
+	int i, err = 0;
+
+	if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+		for (i = 0; i < ctrls->count; i++) {
+			struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+			err = saa7164_try_ctrl(ctrl, 0);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+		}
+		return err;
+	}
+
+	return -EINVAL;
+}
+
+static int saa7164_set_ctrl(struct saa7164_port *port,
+	struct v4l2_ext_control *ctrl)
+{
+	struct saa7164_encoder_params *params = &port->encoder_params;
+	int ret = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		params->bitrate = ctrl->value;
+		break;
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		params->stream_type = ctrl->value;
+		break;
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		params->ctl_mute = ctrl->value;
+		ret = saa7164_api_audio_mute(port, params->ctl_mute);
+		if (ret != SAA_OK) {
+			printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+				ret);
+			ret = -EIO;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		params->ctl_aspect = ctrl->value;
+		ret = saa7164_api_set_aspect_ratio(port);
+		if (ret != SAA_OK) {
+			printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+				ret);
+			ret = -EIO;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+		params->bitrate_mode = ctrl->value;
+		break;
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		params->refdist = ctrl->value;
+		break;
+	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+		params->bitrate_peak = ctrl->value;
+		break;
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		params->gop_size = ctrl->value;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* TODO: Update the hardware */
+
+	return ret;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+	struct v4l2_ext_controls *ctrls)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	int i, err = 0;
+
+	if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+		for (i = 0; i < ctrls->count; i++) {
+			struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+			err = saa7164_try_ctrl(ctrl, 0);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+			err = saa7164_set_ctrl(port, ctrl);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+		}
+		return err;
+
+	}
+
+	return -EINVAL;
+}
+
+static int vidioc_querycap(struct file *file, void  *priv,
+	struct v4l2_capability *cap)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	strcpy(cap->driver, dev->name);
+	strlcpy(cap->card, saa7164_boards[dev->board].name,
+		sizeof(cap->card));
+	sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+
+	cap->capabilities =
+		V4L2_CAP_VIDEO_CAPTURE |
+		V4L2_CAP_READWRITE     |
+		0;
+
+	cap->capabilities |= V4L2_CAP_TUNER;
+	cap->version = 0;
+
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
+	struct v4l2_fmtdesc *f)
+{
+	if (f->index != 0)
+		return -EINVAL;
+
+	strlcpy(f->description, "MPEG", sizeof(f->description));
+	f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		port->ts_packet_size * port->ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+	f->fmt.pix.width        = port->width;
+	f->fmt.pix.height       = port->height;
+
+	dprintk(DBGLVL_ENC, "VIDIOC_G_FMT: w: %d, h: %d\n",
+		port->width, port->height);
+
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		port->ts_packet_size * port->ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+	dprintk(DBGLVL_ENC, "VIDIOC_TRY_FMT: w: %d, h: %d\n",
+		port->width, port->height);
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		port->ts_packet_size * port->ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+
+	dprintk(DBGLVL_ENC, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+		f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+
+	return 0;
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+	return 0;
+}
+
+static int fill_queryctrl(struct saa7164_encoder_params *params,
+	struct v4l2_queryctrl *c)
+{
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 127);
+	case V4L2_CID_CONTRAST:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 66);
+	case V4L2_CID_SATURATION:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 62);
+	case V4L2_CID_HUE:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 128);
+	case V4L2_CID_SHARPNESS:
+		return v4l2_ctrl_query_fill(c, 0x0, 0x0f, 1, 8);
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		return v4l2_ctrl_query_fill(c, 0x0, 0x01, 1, 0);
+	case V4L2_CID_AUDIO_VOLUME:
+		return v4l2_ctrl_query_fill(c, -83, 24, 1, 20);
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		return v4l2_ctrl_query_fill(c,
+			ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE,
+			100000, ENCODER_DEF_BITRATE);
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		return v4l2_ctrl_query_fill(c,
+			V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
+			V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
+			1, V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		return v4l2_ctrl_query_fill(c,
+			V4L2_MPEG_VIDEO_ASPECT_1x1,
+			V4L2_MPEG_VIDEO_ASPECT_221x100,
+			1, V4L2_MPEG_VIDEO_ASPECT_4x3);
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		return v4l2_ctrl_query_fill(c, 1, 255, 1, 15);
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+		return v4l2_ctrl_query_fill(c,
+			V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
+			1, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		return v4l2_ctrl_query_fill(c,
+			1, 3, 1, 1);
+	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+		return v4l2_ctrl_query_fill(c,
+			ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE,
+			100000, ENCODER_DEF_BITRATE);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+	struct v4l2_queryctrl *c)
+{
+	struct saa7164_encoder_fh *fh = priv;
+	struct saa7164_port *port = fh->port;
+	int i, next;
+	u32 id = c->id;
+
+	memset(c, 0, sizeof(*c));
+
+	next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL);
+	c->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
+
+	for (i = 0; i < ARRAY_SIZE(saa7164_v4l2_ctrls); i++) {
+		if (next) {
+			if (c->id < saa7164_v4l2_ctrls[i])
+				c->id = saa7164_v4l2_ctrls[i];
+			else
+				continue;
+		}
+
+		if (c->id == saa7164_v4l2_ctrls[i])
+			return fill_queryctrl(&port->encoder_params, c);
+
+		if (c->id < saa7164_v4l2_ctrls[i])
+			break;
+	}
+
+	return -EINVAL;
+}
+
+static int saa7164_encoder_stop_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_ENC, "%s()    Stopped\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int saa7164_encoder_acquire_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_ENC, "%s() Acquired\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int saa7164_encoder_pause_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_ENC, "%s()   Paused\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+/* Firmware is very windows centric, meaning you have to transition
+ * the part through AVStream / KS Windows stages, forwards or backwards.
+ * States are: stopped, acquired (h/w), paused, started.
+ * We have to leave here will all of the soft buffers on the free list,
+ * else the cfg_post() func won't have soft buffers to correctly configure.
+ */
+static int saa7164_encoder_stop_streaming(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+	struct list_head *c, *n;
+	int ret;
+
+	dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
+
+	ret = saa7164_encoder_pause_port(port);
+	ret = saa7164_encoder_acquire_port(port);
+	ret = saa7164_encoder_stop_port(port);
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) Hardware stopped\n", __func__,
+		port->nr);
+
+	/* Reset the state of any allocated buffer resources */
+	mutex_lock(&port->dmaqueue_lock);
+
+	/* Reset the hard and soft buffer state */
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+		buf = list_entry(c, struct saa7164_buffer, list);
+		buf->flags = SAA7164_BUFFER_FREE;
+		buf->pos = 0;
+	}
+
+	list_for_each_safe(c, n, &port->list_buf_used.list) {
+		ubuf = list_entry(c, struct saa7164_user_buffer, list);
+		ubuf->pos = 0;
+		list_move_tail(&ubuf->list, &port->list_buf_free.list);
+	}
+
+	mutex_unlock(&port->dmaqueue_lock);
+
+	/* Free any allocated resources */
+	saa7164_encoder_buffers_dealloc(port);
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) Released\n", __func__, port->nr);
+
+	return ret;
+}
+
+static int saa7164_encoder_start_streaming(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int result, ret = 0;
+
+	dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
+
+	port->done_first_interrupt = 0;
+
+	/* allocate all of the PCIe DMA buffer resources on the fly,
+	 * allowing switching between TS and PS payloads without
+	 * requiring a complete driver reload.
+	 */
+	saa7164_encoder_buffers_alloc(port);
+
+	/* Configure the encoder with any cache values */
+	saa7164_api_set_encoder(port);
+	saa7164_api_get_encoder(port);
+
+	/* Place the empty buffers on the hardware */
+	saa7164_buffer_cfg_port(port);
+
+	/* Acquire the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n",
+			__func__, result);
+
+		/* Stop the hardware, regardless */
+		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+			printk(KERN_ERR "%s() acquire/forced stop transition "
+				"failed, res = 0x%x\n", __func__, result);
+		}
+		ret = -EIO;
+		goto out;
+	} else
+		dprintk(DBGLVL_ENC, "%s()   Acquired\n", __func__);
+
+	/* Pause the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n",
+				__func__, result);
+
+		/* Stop the hardware, regardless */
+		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+			printk(KERN_ERR "%s() pause/forced stop transition "
+				"failed, res = 0x%x\n", __func__, result);
+		}
+
+		ret = -EIO;
+		goto out;
+	} else
+		dprintk(DBGLVL_ENC, "%s()   Paused\n", __func__);
+
+	/* Start the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() run transition failed, result = 0x%x\n",
+				__func__, result);
+
+		/* Stop the hardware, regardless */
+		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+			printk(KERN_ERR "%s() run/forced stop transition "
+				"failed, res = 0x%x\n", __func__, result);
+		}
+
+		ret = -EIO;
+	} else
+		dprintk(DBGLVL_ENC, "%s()   Running\n", __func__);
+
+out:
+	return ret;
+}
+
+static int fops_open(struct file *file)
+{
+	struct saa7164_dev *dev;
+	struct saa7164_port *port;
+	struct saa7164_encoder_fh *fh;
+
+	port = (struct saa7164_port *)video_get_drvdata(video_devdata(file));
+	if (!port)
+		return -ENODEV;
+
+	dev = port->dev;
+
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	/* allocate + initialize per filehandle data */
+	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+	if (NULL == fh)
+		return -ENOMEM;
+
+	file->private_data = fh;
+	fh->port = port;
+
+	return 0;
+}
+
+static int fops_release(struct file *file)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	/* Shut device down on last close */
+	if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
+		if (atomic_dec_return(&port->v4l_reader_count) == 0) {
+			/* stop mpeg capture then cancel buffers */
+			saa7164_encoder_stop_streaming(port);
+		}
+	}
+
+	file->private_data = NULL;
+	kfree(fh);
+
+	return 0;
+}
+
+struct saa7164_user_buffer *saa7164_enc_next_buf(struct saa7164_port *port)
+{
+	struct saa7164_user_buffer *ubuf = 0;
+	struct saa7164_dev *dev = port->dev;
+	u32 crc;
+
+	mutex_lock(&port->dmaqueue_lock);
+	if (!list_empty(&port->list_buf_used.list)) {
+		ubuf = list_first_entry(&port->list_buf_used.list,
+			struct saa7164_user_buffer, list);
+
+		if (crc_checking) {
+			crc = crc32(0, ubuf->data, ubuf->actual_size);
+			if (crc != ubuf->crc) {
+				printk(KERN_ERR "%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n", __func__,
+					ubuf, ubuf->crc, crc);
+			}
+		}
+
+	}
+	mutex_unlock(&port->dmaqueue_lock);
+
+	dprintk(DBGLVL_ENC, "%s() returns %p\n", __func__, ubuf);
+
+	return ubuf;
+}
+
+static ssize_t fops_read(struct file *file, char __user *buffer,
+	size_t count, loff_t *pos)
+{
+	struct saa7164_encoder_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_user_buffer *ubuf = NULL;
+	struct saa7164_dev *dev = port->dev;
+	int ret = 0;
+	int rem, cnt;
+	u8 *p;
+
+	port->last_read_msecs_diff = port->last_read_msecs;
+	port->last_read_msecs = jiffies_to_msecs(jiffies);
+	port->last_read_msecs_diff = port->last_read_msecs -
+		port->last_read_msecs_diff;
+
+	saa7164_histogram_update(&port->read_interval,
+		port->last_read_msecs_diff);
+
+	if (*pos) {
+		printk(KERN_ERR "%s() ESPIPE\n", __func__);
+		return -ESPIPE;
+	}
+
+	if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+		if (atomic_inc_return(&port->v4l_reader_count) == 1) {
+
+			if (saa7164_encoder_initialize(port) < 0) {
+				printk(KERN_ERR "%s() EINVAL\n", __func__);
+				return -EINVAL;
+			}
+
+			saa7164_encoder_start_streaming(port);
+			msleep(200);
+		}
+	}
+
+	/* blocking wait for buffer */
+	if ((file->f_flags & O_NONBLOCK) == 0) {
+		if (wait_event_interruptible(port->wait_read,
+			saa7164_enc_next_buf(port))) {
+				printk(KERN_ERR "%s() ERESTARTSYS\n", __func__);
+				return -ERESTARTSYS;
+		}
+	}
+
+	/* Pull the first buffer from the used list */
+	ubuf = saa7164_enc_next_buf(port);
+
+	while ((count > 0) && ubuf) {
+
+		/* set remaining bytes to copy */
+		rem = ubuf->actual_size - ubuf->pos;
+		cnt = rem > count ? count : rem;
+
+		p = ubuf->data + ubuf->pos;
+
+		dprintk(DBGLVL_ENC,
+			"%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n",
+			__func__, (int)count, cnt, rem, ubuf, ubuf->pos);
+
+		if (copy_to_user(buffer, p, cnt)) {
+			printk(KERN_ERR "%s() copy_to_user failed\n", __func__);
+			if (!ret) {
+				printk(KERN_ERR "%s() EFAULT\n", __func__);
+				ret = -EFAULT;
+			}
+			goto err;
+		}
+
+		ubuf->pos += cnt;
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+
+		if (ubuf->pos > ubuf->actual_size) {
+			printk(KERN_ERR "read() pos > actual, huh?\n");
+		}
+
+		if (ubuf->pos == ubuf->actual_size) {
+
+			/* finished with current buffer, take next buffer */
+
+			/* Requeue the buffer on the free list */
+			ubuf->pos = 0;
+
+			mutex_lock(&port->dmaqueue_lock);
+			list_move_tail(&ubuf->list, &port->list_buf_free.list);
+			mutex_unlock(&port->dmaqueue_lock);
+
+			/* Dequeue next */
+			if ((file->f_flags & O_NONBLOCK) == 0) {
+				if (wait_event_interruptible(port->wait_read,
+					saa7164_enc_next_buf(port))) {
+						break;
+				}
+			}
+			ubuf = saa7164_enc_next_buf(port);
+		}
+	}
+err:
+	if (!ret && !ubuf) {
+		ret = -EAGAIN;
+	}
+
+	return ret;
+}
+
+static unsigned int fops_poll(struct file *file, poll_table *wait)
+{
+	struct saa7164_encoder_fh *fh = (struct saa7164_encoder_fh *)file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_user_buffer *ubuf;
+	unsigned int mask = 0;
+
+	port->last_poll_msecs_diff = port->last_poll_msecs;
+	port->last_poll_msecs = jiffies_to_msecs(jiffies);
+	port->last_poll_msecs_diff = port->last_poll_msecs -
+		port->last_poll_msecs_diff;
+
+	saa7164_histogram_update(&port->poll_interval,
+		port->last_poll_msecs_diff);
+
+	if (!video_is_registered(port->v4l_device)) {
+		return -EIO;
+	}
+
+	if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+		if (atomic_inc_return(&port->v4l_reader_count) == 1) {
+			if (saa7164_encoder_initialize(port) < 0)
+				return -EINVAL;
+			saa7164_encoder_start_streaming(port);
+			msleep(200);
+		}
+	}
+
+	/* blocking wait for buffer */
+	if ((file->f_flags & O_NONBLOCK) == 0) {
+		if (wait_event_interruptible(port->wait_read,
+			saa7164_enc_next_buf(port))) {
+				return -ERESTARTSYS;
+		}
+	}
+
+	/* Pull the first buffer from the used list */
+	ubuf = list_first_entry(&port->list_buf_used.list,
+		struct saa7164_user_buffer, list);
+
+	if (ubuf)
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+
+static const struct v4l2_file_operations mpeg_fops = {
+	.owner		= THIS_MODULE,
+	.open		= fops_open,
+	.release	= fops_release,
+	.read		= fops_read,
+	.poll		= fops_poll,
+	.unlocked_ioctl	= video_ioctl2,
+};
+
+int saa7164_g_chip_ident(struct file *file, void *fh,
+	struct v4l2_dbg_chip_ident *chip)
+{
+	struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
+	struct saa7164_dev *dev = port->dev;
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	return 0;
+}
+
+int saa7164_g_register(struct file *file, void *fh,
+	struct v4l2_dbg_register *reg)
+{
+	struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
+	struct saa7164_dev *dev = port->dev;
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	return 0;
+}
+
+int saa7164_s_register(struct file *file, void *fh,
+	struct v4l2_dbg_register *reg)
+{
+	struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
+	struct saa7164_dev *dev = port->dev;
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
+	.vidioc_s_std		 = vidioc_s_std,
+	.vidioc_enum_input	 = vidioc_enum_input,
+	.vidioc_g_input		 = vidioc_g_input,
+	.vidioc_s_input		 = vidioc_s_input,
+	.vidioc_g_tuner		 = vidioc_g_tuner,
+	.vidioc_s_tuner		 = vidioc_s_tuner,
+	.vidioc_g_frequency	 = vidioc_g_frequency,
+	.vidioc_s_frequency	 = vidioc_s_frequency,
+	.vidioc_s_ctrl		 = vidioc_s_ctrl,
+	.vidioc_g_ctrl		 = vidioc_g_ctrl,
+	.vidioc_querycap	 = vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap	 = vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap	 = vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap	 = vidioc_s_fmt_vid_cap,
+	.vidioc_g_ext_ctrls	 = vidioc_g_ext_ctrls,
+	.vidioc_s_ext_ctrls	 = vidioc_s_ext_ctrls,
+	.vidioc_try_ext_ctrls	 = vidioc_try_ext_ctrls,
+	.vidioc_log_status	 = vidioc_log_status,
+	.vidioc_queryctrl	 = vidioc_queryctrl,
+	.vidioc_g_chip_ident	 = saa7164_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_register	 = saa7164_g_register,
+	.vidioc_s_register	 = saa7164_s_register,
+#endif
+};
+
+static struct video_device saa7164_mpeg_template = {
+	.name          = "saa7164",
+	.fops          = &mpeg_fops,
+	.ioctl_ops     = &mpeg_ioctl_ops,
+	.minor         = -1,
+	.tvnorms       = SAA7164_NORMS,
+	.current_norm  = V4L2_STD_NTSC_M,
+};
+
+static struct video_device *saa7164_encoder_alloc(
+	struct saa7164_port *port,
+	struct pci_dev *pci,
+	struct video_device *template,
+	char *type)
+{
+	struct video_device *vfd;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	vfd = video_device_alloc();
+	if (NULL == vfd)
+		return NULL;
+
+	*vfd = *template;
+	snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
+		type, saa7164_boards[dev->board].name);
+
+	vfd->parent  = &pci->dev;
+	vfd->release = video_device_release;
+	return vfd;
+}
+
+int saa7164_encoder_register(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int result = -ENODEV;
+
+	dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+	if (port->type != SAA7164_MPEG_ENCODER)
+		BUG();
+
+	/* Sanity check that the PCI configuration space is active */
+	if (port->hwcfg.BARLocation == 0) {
+		printk(KERN_ERR "%s() failed "
+		       "(errno = %d), NO PCI configuration\n",
+			__func__, result);
+		result = -ENOMEM;
+		goto failed;
+	}
+
+	/* Establish encoder defaults here */
+	/* Set default TV standard */
+	port->encodernorm = saa7164_tvnorms[0];
+	port->width = 720;
+	port->mux_input = 1; /* Composite */
+	port->video_format = EU_VIDEO_FORMAT_MPEG_2;
+	port->audio_format = 0;
+	port->video_resolution = 0;
+	port->ctl_brightness = 127;
+	port->ctl_contrast = 66;
+	port->ctl_hue = 128;
+	port->ctl_saturation = 62;
+	port->ctl_sharpness = 8;
+	port->encoder_params.bitrate = ENCODER_DEF_BITRATE;
+	port->encoder_params.bitrate_peak = ENCODER_DEF_BITRATE;
+	port->encoder_params.bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+	port->encoder_params.stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS;
+	port->encoder_params.ctl_mute = 0;
+	port->encoder_params.ctl_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3;
+	port->encoder_params.refdist = 1;
+	port->encoder_params.gop_size = SAA7164_ENCODER_DEFAULT_GOP_SIZE;
+
+	if (port->encodernorm.id & V4L2_STD_525_60)
+		port->height = 480;
+	else
+		port->height = 576;
+
+	/* Allocate and register the video device node */
+	port->v4l_device = saa7164_encoder_alloc(port,
+		dev->pci, &saa7164_mpeg_template, "mpeg");
+
+	if (port->v4l_device == NULL) {
+		printk(KERN_INFO "%s: can't allocate mpeg device\n",
+			dev->name);
+		result = -ENOMEM;
+		goto failed;
+	}
+
+	video_set_drvdata(port->v4l_device, port);
+	result = video_register_device(port->v4l_device,
+		VFL_TYPE_GRABBER, -1);
+	if (result < 0) {
+		printk(KERN_INFO "%s: can't register mpeg device\n",
+			dev->name);
+		/* TODO: We're going to leak here if we don't dealloc
+		 The buffers above. The unreg function can't deal wit it.
+		*/
+		goto failed;
+	}
+
+	printk(KERN_INFO "%s: registered device video%d [mpeg]\n",
+		dev->name, port->v4l_device->num);
+
+	/* Configure the hardware defaults */
+	saa7164_api_set_videomux(port);
+	saa7164_api_set_usercontrol(port, PU_BRIGHTNESS_CONTROL);
+	saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
+	saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
+	saa7164_api_set_usercontrol(port, PU_SATURATION_CONTROL);
+	saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
+	saa7164_api_audio_mute(port, 0);
+	saa7164_api_set_audio_volume(port, 20);
+	saa7164_api_set_aspect_ratio(port);
+
+	/* Disable audio standard detection, it's buggy */
+	saa7164_api_set_audio_detection(port, 0);
+
+	saa7164_api_set_encoder(port);
+	saa7164_api_get_encoder(port);
+
+	result = 0;
+failed:
+	return result;
+}
+
+void saa7164_encoder_unregister(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
+
+	if (port->type != SAA7164_MPEG_ENCODER)
+		BUG();
+
+	if (port->v4l_device) {
+		if (port->v4l_device->minor != -1)
+			video_unregister_device(port->v4l_device);
+		else
+			video_device_release(port->v4l_device);
+
+		port->v4l_device = NULL;
+	}
+
+	dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr);
+}
+
diff --git a/drivers/media/video/saa7164/saa7164-fw.c b/drivers/media/video/saa7164/saa7164-fw.c
index 270245d..484533c 100644
--- a/drivers/media/video/saa7164/saa7164-fw.c
+++ b/drivers/media/video/saa7164/saa7164-fw.c
@@ -1,7 +1,7 @@
 /*
  *  Driver for the NXP SAA7164 PCIe bridge
  *
- *  Copyright (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,11 +24,11 @@
 
 #include "saa7164.h"
 
-#define SAA7164_REV2_FIRMWARE		"v4l-saa7164-1.0.2.fw"
-#define SAA7164_REV2_FIRMWARE_SIZE	3978608
+#define SAA7164_REV2_FIRMWARE		"NXP7164-2010-03-10.1.fw"
+#define SAA7164_REV2_FIRMWARE_SIZE	4019072
 
-#define SAA7164_REV3_FIRMWARE		"v4l-saa7164-1.0.3.fw"
-#define SAA7164_REV3_FIRMWARE_SIZE	3978608
+#define SAA7164_REV3_FIRMWARE		"NXP7164-2010-03-10.1.fw"
+#define SAA7164_REV3_FIRMWARE_SIZE	4019072
 
 struct fw_header {
 	u32	firmwaresize;
@@ -604,6 +604,7 @@
 		}
 	}
 
+	dev->firmwareloaded = 1;
 	ret = 0;
 
 out:
diff --git a/drivers/media/video/saa7164/saa7164-i2c.c b/drivers/media/video/saa7164/saa7164-i2c.c
index e1ae9b0..b5167d3 100644
--- a/drivers/media/video/saa7164/saa7164-i2c.c
+++ b/drivers/media/video/saa7164/saa7164-i2c.c
@@ -1,7 +1,7 @@
 /*
  *  Driver for the NXP SAA7164 PCIe bridge
  *
- *  Copyright (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
diff --git a/drivers/media/video/saa7164/saa7164-reg.h b/drivers/media/video/saa7164/saa7164-reg.h
index 06be4c1..2bbf815 100644
--- a/drivers/media/video/saa7164/saa7164-reg.h
+++ b/drivers/media/video/saa7164/saa7164-reg.h
@@ -1,7 +1,7 @@
 /*
  *  Driver for the NXP SAA7164 PCIe bridge
  *
- *  Copyright (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -60,6 +60,7 @@
 #define GET_STRING_CONTROL		0x03
 #define GET_LANGUAGE_CONTROL		0x05
 #define SET_POWER_CONTROL		0x07
+#define GET_FW_STATUS_CONTROL		0x08
 #define GET_FW_VERSION_CONTROL		0x09
 #define SET_DEBUG_LEVEL_CONTROL		0x0B
 #define GET_DEBUG_DATA_CONTROL		0x0C
@@ -156,11 +157,63 @@
 #define EXU_INTERRUPT_CONTROL		0x03
 
 /* State Transition and args */
+#define SAA_PROBE_CONTROL	0x01
+#define SAA_COMMIT_CONTROL	0x02
 #define SAA_STATE_CONTROL	0x03
 #define SAA_DMASTATE_STOP	0x00
 #define SAA_DMASTATE_ACQUIRE	0x01
 #define SAA_DMASTATE_PAUSE	0x02
 #define SAA_DMASTATE_RUN	0x03
 
-/* Hardware registers */
+/* A/V Mux Input Selector */
+#define SU_INPUT_SELECT_CONTROL 0x01
 
+/* Encoder Profiles */
+#define EU_PROFILE_PS_DVD	0x06
+#define EU_PROFILE_TS_HQ	0x09
+#define EU_VIDEO_FORMAT_MPEG_2	0x02
+
+/* Tuner */
+#define TU_AUDIO_MODE_CONTROL  0x17
+
+/* Video Formats */
+#define TU_STANDARD_CONTROL		0x00
+#define TU_STANDARD_AUTO_CONTROL	0x01
+#define TU_STANDARD_NONE		0x00
+#define TU_STANDARD_NTSC_M		0x01
+#define TU_STANDARD_PAL_I		0x08
+#define TU_STANDARD_MANUAL		0x00
+#define TU_STANDARD_AUTO		0x01
+
+/* Video Controls */
+#define PU_BRIGHTNESS_CONTROL	0x02
+#define PU_CONTRAST_CONTROL	0x03
+#define PU_HUE_CONTROL		0x06
+#define PU_SATURATION_CONTROL	0x07
+#define PU_SHARPNESS_CONTROL	0x08
+
+/* Audio Controls */
+#define MUTE_CONTROL		0x01
+#define VOLUME_CONTROL		0x02
+#define AUDIO_DEFAULT_CONTROL	0x0D
+
+/* Default Volume Levels */
+#define TMHW_LEV_ADJ_DECLEV_DEFAULT     0x00
+#define TMHW_LEV_ADJ_MONOLEV_DEFAULT    0x00
+#define TMHW_LEV_ADJ_NICLEV_DEFAULT     0x00
+#define TMHW_LEV_ADJ_SAPLEV_DEFAULT     0x00
+#define TMHW_LEV_ADJ_ADCLEV_DEFAULT     0x00
+
+/* Encoder Related Commands */
+#define EU_PROFILE_CONTROL		0x00
+#define EU_VIDEO_FORMAT_CONTROL		0x01
+#define EU_VIDEO_BIT_RATE_CONTROL	0x02
+#define EU_VIDEO_RESOLUTION_CONTROL	0x03
+#define EU_VIDEO_GOP_STRUCTURE_CONTROL	0x04
+#define EU_VIDEO_INPUT_ASPECT_CONTROL	0x0A
+#define EU_AUDIO_FORMAT_CONTROL		0x0C
+#define EU_AUDIO_BIT_RATE_CONTROL	0x0D
+
+/* Firmware Debugging */
+#define SET_DEBUG_LEVEL_CONTROL	0x0B
+#define GET_DEBUG_DATA_CONTROL	0x0C
diff --git a/drivers/media/video/saa7164/saa7164-types.h b/drivers/media/video/saa7164/saa7164-types.h
index 99093f2..df1d299 100644
--- a/drivers/media/video/saa7164/saa7164-types.h
+++ b/drivers/media/video/saa7164/saa7164-types.h
@@ -1,7 +1,7 @@
 /*
  *  Driver for the NXP SAA7164 PCIe bridge
  *
- *  Copyright (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,7 +24,7 @@
 /* Some structues are passed directly to/from the firmware and
  * have strict alignment requirements. This is one of them.
  */
-typedef struct {
+struct tmComResHWDescr {
 	u8	bLength;
 	u8	bDescriptorType;
 	u8	bDescriptorSubtype;
@@ -37,14 +37,14 @@
 	u32	dwHostMemoryRegionSize;
 	u32	dwHostHibernatMemRegion;
 	u32	dwHostHibernatMemRegionSize;
-} __attribute__((packed)) tmComResHWDescr_t;
+} __attribute__((packed));
 
 /* This is DWORD aligned on windows but I can't find the right
  * gcc syntax to match the binary data from the device.
  * I've manually padded with Reserved[3] bytes to match the hardware,
  * but this could break if GCC decies to pack in a different way.
  */
-typedef struct {
+struct tmComResInterfaceDescr {
 	u8	bLength;
 	u8	bDescriptorType;
 	u8	bDescriptorSubtype;
@@ -56,52 +56,52 @@
 	u8	bDebugInterruptId;
 	u8	BARLocation;
 	u8	Reserved[3];
-} tmComResInterfaceDescr_t;
+};
 
-typedef struct {
+struct tmComResBusDescr {
 	u64	CommandRing;
 	u64	ResponseRing;
 	u32	CommandWrite;
 	u32	CommandRead;
 	u32	ResponseWrite;
 	u32	ResponseRead;
-} tmComResBusDescr_t;
+};
 
-typedef enum {
+enum tmBusType {
 	NONE		= 0,
 	TYPE_BUS_PCI	= 1,
 	TYPE_BUS_PCIe	= 2,
 	TYPE_BUS_USB	= 3,
 	TYPE_BUS_I2C	= 4
-} tmBusType_t;
+};
 
-typedef struct {
-	tmBusType_t Type;
+struct tmComResBusInfo {
+	enum tmBusType Type;
 	u16	m_wMaxReqSize;
 	u8	*m_pdwSetRing;
 	u32	m_dwSizeSetRing;
 	u8	*m_pdwGetRing;
 	u32	m_dwSizeGetRing;
-	u32	*m_pdwSetWritePos;
-	u32	*m_pdwSetReadPos;
-	u32	*m_pdwGetWritePos;
-	u32	*m_pdwGetReadPos;
+	u32	m_dwSetWritePos;
+	u32	m_dwSetReadPos;
+	u32	m_dwGetWritePos;
+	u32	m_dwGetReadPos;
 
 	/* All access is protected */
 	struct mutex lock;
 
-} tmComResBusInfo_t;
+};
 
-typedef struct {
+struct tmComResInfo {
 	u8	id;
 	u8	flags;
 	u16	size;
 	u32	command;
 	u16	controlselector;
 	u8	seqno;
-} __attribute__((packed)) tmComResInfo_t;
+} __attribute__((packed));
 
-typedef enum {
+enum tmComResCmd {
 	SET_CUR  = 0x01,
 	GET_CUR  = 0x81,
 	GET_MIN  = 0x82,
@@ -110,7 +110,7 @@
 	GET_LEN  = 0x85,
 	GET_INFO = 0x86,
 	GET_DEF  = 0x87
-} tmComResCmd_t;
+};
 
 struct cmd {
 	u8 seqno;
@@ -121,20 +121,20 @@
 	wait_queue_head_t wait;
 };
 
-typedef struct {
+struct tmDescriptor {
 	u32	pathid;
 	u32	size;
 	void	*descriptor;
-} tmDescriptor_t;
+};
 
-typedef struct {
+struct tmComResDescrHeader {
 	u8	len;
 	u8	type;
 	u8	subtype;
 	u8	unitid;
-} __attribute__((packed)) tmComResDescrHeader_t;
+} __attribute__((packed));
 
-typedef struct {
+struct tmComResExtDevDescrHeader {
 	u8	len;
 	u8	type;
 	u8	subtype;
@@ -144,22 +144,22 @@
 	u32	numgpiopins;
 	u8	numgpiogroups;
 	u8	controlsize;
-} __attribute__((packed)) tmComResExtDevDescrHeader_t;
+} __attribute__((packed));
 
-typedef struct {
+struct tmComResGPIO {
 	u32	pin;
 	u8	state;
-} __attribute__((packed)) tmComResGPIO_t;
+} __attribute__((packed));
 
-typedef struct {
+struct tmComResPathDescrHeader {
 	u8	len;
 	u8	type;
 	u8	subtype;
 	u8	pathid;
-} __attribute__((packed)) tmComResPathDescrHeader_t;
+} __attribute__((packed));
 
 /* terminaltype */
-typedef enum {
+enum tmComResTermType {
 	ITT_ANTENNA              = 0x0203,
 	LINE_CONNECTOR           = 0x0603,
 	SPDIF_CONNECTOR          = 0x0605,
@@ -167,9 +167,9 @@
 	SVIDEO_CONNECTOR         = 0x0402,
 	COMPONENT_CONNECTOR      = 0x0403,
 	STANDARD_DMA             = 0xF101
-} tmComResTermType_t;
+};
 
-typedef struct {
+struct tmComResAntTermDescrHeader {
 	u8	len;
 	u8	type;
 	u8	subtype;
@@ -178,9 +178,9 @@
 	u8	assocterminal;
 	u8	iterminal;
 	u8	controlsize;
-} __attribute__((packed)) tmComResAntTermDescrHeader_t;
+} __attribute__((packed));
 
-typedef struct {
+struct tmComResTunerDescrHeader {
 	u8	len;
 	u8	type;
 	u8	subtype;
@@ -190,9 +190,9 @@
 	u32	tuningstandards;
 	u8	controlsize;
 	u32	controls;
-} __attribute__((packed)) tmComResTunerDescrHeader_t;
+} __attribute__((packed));
 
-typedef enum {
+enum tmBufferFlag {
 	/* the buffer does not contain any valid data */
 	TM_BUFFER_FLAG_EMPTY,
 
@@ -201,23 +201,23 @@
 
 	/* the buffer is the dummy buffer - TODO??? */
 	TM_BUFFER_FLAG_DUMMY_BUFFER
-} tmBufferFlag_t;
+};
 
-typedef struct {
+struct tmBuffer {
 	u64		*pagetablevirt;
 	u64		pagetablephys;
 	u16		offset;
 	u8		*context;
 	u64		timestamp;
-	tmBufferFlag_t	BufferFlag_t;
+	enum tmBufferFlag BufferFlag;
 	u32		lostbuffers;
 	u32		validbuffers;
 	u64		*dummypagevirt;
 	u64		dummypagephys;
 	u64		*addressvirt;
-} tmBuffer_t;
+};
 
-typedef struct {
+struct tmHWStreamParameters {
 	u32	bitspersample;
 	u32	samplesperline;
 	u32	numberoflines;
@@ -227,15 +227,15 @@
 	u64	*pagetablelistphys;
 	u32	numpagetables;
 	u32	numpagetableentries;
-} tmHWStreamParameters_t;
+};
 
-typedef struct {
-	tmHWStreamParameters_t		HWStreamParameters_t;
+struct tmStreamParameters {
+	struct tmHWStreamParameters	HWStreamParameters;
 	u64				qwDummyPageTablePhys;
 	u64				*pDummyPageTableVirt;
-} tmStreamParameters_t;
+};
 
-typedef struct {
+struct tmComResDMATermDescrHeader {
 	u8	len;
 	u8	type;
 	u8	subtyle;
@@ -251,7 +251,7 @@
 	u8	metadatasize;
 	u8	numformats;
 	u8	controlsize;
-} __attribute__((packed)) tmComResDMATermDescrHeader_t;
+} __attribute__((packed));
 
 /*
  *
@@ -274,7 +274,7 @@
  *                            Data is to be ignored by the application.
  *
  */
-typedef struct {
+struct tmComResTSFormatDescrHeader {
 	u8	len;
 	u8	type;
 	u8	subtype;
@@ -283,5 +283,160 @@
 	u8	bPacketLength;
 	u8	bStrideLength;
 	u8	guidStrideFormat[16];
-} __attribute__((packed)) tmComResTSFormatDescrHeader_t;
+} __attribute__((packed));
 
+/* Encoder related structures */
+
+/* A/V Mux Selector */
+struct tmComResSelDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	unitid;
+	u8	nrinpins;
+	u8	sourceid;
+} __attribute__((packed));
+
+/* A/V Audio processor definitions */
+struct tmComResProcDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	unitid;
+	u8	sourceid;
+	u16	wreserved;
+	u8	controlsize;
+} __attribute__((packed));
+
+/* Video bitrate control message */
+#define EU_VIDEO_BIT_RATE_MODE_CONSTANT		(0)
+#define EU_VIDEO_BIT_RATE_MODE_VARIABLE_AVERAGE (1)
+#define EU_VIDEO_BIT_RATE_MODE_VARIABLE_PEAK	(2)
+struct tmComResEncVideoBitRate {
+	u8	ucVideoBitRateMode;
+	u32	dwVideoBitRate;
+	u32	dwVideoBitRatePeak;
+} __attribute__((packed));
+
+/* Video Encoder Aspect Ratio message */
+struct tmComResEncVideoInputAspectRatio {
+	u8	width;
+	u8	height;
+} __attribute__((packed));
+
+/* Video Encoder GOP IBP message */
+/* 1. IPPPPPPPPPPPPPP */
+/* 2. IBPBPBPBPBPBPBP */
+/* 3. IBBPBBPBBPBBP   */
+#define SAA7164_ENCODER_DEFAULT_GOP_DIST (1)
+#define SAA7164_ENCODER_DEFAULT_GOP_SIZE (15)
+struct tmComResEncVideoGopStructure {
+	u8	ucGOPSize;	/* GOP Size 12, 15 */
+	u8	ucRefFrameDist; /* Reference Frame Distance */
+} __attribute__((packed));
+
+/* Encoder processor definition */
+struct tmComResEncoderDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	unitid;
+	u8	vsourceid;
+	u8	asourceid;
+	u8	iunit;
+	u32	dwmControlCap;
+	u32	dwmProfileCap;
+	u32	dwmVidFormatCap;
+	u8	bmVidBitrateCap;
+	u16	wmVidResolutionsCap;
+	u16	wmVidFrmRateCap;
+	u32	dwmAudFormatCap;
+	u8	bmAudBitrateCap;
+} __attribute__((packed));
+
+/* Audio processor definition */
+struct tmComResAFeatureDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	unitid;
+	u8	sourceid;
+	u8	controlsize;
+} __attribute__((packed));
+
+/* Audio control messages */
+struct tmComResAudioDefaults {
+	u8	ucDecoderLevel;
+	u8	ucDecoderFM_Level;
+	u8	ucMonoLevel;
+	u8	ucNICAM_Level;
+	u8	ucSAP_Level;
+	u8	ucADC_Level;
+} __attribute__((packed));
+
+/* Audio bitrate control message */
+struct tmComResEncAudioBitRate {
+	u8	ucAudioBitRateMode;
+	u32	dwAudioBitRate;
+	u32	dwAudioBitRatePeak;
+} __attribute__((packed));
+
+/* Tuner / AV Decoder messages */
+struct tmComResTunerStandard {
+	u8	std;
+	u32	country;
+} __attribute__((packed));
+
+struct tmComResTunerStandardAuto {
+	u8	mode;
+} __attribute__((packed));
+
+/* EEPROM definition for PS stream types */
+struct tmComResPSFormatDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype;
+	u8	bFormatIndex;
+	u16	wPacketLength;
+	u16	wPackLength;
+	u8	bPackDataType;
+} __attribute__((packed));
+
+/* VBI control structure */
+struct tmComResVBIFormatDescrHeader {
+	u8	len;
+	u8	type;
+	u8	subtype; /* VS_FORMAT_VBI */
+	u8	bFormatIndex;
+	u32	VideoStandard; /* See KS_AnalogVideoStandard, NTSC = 1 */
+	u8	StartLine; /* NTSC Start = 10 */
+	u8	EndLine; /* NTSC = 21 */
+	u8	FieldRate; /* 60 for NTSC */
+	u8	bNumLines; /* Unsed - scheduled for removal */
+} __attribute__((packed));
+
+struct tmComResProbeCommit {
+	u16	bmHint;
+	u8	bFormatIndex;
+	u8	bFrameIndex;
+} __attribute__((packed));
+
+struct tmComResDebugSetLevel {
+	u32	dwDebugLevel;
+} __attribute__((packed));
+
+struct tmComResDebugGetData {
+	u32	dwResult;
+	u8	ucDebugData[256];
+} __attribute__((packed));
+
+struct tmFwInfoStruct {
+	u32	status;
+	u32	mode;
+	u32	devicespec;
+	u32	deviceinst;
+	u32	CPULoad;
+	u32	RemainHeap;
+	u32	CPUClock;
+	u32	RAMSpeed;
+} __attribute__((packed));
diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/video/saa7164/saa7164-vbi.c
new file mode 100644
index 0000000..323c7cd
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-vbi.c
@@ -0,0 +1,1375 @@
+/*
+ *  Driver for the NXP SAA7164 PCIe bridge
+ *
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "saa7164.h"
+
+static struct saa7164_tvnorm saa7164_tvnorms[] = {
+	{
+		.name      = "NTSC-M",
+		.id        = V4L2_STD_NTSC_M,
+	}, {
+		.name      = "NTSC-JP",
+		.id        = V4L2_STD_NTSC_M_JP,
+	}
+};
+
+static const u32 saa7164_v4l2_ctrls[] = {
+	0
+};
+
+/* Take the encoder configuration from the port struct and
+ * flush it to the hardware.
+ */
+static void saa7164_vbi_configure(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+	port->vbi_params.width = port->width;
+	port->vbi_params.height = port->height;
+	port->vbi_params.is_50hz =
+		(port->encodernorm.id & V4L2_STD_625_50) != 0;
+
+	/* Set up the DIF (enable it) for analog mode by default */
+	saa7164_api_initialize_dif(port);
+
+//	/* Configure the correct video standard */
+//	saa7164_api_configure_dif(port, port->encodernorm.id);
+
+//	/* Ensure the audio decoder is correct configured */
+//	saa7164_api_set_audio_std(port);
+	dprintk(DBGLVL_VBI, "%s() ends\n", __func__);
+}
+
+static int saa7164_vbi_buffers_dealloc(struct saa7164_port *port)
+{
+	struct list_head *c, *n, *p, *q, *l, *v;
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+
+	/* Remove any allocated buffers */
+	mutex_lock(&port->dmaqueue_lock);
+
+	dprintk(DBGLVL_VBI, "%s(port=%d) dmaqueue\n", __func__, port->nr);
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+		buf = list_entry(c, struct saa7164_buffer, list);
+		list_del(c);
+		saa7164_buffer_dealloc(buf);
+	}
+
+	dprintk(DBGLVL_VBI, "%s(port=%d) used\n", __func__, port->nr);
+	list_for_each_safe(p, q, &port->list_buf_used.list) {
+		ubuf = list_entry(p, struct saa7164_user_buffer, list);
+		list_del(p);
+		saa7164_buffer_dealloc_user(ubuf);
+	}
+
+	dprintk(DBGLVL_VBI, "%s(port=%d) free\n", __func__, port->nr);
+	list_for_each_safe(l, v, &port->list_buf_free.list) {
+		ubuf = list_entry(l, struct saa7164_user_buffer, list);
+		list_del(l);
+		saa7164_buffer_dealloc_user(ubuf);
+	}
+
+	mutex_unlock(&port->dmaqueue_lock);
+	dprintk(DBGLVL_VBI, "%s(port=%d) done\n", __func__, port->nr);
+
+	return 0;
+}
+
+/* Dynamic buffer switch at vbi start time */
+static int saa7164_vbi_buffers_alloc(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+	struct tmHWStreamParameters *params = &port->hw_streamingparams;
+	int result = -ENODEV, i;
+	int len = 0;
+
+	dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+	/* TODO: NTSC SPECIFIC */
+	/* Init and establish defaults */
+	params->samplesperline = 1440;
+	params->numberoflines = 12;
+	params->numberoflines = 18;
+	params->pitch = 1600;
+	params->pitch = 1440;
+	params->numpagetables = 2 +
+		((params->numberoflines * params->pitch) / PAGE_SIZE);
+	params->bitspersample = 8;
+	params->linethreshold = 0;
+	params->pagetablelistvirt = 0;
+	params->pagetablelistphys = 0;
+	params->numpagetableentries = port->hwcfg.buffercount;
+
+	/* Allocate the PCI resources, buffers (hard) */
+	for (i = 0; i < port->hwcfg.buffercount; i++) {
+		buf = saa7164_buffer_alloc(port,
+			params->numberoflines *
+			params->pitch);
+
+		if (!buf) {
+			printk(KERN_ERR "%s() failed "
+			       "(errno = %d), unable to allocate buffer\n",
+				__func__, result);
+			result = -ENOMEM;
+			goto failed;
+		} else {
+
+			mutex_lock(&port->dmaqueue_lock);
+			list_add_tail(&buf->list, &port->dmaqueue.list);
+			mutex_unlock(&port->dmaqueue_lock);
+
+		}
+	}
+
+	/* Allocate some kenrel kernel buffers for copying
+	 * to userpsace.
+	 */
+	len = params->numberoflines * params->pitch;
+
+	if (vbi_buffers < 16)
+		vbi_buffers = 16;
+	if (vbi_buffers > 512)
+		vbi_buffers = 512;
+
+	for (i = 0; i < vbi_buffers; i++) {
+
+		ubuf = saa7164_buffer_alloc_user(dev, len);
+		if (ubuf) {
+			mutex_lock(&port->dmaqueue_lock);
+			list_add_tail(&ubuf->list, &port->list_buf_free.list);
+			mutex_unlock(&port->dmaqueue_lock);
+		}
+
+	}
+
+	result = 0;
+
+failed:
+	return result;
+}
+
+
+static int saa7164_vbi_initialize(struct saa7164_port *port)
+{
+	saa7164_vbi_configure(port);
+	return 0;
+}
+
+/* -- V4L2 --------------------------------------------------------- */
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+	unsigned int i;
+
+	dprintk(DBGLVL_VBI, "%s(id=0x%x)\n", __func__, (u32)*id);
+
+	for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) {
+		if (*id & saa7164_tvnorms[i].id)
+			break;
+	}
+	if (i == ARRAY_SIZE(saa7164_tvnorms))
+		return -EINVAL;
+
+	port->encodernorm = saa7164_tvnorms[i];
+
+	/* Update the audio decoder while is not running in
+	 * auto detect mode.
+	 */
+	saa7164_api_set_audio_std(port);
+
+	dprintk(DBGLVL_VBI, "%s(id=0x%x) OK\n", __func__, (u32)*id);
+
+	return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+	struct v4l2_input *i)
+{
+	int n;
+
+	char *inputs[] = { "tuner", "composite", "svideo", "aux",
+		"composite 2", "svideo 2", "aux 2" };
+
+	if (i->index >= 7)
+		return -EINVAL;
+
+	strcpy(i->name, inputs[i->index]);
+
+	if (i->index == 0)
+		i->type = V4L2_INPUT_TYPE_TUNER;
+	else
+		i->type  = V4L2_INPUT_TYPE_CAMERA;
+
+	for (n = 0; n < ARRAY_SIZE(saa7164_tvnorms); n++)
+		i->std |= saa7164_tvnorms[n].id;
+
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	if (saa7164_api_get_videomux(port) != SAA_OK)
+		return -EIO;
+
+	*i = (port->mux_input - 1);
+
+	dprintk(DBGLVL_VBI, "%s() input=%d\n", __func__, *i);
+
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_VBI, "%s() input=%d\n", __func__, i);
+
+	if (i >= 7)
+		return -EINVAL;
+
+	port->mux_input = i + 1;
+
+	if (saa7164_api_set_videomux(port) != SAA_OK)
+		return -EIO;
+
+	return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+	struct v4l2_tuner *t)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	if (0 != t->index)
+		return -EINVAL;
+
+	strcpy(t->name, "tuner");
+	t->type = V4L2_TUNER_ANALOG_TV;
+	t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO;
+
+	dprintk(DBGLVL_VBI, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
+
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+	struct v4l2_tuner *t)
+{
+	/* Update the A/V core */
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+	struct v4l2_frequency *f)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+
+	f->type = V4L2_TUNER_ANALOG_TV;
+	f->frequency = port->freq;
+
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+	struct v4l2_frequency *f)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_port *tsport;
+	struct dvb_frontend *fe;
+
+	/* TODO: Pull this for the std */
+	struct analog_parameters params = {
+		.mode      = V4L2_TUNER_ANALOG_TV,
+		.audmode   = V4L2_TUNER_MODE_STEREO,
+		.std       = port->encodernorm.id,
+		.frequency = f->frequency
+	};
+
+	/* Stop the encoder */
+	dprintk(DBGLVL_VBI, "%s() frequency=%d tuner=%d\n", __func__,
+		f->frequency, f->tuner);
+
+	if (f->tuner != 0)
+		return -EINVAL;
+
+	if (f->type != V4L2_TUNER_ANALOG_TV)
+		return -EINVAL;
+
+	port->freq = f->frequency;
+
+	/* Update the hardware */
+	if (port->nr == SAA7164_PORT_VBI1)
+		tsport = &dev->ports[SAA7164_PORT_TS1];
+	else
+	if (port->nr == SAA7164_PORT_VBI2)
+		tsport = &dev->ports[SAA7164_PORT_TS2];
+	else
+		BUG();
+
+	fe = tsport->dvb.frontend;
+
+	if (fe && fe->ops.tuner_ops.set_analog_params)
+		fe->ops.tuner_ops.set_analog_params(fe, &params);
+	else
+		printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__);
+
+	saa7164_vbi_initialize(port);
+
+	return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+	struct v4l2_control *ctl)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_VBI, "%s(id=%d, value=%d)\n", __func__,
+		ctl->id, ctl->value);
+
+	switch (ctl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		ctl->value = port->ctl_brightness;
+		break;
+	case V4L2_CID_CONTRAST:
+		ctl->value = port->ctl_contrast;
+		break;
+	case V4L2_CID_SATURATION:
+		ctl->value = port->ctl_saturation;
+		break;
+	case V4L2_CID_HUE:
+		ctl->value = port->ctl_hue;
+		break;
+	case V4L2_CID_SHARPNESS:
+		ctl->value = port->ctl_sharpness;
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+		ctl->value = port->ctl_volume;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+	struct v4l2_control *ctl)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+	int ret = 0;
+
+	dprintk(DBGLVL_VBI, "%s(id=%d, value=%d)\n", __func__,
+		ctl->id, ctl->value);
+
+	switch (ctl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_brightness = ctl->value;
+			saa7164_api_set_usercontrol(port,
+				PU_BRIGHTNESS_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_CONTRAST:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_contrast = ctl->value;
+			saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_SATURATION:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_saturation = ctl->value;
+			saa7164_api_set_usercontrol(port,
+				PU_SATURATION_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_HUE:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_hue = ctl->value;
+			saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_SHARPNESS:
+		if ((ctl->value >= 0) && (ctl->value <= 255)) {
+			port->ctl_sharpness = ctl->value;
+			saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
+		} else
+			ret = -EINVAL;
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+		if ((ctl->value >= -83) && (ctl->value <= 24)) {
+			port->ctl_volume = ctl->value;
+			saa7164_api_set_audio_volume(port, port->ctl_volume);
+		} else
+			ret = -EINVAL;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int saa7164_get_ctrl(struct saa7164_port *port,
+	struct v4l2_ext_control *ctrl)
+{
+	struct saa7164_vbi_params *params = &port->vbi_params;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		ctrl->value = params->stream_type;
+		break;
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		ctrl->value = params->ctl_mute;
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		ctrl->value = params->ctl_aspect;
+		break;
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		ctrl->value = params->refdist;
+		break;
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		ctrl->value = params->gop_size;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+	struct v4l2_ext_controls *ctrls)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	int i, err = 0;
+
+	if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+		for (i = 0; i < ctrls->count; i++) {
+			struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+			err = saa7164_get_ctrl(port, ctrl);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+		}
+		return err;
+
+	}
+
+	return -EINVAL;
+}
+
+static int saa7164_try_ctrl(struct v4l2_ext_control *ctrl, int ac3)
+{
+	int ret = -EINVAL;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		if ((ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) ||
+			(ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		if ((ctrl->value >= 0) &&
+			(ctrl->value <= 1))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		if ((ctrl->value >= V4L2_MPEG_VIDEO_ASPECT_1x1) &&
+			(ctrl->value <= V4L2_MPEG_VIDEO_ASPECT_221x100))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		if ((ctrl->value >= 0) &&
+			(ctrl->value <= 255))
+			ret = 0;
+		break;
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		if ((ctrl->value >= 1) &&
+			(ctrl->value <= 3))
+			ret = 0;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+	struct v4l2_ext_controls *ctrls)
+{
+	int i, err = 0;
+
+	if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+		for (i = 0; i < ctrls->count; i++) {
+			struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+			err = saa7164_try_ctrl(ctrl, 0);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+		}
+		return err;
+	}
+
+	return -EINVAL;
+}
+
+static int saa7164_set_ctrl(struct saa7164_port *port,
+	struct v4l2_ext_control *ctrl)
+{
+	struct saa7164_vbi_params *params = &port->vbi_params;
+	int ret = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		params->stream_type = ctrl->value;
+		break;
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		params->ctl_mute = ctrl->value;
+		ret = saa7164_api_audio_mute(port, params->ctl_mute);
+		if (ret != SAA_OK) {
+			printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+				ret);
+			ret = -EIO;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		params->ctl_aspect = ctrl->value;
+		ret = saa7164_api_set_aspect_ratio(port);
+		if (ret != SAA_OK) {
+			printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+				ret);
+			ret = -EIO;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		params->refdist = ctrl->value;
+		break;
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		params->gop_size = ctrl->value;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* TODO: Update the hardware */
+
+	return ret;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+	struct v4l2_ext_controls *ctrls)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	int i, err = 0;
+
+	if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+		for (i = 0; i < ctrls->count; i++) {
+			struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+			err = saa7164_try_ctrl(ctrl, 0);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+			err = saa7164_set_ctrl(port, ctrl);
+			if (err) {
+				ctrls->error_idx = i;
+				break;
+			}
+		}
+		return err;
+
+	}
+
+	return -EINVAL;
+}
+
+static int vidioc_querycap(struct file *file, void  *priv,
+	struct v4l2_capability *cap)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	strcpy(cap->driver, dev->name);
+	strlcpy(cap->card, saa7164_boards[dev->board].name,
+		sizeof(cap->card));
+	sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+
+	cap->capabilities =
+		V4L2_CAP_VBI_CAPTURE |
+		V4L2_CAP_READWRITE     |
+		0;
+
+	cap->capabilities |= V4L2_CAP_TUNER;
+	cap->version = 0;
+
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
+	struct v4l2_fmtdesc *f)
+{
+	if (f->index != 0)
+		return -EINVAL;
+
+	strlcpy(f->description, "VBI", sizeof(f->description));
+	f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		port->ts_packet_size * port->ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+	f->fmt.pix.width        = port->width;
+	f->fmt.pix.height       = port->height;
+
+	dprintk(DBGLVL_VBI, "VIDIOC_G_FMT: w: %d, h: %d\n",
+		port->width, port->height);
+
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		port->ts_packet_size * port->ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+	dprintk(DBGLVL_VBI, "VIDIOC_TRY_FMT: w: %d, h: %d\n",
+		port->width, port->height);
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+	f->fmt.pix.bytesperline = 0;
+	f->fmt.pix.sizeimage    =
+		port->ts_packet_size * port->ts_packet_count;
+	f->fmt.pix.colorspace   = 0;
+
+	dprintk(DBGLVL_VBI, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+		f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+
+	return 0;
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+	return 0;
+}
+
+static int fill_queryctrl(struct saa7164_vbi_params *params,
+	struct v4l2_queryctrl *c)
+{
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 127);
+	case V4L2_CID_CONTRAST:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 66);
+	case V4L2_CID_SATURATION:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 62);
+	case V4L2_CID_HUE:
+		return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 128);
+	case V4L2_CID_SHARPNESS:
+		return v4l2_ctrl_query_fill(c, 0x0, 0x0f, 1, 8);
+	case V4L2_CID_MPEG_AUDIO_MUTE:
+		return v4l2_ctrl_query_fill(c, 0x0, 0x01, 1, 0);
+	case V4L2_CID_AUDIO_VOLUME:
+		return v4l2_ctrl_query_fill(c, -83, 24, 1, 20);
+	case V4L2_CID_MPEG_STREAM_TYPE:
+		return v4l2_ctrl_query_fill(c,
+			V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
+			V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
+			1, V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
+	case V4L2_CID_MPEG_VIDEO_ASPECT:
+		return v4l2_ctrl_query_fill(c,
+			V4L2_MPEG_VIDEO_ASPECT_1x1,
+			V4L2_MPEG_VIDEO_ASPECT_221x100,
+			1, V4L2_MPEG_VIDEO_ASPECT_4x3);
+	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+		return v4l2_ctrl_query_fill(c, 1, 255, 1, 15);
+	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+		return v4l2_ctrl_query_fill(c,
+			1, 3, 1, 1);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+	struct v4l2_queryctrl *c)
+{
+	struct saa7164_vbi_fh *fh = priv;
+	struct saa7164_port *port = fh->port;
+	int i, next;
+	u32 id = c->id;
+
+	memset(c, 0, sizeof(*c));
+
+	next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL);
+	c->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
+
+	for (i = 0; i < ARRAY_SIZE(saa7164_v4l2_ctrls); i++) {
+		if (next) {
+			if (c->id < saa7164_v4l2_ctrls[i])
+				c->id = saa7164_v4l2_ctrls[i];
+			else
+				continue;
+		}
+
+		if (c->id == saa7164_v4l2_ctrls[i])
+			return fill_queryctrl(&port->vbi_params, c);
+
+		if (c->id < saa7164_v4l2_ctrls[i])
+			break;
+	}
+
+	return -EINVAL;
+}
+
+static int saa7164_vbi_stop_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_VBI, "%s()    Stopped\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int saa7164_vbi_acquire_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_VBI, "%s() Acquired\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int saa7164_vbi_pause_port(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int ret;
+
+	ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n",
+			__func__, ret);
+		ret = -EIO;
+	} else {
+		dprintk(DBGLVL_VBI, "%s()   Paused\n", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+/* Firmware is very windows centric, meaning you have to transition
+ * the part through AVStream / KS Windows stages, forwards or backwards.
+ * States are: stopped, acquired (h/w), paused, started.
+ * We have to leave here will all of the soft buffers on the free list,
+ * else the cfg_post() func won't have soft buffers to correctly configure.
+ */
+static int saa7164_vbi_stop_streaming(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	struct saa7164_buffer *buf;
+	struct saa7164_user_buffer *ubuf;
+	struct list_head *c, *n;
+	int ret;
+
+	dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);
+
+	ret = saa7164_vbi_pause_port(port);
+	ret = saa7164_vbi_acquire_port(port);
+	ret = saa7164_vbi_stop_port(port);
+
+	dprintk(DBGLVL_VBI, "%s(port=%d) Hardware stopped\n", __func__,
+		port->nr);
+
+	/* Reset the state of any allocated buffer resources */
+	mutex_lock(&port->dmaqueue_lock);
+
+	/* Reset the hard and soft buffer state */
+	list_for_each_safe(c, n, &port->dmaqueue.list) {
+		buf = list_entry(c, struct saa7164_buffer, list);
+		buf->flags = SAA7164_BUFFER_FREE;
+		buf->pos = 0;
+	}
+
+	list_for_each_safe(c, n, &port->list_buf_used.list) {
+		ubuf = list_entry(c, struct saa7164_user_buffer, list);
+		ubuf->pos = 0;
+		list_move_tail(&ubuf->list, &port->list_buf_free.list);
+	}
+
+	mutex_unlock(&port->dmaqueue_lock);
+
+	/* Free any allocated resources */
+	saa7164_vbi_buffers_dealloc(port);
+
+	dprintk(DBGLVL_VBI, "%s(port=%d) Released\n", __func__, port->nr);
+
+	return ret;
+}
+
+static int saa7164_vbi_start_streaming(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int result, ret = 0;
+
+	dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);
+
+	port->done_first_interrupt = 0;
+
+	/* allocate all of the PCIe DMA buffer resources on the fly,
+	 * allowing switching between TS and PS payloads without
+	 * requiring a complete driver reload.
+	 */
+	saa7164_vbi_buffers_alloc(port);
+
+	/* Configure the encoder with any cache values */
+//	saa7164_api_set_encoder(port);
+//	saa7164_api_get_encoder(port);
+
+	/* Place the empty buffers on the hardware */
+	saa7164_buffer_cfg_port(port);
+
+	/* Negotiate format */
+	if (saa7164_api_set_vbi_format(port) != SAA_OK) {
+		printk(KERN_ERR "%s() No supported VBI format\n", __func__);
+		ret = -EIO;
+		goto out;
+	}
+
+	/* Acquire the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n",
+			__func__, result);
+
+		ret = -EIO;
+		goto out;
+	} else
+		dprintk(DBGLVL_VBI, "%s()   Acquired\n", __func__);
+
+	/* Pause the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n",
+				__func__, result);
+
+		/* Stop the hardware, regardless */
+		result = saa7164_vbi_stop_port(port);
+		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+			printk(KERN_ERR "%s() pause/forced stop transition "
+				"failed, res = 0x%x\n", __func__, result);
+		}
+
+		ret = -EIO;
+		goto out;
+	} else
+		dprintk(DBGLVL_VBI, "%s()   Paused\n", __func__);
+
+	/* Start the hardware */
+	result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN);
+	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+		printk(KERN_ERR "%s() run transition failed, result = 0x%x\n",
+				__func__, result);
+
+		/* Stop the hardware, regardless */
+		result = saa7164_vbi_acquire_port(port);
+		result = saa7164_vbi_stop_port(port);
+		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+			printk(KERN_ERR "%s() run/forced stop transition "
+				"failed, res = 0x%x\n", __func__, result);
+		}
+
+		ret = -EIO;
+	} else
+		dprintk(DBGLVL_VBI, "%s()   Running\n", __func__);
+
+out:
+	return ret;
+}
+
+int saa7164_vbi_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	/* ntsc */
+	f->fmt.vbi.samples_per_line = 1600;
+	f->fmt.vbi.samples_per_line = 1440;
+	f->fmt.vbi.sampling_rate = 27000000;
+	f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+	f->fmt.vbi.offset = 0;
+	f->fmt.vbi.flags = 0;
+	f->fmt.vbi.start[0] = 10;
+	f->fmt.vbi.count[0] = 18;
+	f->fmt.vbi.start[1] = 263 + 10 + 1;
+	f->fmt.vbi.count[1] = 18;
+	return 0;
+}
+
+static int fops_open(struct file *file)
+{
+	struct saa7164_dev *dev;
+	struct saa7164_port *port;
+	struct saa7164_vbi_fh *fh;
+
+	port = (struct saa7164_port *)video_get_drvdata(video_devdata(file));
+	if (!port)
+		return -ENODEV;
+
+	dev = port->dev;
+
+	dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+	/* allocate + initialize per filehandle data */
+	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+	if (NULL == fh)
+		return -ENOMEM;
+
+	file->private_data = fh;
+	fh->port = port;
+
+	return 0;
+}
+
+static int fops_release(struct file *file)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+	/* Shut device down on last close */
+	if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
+		if (atomic_dec_return(&port->v4l_reader_count) == 0) {
+			/* stop vbi capture then cancel buffers */
+			saa7164_vbi_stop_streaming(port);
+		}
+	}
+
+	file->private_data = NULL;
+	kfree(fh);
+
+	return 0;
+}
+
+struct saa7164_user_buffer *saa7164_vbi_next_buf(struct saa7164_port *port)
+{
+	struct saa7164_user_buffer *ubuf = 0;
+	struct saa7164_dev *dev = port->dev;
+	u32 crc;
+
+	mutex_lock(&port->dmaqueue_lock);
+	if (!list_empty(&port->list_buf_used.list)) {
+		ubuf = list_first_entry(&port->list_buf_used.list,
+			struct saa7164_user_buffer, list);
+
+		if (crc_checking) {
+			crc = crc32(0, ubuf->data, ubuf->actual_size);
+			if (crc != ubuf->crc) {
+				printk(KERN_ERR "%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n", __func__,
+					ubuf, ubuf->crc, crc);
+			}
+		}
+
+	}
+	mutex_unlock(&port->dmaqueue_lock);
+
+	dprintk(DBGLVL_VBI, "%s() returns %p\n", __func__, ubuf);
+
+	return ubuf;
+}
+
+static ssize_t fops_read(struct file *file, char __user *buffer,
+	size_t count, loff_t *pos)
+{
+	struct saa7164_vbi_fh *fh = file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_user_buffer *ubuf = NULL;
+	struct saa7164_dev *dev = port->dev;
+	int ret = 0;
+	int rem, cnt;
+	u8 *p;
+
+	port->last_read_msecs_diff = port->last_read_msecs;
+	port->last_read_msecs = jiffies_to_msecs(jiffies);
+	port->last_read_msecs_diff = port->last_read_msecs -
+		port->last_read_msecs_diff;
+
+	saa7164_histogram_update(&port->read_interval,
+		port->last_read_msecs_diff);
+
+	if (*pos) {
+		printk(KERN_ERR "%s() ESPIPE\n", __func__);
+		return -ESPIPE;
+	}
+
+	if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+		if (atomic_inc_return(&port->v4l_reader_count) == 1) {
+
+			if (saa7164_vbi_initialize(port) < 0) {
+				printk(KERN_ERR "%s() EINVAL\n", __func__);
+				return -EINVAL;
+			}
+
+			saa7164_vbi_start_streaming(port);
+			msleep(200);
+		}
+	}
+
+	/* blocking wait for buffer */
+	if ((file->f_flags & O_NONBLOCK) == 0) {
+		if (wait_event_interruptible(port->wait_read,
+			saa7164_vbi_next_buf(port))) {
+				printk(KERN_ERR "%s() ERESTARTSYS\n", __func__);
+				return -ERESTARTSYS;
+		}
+	}
+
+	/* Pull the first buffer from the used list */
+	ubuf = saa7164_vbi_next_buf(port);
+
+	while ((count > 0) && ubuf) {
+
+		/* set remaining bytes to copy */
+		rem = ubuf->actual_size - ubuf->pos;
+		cnt = rem > count ? count : rem;
+
+		p = ubuf->data + ubuf->pos;
+
+		dprintk(DBGLVL_VBI,
+			"%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n",
+			__func__, (int)count, cnt, rem, ubuf, ubuf->pos);
+
+		if (copy_to_user(buffer, p, cnt)) {
+			printk(KERN_ERR "%s() copy_to_user failed\n", __func__);
+			if (!ret) {
+				printk(KERN_ERR "%s() EFAULT\n", __func__);
+				ret = -EFAULT;
+			}
+			goto err;
+		}
+
+		ubuf->pos += cnt;
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+
+		if (ubuf->pos > ubuf->actual_size) {
+			printk(KERN_ERR "read() pos > actual, huh?\n");
+		}
+
+		if (ubuf->pos == ubuf->actual_size) {
+
+			/* finished with current buffer, take next buffer */
+
+			/* Requeue the buffer on the free list */
+			ubuf->pos = 0;
+
+			mutex_lock(&port->dmaqueue_lock);
+			list_move_tail(&ubuf->list, &port->list_buf_free.list);
+			mutex_unlock(&port->dmaqueue_lock);
+
+			/* Dequeue next */
+			if ((file->f_flags & O_NONBLOCK) == 0) {
+				if (wait_event_interruptible(port->wait_read,
+					saa7164_vbi_next_buf(port))) {
+						break;
+				}
+			}
+			ubuf = saa7164_vbi_next_buf(port);
+		}
+	}
+err:
+	if (!ret && !ubuf) {
+		printk(KERN_ERR "%s() EAGAIN\n", __func__);
+		ret = -EAGAIN;
+	}
+
+	return ret;
+}
+
+static unsigned int fops_poll(struct file *file, poll_table *wait)
+{
+	struct saa7164_vbi_fh *fh = (struct saa7164_vbi_fh *)file->private_data;
+	struct saa7164_port *port = fh->port;
+	struct saa7164_user_buffer *ubuf;
+	unsigned int mask = 0;
+
+	port->last_poll_msecs_diff = port->last_poll_msecs;
+	port->last_poll_msecs = jiffies_to_msecs(jiffies);
+	port->last_poll_msecs_diff = port->last_poll_msecs -
+		port->last_poll_msecs_diff;
+
+	saa7164_histogram_update(&port->poll_interval,
+		port->last_poll_msecs_diff);
+
+	if (!video_is_registered(port->v4l_device)) {
+		return -EIO;
+	}
+
+	if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+		if (atomic_inc_return(&port->v4l_reader_count) == 1) {
+			if (saa7164_vbi_initialize(port) < 0)
+				return -EINVAL;
+			saa7164_vbi_start_streaming(port);
+			msleep(200);
+		}
+	}
+
+	/* blocking wait for buffer */
+	if ((file->f_flags & O_NONBLOCK) == 0) {
+		if (wait_event_interruptible(port->wait_read,
+			saa7164_vbi_next_buf(port))) {
+				return -ERESTARTSYS;
+		}
+	}
+
+	/* Pull the first buffer from the used list */
+	ubuf = list_first_entry(&port->list_buf_used.list,
+		struct saa7164_user_buffer, list);
+
+	if (ubuf)
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+static const struct v4l2_file_operations vbi_fops = {
+	.owner		= THIS_MODULE,
+	.open		= fops_open,
+	.release	= fops_release,
+	.read		= fops_read,
+	.poll		= fops_poll,
+	.unlocked_ioctl	= video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops vbi_ioctl_ops = {
+	.vidioc_s_std		 = vidioc_s_std,
+	.vidioc_enum_input	 = vidioc_enum_input,
+	.vidioc_g_input		 = vidioc_g_input,
+	.vidioc_s_input		 = vidioc_s_input,
+	.vidioc_g_tuner		 = vidioc_g_tuner,
+	.vidioc_s_tuner		 = vidioc_s_tuner,
+	.vidioc_g_frequency	 = vidioc_g_frequency,
+	.vidioc_s_frequency	 = vidioc_s_frequency,
+	.vidioc_s_ctrl		 = vidioc_s_ctrl,
+	.vidioc_g_ctrl		 = vidioc_g_ctrl,
+	.vidioc_querycap	 = vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap	 = vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap	 = vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap	 = vidioc_s_fmt_vid_cap,
+	.vidioc_g_ext_ctrls	 = vidioc_g_ext_ctrls,
+	.vidioc_s_ext_ctrls	 = vidioc_s_ext_ctrls,
+	.vidioc_try_ext_ctrls	 = vidioc_try_ext_ctrls,
+	.vidioc_log_status	 = vidioc_log_status,
+	.vidioc_queryctrl	 = vidioc_queryctrl,
+//	.vidioc_g_chip_ident	 = saa7164_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+//	.vidioc_g_register	 = saa7164_g_register,
+//	.vidioc_s_register	 = saa7164_s_register,
+#endif
+	.vidioc_g_fmt_vbi_cap	 = saa7164_vbi_fmt,
+	.vidioc_try_fmt_vbi_cap	 = saa7164_vbi_fmt,
+	.vidioc_s_fmt_vbi_cap	 = saa7164_vbi_fmt,
+};
+
+static struct video_device saa7164_vbi_template = {
+	.name          = "saa7164",
+	.fops          = &vbi_fops,
+	.ioctl_ops     = &vbi_ioctl_ops,
+	.minor         = -1,
+	.tvnorms       = SAA7164_NORMS,
+	.current_norm  = V4L2_STD_NTSC_M,
+};
+
+static struct video_device *saa7164_vbi_alloc(
+	struct saa7164_port *port,
+	struct pci_dev *pci,
+	struct video_device *template,
+	char *type)
+{
+	struct video_device *vfd;
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+	vfd = video_device_alloc();
+	if (NULL == vfd)
+		return NULL;
+
+	*vfd = *template;
+	snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
+		type, saa7164_boards[dev->board].name);
+
+	vfd->parent  = &pci->dev;
+	vfd->release = video_device_release;
+	return vfd;
+}
+
+int saa7164_vbi_register(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+	int result = -ENODEV;
+
+	dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+	if (port->type != SAA7164_MPEG_VBI)
+		BUG();
+
+	/* Sanity check that the PCI configuration space is active */
+	if (port->hwcfg.BARLocation == 0) {
+		printk(KERN_ERR "%s() failed "
+		       "(errno = %d), NO PCI configuration\n",
+			__func__, result);
+		result = -ENOMEM;
+		goto failed;
+	}
+
+	/* Establish VBI defaults here */
+
+	/* Allocate and register the video device node */
+	port->v4l_device = saa7164_vbi_alloc(port,
+		dev->pci, &saa7164_vbi_template, "vbi");
+
+	if (port->v4l_device == NULL) {
+		printk(KERN_INFO "%s: can't allocate vbi device\n",
+			dev->name);
+		result = -ENOMEM;
+		goto failed;
+	}
+
+	video_set_drvdata(port->v4l_device, port);
+	result = video_register_device(port->v4l_device,
+		VFL_TYPE_VBI, -1);
+	if (result < 0) {
+		printk(KERN_INFO "%s: can't register vbi device\n",
+			dev->name);
+		/* TODO: We're going to leak here if we don't dealloc
+		 The buffers above. The unreg function can't deal wit it.
+		*/
+		goto failed;
+	}
+
+	printk(KERN_INFO "%s: registered device vbi%d [vbi]\n",
+		dev->name, port->v4l_device->num);
+
+	/* Configure the hardware defaults */
+
+	result = 0;
+failed:
+	return result;
+}
+
+void saa7164_vbi_unregister(struct saa7164_port *port)
+{
+	struct saa7164_dev *dev = port->dev;
+
+	dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);
+
+	if (port->type != SAA7164_MPEG_VBI)
+		BUG();
+
+	if (port->v4l_device) {
+		if (port->v4l_device->minor != -1)
+			video_unregister_device(port->v4l_device);
+		else
+			video_device_release(port->v4l_device);
+
+		port->v4l_device = NULL;
+	}
+
+}
diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h
index 42660b5..1d9c5cb 100644
--- a/drivers/media/video/saa7164/saa7164.h
+++ b/drivers/media/video/saa7164/saa7164.h
@@ -1,7 +1,7 @@
 /*
  *  Driver for the NXP SAA7164 PCIe bridge
  *
- *  Copyright (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *  Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -48,18 +48,29 @@
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
 #include <linux/kdev_t.h>
+#include <linux/version.h>
+#include <linux/mutex.h>
+#include <linux/crc32.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
 
 #include <media/tuner.h>
 #include <media/tveeprom.h>
 #include <media/videobuf-dma-sg.h>
 #include <media/videobuf-dvb.h>
+#include <linux/smp_lock.h>
+#include <dvb_demux.h>
+#include <dvb_frontend.h>
+#include <dvb_net.h>
+#include <dvbdev.h>
+#include <dmxdev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
 
 #include "saa7164-reg.h"
 #include "saa7164-types.h"
 
-#include <linux/version.h>
-#include <linux/mutex.h>
-
 #define SAA7164_MAXBOARDS 8
 
 #define UNSET (-1U)
@@ -76,7 +87,19 @@
 
 #define SAA7164_MAX_UNITS		8
 #define SAA7164_TS_NUMBER_OF_LINES	312
+#define SAA7164_PS_NUMBER_OF_LINES	256
 #define SAA7164_PT_ENTRIES		16 /* (312 * 188) / 4096 */
+#define SAA7164_MAX_ENCODER_BUFFERS	64 /* max 5secs of latency at 6Mbps */
+#define SAA7164_MAX_VBI_BUFFERS		64
+
+/* Port related defines */
+#define SAA7164_PORT_TS1	(0)
+#define SAA7164_PORT_TS2	(SAA7164_PORT_TS1 + 1)
+#define SAA7164_PORT_ENC1	(SAA7164_PORT_TS2 + 1)
+#define SAA7164_PORT_ENC2	(SAA7164_PORT_ENC1 + 1)
+#define SAA7164_PORT_VBI1	(SAA7164_PORT_ENC2 + 1)
+#define SAA7164_PORT_VBI2	(SAA7164_PORT_VBI1 + 1)
+#define SAA7164_MAX_PORTS	(SAA7164_PORT_VBI2 + 1)
 
 #define DBGLVL_FW    4
 #define DBGLVL_DVB   8
@@ -86,10 +109,18 @@
 #define DBGLVL_BUS 128
 #define DBGLVL_IRQ 256
 #define DBGLVL_BUF 512
+#define DBGLVL_ENC 1024
+#define DBGLVL_VBI 2048
+#define DBGLVL_THR 4096
+#define DBGLVL_CPU 8192
+
+#define SAA7164_NORMS (V4L2_STD_NTSC_M |  V4L2_STD_NTSC_M_JP |  V4L2_STD_NTSC_443)
 
 enum port_t {
 	SAA7164_MPEG_UNDEFINED = 0,
 	SAA7164_MPEG_DVB,
+	SAA7164_MPEG_ENCODER,
+	SAA7164_MPEG_VBI,
 };
 
 enum saa7164_i2c_bus_nr {
@@ -134,7 +165,8 @@
 
 struct saa7164_board {
 	char	*name;
-	enum port_t porta, portb;
+	enum port_t porta, portb, portc,
+		portd, porte, portf;
 	enum {
 		SAA7164_CHIP_UNDEFINED = 0,
 		SAA7164_CHIP_REV2,
@@ -149,6 +181,42 @@
 	u32     card;
 };
 
+struct saa7164_encoder_fh {
+	struct saa7164_port *port;
+//	u32 freq;
+//	u32 tuner_type;
+	atomic_t v4l_reading;
+};
+
+struct saa7164_vbi_fh {
+	struct saa7164_port *port;
+//	u32 freq;
+//	u32 tuner_type;
+	atomic_t v4l_reading;
+};
+
+struct saa7164_histogram_bucket {
+	u32 val;
+	u32 count;
+	u64 update_time;
+};
+
+struct saa7164_histogram {
+	char name[32];
+	struct saa7164_histogram_bucket counter1[64];
+};
+
+struct saa7164_user_buffer {
+	struct list_head list;
+
+	/* Attributes */
+	u8  *data;
+	u32 pos;
+	u32 actual_size;
+
+	u32 crc;
+};
+
 struct saa7164_fw_status {
 
 	/* RISC Core details */
@@ -191,14 +259,60 @@
 	u32				i2c_rc;
 };
 
-struct saa7164_tsport;
+struct saa7164_ctrl {
+	struct v4l2_queryctrl v;
+};
+
+struct saa7164_tvnorm {
+	char		*name;
+	v4l2_std_id	id;
+//	u32		cxiformat;
+//	u32		cxoformat;
+};
+
+struct saa7164_encoder_params {
+	struct saa7164_tvnorm encodernorm;
+	u32 height;
+	u32 width;
+	u32 is_50hz;
+	u32 bitrate; /* bps */
+	u32 bitrate_peak; /* bps */
+	u32 bitrate_mode;
+	u32 stream_type; /* V4L2_MPEG_STREAM_TYPE_MPEG2_TS */
+
+	u32 audio_sampling_freq;
+	u32 ctl_mute;
+	u32 ctl_aspect;
+	u32 refdist;
+	u32 gop_size;
+};
+
+struct saa7164_vbi_params {
+	struct saa7164_tvnorm encodernorm;
+	u32 height;
+	u32 width;
+	u32 is_50hz;
+	u32 bitrate; /* bps */
+	u32 bitrate_peak; /* bps */
+	u32 bitrate_mode;
+	u32 stream_type; /* V4L2_MPEG_STREAM_TYPE_MPEG2_TS */
+
+	u32 audio_sampling_freq;
+	u32 ctl_mute;
+	u32 ctl_aspect;
+	u32 refdist;
+	u32 gop_size;
+};
+
+struct saa7164_port;
 
 struct saa7164_buffer {
 	struct list_head list;
 
-	u32 nr;
+	/* Note of which h/w buffer list index position we occupy */
+	int idx;
 
-	struct saa7164_tsport *port;
+	struct saa7164_port *port;
 
 	/* Hardware Specific */
 	/* PCI Memory allocations */
@@ -206,28 +320,33 @@
 
 	/* A block of page align PCI memory */
 	u32 pci_size;	/* PCI allocation size in bytes */
-	u64 *cpu;	/* Virtual address */
+	u64 __iomem *cpu;	/* Virtual address */
 	dma_addr_t dma;	/* Physical address */
+	u32 crc;	/* Checksum for the entire buffer data */
 
 	/* A page table that splits the block into a number of entries */
 	u32 pt_size;		/* PCI allocation size in bytes */
-	u64 *pt_cpu;		/* Virtual address */
+	u64 __iomem *pt_cpu;		/* Virtual address */
 	dma_addr_t pt_dma;	/* Physical address */
+
+	/* Encoder fops */
+	u32 pos;
+	u32 actual_size;
 };
 
-struct saa7164_tsport {
+struct saa7164_port {
 
 	struct saa7164_dev *dev;
-	int nr;
 	enum port_t type;
+	int nr;
 
-	struct saa7164_dvb dvb;
+	/* --- Generic port attributes --- */
 
-	/* HW related stream parameters */
-	tmHWStreamParameters_t hw_streamingparams;
+	/* HW stream parameters */
+	struct tmHWStreamParameters hw_streamingparams;
 
 	/* DMA configuration values, is seeded during initialization */
-	tmComResDMATermDescrHeader_t hwcfg;
+	struct tmComResDMATermDescrHeader hwcfg;
 
 	/* hardware specific registers */
 	u32 bufcounter;
@@ -239,11 +358,76 @@
 	u64 bufptr64;
 
 	u32 numpte;	/* Number of entries in array, only valid in head */
-	struct mutex dmaqueue_lock;
-	struct mutex dummy_dmaqueue_lock;
-	struct saa7164_buffer dmaqueue;
-	struct saa7164_buffer dummy_dmaqueue;
 
+	struct mutex dmaqueue_lock;
+	struct saa7164_buffer dmaqueue;
+
+	u64 last_irq_msecs, last_svc_msecs;
+	u64 last_irq_msecs_diff, last_svc_msecs_diff;
+	u32 last_svc_wp;
+	u32 last_svc_rp;
+	u64 last_irq_svc_msecs_diff;
+	u64 last_read_msecs, last_read_msecs_diff;
+	u64 last_poll_msecs, last_poll_msecs_diff;
+
+	struct saa7164_histogram irq_interval;
+	struct saa7164_histogram svc_interval;
+	struct saa7164_histogram irq_svc_interval;
+	struct saa7164_histogram read_interval;
+	struct saa7164_histogram poll_interval;
+
+	/* --- DVB Transport Specific --- */
+	struct saa7164_dvb dvb;
+
+	/* --- Encoder/V4L related attributes --- */
+	/* Encoder */
+	/* Defaults established in saa7164-encoder.c */
+	struct saa7164_tvnorm encodernorm;
+	u32 height;
+	u32 width;
+	u32 freq;
+	u32 ts_packet_size;
+	u32 ts_packet_count;
+	u8 mux_input;
+	u8 encoder_profile;
+	u8 video_format;
+	u8 audio_format;
+	u8 video_resolution;
+	u16 ctl_brightness;
+	u16 ctl_contrast;
+	u16 ctl_hue;
+	u16 ctl_saturation;
+	u16 ctl_sharpness;
+	s8 ctl_volume;
+
+	struct tmComResAFeatureDescrHeader audfeat;
+	struct tmComResEncoderDescrHeader encunit;
+	struct tmComResProcDescrHeader vidproc;
+	struct tmComResExtDevDescrHeader ifunit;
+	struct tmComResTunerDescrHeader tunerunit;
+
+	struct work_struct workenc;
+
+	/* V4L Encoder Video */
+	struct saa7164_encoder_params encoder_params;
+	struct video_device *v4l_device;
+	atomic_t v4l_reader_count;
+
+	struct saa7164_buffer list_buf_used;
+	struct saa7164_buffer list_buf_free;
+	wait_queue_head_t wait_read;
+
+	/* V4L VBI */
+	struct tmComResVBIFormatDescrHeader vbi_fmt_ntsc;
+	struct saa7164_vbi_params vbi_params;
+
+	/* Debug */
+	u32 sync_errors;
+	u32 v_cc_errors;
+	u32 a_cc_errors;
+	u8 last_v_cc;
+	u8 last_a_cc;
+	u32 done_first_interrupt;
 };
 
 struct saa7164_dev {
@@ -268,12 +452,13 @@
 
 	/* firmware status */
 	struct saa7164_fw_status	fw_status;
+	u32				firmwareloaded;
 
-	tmComResHWDescr_t		hwdesc;
-	tmComResInterfaceDescr_t	intfdesc;
-	tmComResBusDescr_t		busdesc;
+	struct tmComResHWDescr		hwdesc;
+	struct tmComResInterfaceDescr	intfdesc;
+	struct tmComResBusDescr		busdesc;
 
-	tmComResBusInfo_t		bus;
+	struct tmComResBusInfo		bus;
 
 	/* Interrupt status and ack registers */
 	u32 int_status;
@@ -286,15 +471,22 @@
 	struct saa7164_i2c i2c_bus[3];
 
 	/* Transport related */
-	struct saa7164_tsport ts1, ts2;
+	struct saa7164_port ports[SAA7164_MAX_PORTS];
 
 	/* Deferred command/api interrupts handling */
 	struct work_struct workcmd;
 
+	/* A kernel thread to monitor the firmware log, used
+	 * only in debug mode.
+	 */
+	struct task_struct *kthread;
+
 };
 
 extern struct list_head saa7164_devlist;
 extern unsigned int waitsecs;
+extern unsigned int encoder_buffers;
+extern unsigned int vbi_buffers;
 
 /* ----------------------------------------------------------- */
 /* saa7164-core.c                                              */
@@ -302,6 +494,7 @@
 void saa7164_dumphex16(struct saa7164_dev *dev, u8 *buf, int len);
 void saa7164_getfirmwarestatus(struct saa7164_dev *dev);
 u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev);
+void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val);
 
 /* ----------------------------------------------------------- */
 /* saa7164-fw.c                                                */
@@ -318,14 +511,14 @@
 /* saa7164-bus.c                                               */
 int saa7164_bus_setup(struct saa7164_dev *dev);
 void saa7164_bus_dump(struct saa7164_dev *dev);
-int saa7164_bus_set(struct saa7164_dev *dev, tmComResInfo_t* msg, void *buf);
-int saa7164_bus_get(struct saa7164_dev *dev, tmComResInfo_t* msg,
+int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg, void *buf);
+int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
 	void *buf, int peekonly);
 
 /* ----------------------------------------------------------- */
 /* saa7164-cmd.c                                               */
 int saa7164_cmd_send(struct saa7164_dev *dev,
-	u8 id, tmComResCmd_t command, u16 controlselector,
+	u8 id, enum tmComResCmd command, u16 controlselector,
 	u16 size, void *buf);
 void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno);
 int saa7164_irq_dequeue(struct saa7164_dev *dev);
@@ -343,7 +536,24 @@
 int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen);
 int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin);
 int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin);
-int saa7164_api_transition_port(struct saa7164_tsport *port, u8 mode);
+int saa7164_api_transition_port(struct saa7164_port *port, u8 mode);
+int saa7164_api_initialize_dif(struct saa7164_port *port);
+int saa7164_api_configure_dif(struct saa7164_port *port, u32 std);
+int saa7164_api_set_encoder(struct saa7164_port *port);
+int saa7164_api_get_encoder(struct saa7164_port *port);
+int saa7164_api_set_aspect_ratio(struct saa7164_port *port);
+int saa7164_api_set_usercontrol(struct saa7164_port *port, u8 ctl);
+int saa7164_api_get_usercontrol(struct saa7164_port *port, u8 ctl);
+int saa7164_api_set_videomux(struct saa7164_port *port);
+int saa7164_api_audio_mute(struct saa7164_port *port, int mute);
+int saa7164_api_set_audio_volume(struct saa7164_port *port, s8 level);
+int saa7164_api_set_audio_std(struct saa7164_port *port);
+int saa7164_api_set_audio_detection(struct saa7164_port *port, int autodetect);
+int saa7164_api_get_videomux(struct saa7164_port *port);
+int saa7164_api_set_vbi_format(struct saa7164_port *port);
+int saa7164_api_set_debug(struct saa7164_dev *dev, u8 level);
+int saa7164_api_collect_debug(struct saa7164_dev *dev);
+int saa7164_api_get_load_info(struct saa7164_dev *dev, struct tmFwInfoStruct *i);
 
 /* ----------------------------------------------------------- */
 /* saa7164-cards.c                                             */
@@ -363,17 +573,35 @@
 
 /* ----------------------------------------------------------- */
 /* saa7164-dvb.c                                               */
-extern int saa7164_dvb_register(struct saa7164_tsport *port);
-extern int saa7164_dvb_unregister(struct saa7164_tsport *port);
+extern int saa7164_dvb_register(struct saa7164_port *port);
+extern int saa7164_dvb_unregister(struct saa7164_port *port);
 
 /* ----------------------------------------------------------- */
 /* saa7164-buffer.c                                            */
-extern struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_tsport *port,
-	u32 len);
-extern int saa7164_buffer_dealloc(struct saa7164_tsport *port,
-	struct saa7164_buffer *buf);
+extern struct saa7164_buffer *saa7164_buffer_alloc(
+	struct saa7164_port *port, u32 len);
+extern int saa7164_buffer_dealloc(struct saa7164_buffer *buf);
+extern void saa7164_buffer_display(struct saa7164_buffer *buf);
+extern int saa7164_buffer_activate(struct saa7164_buffer *buf, int i);
+extern int saa7164_buffer_cfg_port(struct saa7164_port *port);
+extern struct saa7164_user_buffer *saa7164_buffer_alloc_user(
+	struct saa7164_dev *dev, u32 len);
+extern void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf);
+extern int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i);
 
 /* ----------------------------------------------------------- */
+/* saa7164-encoder.c                                            */
+int saa7164_encoder_register(struct saa7164_port *port);
+void saa7164_encoder_unregister(struct saa7164_port *port);
+
+/* ----------------------------------------------------------- */
+/* saa7164-vbi.c                                            */
+int saa7164_vbi_register(struct saa7164_port *port);
+void saa7164_vbi_unregister(struct saa7164_port *port);
+
+/* ----------------------------------------------------------- */
+
+extern unsigned int crc_checking;
 
 extern unsigned int saa_debug;
 #define dprintk(level, fmt, arg...)\
@@ -394,7 +622,6 @@
 #define saa7164_readl(reg) readl(dev->lmmio + ((reg) >> 2))
 #define saa7164_writel(reg, value) writel((value), dev->lmmio + ((reg) >> 2))
 
-
 #define saa7164_readb(reg)             readl(dev->bmmio + (reg))
 #define saa7164_writeb(reg, value)     writel((value), dev->bmmio + (reg))
 
diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c
index 45f8bfc..b6172c2 100644
--- a/drivers/media/video/saa717x.c
+++ b/drivers/media/video/saa717x.c
@@ -39,7 +39,6 @@
 #include <linux/i2c.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ctrls.h>
-#include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver");
 MODULE_AUTHOR("K. Ohta, T. Adachi, Hans Verkuil");
@@ -1366,9 +1365,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, saa717x_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "saa717x",
-	.probe = saa717x_probe,
-	.remove = saa717x_remove,
-	.id_table = saa717x_id,
+static struct i2c_driver saa717x_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "saa717x",
+	},
+	.probe		= saa717x_probe,
+	.remove		= saa717x_remove,
+	.id_table	= saa717x_id,
 };
+
+static __init int init_saa717x(void)
+{
+	return i2c_add_driver(&saa717x_driver);
+}
+
+static __exit void exit_saa717x(void)
+{
+	i2c_del_driver(&saa717x_driver);
+}
+
+module_init(init_saa717x);
+module_exit(exit_saa717x);
diff --git a/drivers/media/video/saa7185.c b/drivers/media/video/saa7185.c
index 77db203..96f56c2 100644
--- a/drivers/media/video/saa7185.c
+++ b/drivers/media/video/saa7185.c
@@ -30,11 +30,9 @@
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("Philips SAA7185 video encoder driver");
 MODULE_AUTHOR("Dave Perks");
@@ -366,9 +364,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, saa7185_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "saa7185",
-	.probe = saa7185_probe,
-	.remove = saa7185_remove,
-	.id_table = saa7185_id,
+static struct i2c_driver saa7185_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "saa7185",
+	},
+	.probe		= saa7185_probe,
+	.remove		= saa7185_remove,
+	.id_table	= saa7185_id,
 };
+
+static __init int init_saa7185(void)
+{
+	return i2c_add_driver(&saa7185_driver);
+}
+
+static __exit void exit_saa7185(void)
+{
+	i2c_del_driver(&saa7185_driver);
+}
+
+module_init(init_saa7185);
+module_exit(exit_saa7185);
diff --git a/drivers/media/video/saa7191.c b/drivers/media/video/saa7191.c
index a251377..211fa25 100644
--- a/drivers/media/video/saa7191.c
+++ b/drivers/media/video/saa7191.c
@@ -23,7 +23,6 @@
 #include <linux/i2c.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 #include "saa7191.h"
 
@@ -647,9 +646,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, saa7191_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "saa7191",
-	.probe = saa7191_probe,
-	.remove = saa7191_remove,
-	.id_table = saa7191_id,
+static struct i2c_driver saa7191_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "saa7191",
+	},
+	.probe		= saa7191_probe,
+	.remove		= saa7191_remove,
+	.id_table	= saa7191_id,
 };
+
+static __init int init_saa7191(void)
+{
+	return i2c_add_driver(&saa7191_driver);
+}
+
+static __exit void exit_saa7191(void)
+{
+	i2c_del_driver(&saa7191_driver);
+}
+
+module_init(init_saa7191);
+module_exit(exit_saa7191);
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index 2b24bd0..5c209af 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -245,7 +245,7 @@
 	if (in_interrupt())
 		BUG();
 
-	videobuf_waiton(&buf->vb, 0, 0);
+	videobuf_waiton(vq, &buf->vb, 0, 0);
 	videobuf_dma_contig_free(vq, &buf->vb);
 	dev_dbg(dev, "%s freed\n", __func__);
 	buf->vb.state = VIDEOBUF_NEEDS_INIT;
@@ -1726,7 +1726,7 @@
 	return ret;
 }
 
-static int sh_mobile_ceu_reqbufs(struct soc_camera_file *icf,
+static int sh_mobile_ceu_reqbufs(struct soc_camera_device *icd,
 				 struct v4l2_requestbuffers *p)
 {
 	int i;
@@ -1740,7 +1740,7 @@
 	for (i = 0; i < p->count; i++) {
 		struct sh_mobile_ceu_buffer *buf;
 
-		buf = container_of(icf->vb_vidq.bufs[i],
+		buf = container_of(icd->vb_vidq.bufs[i],
 				   struct sh_mobile_ceu_buffer, vb);
 		INIT_LIST_HEAD(&buf->vb.queue);
 	}
@@ -1750,10 +1750,10 @@
 
 static unsigned int sh_mobile_ceu_poll(struct file *file, poll_table *pt)
 {
-	struct soc_camera_file *icf = file->private_data;
+	struct soc_camera_device *icd = file->private_data;
 	struct sh_mobile_ceu_buffer *buf;
 
-	buf = list_entry(icf->vb_vidq.stream.next,
+	buf = list_entry(icd->vb_vidq.stream.next,
 			 struct sh_mobile_ceu_buffer, vb.stream);
 
 	poll_wait(file, &buf->vb.done, pt);
@@ -1786,23 +1786,7 @@
 				       V4L2_BUF_TYPE_VIDEO_CAPTURE,
 				       pcdev->field,
 				       sizeof(struct sh_mobile_ceu_buffer),
-				       icd);
-}
-
-static int sh_mobile_ceu_get_parm(struct soc_camera_device *icd,
-				  struct v4l2_streamparm *parm)
-{
-	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-
-	return v4l2_subdev_call(sd, video, g_parm, parm);
-}
-
-static int sh_mobile_ceu_set_parm(struct soc_camera_device *icd,
-				  struct v4l2_streamparm *parm)
-{
-	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-
-	return v4l2_subdev_call(sd, video, s_parm, parm);
+				       icd, NULL);
 }
 
 static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd,
@@ -1866,8 +1850,6 @@
 	.try_fmt	= sh_mobile_ceu_try_fmt,
 	.set_ctrl	= sh_mobile_ceu_set_ctrl,
 	.get_ctrl	= sh_mobile_ceu_get_ctrl,
-	.set_parm	= sh_mobile_ceu_set_parm,
-	.get_parm	= sh_mobile_ceu_get_parm,
 	.reqbufs	= sh_mobile_ceu_reqbufs,
 	.poll		= sh_mobile_ceu_poll,
 	.querycap	= sh_mobile_ceu_querycap,
diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c
index d394187..0f49061 100644
--- a/drivers/media/video/sh_vou.c
+++ b/drivers/media/video/sh_vou.c
@@ -230,7 +230,7 @@
 	BUG_ON(in_interrupt());
 
 	/* Wait until this buffer is no longer in STATE_QUEUED or STATE_ACTIVE */
-	videobuf_waiton(vb, 0, 0);
+	videobuf_waiton(vq, vb, 0, 0);
 	videobuf_dma_contig_free(vq, vb);
 	vb->state = VIDEOBUF_NEEDS_INIT;
 }
@@ -1189,7 +1189,8 @@
 				       vou_dev->v4l2_dev.dev, &vou_dev->lock,
 				       V4L2_BUF_TYPE_VIDEO_OUTPUT,
 				       V4L2_FIELD_NONE,
-				       sizeof(struct videobuf_buffer), vdev);
+				       sizeof(struct videobuf_buffer), vdev,
+				       NULL);
 
 	return 0;
 }
@@ -1405,7 +1406,7 @@
 		goto ereset;
 
 	subdev = v4l2_i2c_new_subdev_board(&vou_dev->v4l2_dev, i2c_adap,
-			vou_pdata->module_name, vou_pdata->board_info, NULL);
+			NULL, vou_pdata->board_info, NULL);
 	if (!subdev) {
 		ret = -ENOMEM;
 		goto ei2cnd;
diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h
index b6643ca..ccfa59c 100644
--- a/drivers/media/video/sn9c102/sn9c102_devtable.h
+++ b/drivers/media/video/sn9c102/sn9c102_devtable.h
@@ -116,10 +116,14 @@
 	{ SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), },
 	/* SN9C120 */
 	{ SN9C102_USB_DEVICE(0x0458, 0x7025, BRIDGE_SN9C120), },
+#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE
 	{ SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), },
+#endif
 	{ SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), },
 	{ SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), },
+#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE
 	{ SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), },
+#endif
 /*	{ SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */
 #if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE
 	{ SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), },
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index a499cac..43848a7 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -92,8 +92,7 @@
 static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv,
 				      struct v4l2_format *f)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 
 	WARN_ON(priv != file->private_data);
@@ -105,8 +104,7 @@
 static int soc_camera_enum_input(struct file *file, void *priv,
 				 struct v4l2_input *inp)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	int ret = 0;
 
 	if (inp->index != 0)
@@ -141,8 +139,7 @@
 
 static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 
 	return v4l2_subdev_call(sd, core, s_std, *a);
@@ -152,47 +149,59 @@
 			      struct v4l2_requestbuffers *p)
 {
 	int ret;
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 
 	WARN_ON(priv != file->private_data);
 
-	ret = videobuf_reqbufs(&icf->vb_vidq, p);
+	if (icd->streamer && icd->streamer != file)
+		return -EBUSY;
+
+	ret = videobuf_reqbufs(&icd->vb_vidq, p);
 	if (ret < 0)
 		return ret;
 
-	return ici->ops->reqbufs(icf, p);
+	ret = ici->ops->reqbufs(icd, p);
+	if (!ret && !icd->streamer)
+		icd->streamer = file;
+
+	return ret;
 }
 
 static int soc_camera_querybuf(struct file *file, void *priv,
 			       struct v4l2_buffer *p)
 {
-	struct soc_camera_file *icf = file->private_data;
+	struct soc_camera_device *icd = file->private_data;
 
 	WARN_ON(priv != file->private_data);
 
-	return videobuf_querybuf(&icf->vb_vidq, p);
+	return videobuf_querybuf(&icd->vb_vidq, p);
 }
 
 static int soc_camera_qbuf(struct file *file, void *priv,
 			   struct v4l2_buffer *p)
 {
-	struct soc_camera_file *icf = file->private_data;
+	struct soc_camera_device *icd = file->private_data;
 
 	WARN_ON(priv != file->private_data);
 
-	return videobuf_qbuf(&icf->vb_vidq, p);
+	if (icd->streamer != file)
+		return -EBUSY;
+
+	return videobuf_qbuf(&icd->vb_vidq, p);
 }
 
 static int soc_camera_dqbuf(struct file *file, void *priv,
 			    struct v4l2_buffer *p)
 {
-	struct soc_camera_file *icf = file->private_data;
+	struct soc_camera_device *icd = file->private_data;
 
 	WARN_ON(priv != file->private_data);
 
-	return videobuf_dqbuf(&icf->vb_vidq, p, file->f_flags & O_NONBLOCK);
+	if (icd->streamer != file)
+		return -EBUSY;
+
+	return videobuf_dqbuf(&icd->vb_vidq, p, file->f_flags & O_NONBLOCK);
 }
 
 /* Always entered with .video_lock held */
@@ -280,10 +289,9 @@
 	((x) >> 24) & 0xff
 
 /* Called with .vb_lock held, or from the first open(2), see comment there */
-static int soc_camera_set_fmt(struct soc_camera_file *icf,
+static int soc_camera_set_fmt(struct soc_camera_device *icd,
 			      struct v4l2_format *f)
 {
-	struct soc_camera_device *icd = icf->icd;
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 	struct v4l2_pix_format *pix = &f->fmt.pix;
 	int ret;
@@ -309,7 +317,7 @@
 	icd->user_width		= pix->width;
 	icd->user_height	= pix->height;
 	icd->colorspace		= pix->colorspace;
-	icf->vb_vidq.field	=
+	icd->vb_vidq.field	=
 		icd->field	= pix->field;
 
 	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -331,7 +339,6 @@
 						     dev);
 	struct soc_camera_link *icl = to_soc_camera_link(icd);
 	struct soc_camera_host *ici;
-	struct soc_camera_file *icf;
 	int ret;
 
 	if (!icd->ops)
@@ -340,14 +347,9 @@
 
 	ici = to_soc_camera_host(icd->dev.parent);
 
-	icf = vmalloc(sizeof(*icf));
-	if (!icf)
-		return -ENOMEM;
-
 	if (!try_module_get(ici->ops->owner)) {
 		dev_err(&icd->dev, "Couldn't lock capture bus driver.\n");
-		ret = -EINVAL;
-		goto emgi;
+		return -EINVAL;
 	}
 
 	/*
@@ -356,7 +358,6 @@
 	 */
 	mutex_lock(&icd->video_lock);
 
-	icf->icd = icd;
 	icd->use_count++;
 
 	/* Now we really have to activate the camera */
@@ -401,15 +402,15 @@
 		 * apart from someone else calling open() simultaneously, but
 		 * .video_lock is protecting us against it.
 		 */
-		ret = soc_camera_set_fmt(icf, &f);
+		ret = soc_camera_set_fmt(icd, &f);
 		if (ret < 0)
 			goto esfmt;
 	}
 
-	file->private_data = icf;
+	file->private_data = icd;
 	dev_dbg(&icd->dev, "camera device open\n");
 
-	ici->ops->init_videobuf(&icf->vb_vidq, icd);
+	ici->ops->init_videobuf(&icd->vb_vidq, icd);
 
 	mutex_unlock(&icd->video_lock);
 
@@ -430,15 +431,13 @@
 	icd->use_count--;
 	mutex_unlock(&icd->video_lock);
 	module_put(ici->ops->owner);
-emgi:
-	vfree(icf);
+
 	return ret;
 }
 
 static int soc_camera_close(struct file *file)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 
 	mutex_lock(&icd->video_lock);
@@ -455,12 +454,13 @@
 			icl->power(icd->pdev, 0);
 	}
 
+	if (icd->streamer == file)
+		icd->streamer = NULL;
+
 	mutex_unlock(&icd->video_lock);
 
 	module_put(ici->ops->owner);
 
-	vfree(icf);
-
 	dev_dbg(&icd->dev, "camera device close\n");
 
 	return 0;
@@ -469,8 +469,7 @@
 static ssize_t soc_camera_read(struct file *file, char __user *buf,
 			       size_t count, loff_t *ppos)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	int err = -EINVAL;
 
 	dev_err(&icd->dev, "camera device read not implemented\n");
@@ -480,13 +479,15 @@
 
 static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	int err;
 
 	dev_dbg(&icd->dev, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
 
-	err = videobuf_mmap_mapper(&icf->vb_vidq, vma);
+	if (icd->streamer != file)
+		return -EBUSY;
+
+	err = videobuf_mmap_mapper(&icd->vb_vidq, vma);
 
 	dev_dbg(&icd->dev, "vma start=0x%08lx, size=%ld, ret=%d\n",
 		(unsigned long)vma->vm_start,
@@ -498,11 +499,13 @@
 
 static unsigned int soc_camera_poll(struct file *file, poll_table *pt)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 
-	if (list_empty(&icf->vb_vidq.stream)) {
+	if (icd->streamer != file)
+		return -EBUSY;
+
+	if (list_empty(&icd->vb_vidq.stream)) {
 		dev_err(&icd->dev, "Trying to poll with no queued buffers!\n");
 		return POLLERR;
 	}
@@ -523,24 +526,29 @@
 static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
 				    struct v4l2_format *f)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	int ret;
 
 	WARN_ON(priv != file->private_data);
 
-	mutex_lock(&icf->vb_vidq.vb_lock);
+	if (icd->streamer && icd->streamer != file)
+		return -EBUSY;
 
-	if (icf->vb_vidq.bufs[0]) {
+	mutex_lock(&icd->vb_vidq.vb_lock);
+
+	if (icd->vb_vidq.bufs[0]) {
 		dev_err(&icd->dev, "S_FMT denied: queue initialised\n");
 		ret = -EBUSY;
 		goto unlock;
 	}
 
-	ret = soc_camera_set_fmt(icf, f);
+	ret = soc_camera_set_fmt(icd, f);
+
+	if (!ret && !icd->streamer)
+		icd->streamer = file;
 
 unlock:
-	mutex_unlock(&icf->vb_vidq.vb_lock);
+	mutex_unlock(&icd->vb_vidq.vb_lock);
 
 	return ret;
 }
@@ -548,8 +556,7 @@
 static int soc_camera_enum_fmt_vid_cap(struct file *file, void  *priv,
 				       struct v4l2_fmtdesc *f)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	const struct soc_mbus_pixelfmt *format;
 
 	WARN_ON(priv != file->private_data);
@@ -568,15 +575,14 @@
 static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv,
 				    struct v4l2_format *f)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct v4l2_pix_format *pix = &f->fmt.pix;
 
 	WARN_ON(priv != file->private_data);
 
 	pix->width		= icd->user_width;
 	pix->height		= icd->user_height;
-	pix->field		= icf->vb_vidq.field;
+	pix->field		= icd->vb_vidq.field;
 	pix->pixelformat	= icd->current_fmt->host_fmt->fourcc;
 	pix->bytesperline	= soc_mbus_bytes_per_line(pix->width,
 						icd->current_fmt->host_fmt);
@@ -592,8 +598,7 @@
 static int soc_camera_querycap(struct file *file, void  *priv,
 			       struct v4l2_capability *cap)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 
 	WARN_ON(priv != file->private_data);
@@ -605,8 +610,7 @@
 static int soc_camera_streamon(struct file *file, void *priv,
 			       enum v4l2_buf_type i)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	int ret;
 
@@ -615,12 +619,15 @@
 	if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		return -EINVAL;
 
+	if (icd->streamer != file)
+		return -EBUSY;
+
 	mutex_lock(&icd->video_lock);
 
 	v4l2_subdev_call(sd, video, s_stream, 1);
 
 	/* This calls buf_queue from host driver's videobuf_queue_ops */
-	ret = videobuf_streamon(&icf->vb_vidq);
+	ret = videobuf_streamon(&icd->vb_vidq);
 
 	mutex_unlock(&icd->video_lock);
 
@@ -630,8 +637,7 @@
 static int soc_camera_streamoff(struct file *file, void *priv,
 				enum v4l2_buf_type i)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 
 	WARN_ON(priv != file->private_data);
@@ -639,13 +645,16 @@
 	if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		return -EINVAL;
 
+	if (icd->streamer != file)
+		return -EBUSY;
+
 	mutex_lock(&icd->video_lock);
 
 	/*
 	 * This calls buf_release from host driver's videobuf_queue_ops for all
 	 * remaining buffers. When the last buffer is freed, stop capture
 	 */
-	videobuf_streamoff(&icf->vb_vidq);
+	videobuf_streamoff(&icd->vb_vidq);
 
 	v4l2_subdev_call(sd, video, s_stream, 0);
 
@@ -657,8 +666,7 @@
 static int soc_camera_queryctrl(struct file *file, void *priv,
 				struct v4l2_queryctrl *qc)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 	int i;
 
@@ -689,8 +697,7 @@
 static int soc_camera_g_ctrl(struct file *file, void *priv,
 			     struct v4l2_control *ctrl)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	int ret;
@@ -709,8 +716,7 @@
 static int soc_camera_s_ctrl(struct file *file, void *priv,
 			     struct v4l2_control *ctrl)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	int ret;
@@ -729,8 +735,7 @@
 static int soc_camera_cropcap(struct file *file, void *fh,
 			      struct v4l2_cropcap *a)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 
 	return ici->ops->cropcap(icd, a);
@@ -739,14 +744,13 @@
 static int soc_camera_g_crop(struct file *file, void *fh,
 			     struct v4l2_crop *a)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 	int ret;
 
-	mutex_lock(&icf->vb_vidq.vb_lock);
+	mutex_lock(&icd->vb_vidq.vb_lock);
 	ret = ici->ops->get_crop(icd, a);
-	mutex_unlock(&icf->vb_vidq.vb_lock);
+	mutex_unlock(&icd->vb_vidq.vb_lock);
 
 	return ret;
 }
@@ -759,8 +763,7 @@
 static int soc_camera_s_crop(struct file *file, void *fh,
 			     struct v4l2_crop *a)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 	struct v4l2_rect *rect = &a->c;
 	struct v4l2_crop current_crop;
@@ -773,7 +776,7 @@
 		rect->width, rect->height, rect->left, rect->top);
 
 	/* Cropping is allowed during a running capture, guard consistency */
-	mutex_lock(&icf->vb_vidq.vb_lock);
+	mutex_lock(&icd->vb_vidq.vb_lock);
 
 	/* If get_crop fails, we'll let host and / or client drivers decide */
 	ret = ici->ops->get_crop(icd, &current_crop);
@@ -782,7 +785,7 @@
 	if (ret < 0) {
 		dev_err(&icd->dev,
 			"S_CROP denied: getting current crop failed\n");
-	} else if (icf->vb_vidq.bufs[0] &&
+	} else if (icd->vb_vidq.bufs[0] &&
 		   (a->c.width != current_crop.c.width ||
 		    a->c.height != current_crop.c.height)) {
 		dev_err(&icd->dev,
@@ -792,7 +795,7 @@
 		ret = ici->ops->set_crop(icd, a);
 	}
 
-	mutex_unlock(&icf->vb_vidq.vb_lock);
+	mutex_unlock(&icd->vb_vidq.vb_lock);
 
 	return ret;
 }
@@ -800,8 +803,7 @@
 static int soc_camera_g_parm(struct file *file, void *fh,
 			     struct v4l2_streamparm *a)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 
 	if (ici->ops->get_parm)
@@ -813,8 +815,7 @@
 static int soc_camera_s_parm(struct file *file, void *fh,
 			     struct v4l2_streamparm *a)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
 
 	if (ici->ops->set_parm)
@@ -826,8 +827,7 @@
 static int soc_camera_g_chip_ident(struct file *file, void *fh,
 				   struct v4l2_dbg_chip_ident *id)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 
 	return v4l2_subdev_call(sd, core, g_chip_ident, id);
@@ -837,8 +837,7 @@
 static int soc_camera_g_register(struct file *file, void *fh,
 				 struct v4l2_dbg_register *reg)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 
 	return v4l2_subdev_call(sd, core, g_register, reg);
@@ -847,8 +846,7 @@
 static int soc_camera_s_register(struct file *file, void *fh,
 				 struct v4l2_dbg_register *reg)
 {
-	struct soc_camera_file *icf = file->private_data;
-	struct soc_camera_device *icd = icf->icd;
+	struct soc_camera_device *icd = file->private_data;
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 
 	return v4l2_subdev_call(sd, core, s_register, reg);
@@ -898,11 +896,11 @@
 	icl->board_info->platform_data = icd;
 
 	subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
-				icl->module_name, icl->board_info, NULL);
+				NULL, icl->board_info, NULL);
 	if (!subdev)
 		goto ei2cnd;
 
-	client = subdev->priv;
+	client = v4l2_get_subdevdata(subdev);
 
 	/* Use to_i2c_client(dev) to recover the i2c client */
 	dev_set_drvdata(&icd->dev, &client->dev);
@@ -1148,6 +1146,20 @@
 	return v4l2_subdev_call(sd, video, s_crop, a);
 }
 
+static int default_g_parm(struct soc_camera_device *icd,
+			  struct v4l2_streamparm *parm)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	return v4l2_subdev_call(sd, video, g_parm, parm);
+}
+
+static int default_s_parm(struct soc_camera_device *icd,
+			  struct v4l2_streamparm *parm)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	return v4l2_subdev_call(sd, video, s_parm, parm);
+}
+
 static void soc_camera_device_init(struct device *dev, void *pdata)
 {
 	dev->platform_data	= pdata;
@@ -1179,6 +1191,10 @@
 		ici->ops->get_crop = default_g_crop;
 	if (!ici->ops->cropcap)
 		ici->ops->cropcap = default_cropcap;
+	if (!ici->ops->set_parm)
+		ici->ops->set_parm = default_s_parm;
+	if (!ici->ops->get_parm)
+		ici->ops->get_parm = default_g_parm;
 
 	mutex_lock(&list_lock);
 	list_for_each_entry(ix, &hosts, list) {
diff --git a/drivers/media/video/sr030pc30.c b/drivers/media/video/sr030pc30.c
new file mode 100644
index 0000000..c9dc67a
--- /dev/null
+++ b/drivers/media/video/sr030pc30.c
@@ -0,0 +1,894 @@
+/*
+ * Driver for SiliconFile SR030PC30 VGA (1/10-Inch) Image Sensor with ISP
+ *
+ * Copyright (C) 2010 Samsung Electronics Co., Ltd
+ * Author: Sylwester Nawrocki, s.nawrocki@samsung.com
+ *
+ * Based on original driver authored by Dongsoo Nathaniel Kim
+ * and HeungJun Kim <riverful.kim@samsung.com>.
+ *
+ * Based on mt9v011 Micron Digital Image Sensor driver
+ * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-mediabus.h>
+#include <media/sr030pc30.h>
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define MODULE_NAME	"SR030PC30"
+
+/*
+ * Register offsets within a page
+ * b15..b8 - page id, b7..b0 - register address
+ */
+#define POWER_CTRL_REG		0x0001
+#define PAGEMODE_REG		0x03
+#define DEVICE_ID_REG		0x0004
+#define NOON010PC30_ID		0x86
+#define SR030PC30_ID		0x8C
+#define VDO_CTL1_REG		0x0010
+#define SUBSAMPL_NONE_VGA	0
+#define SUBSAMPL_QVGA		0x10
+#define SUBSAMPL_QQVGA		0x20
+#define VDO_CTL2_REG		0x0011
+#define SYNC_CTL_REG		0x0012
+#define WIN_ROWH_REG		0x0020
+#define WIN_ROWL_REG		0x0021
+#define WIN_COLH_REG		0x0022
+#define WIN_COLL_REG		0x0023
+#define WIN_HEIGHTH_REG		0x0024
+#define WIN_HEIGHTL_REG		0x0025
+#define WIN_WIDTHH_REG		0x0026
+#define WIN_WIDTHL_REG		0x0027
+#define HBLANKH_REG		0x0040
+#define HBLANKL_REG		0x0041
+#define VSYNCH_REG		0x0042
+#define VSYNCL_REG		0x0043
+/* page 10 */
+#define ISP_CTL_REG(n)		(0x1010 + (n))
+#define YOFS_REG		0x1040
+#define DARK_YOFS_REG		0x1041
+#define AG_ABRTH_REG		0x1050
+#define SAT_CTL_REG		0x1060
+#define BSAT_REG		0x1061
+#define RSAT_REG		0x1062
+#define AG_SAT_TH_REG		0x1063
+/* page 11 */
+#define ZLPF_CTRL_REG		0x1110
+#define ZLPF_CTRL2_REG		0x1112
+#define ZLPF_AGH_THR_REG	0x1121
+#define ZLPF_THR_REG		0x1160
+#define ZLPF_DYN_THR_REG	0x1160
+/* page 12 */
+#define YCLPF_CTL1_REG		0x1240
+#define YCLPF_CTL2_REG		0x1241
+#define YCLPF_THR_REG		0x1250
+#define BLPF_CTL_REG		0x1270
+#define BLPF_THR1_REG		0x1274
+#define BLPF_THR2_REG		0x1275
+/* page 14 - Lens Shading Compensation */
+#define LENS_CTRL_REG		0x1410
+#define LENS_XCEN_REG		0x1420
+#define LENS_YCEN_REG		0x1421
+#define LENS_R_COMP_REG		0x1422
+#define LENS_G_COMP_REG		0x1423
+#define LENS_B_COMP_REG		0x1424
+/* page 15 - Color correction */
+#define CMC_CTL_REG		0x1510
+#define CMC_OFSGH_REG		0x1514
+#define CMC_OFSGL_REG		0x1516
+#define CMC_SIGN_REG		0x1517
+/* Color correction coefficients */
+#define CMC_COEF_REG(n)		(0x1530 + (n))
+/* Color correction offset coefficients */
+#define CMC_OFS_REG(n)		(0x1540 + (n))
+/* page 16 - Gamma correction */
+#define GMA_CTL_REG		0x1610
+/* Gamma correction coefficients 0.14 */
+#define GMA_COEF_REG(n)		(0x1630 + (n))
+/* page 20 - Auto Exposure */
+#define AE_CTL1_REG		0x2010
+#define AE_CTL2_REG		0x2011
+#define AE_FRM_CTL_REG		0x2020
+#define AE_FINE_CTL_REG(n)	(0x2028 + (n))
+#define EXP_TIMEH_REG		0x2083
+#define EXP_TIMEM_REG		0x2084
+#define EXP_TIMEL_REG		0x2085
+#define EXP_MMINH_REG		0x2086
+#define EXP_MMINL_REG		0x2087
+#define EXP_MMAXH_REG		0x2088
+#define EXP_MMAXM_REG		0x2089
+#define EXP_MMAXL_REG		0x208A
+/* page 22 - Auto White Balance */
+#define AWB_CTL1_REG		0x2210
+#define AWB_ENABLE		0x80
+#define AWB_CTL2_REG		0x2211
+#define MWB_ENABLE		0x01
+/* RGB gain control (manual WB) when AWB_CTL1[7]=0 */
+#define AWB_RGAIN_REG		0x2280
+#define AWB_GGAIN_REG		0x2281
+#define AWB_BGAIN_REG		0x2282
+#define AWB_RMAX_REG		0x2283
+#define AWB_RMIN_REG		0x2284
+#define AWB_BMAX_REG		0x2285
+#define AWB_BMIN_REG		0x2286
+/* R, B gain range in bright light conditions */
+#define AWB_RMAXB_REG		0x2287
+#define AWB_RMINB_REG		0x2288
+#define AWB_BMAXB_REG		0x2289
+#define AWB_BMINB_REG		0x228A
+/* manual white balance, when AWB_CTL2[0]=1 */
+#define MWB_RGAIN_REG		0x22B2
+#define MWB_BGAIN_REG		0x22B3
+/* the token to mark an array end */
+#define REG_TERM		0xFFFF
+
+/* Minimum and maximum exposure time in ms */
+#define EXPOS_MIN_MS		1
+#define EXPOS_MAX_MS		125
+
+struct sr030pc30_info {
+	struct v4l2_subdev sd;
+	const struct sr030pc30_platform_data *pdata;
+	const struct sr030pc30_format *curr_fmt;
+	const struct sr030pc30_frmsize *curr_win;
+	unsigned int auto_wb:1;
+	unsigned int auto_exp:1;
+	unsigned int hflip:1;
+	unsigned int vflip:1;
+	unsigned int sleep:1;
+	unsigned int exposure;
+	u8 blue_balance;
+	u8 red_balance;
+	u8 i2c_reg_page;
+};
+
+struct sr030pc30_format {
+	enum v4l2_mbus_pixelcode code;
+	enum v4l2_colorspace colorspace;
+	u16 ispctl1_reg;
+};
+
+struct sr030pc30_frmsize {
+	u16 width;
+	u16 height;
+	int vid_ctl1;
+};
+
+struct i2c_regval {
+	u16 addr;
+	u16 val;
+};
+
+static const struct v4l2_queryctrl sr030pc30_ctrl[] = {
+	{
+		.id		= V4L2_CID_AUTO_WHITE_BALANCE,
+		.type		= V4L2_CTRL_TYPE_BOOLEAN,
+		.name		= "Auto White Balance",
+		.minimum	= 0,
+		.maximum	= 1,
+		.step		= 1,
+		.default_value	= 1,
+	}, {
+		.id		= V4L2_CID_RED_BALANCE,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Red Balance",
+		.minimum	= 0,
+		.maximum	= 127,
+		.step		= 1,
+		.default_value	= 64,
+		.flags		= 0,
+	}, {
+		.id		= V4L2_CID_BLUE_BALANCE,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Blue Balance",
+		.minimum	= 0,
+		.maximum	= 127,
+		.step		= 1,
+		.default_value	= 64,
+	}, {
+		.id		= V4L2_CID_EXPOSURE_AUTO,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Auto Exposure",
+		.minimum	= 0,
+		.maximum	= 1,
+		.step		= 1,
+		.default_value	= 1,
+	}, {
+		.id		= V4L2_CID_EXPOSURE,
+		.type		= V4L2_CTRL_TYPE_INTEGER,
+		.name		= "Exposure",
+		.minimum	= EXPOS_MIN_MS,
+		.maximum	= EXPOS_MAX_MS,
+		.step		= 1,
+		.default_value	= 1,
+	}, {
+	}
+};
+
+/* supported resolutions */
+static const struct sr030pc30_frmsize sr030pc30_sizes[] = {
+	{
+		.width		= 640,
+		.height		= 480,
+		.vid_ctl1	= SUBSAMPL_NONE_VGA,
+	}, {
+		.width		= 320,
+		.height		= 240,
+		.vid_ctl1	= SUBSAMPL_QVGA,
+	}, {
+		.width		= 160,
+		.height		= 120,
+		.vid_ctl1	= SUBSAMPL_QQVGA,
+	},
+};
+
+/* supported pixel formats */
+static const struct sr030pc30_format sr030pc30_formats[] = {
+	{
+		.code		= V4L2_MBUS_FMT_YUYV8_2X8,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+		.ispctl1_reg	= 0x03,
+	}, {
+		.code		= V4L2_MBUS_FMT_YVYU8_2X8,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+		.ispctl1_reg	= 0x02,
+	}, {
+		.code		= V4L2_MBUS_FMT_VYUY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+		.ispctl1_reg	= 0,
+	}, {
+		.code		= V4L2_MBUS_FMT_UYVY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+		.ispctl1_reg	= 0x01,
+	}, {
+		.code		= V4L2_MBUS_FMT_RGB565_2X8_BE,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+		.ispctl1_reg	= 0x40,
+	},
+};
+
+static const struct i2c_regval sr030pc30_base_regs[] = {
+	/* Window size and position within pixel matrix */
+	{ WIN_ROWH_REG,		0x00 }, { WIN_ROWL_REG,		0x06 },
+	{ WIN_COLH_REG,		0x00 },	{ WIN_COLL_REG,		0x06 },
+	{ WIN_HEIGHTH_REG,	0x01 }, { WIN_HEIGHTL_REG,	0xE0 },
+	{ WIN_WIDTHH_REG,	0x02 }, { WIN_WIDTHL_REG,	0x80 },
+	{ HBLANKH_REG,		0x01 }, { HBLANKL_REG,		0x50 },
+	{ VSYNCH_REG,		0x00 }, { VSYNCL_REG,		0x14 },
+	{ SYNC_CTL_REG,		0 },
+	/* Color corection and saturation */
+	{ ISP_CTL_REG(0),	0x30 }, { YOFS_REG,		0x80 },
+	{ DARK_YOFS_REG,	0x04 }, { AG_ABRTH_REG,		0x78 },
+	{ SAT_CTL_REG,		0x1F }, { BSAT_REG,		0x90 },
+	{ AG_SAT_TH_REG,	0xF0 }, { 0x1064,		0x80 },
+	{ CMC_CTL_REG,		0x03 }, { CMC_OFSGH_REG,	0x3C },
+	{ CMC_OFSGL_REG,	0x2C }, { CMC_SIGN_REG,		0x2F },
+	{ CMC_COEF_REG(0),	0xCB }, { CMC_OFS_REG(0),	0x87 },
+	{ CMC_COEF_REG(1),	0x61 }, { CMC_OFS_REG(1),	0x18 },
+	{ CMC_COEF_REG(2),	0x16 }, { CMC_OFS_REG(2),	0x91 },
+	{ CMC_COEF_REG(3),	0x23 }, { CMC_OFS_REG(3),	0x94 },
+	{ CMC_COEF_REG(4),	0xCE }, { CMC_OFS_REG(4),	0x9f },
+	{ CMC_COEF_REG(5),	0x2B }, { CMC_OFS_REG(5),	0x33 },
+	{ CMC_COEF_REG(6),	0x01 }, { CMC_OFS_REG(6),	0x00 },
+	{ CMC_COEF_REG(7),	0x34 }, { CMC_OFS_REG(7),	0x94 },
+	{ CMC_COEF_REG(8),	0x75 }, { CMC_OFS_REG(8),	0x14 },
+	/* Color corection coefficients */
+	{ GMA_CTL_REG,		0x03 },	{ GMA_COEF_REG(0),	0x00 },
+	{ GMA_COEF_REG(1),	0x19 },	{ GMA_COEF_REG(2),	0x26 },
+	{ GMA_COEF_REG(3),	0x3B },	{ GMA_COEF_REG(4),	0x5D },
+	{ GMA_COEF_REG(5),	0x79 }, { GMA_COEF_REG(6),	0x8E },
+	{ GMA_COEF_REG(7),	0x9F },	{ GMA_COEF_REG(8),	0xAF },
+	{ GMA_COEF_REG(9),	0xBD },	{ GMA_COEF_REG(10),	0xCA },
+	{ GMA_COEF_REG(11),	0xDD }, { GMA_COEF_REG(12),	0xEC },
+	{ GMA_COEF_REG(13),	0xF7 },	{ GMA_COEF_REG(14),	0xFF },
+	/* Noise reduction, Z-LPF, YC-LPF and BLPF filters setup */
+	{ ZLPF_CTRL_REG,	0x99 }, { ZLPF_CTRL2_REG,	0x0E },
+	{ ZLPF_AGH_THR_REG,	0x29 }, { ZLPF_THR_REG,		0x0F },
+	{ ZLPF_DYN_THR_REG,	0x63 }, { YCLPF_CTL1_REG,	0x23 },
+	{ YCLPF_CTL2_REG,	0x3B }, { YCLPF_THR_REG,	0x05 },
+	{ BLPF_CTL_REG,		0x1D }, { BLPF_THR1_REG,	0x05 },
+	{ BLPF_THR2_REG,	0x04 },
+	/* Automatic white balance */
+	{ AWB_CTL1_REG,		0xFB }, { AWB_CTL2_REG,		0x26 },
+	{ AWB_RMAX_REG,		0x54 }, { AWB_RMIN_REG,		0x2B },
+	{ AWB_BMAX_REG,		0x57 }, { AWB_BMIN_REG,		0x29 },
+	{ AWB_RMAXB_REG,	0x50 }, { AWB_RMINB_REG,	0x43 },
+	{ AWB_BMAXB_REG,	0x30 }, { AWB_BMINB_REG,	0x22 },
+	/* Auto exposure */
+	{ AE_CTL1_REG,		0x8C }, { AE_CTL2_REG,		0x04 },
+	{ AE_FRM_CTL_REG,	0x01 }, { AE_FINE_CTL_REG(0),	0x3F },
+	{ AE_FINE_CTL_REG(1),	0xA3 }, { AE_FINE_CTL_REG(3),	0x34 },
+	/* Lens shading compensation */
+	{ LENS_CTRL_REG,	0x01 }, { LENS_XCEN_REG,	0x80 },
+	{ LENS_YCEN_REG,	0x70 }, { LENS_R_COMP_REG,	0x53 },
+	{ LENS_G_COMP_REG,	0x40 }, { LENS_B_COMP_REG,	0x3e },
+	{ REG_TERM,		0 },
+};
+
+static inline struct sr030pc30_info *to_sr030pc30(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct sr030pc30_info, sd);
+}
+
+static inline int set_i2c_page(struct sr030pc30_info *info,
+			       struct i2c_client *client, unsigned int reg)
+{
+	int ret = 0;
+	u32 page = reg >> 8 & 0xFF;
+
+	if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) {
+		ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page);
+		if (!ret)
+			info->i2c_reg_page = page;
+	}
+	return ret;
+}
+
+static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+
+	int ret = set_i2c_page(info, client, reg_addr);
+	if (!ret)
+		ret = i2c_smbus_read_byte_data(client, reg_addr & 0xFF);
+	return ret;
+}
+
+static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+
+	int ret = set_i2c_page(info, client, reg_addr);
+	if (!ret)
+		ret = i2c_smbus_write_byte_data(
+			client, reg_addr & 0xFF, val);
+	return ret;
+}
+
+static inline int sr030pc30_bulk_write_reg(struct v4l2_subdev *sd,
+				const struct i2c_regval *msg)
+{
+	while (msg->addr != REG_TERM) {
+		int ret = cam_i2c_write(sd, msg->addr, msg->val);
+		if (ret)
+			return ret;
+		msg++;
+	}
+	return 0;
+}
+
+/* Device reset and sleep mode control */
+static int sr030pc30_pwr_ctrl(struct v4l2_subdev *sd,
+				     bool reset, bool sleep)
+{
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+	u8 reg = sleep ? 0xF1 : 0xF0;
+	int ret = 0;
+
+	if (reset)
+		ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02);
+	if (!ret) {
+		ret = cam_i2c_write(sd, POWER_CTRL_REG, reg);
+		if (!ret) {
+			info->sleep = sleep;
+			if (reset)
+				info->i2c_reg_page = -1;
+		}
+	}
+	return ret;
+}
+
+static inline int sr030pc30_enable_autoexposure(struct v4l2_subdev *sd, int on)
+{
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+	/* auto anti-flicker is also enabled here */
+	int ret = cam_i2c_write(sd, AE_CTL1_REG, on ? 0xDC : 0x0C);
+	if (!ret)
+		info->auto_exp = on;
+	return ret;
+}
+
+static int sr030pc30_set_exposure(struct v4l2_subdev *sd, int value)
+{
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+
+	unsigned long expos = value * info->pdata->clk_rate / (8 * 1000);
+
+	int ret = cam_i2c_write(sd, EXP_TIMEH_REG, expos >> 16 & 0xFF);
+	if (!ret)
+		ret = cam_i2c_write(sd, EXP_TIMEM_REG, expos >> 8 & 0xFF);
+	if (!ret)
+		ret = cam_i2c_write(sd, EXP_TIMEL_REG, expos & 0xFF);
+	if (!ret) { /* Turn off AE */
+		info->exposure = value;
+		ret = sr030pc30_enable_autoexposure(sd, 0);
+	}
+	return ret;
+}
+
+/* Automatic white balance control */
+static int sr030pc30_enable_autowhitebalance(struct v4l2_subdev *sd, int on)
+{
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+
+	int ret = cam_i2c_write(sd, AWB_CTL2_REG, on ? 0x2E : 0x2F);
+	if (!ret)
+		ret = cam_i2c_write(sd, AWB_CTL1_REG, on ? 0xFB : 0x7B);
+	if (!ret)
+		info->auto_wb = on;
+
+	return ret;
+}
+
+static int sr030pc30_set_flip(struct v4l2_subdev *sd)
+{
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+
+	s32 reg = cam_i2c_read(sd, VDO_CTL2_REG);
+	if (reg < 0)
+		return reg;
+
+	reg &= 0x7C;
+	if (info->hflip)
+		reg |= 0x01;
+	if (info->vflip)
+		reg |= 0x02;
+	return cam_i2c_write(sd, VDO_CTL2_REG, reg | 0x80);
+}
+
+/* Configure resolution, color format and image flip */
+static int sr030pc30_set_params(struct v4l2_subdev *sd)
+{
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+	int ret;
+
+	if (!info->curr_win)
+		return -EINVAL;
+
+	/* Configure the resolution through subsampling */
+	ret = cam_i2c_write(sd, VDO_CTL1_REG,
+			    info->curr_win->vid_ctl1);
+
+	if (!ret && info->curr_fmt)
+		ret = cam_i2c_write(sd, ISP_CTL_REG(0),
+				info->curr_fmt->ispctl1_reg);
+	if (!ret)
+		ret = sr030pc30_set_flip(sd);
+
+	return ret;
+}
+
+/* Find nearest matching image pixel size. */
+static int sr030pc30_try_frame_size(struct v4l2_mbus_framefmt *mf)
+{
+	unsigned int min_err = ~0;
+	int i = ARRAY_SIZE(sr030pc30_sizes);
+	const struct sr030pc30_frmsize *fsize = &sr030pc30_sizes[0],
+					*match = NULL;
+	while (i--) {
+		int err = abs(fsize->width - mf->width)
+				+ abs(fsize->height - mf->height);
+		if (err < min_err) {
+			min_err = err;
+			match = fsize;
+		}
+		fsize++;
+	}
+	if (match) {
+		mf->width  = match->width;
+		mf->height = match->height;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int sr030pc30_queryctrl(struct v4l2_subdev *sd,
+			       struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++)
+		if (qc->id == sr030pc30_ctrl[i].id) {
+			*qc = sr030pc30_ctrl[i];
+			v4l2_dbg(1, debug, sd, "%s id: %d\n",
+				 __func__, qc->id);
+			return 0;
+		}
+
+	return -EINVAL;
+}
+
+static inline int sr030pc30_set_bluebalance(struct v4l2_subdev *sd, int value)
+{
+	int ret = cam_i2c_write(sd, MWB_BGAIN_REG, value);
+	if (!ret)
+		to_sr030pc30(sd)->blue_balance = value;
+	return ret;
+}
+
+static inline int sr030pc30_set_redbalance(struct v4l2_subdev *sd, int value)
+{
+	int ret = cam_i2c_write(sd, MWB_RGAIN_REG, value);
+	if (!ret)
+		to_sr030pc30(sd)->red_balance = value;
+	return ret;
+}
+
+static int sr030pc30_s_ctrl(struct v4l2_subdev *sd,
+			    struct v4l2_control *ctrl)
+{
+	int i, ret = 0;
+
+	for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++)
+		if (ctrl->id == sr030pc30_ctrl[i].id)
+			break;
+
+	if (i == ARRAY_SIZE(sr030pc30_ctrl))
+		return -EINVAL;
+
+	if (ctrl->value < sr030pc30_ctrl[i].minimum ||
+		ctrl->value > sr030pc30_ctrl[i].maximum)
+			return -ERANGE;
+
+	v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n",
+			 __func__, ctrl->id, ctrl->value);
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		sr030pc30_enable_autowhitebalance(sd, ctrl->value);
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		ret = sr030pc30_set_bluebalance(sd, ctrl->value);
+		break;
+	case V4L2_CID_RED_BALANCE:
+		ret = sr030pc30_set_redbalance(sd, ctrl->value);
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+		sr030pc30_enable_autoexposure(sd,
+			ctrl->value == V4L2_EXPOSURE_AUTO);
+		break;
+	case V4L2_CID_EXPOSURE:
+		ret = sr030pc30_set_exposure(sd, ctrl->value);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int sr030pc30_g_ctrl(struct v4l2_subdev *sd,
+			    struct v4l2_control *ctrl)
+{
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+
+	v4l2_dbg(1, debug, sd, "%s: id: %d\n", __func__, ctrl->id);
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ctrl->value = info->auto_wb;
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		ctrl->value = info->blue_balance;
+		break;
+	case V4L2_CID_RED_BALANCE:
+		ctrl->value = info->red_balance;
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+		ctrl->value = info->auto_exp;
+		break;
+	case V4L2_CID_EXPOSURE:
+		ctrl->value = info->exposure;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int sr030pc30_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+			      enum v4l2_mbus_pixelcode *code)
+{
+	if (!code || index >= ARRAY_SIZE(sr030pc30_formats))
+		return -EINVAL;
+
+	*code = sr030pc30_formats[index].code;
+	return 0;
+}
+
+static int sr030pc30_g_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_mbus_framefmt *mf)
+{
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+	int ret;
+
+	if (!mf)
+		return -EINVAL;
+
+	if (!info->curr_win || !info->curr_fmt) {
+		ret = sr030pc30_set_params(sd);
+		if (ret)
+			return ret;
+	}
+
+	mf->width	= info->curr_win->width;
+	mf->height	= info->curr_win->height;
+	mf->code	= info->curr_fmt->code;
+	mf->colorspace	= info->curr_fmt->colorspace;
+	mf->field	= V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+/* Return nearest media bus frame format. */
+static const struct sr030pc30_format *try_fmt(struct v4l2_subdev *sd,
+					      struct v4l2_mbus_framefmt *mf)
+{
+	int i = ARRAY_SIZE(sr030pc30_formats);
+
+	sr030pc30_try_frame_size(mf);
+
+	while (i--)
+		if (mf->code == sr030pc30_formats[i].code)
+			break;
+
+	mf->code = sr030pc30_formats[i].code;
+
+	return &sr030pc30_formats[i];
+}
+
+/* Return nearest media bus frame format. */
+static int sr030pc30_try_fmt(struct v4l2_subdev *sd,
+			     struct v4l2_mbus_framefmt *mf)
+{
+	if (!sd || !mf)
+		return -EINVAL;
+
+	try_fmt(sd, mf);
+	return 0;
+}
+
+static int sr030pc30_s_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_mbus_framefmt *mf)
+{
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+
+	if (!sd || !mf)
+		return -EINVAL;
+
+	info->curr_fmt = try_fmt(sd, mf);
+
+	return sr030pc30_set_params(sd);
+}
+
+static int sr030pc30_base_config(struct v4l2_subdev *sd)
+{
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+	int ret;
+	unsigned long expmin, expmax;
+
+	ret = sr030pc30_bulk_write_reg(sd, sr030pc30_base_regs);
+	if (!ret) {
+		info->curr_fmt = &sr030pc30_formats[0];
+		info->curr_win = &sr030pc30_sizes[0];
+		ret = sr030pc30_set_params(sd);
+	}
+	if (!ret)
+		ret = sr030pc30_pwr_ctrl(sd, false, false);
+
+	if (!ret && !info->pdata)
+		return ret;
+
+	expmin = EXPOS_MIN_MS * info->pdata->clk_rate / (8 * 1000);
+	expmax = EXPOS_MAX_MS * info->pdata->clk_rate / (8 * 1000);
+
+	v4l2_dbg(1, debug, sd, "%s: expmin= %lx, expmax= %lx", __func__,
+		 expmin, expmax);
+
+	/* Setting up manual exposure time range */
+	ret = cam_i2c_write(sd, EXP_MMINH_REG, expmin >> 8 & 0xFF);
+	if (!ret)
+		ret = cam_i2c_write(sd, EXP_MMINL_REG, expmin & 0xFF);
+	if (!ret)
+		ret = cam_i2c_write(sd, EXP_MMAXH_REG, expmax >> 16 & 0xFF);
+	if (!ret)
+		ret = cam_i2c_write(sd, EXP_MMAXM_REG, expmax >> 8 & 0xFF);
+	if (!ret)
+		ret = cam_i2c_write(sd, EXP_MMAXL_REG, expmax & 0xFF);
+
+	return ret;
+}
+
+static int sr030pc30_s_config(struct v4l2_subdev *sd,
+			      int irq, void *platform_data)
+{
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+
+	info->pdata = platform_data;
+	return 0;
+}
+
+static int sr030pc30_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	return 0;
+}
+
+static int sr030pc30_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+	const struct sr030pc30_platform_data *pdata = info->pdata;
+	int ret;
+
+	if (WARN(pdata == NULL, "No platform data!"))
+		return -ENOMEM;
+
+	/*
+	 * Put sensor into power sleep mode before switching off
+	 * power and disabling MCLK.
+	 */
+	if (!on)
+		sr030pc30_pwr_ctrl(sd, false, true);
+
+	/* set_power controls sensor's power and clock */
+	if (pdata->set_power) {
+		ret = pdata->set_power(&client->dev, on);
+		if (ret)
+			return ret;
+	}
+
+	if (on) {
+		ret = sr030pc30_base_config(sd);
+	} else {
+		info->curr_win = NULL;
+		info->curr_fmt = NULL;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_subdev_core_ops sr030pc30_core_ops = {
+	.s_config	= sr030pc30_s_config,
+	.s_power	= sr030pc30_s_power,
+	.queryctrl	= sr030pc30_queryctrl,
+	.s_ctrl		= sr030pc30_s_ctrl,
+	.g_ctrl		= sr030pc30_g_ctrl,
+};
+
+static const struct v4l2_subdev_video_ops sr030pc30_video_ops = {
+	.s_stream	= sr030pc30_s_stream,
+	.g_mbus_fmt	= sr030pc30_g_fmt,
+	.s_mbus_fmt	= sr030pc30_s_fmt,
+	.try_mbus_fmt	= sr030pc30_try_fmt,
+	.enum_mbus_fmt	= sr030pc30_enum_fmt,
+};
+
+static const struct v4l2_subdev_ops sr030pc30_ops = {
+	.core	= &sr030pc30_core_ops,
+	.video	= &sr030pc30_video_ops,
+};
+
+/*
+ * Detect sensor type. Return 0 if SR030PC30 was detected
+ * or -ENODEV otherwise.
+ */
+static int sr030pc30_detect(struct i2c_client *client)
+{
+	const struct sr030pc30_platform_data *pdata
+		= client->dev.platform_data;
+	int ret;
+
+	/* Enable sensor's power and clock */
+	if (pdata->set_power) {
+		ret = pdata->set_power(&client->dev, 1);
+		if (ret)
+			return ret;
+	}
+
+	ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG);
+
+	if (pdata->set_power)
+		pdata->set_power(&client->dev, 0);
+
+	if (ret < 0) {
+		dev_err(&client->dev, "%s: I2C read failed\n", __func__);
+		return ret;
+	}
+
+	return ret == SR030PC30_ID ? 0 : -ENODEV;
+}
+
+
+static int sr030pc30_probe(struct i2c_client *client,
+			   const struct i2c_device_id *id)
+{
+	struct sr030pc30_info *info;
+	struct v4l2_subdev *sd;
+	const struct sr030pc30_platform_data *pdata
+		= client->dev.platform_data;
+	int ret;
+
+	if (!pdata) {
+		dev_err(&client->dev, "No platform data!");
+		return -EIO;
+	}
+
+	ret = sr030pc30_detect(client);
+	if (ret)
+		return ret;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	sd = &info->sd;
+	strcpy(sd->name, MODULE_NAME);
+	info->pdata = client->dev.platform_data;
+
+	v4l2_i2c_subdev_init(sd, client, &sr030pc30_ops);
+
+	info->i2c_reg_page	= -1;
+	info->hflip		= 1;
+	info->auto_exp		= 1;
+	info->exposure		= 30;
+
+	return 0;
+}
+
+static int sr030pc30_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct sr030pc30_info *info = to_sr030pc30(sd);
+
+	v4l2_device_unregister_subdev(sd);
+	kfree(info);
+	return 0;
+}
+
+static const struct i2c_device_id sr030pc30_id[] = {
+	{ MODULE_NAME, 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, sr030pc30_id);
+
+
+static struct i2c_driver sr030pc30_i2c_driver = {
+	.driver = {
+		.name = MODULE_NAME
+	},
+	.probe		= sr030pc30_probe,
+	.remove		= sr030pc30_remove,
+	.id_table	= sr030pc30_id,
+};
+
+static int __init sr030pc30_init(void)
+{
+	return i2c_add_driver(&sr030pc30_i2c_driver);
+}
+
+static void __exit sr030pc30_exit(void)
+{
+	i2c_del_driver(&sr030pc30_i2c_driver);
+}
+
+module_init(sr030pc30_init);
+module_exit(sr030pc30_exit);
+
+MODULE_DESCRIPTION("Siliconfile SR030PC30 camera driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c
index 80f1cee..3941f95 100644
--- a/drivers/media/video/tda7432.c
+++ b/drivers/media/video/tda7432.c
@@ -36,7 +36,6 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
 #include <media/i2c-addr.h>
-#include <media/v4l2-i2c-drv.h>
 
 #ifndef VIDEO_AUDIO_BALANCE
 # define VIDEO_AUDIO_BALANCE 32
@@ -472,9 +471,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, tda7432_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "tda7432",
-	.probe = tda7432_probe,
-	.remove = tda7432_remove,
-	.id_table = tda7432_id,
+static struct i2c_driver tda7432_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "tda7432",
+	},
+	.probe		= tda7432_probe,
+	.remove		= tda7432_remove,
+	.id_table	= tda7432_id,
 };
+
+static __init int init_tda7432(void)
+{
+	return i2c_add_driver(&tda7432_driver);
+}
+
+static __exit void exit_tda7432(void)
+{
+	i2c_del_driver(&tda7432_driver);
+}
+
+module_init(init_tda7432);
+module_exit(exit_tda7432);
diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c
index 92d22d8..5d4cf3b 100644
--- a/drivers/media/video/tda9840.c
+++ b/drivers/media/video/tda9840.c
@@ -32,7 +32,6 @@
 #include <linux/i2c.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
 MODULE_DESCRIPTION("tda9840 driver");
@@ -199,9 +198,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, tda9840_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "tda9840",
-	.probe = tda9840_probe,
-	.remove = tda9840_remove,
-	.id_table = tda9840_id,
+static struct i2c_driver tda9840_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "tda9840",
+	},
+	.probe		= tda9840_probe,
+	.remove		= tda9840_remove,
+	.id_table	= tda9840_id,
 };
+
+static __init int init_tda9840(void)
+{
+	return i2c_add_driver(&tda9840_driver);
+}
+
+static __exit void exit_tda9840(void)
+{
+	i2c_del_driver(&tda9840_driver);
+}
+
+module_init(init_tda9840);
+module_exit(exit_tda9840);
diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c
index 24e2b7d..35b6ff5 100644
--- a/drivers/media/video/tda9875.c
+++ b/drivers/media/video/tda9875.c
@@ -28,7 +28,6 @@
 #include <linux/i2c.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
-#include <media/v4l2-i2c-drv.h>
 #include <media/i2c-addr.h>
 
 static int debug; /* insmod parameter */
@@ -388,9 +387,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, tda9875_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "tda9875",
-	.probe = tda9875_probe,
-	.remove = tda9875_remove,
-	.id_table = tda9875_id,
+static struct i2c_driver tda9875_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "tda9875",
+	},
+	.probe		= tda9875_probe,
+	.remove		= tda9875_remove,
+	.id_table	= tda9875_id,
 };
+
+static __init int init_tda9875(void)
+{
+	return i2c_add_driver(&tda9875_driver);
+}
+
+static __exit void exit_tda9875(void)
+{
+	i2c_del_driver(&tda9875_driver);
+}
+
+module_init(init_tda9875);
+module_exit(exit_tda9875);
diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c
index 3021a1e..3e99cea 100644
--- a/drivers/media/video/tea6415c.c
+++ b/drivers/media/video/tea6415c.c
@@ -34,7 +34,6 @@
 #include <linux/i2c.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 #include "tea6415c.h"
 
 MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
@@ -175,9 +174,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, tea6415c_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "tea6415c",
-	.probe = tea6415c_probe,
-	.remove = tea6415c_remove,
-	.id_table = tea6415c_id,
+static struct i2c_driver tea6415c_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "tea6415c",
+	},
+	.probe		= tea6415c_probe,
+	.remove		= tea6415c_remove,
+	.id_table	= tea6415c_id,
 };
+
+static __init int init_tea6415c(void)
+{
+	return i2c_add_driver(&tea6415c_driver);
+}
+
+static __exit void exit_tea6415c(void)
+{
+	i2c_del_driver(&tea6415c_driver);
+}
+
+module_init(init_tea6415c);
+module_exit(exit_tea6415c);
diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c
index 49dafc5..5ea8404 100644
--- a/drivers/media/video/tea6420.c
+++ b/drivers/media/video/tea6420.c
@@ -34,7 +34,6 @@
 #include <linux/i2c.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 #include "tea6420.h"
 
 MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
@@ -157,9 +156,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, tea6420_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "tea6420",
-	.probe = tea6420_probe,
-	.remove = tea6420_remove,
-	.id_table = tea6420_id,
+static struct i2c_driver tea6420_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "tea6420",
+	},
+	.probe		= tea6420_probe,
+	.remove		= tea6420_remove,
+	.id_table	= tea6420_id,
 };
+
+static __init int init_tea6420(void)
+{
+	return i2c_add_driver(&tea6420_driver);
+}
+
+static __exit void exit_tea6420(void)
+{
+	i2c_del_driver(&tea6420_driver);
+}
+
+module_init(init_tea6420);
+module_exit(exit_tea6420);
diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c
index d0cc012..a1ffe18 100644
--- a/drivers/media/video/tlg2300/pd-video.c
+++ b/drivers/media/video/tlg2300/pd-video.c
@@ -1434,7 +1434,7 @@
 				V4L2_BUF_TYPE_VIDEO_CAPTURE,
 				V4L2_FIELD_INTERLACED,/* video is interlacd */
 				sizeof(struct videobuf_buffer),/*it's enough*/
-				front);
+				front, NULL);
 	} else if (vfd->vfl_type == VFL_TYPE_VBI
 		&& !(pd->state & POSEIDON_STATE_VBI)) {
 		front = kzalloc(sizeof(struct front_face), GFP_KERNEL);
@@ -1451,7 +1451,7 @@
 				V4L2_BUF_TYPE_VBI_CAPTURE,
 				V4L2_FIELD_NONE, /* vbi is NONE mode */
 				sizeof(struct videobuf_buffer),
-				front);
+				front, NULL);
 	} else {
 		/* maybe add FM support here */
 		log("other ");
diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c
index 9ddb32b..dfc4dd7 100644
--- a/drivers/media/video/tlv320aic23b.c
+++ b/drivers/media/video/tlv320aic23b.c
@@ -29,10 +29,8 @@
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
-#include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("tlv320aic23b driver");
 MODULE_AUTHOR("Scott Alfter, Ulf Eklund, Hans Verkuil");
@@ -199,9 +197,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "tlv320aic23b",
-	.probe = tlv320aic23b_probe,
-	.remove = tlv320aic23b_remove,
-	.id_table = tlv320aic23b_id,
+static struct i2c_driver tlv320aic23b_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "tlv320aic23b",
+	},
+	.probe		= tlv320aic23b_probe,
+	.remove		= tlv320aic23b_remove,
+	.id_table	= tlv320aic23b_id,
 };
+
+static __init int init_tlv320aic23b(void)
+{
+	return i2c_add_driver(&tlv320aic23b_driver);
+}
+
+static __exit void exit_tlv320aic23b(void)
+{
+	i2c_del_driver(&tlv320aic23b_driver);
+}
+
+module_init(init_tlv320aic23b);
+module_exit(exit_tlv320aic23b);
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index c4dab6c..1cec122 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -20,7 +20,6 @@
 #include <media/tuner-types.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
-#include <media/v4l2-i2c-drv.h>
 #include "mt20xx.h"
 #include "tda8290.h"
 #include "tea5761.h"
@@ -428,6 +427,7 @@
 	{
 		struct tda18271_config cfg = {
 			.config = t->config,
+			.small_i2c = TDA18271_03_BYTE_CHUNK_INIT,
 		};
 
 		if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr,
@@ -1053,12 +1053,6 @@
 			printk(KERN_CONT "%02x ", buffer[i]);
 		printk("\n");
 	}
-	/* HACK: This test was added to avoid tuner to probe tda9840 and
-	   tea6415c on the MXB card */
-	if (client->adapter->id == I2C_HW_SAA7146 && client->addr < 0x4a) {
-		kfree(t);
-		return -ENODEV;
-	}
 
 	/* autodetection code based on the i2c addr */
 	if (!no_autodetect) {
@@ -1176,16 +1170,32 @@
 };
 MODULE_DEVICE_TABLE(i2c, tuner_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "tuner",
-	.probe = tuner_probe,
-	.remove = tuner_remove,
-	.command = tuner_command,
-	.suspend = tuner_suspend,
-	.resume = tuner_resume,
-	.id_table = tuner_id,
+static struct i2c_driver tuner_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "tuner",
+	},
+	.probe		= tuner_probe,
+	.remove		= tuner_remove,
+	.command	= tuner_command,
+	.suspend	= tuner_suspend,
+	.resume		= tuner_resume,
+	.id_table	= tuner_id,
 };
 
+static __init int init_tuner(void)
+{
+	return i2c_add_driver(&tuner_driver);
+}
+
+static __exit void exit_tuner(void)
+{
+	i2c_del_driver(&tuner_driver);
+}
+
+module_init(init_tuner);
+module_exit(exit_tuner);
+
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * ---------------------------------------------------------------------------
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
index 800fc1b..a25e2b5 100644
--- a/drivers/media/video/tvaudio.c
+++ b/drivers/media/video/tvaudio.c
@@ -35,7 +35,6 @@
 #include <media/tvaudio.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 #include <media/i2c-addr.h>
 
@@ -1227,18 +1226,6 @@
 static int tda8425_shift10(int val) { return (val >> 10) | 0xc0; }
 static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; }
 
-static int tda8425_initialize(struct CHIPSTATE *chip)
-{
-	struct CHIPDESC *desc = chip->desc;
-	struct i2c_client *c = v4l2_get_subdevdata(&chip->sd);
-	int inputmap[4] = { /* tuner	*/ TDA8425_S1_CH2, /* radio  */ TDA8425_S1_CH1,
-			    /* extern	*/ TDA8425_S1_CH1, /* intern */ TDA8425_S1_OFF};
-
-	if (c->adapter->id == I2C_HW_B_RIVA)
-		memcpy(desc->inputmap, inputmap, sizeof(inputmap));
-	return 0;
-}
-
 static void tda8425_setmode(struct CHIPSTATE *chip, int mode)
 {
 	int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1;
@@ -1574,7 +1561,6 @@
 		.treblereg  = TDA8425_TR,
 
 		/* callbacks */
-		.initialize = tda8425_initialize,
 		.volfunc    = tda8425_shift10,
 		.bassfunc   = tda8425_shift12,
 		.treblefunc = tda8425_shift12,
@@ -2079,9 +2065,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, tvaudio_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "tvaudio",
-	.probe = tvaudio_probe,
-	.remove = tvaudio_remove,
-	.id_table = tvaudio_id,
+static struct i2c_driver tvaudio_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "tvaudio",
+	},
+	.probe		= tvaudio_probe,
+	.remove		= tvaudio_remove,
+	.id_table	= tvaudio_id,
 };
+
+static __init int init_tvaudio(void)
+{
+	return i2c_add_driver(&tvaudio_driver);
+}
+
+static __exit void exit_tvaudio(void)
+{
+	i2c_del_driver(&tvaudio_driver);
+}
+
+module_init(init_tvaudio);
+module_exit(exit_tvaudio);
diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c
index 71c73fa..45bcf03 100644
--- a/drivers/media/video/tvp514x.c
+++ b/drivers/media/video/tvp514x.c
@@ -35,6 +35,7 @@
 
 #include <media/v4l2-device.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-mediabus.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/tvp514x.h>
 
@@ -929,69 +930,51 @@
 }
 
 /**
- * tvp514x_enum_fmt_cap() - V4L2 decoder interface handler for enum_fmt
+ * tvp514x_enum_mbus_fmt() - V4L2 decoder interface handler for enum_mbus_fmt
  * @sd: pointer to standard V4L2 sub-device structure
- * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure
+ * @index: index of pixelcode to retrieve
+ * @code: receives the pixelcode
  *
- * Implement the VIDIOC_ENUM_FMT ioctl to enumerate supported formats
+ * Enumerates supported mediabus formats
  */
 static int
-tvp514x_enum_fmt_cap(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmt)
+tvp514x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+					enum v4l2_mbus_pixelcode *code)
 {
-	if (fmt == NULL || fmt->index)
+	if (index)
 		return -EINVAL;
 
-	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		/* only capture is supported */
-		return -EINVAL;
-
-	/* only one format */
-	fmt->flags = 0;
-	strlcpy(fmt->description, "8-bit UYVY 4:2:2 Format",
-					sizeof(fmt->description));
-	fmt->pixelformat = V4L2_PIX_FMT_UYVY;
+	*code = V4L2_MBUS_FMT_YUYV10_2X10;
 	return 0;
 }
 
 /**
- * tvp514x_fmt_cap() - V4L2 decoder interface handler for try/s/g_fmt
+ * tvp514x_mbus_fmt_cap() - V4L2 decoder interface handler for try/s/g_mbus_fmt
  * @sd: pointer to standard V4L2 sub-device structure
- * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure
+ * @f: pointer to the mediabus format structure
  *
- * Implement the VIDIOC_TRY/S/G_FMT ioctl for the CAPTURE buffer type. This
- * ioctl is used to negotiate the image capture size and pixel format.
+ * Negotiates the image capture size and mediabus format.
  */
 static int
-tvp514x_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f)
+tvp514x_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)
 {
 	struct tvp514x_decoder *decoder = to_decoder(sd);
-	struct v4l2_pix_format *pix;
 	enum tvp514x_std current_std;
 
 	if (f == NULL)
 		return -EINVAL;
 
-	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		return -EINVAL;
-
-	pix = &f->fmt.pix;
-
 	/* Calculate height and width based on current standard */
 	current_std = decoder->current_std;
 
-	pix->pixelformat = V4L2_PIX_FMT_UYVY;
-	pix->width = decoder->std_list[current_std].width;
-	pix->height = decoder->std_list[current_std].height;
-	pix->field = V4L2_FIELD_INTERLACED;
-	pix->bytesperline = pix->width * 2;
-	pix->sizeimage = pix->bytesperline * pix->height;
-	pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
-	pix->priv = 0;
+	f->code = V4L2_MBUS_FMT_YUYV10_2X10;
+	f->width = decoder->std_list[current_std].width;
+	f->height = decoder->std_list[current_std].height;
+	f->field = V4L2_FIELD_INTERLACED;
+	f->colorspace = V4L2_COLORSPACE_SMPTE170M;
 
-	v4l2_dbg(1, debug, sd, "FMT: bytesperline - %d"
-			"Width - %d, Height - %d\n",
-			pix->bytesperline,
-			pix->width, pix->height);
+	v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d\n",
+			f->width, f->height);
 	return 0;
 }
 
@@ -1131,10 +1114,10 @@
 static const struct v4l2_subdev_video_ops tvp514x_video_ops = {
 	.s_routing = tvp514x_s_routing,
 	.querystd = tvp514x_querystd,
-	.enum_fmt = tvp514x_enum_fmt_cap,
-	.g_fmt = tvp514x_fmt_cap,
-	.try_fmt = tvp514x_fmt_cap,
-	.s_fmt = tvp514x_fmt_cap,
+	.enum_mbus_fmt = tvp514x_enum_mbus_fmt,
+	.g_mbus_fmt = tvp514x_mbus_fmt,
+	.try_mbus_fmt = tvp514x_mbus_fmt,
+	.s_mbus_fmt = tvp514x_mbus_fmt,
 	.g_parm = tvp514x_g_parm,
 	.s_parm = tvp514x_s_parm,
 	.s_stream = tvp514x_s_stream,
diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c
index 1654f65..5892766 100644
--- a/drivers/media/video/tvp5150.c
+++ b/drivers/media/video/tvp5150.c
@@ -11,7 +11,6 @@
 #include <linux/delay.h>
 #include <media/v4l2-device.h>
 #include <media/tvp5150.h>
-#include <media/v4l2-i2c-drv.h>
 #include <media/v4l2-chip-ident.h>
 
 #include "tvp5150_reg.h"
@@ -277,7 +276,7 @@
 
 static inline void tvp5150_selmux(struct v4l2_subdev *sd)
 {
-	int opmode=0;
+	int opmode = 0;
 	struct tvp5150 *decoder = to_tvp5150(sd);
 	int input = 0;
 	unsigned char val;
@@ -290,12 +289,10 @@
 		input |= 2;
 		/* fall through */
 	case TVP5150_COMPOSITE0:
-		opmode=0x30;		/* TV Mode */
 		break;
 	case TVP5150_SVIDEO:
 	default:
 		input |= 1;
-		opmode=0;		/* Auto Mode */
 		break;
 	}
 
@@ -1111,9 +1108,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, tvp5150_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "tvp5150",
-	.probe = tvp5150_probe,
-	.remove = tvp5150_remove,
-	.id_table = tvp5150_id,
+static struct i2c_driver tvp5150_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "tvp5150",
+	},
+	.probe		= tvp5150_probe,
+	.remove		= tvp5150_remove,
+	.id_table	= tvp5150_id,
 };
+
+static __init int init_tvp5150(void)
+{
+	return i2c_add_driver(&tvp5150_driver);
+}
+
+static __exit void exit_tvp5150(void)
+{
+	i2c_del_driver(&tvp5150_driver);
+}
+
+module_init(init_tvp5150);
+module_exit(exit_tvp5150);
diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c
index 48f5c76..e63b40f 100644
--- a/drivers/media/video/tvp7002.c
+++ b/drivers/media/video/tvp7002.c
@@ -330,19 +330,6 @@
 	{ TVP7002_EOR, 0xff, TVP7002_RESERVED }
 };
 
-/* Struct list for available formats */
-static const struct v4l2_fmtdesc tvp7002_fmt_list[] = {
-	{
-	 .index = 0,
-	 .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
-	 .flags = 0,
-	 .description = "8-bit UYVY 4:2:2 Format",
-	 .pixelformat = V4L2_PIX_FMT_UYVY,
-	},
-};
-
-#define NUM_FORMATS		ARRAY_SIZE(tvp7002_fmt_list)
-
 /* Preset definition for handling device operation */
 struct tvp7002_preset_definition {
 	u32 preset;
@@ -439,7 +426,6 @@
 	int ver;
 	int streaming;
 
-	struct v4l2_pix_format pix;
 	const struct tvp7002_preset_definition *current_preset;
 	u8 gain;
 };
@@ -695,81 +681,33 @@
 }
 
 /*
- * tvp7002_try_fmt_cap() - V4L2 decoder interface handler for try_fmt
+ * tvp7002_mbus_fmt() - V4L2 decoder interface handler for try/s/g_mbus_fmt
  * @sd: pointer to standard V4L2 sub-device structure
- * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure
+ * @f: pointer to mediabus format structure
  *
- * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This
- * ioctl is used to negotiate the image capture size and pixel format
- * without actually making it take effect.
+ * Negotiate the image capture size and mediabus format.
+ * There is only one possible format, so this single function works for
+ * get, set and try.
  */
-static int tvp7002_try_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f)
+static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)
 {
 	struct tvp7002 *device = to_tvp7002(sd);
 	struct v4l2_dv_enum_preset e_preset;
-	struct v4l2_pix_format *pix;
-	int error = 0;
-
-	pix = &f->fmt.pix;
+	int error;
 
 	/* Calculate height and width based on current standard */
 	error = v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset);
 	if (error)
-		return -EINVAL;
+		return error;
 
-	pix->width = e_preset.width;
-	pix->height = e_preset.height;
-	pix->pixelformat = V4L2_PIX_FMT_UYVY;
-	pix->field = device->current_preset->scanmode;
-	pix->bytesperline = pix->width * 2;
-	pix->sizeimage = pix->bytesperline * pix->height;
-	pix->colorspace = device->current_preset->color_space;
-	pix->priv = 0;
+	f->width = e_preset.width;
+	f->height = e_preset.height;
+	f->code = V4L2_MBUS_FMT_YUYV10_1X20;
+	f->field = device->current_preset->scanmode;
+	f->colorspace = device->current_preset->color_space;
 
-	v4l2_dbg(1, debug, sd, "Try FMT: pixelformat - %s, bytesperline - %d"
-			"Width - %d, Height - %d", "8-bit UYVY 4:2:2 Format",
-			pix->bytesperline, pix->width, pix->height);
-	return error;
-}
-
-/*
- * tvp7002_s_fmt() - V4L2 decoder interface handler for s_fmt
- * @sd: pointer to standard V4L2 sub-device structure
- * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure
- *
- * If the requested format is supported, configures the HW to use that
- * format, returns error code if format not supported or HW can't be
- * correctly configured.
- */
-static int tvp7002_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
-{
-	struct tvp7002 *decoder = to_tvp7002(sd);
-	int rval;
-
-	rval = tvp7002_try_fmt_cap(sd, f);
-	if (!rval)
-		decoder->pix = f->fmt.pix;
-	return rval;
-}
-
-/*
- * tvp7002_g_fmt() - V4L2 decoder interface handler for tvp7002_g_fmt
- * @sd: pointer to standard V4L2 sub-device structure
- * @f: pointer to standard V4L2 v4l2_format structure
- *
- * Returns the decoder's current pixel format in the v4l2_format
- * parameter.
- */
-static int tvp7002_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
-{
-	struct tvp7002 *decoder = to_tvp7002(sd);
-
-	f->fmt.pix = decoder->pix;
-
-	v4l2_dbg(1, debug, sd, "Current FMT: bytesperline - %d"
-			"Width - %d, Height - %d",
-			decoder->pix.bytesperline,
-			decoder->pix.width, decoder->pix.height);
+	v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d",
+			f->width, f->height);
 	return 0;
 }
 
@@ -894,21 +832,21 @@
 #endif
 
 /*
- * tvp7002_enum_fmt() - Enum supported formats
+ * tvp7002_enum_mbus_fmt() - Enum supported mediabus formats
  * @sd: pointer to standard V4L2 sub-device structure
- * @fmtdesc: pointer to format struct
+ * @index: format index
+ * @code: pointer to mediabus format
  *
- * Enumerate supported formats.
+ * Enumerate supported mediabus formats.
  */
 
-static int tvp7002_enum_fmt(struct v4l2_subdev *sd,
-						struct v4l2_fmtdesc *fmtdesc)
+static int tvp7002_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+					enum v4l2_mbus_pixelcode *code)
 {
 	/* Check requested format index is within range */
-	if (fmtdesc->index < 0 || fmtdesc->index >= NUM_FORMATS)
+	if (index)
 		return -EINVAL;
-	*fmtdesc = tvp7002_fmt_list[fmtdesc->index];
-
+	*code = V4L2_MBUS_FMT_YUYV10_1X20;
 	return 0;
 }
 
@@ -1027,9 +965,10 @@
 	.s_dv_preset = tvp7002_s_dv_preset,
 	.query_dv_preset = tvp7002_query_dv_preset,
 	.s_stream = tvp7002_s_stream,
-	.g_fmt = tvp7002_g_fmt,
-	.s_fmt = tvp7002_s_fmt,
-	.enum_fmt = tvp7002_enum_fmt,
+	.g_mbus_fmt = tvp7002_mbus_fmt,
+	.try_mbus_fmt = tvp7002_mbus_fmt,
+	.s_mbus_fmt = tvp7002_mbus_fmt,
+	.enum_mbus_fmt = tvp7002_enum_mbus_fmt,
 };
 
 /* V4L2 top level operation handlers */
@@ -1040,17 +979,6 @@
 
 static struct tvp7002 tvp7002_dev = {
 	.streaming = 0,
-
-	.pix = {
-		.width = 1280,
-		.height = 720,
-		.pixelformat = V4L2_PIX_FMT_UYVY,
-		.field = V4L2_FIELD_NONE,
-		.bytesperline = 1280 * 2,
-		.sizeimage = 1280 * 2 * 720,
-		.colorspace = V4L2_COLORSPACE_REC709,
-		},
-
 	.current_preset = tvp7002_presets,
 	.gain = 0,
 };
diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c
index a7279627..0347bbe 100644
--- a/drivers/media/video/tw9910.c
+++ b/drivers/media/video/tw9910.c
@@ -469,7 +469,7 @@
  */
 static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct tw9910_priv *priv = to_tw9910(client);
 	u8 val;
 	int ret;
@@ -511,7 +511,7 @@
 				unsigned long flags)
 {
 	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	u8 val = VSSL_VVALID | HSSL_DVALID;
 
 	/*
@@ -565,7 +565,7 @@
 static int tw9910_g_chip_ident(struct v4l2_subdev *sd,
 			       struct v4l2_dbg_chip_ident *id)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct tw9910_priv *priv = to_tw9910(client);
 
 	id->ident = V4L2_IDENT_TW9910;
@@ -578,7 +578,7 @@
 static int tw9910_g_register(struct v4l2_subdev *sd,
 			     struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	int ret;
 
 	if (reg->reg > 0xff)
@@ -600,7 +600,7 @@
 static int tw9910_s_register(struct v4l2_subdev *sd,
 			     struct v4l2_dbg_register *reg)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	if (reg->reg > 0xff ||
 	    reg->val > 0xff)
@@ -613,7 +613,7 @@
 static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
 	struct v4l2_rect *rect = &a->c;
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct tw9910_priv *priv = to_tw9910(client);
 	struct soc_camera_device *icd = client->dev.platform_data;
 	int                 ret  = -EINVAL;
@@ -701,7 +701,7 @@
 
 static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct tw9910_priv *priv = to_tw9910(client);
 
 	if (!priv->scale) {
@@ -748,7 +748,7 @@
 static int tw9910_g_fmt(struct v4l2_subdev *sd,
 			struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct tw9910_priv *priv = to_tw9910(client);
 
 	if (!priv->scale) {
@@ -778,7 +778,7 @@
 static int tw9910_s_fmt(struct v4l2_subdev *sd,
 			struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct tw9910_priv *priv = to_tw9910(client);
 	/* See tw9910_s_crop() - no proper cropping support */
 	struct v4l2_crop a = {
@@ -813,7 +813,7 @@
 static int tw9910_try_fmt(struct v4l2_subdev *sd,
 			  struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = sd->priv;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct soc_camera_device *icd = client->dev.platform_data;
 	const struct tw9910_scale_ctrl *scale;
 
diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c
index 36c0c46..f8138c7 100644
--- a/drivers/media/video/upd64031a.c
+++ b/drivers/media/video/upd64031a.c
@@ -28,7 +28,6 @@
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 #include <media/upd64031a.h>
 
 /* --------------------- read registers functions define -------------------- */
@@ -262,9 +261,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, upd64031a_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "upd64031a",
-	.probe = upd64031a_probe,
-	.remove = upd64031a_remove,
-	.id_table = upd64031a_id,
+static struct i2c_driver upd64031a_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "upd64031a",
+	},
+	.probe		= upd64031a_probe,
+	.remove		= upd64031a_remove,
+	.id_table	= upd64031a_id,
 };
+
+static __init int init_upd64031a(void)
+{
+	return i2c_add_driver(&upd64031a_driver);
+}
+
+static __exit void exit_upd64031a(void)
+{
+	i2c_del_driver(&upd64031a_driver);
+}
+
+module_init(init_upd64031a);
+module_exit(exit_upd64031a);
diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c
index c5af93b..28e0e6b 100644
--- a/drivers/media/video/upd64083.c
+++ b/drivers/media/video/upd64083.c
@@ -28,7 +28,6 @@
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 #include <media/upd64083.h>
 
 MODULE_DESCRIPTION("uPD64083 driver");
@@ -234,9 +233,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, upd64083_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "upd64083",
-	.probe = upd64083_probe,
-	.remove = upd64083_remove,
-	.id_table = upd64083_id,
+static struct i2c_driver upd64083_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "upd64083",
+	},
+	.probe		= upd64083_probe,
+	.remove		= upd64083_remove,
+	.id_table	= upd64083_id,
 };
+
+static __init int init_upd64083(void)
+{
+	return i2c_add_driver(&upd64083_driver);
+}
+
+static __exit void exit_upd64083(void)
+{
+	i2c_del_driver(&upd64083_driver);
+}
+
+module_init(init_upd64083);
+module_exit(exit_upd64083);
diff --git a/drivers/media/video/usbvideo/Kconfig b/drivers/media/video/usbvideo/Kconfig
index d6e1695..dfa7fc6 100644
--- a/drivers/media/video/usbvideo/Kconfig
+++ b/drivers/media/video/usbvideo/Kconfig
@@ -12,10 +12,13 @@
 	  module will be called vicam.
 
 config USB_IBMCAM
-	tristate "USB IBM (Xirlink) C-it Camera support"
+	tristate "USB IBM (Xirlink) C-it Camera support (DEPRECATED)"
 	depends on VIDEO_V4L1
 	select VIDEO_USBVIDEO
 	---help---
+	  This driver is DEPRECATED please use the gspca xirlink_cit module
+	  instead.
+
 	  Say Y here if you want to connect a IBM "C-It" camera, also known as
 	  "Xirlink PC Camera" to your computer's USB port.
 
@@ -27,10 +30,13 @@
 	  <file:Documentation/video4linux/ibmcam.txt> to learn more.
 
 config USB_KONICAWC
-	tristate "USB Konica Webcam support"
+	tristate "USB Konica Webcam support (DEPRECATED)"
 	depends on VIDEO_V4L1
 	select VIDEO_USBVIDEO
 	---help---
+	  This driver is DEPRECATED (and known to crash) please use the
+	  gspca konica module instead.
+
 	  Say Y here if you want support for webcams based on a Konica
 	  chipset. This is known to work with the Intel YC76 webcam.
 
diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c
index 42ba287..e3bbae2 100644
--- a/drivers/media/video/usbvision/usbvision-i2c.c
+++ b/drivers/media/video/usbvision/usbvision-i2c.c
@@ -211,6 +211,9 @@
 		0x42 >> 1, 0x40 >> 1,	/* SAA7114, SAA7115 and SAA7118 */
 		I2C_CLIENT_END };
 
+	if (usbvision->registered_i2c)
+		return 0;
+
 	memcpy(&usbvision->i2c_adap, &i2c_adap_template,
 	       sizeof(struct i2c_adapter));
 
@@ -248,7 +251,7 @@
 		   hit-and-miss. */
 		mdelay(10);
 		v4l2_i2c_new_subdev(&usbvision->v4l2_dev,
-				&usbvision->i2c_adap, "saa7115",
+				&usbvision->i2c_adap, NULL,
 				"saa7115_auto", 0, saa711x_addrs);
 		break;
 	}
@@ -258,16 +261,18 @@
 		struct tuner_setup tun_setup;
 
 		sd = v4l2_i2c_new_subdev(&usbvision->v4l2_dev,
-				&usbvision->i2c_adap, "tuner",
+				&usbvision->i2c_adap, NULL,
 				"tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
 		/* depending on whether we found a demod or not, select
 		   the tuner type. */
 		type = sd ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
 
 		sd = v4l2_i2c_new_subdev(&usbvision->v4l2_dev,
-				&usbvision->i2c_adap, "tuner",
+				&usbvision->i2c_adap, NULL,
 				"tuner", 0, v4l2_i2c_tuner_addrs(type));
 
+		if (sd == NULL)
+			return -ENODEV;
 		if (usbvision->tuner_type != -1) {
 			tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
 			tun_setup.type = usbvision->tuner_type;
@@ -275,14 +280,18 @@
 			call_all(usbvision, tuner, s_type_addr, &tun_setup);
 		}
 	}
+	usbvision->registered_i2c = 1;
 
 	return 0;
 }
 
 int usbvision_i2c_unregister(struct usb_usbvision *usbvision)
 {
+	if (!usbvision->registered_i2c)
+		return 0;
 
 	i2c_del_adapter(&(usbvision->i2c_adap));
+	usbvision->registered_i2c = 0;
 
 	PDEBUG(DBG_I2C,"i2c bus for %s unregistered", usbvision->i2c_adap.name);
 
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
index c2690df..db6b828 100644
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -357,7 +357,7 @@
 
 	PDEBUG(DBG_IO, "open");
 
-	lock_kernel();
+	mutex_lock(&usbvision->lock);
 	usbvision_reset_powerOffTimer(usbvision);
 
 	if (usbvision->user)
@@ -379,7 +379,6 @@
 
 	/* If so far no errors then we shall start the camera */
 	if (!errCode) {
-		mutex_lock(&usbvision->lock);
 		if (usbvision->power == 0) {
 			usbvision_power_on(usbvision);
 			usbvision_i2c_register(usbvision);
@@ -408,14 +407,13 @@
 				usbvision->initialized = 0;
 			}
 		}
-		mutex_unlock(&usbvision->lock);
 	}
 
 	/* prepare queues */
 	usbvision_empty_framequeues(usbvision);
 
 	PDEBUG(DBG_IO, "success");
-	unlock_kernel();
+	mutex_unlock(&usbvision->lock);
 	return errCode;
 }
 
@@ -1645,8 +1643,8 @@
 	usbvision->usb_bandwidth = 0;
 	usbvision->user = 0;
 	usbvision->streaming = Stream_Off;
-	usbvision_register_video(usbvision);
 	usbvision_configure_video(usbvision);
+	usbvision_register_video(usbvision);
 	mutex_unlock(&usbvision->lock);
 
 	usbvision_create_sysfs(usbvision->vdev);
diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h
index d1b3cc0..cc4e96c 100644
--- a/drivers/media/video/usbvision/usbvision.h
+++ b/drivers/media/video/usbvision/usbvision.h
@@ -363,6 +363,7 @@
 
 	/* i2c Declaration Section*/
 	struct i2c_adapter i2c_adap;
+	int registered_i2c;
 
 	struct urb *ctrlUrb;
 	unsigned char ctrlUrbBuffer[8];
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index a350fad..f169f77 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -1,8 +1,8 @@
 /*
  *      uvc_ctrl.c  --  USB Video Class driver - Controls
  *
- *      Copyright (C) 2005-2009
- *          Laurent Pinchart (laurent.pinchart@skynet.be)
+ *      Copyright (C) 2005-2010
+ *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
@@ -643,7 +643,7 @@
 
 static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
 {
-	return ctrl->uvc_data + id * ctrl->info->size;
+	return ctrl->uvc_data + id * ctrl->info.size;
 }
 
 static inline int uvc_test_bit(const __u8 *data, int bit)
@@ -727,7 +727,8 @@
 static const __u8 uvc_media_transport_input_guid[16] =
 	UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT;
 
-static int uvc_entity_match_guid(struct uvc_entity *entity, __u8 guid[16])
+static int uvc_entity_match_guid(const struct uvc_entity *entity,
+	const __u8 guid[16])
 {
 	switch (UVC_ENTITY_TYPE(entity)) {
 	case UVC_ITT_CAMERA:
@@ -765,10 +766,10 @@
 
 	for (i = 0; i < entity->ncontrols; ++i) {
 		ctrl = &entity->controls[i];
-		if (ctrl->info == NULL)
+		if (!ctrl->initialized)
 			continue;
 
-		list_for_each_entry(map, &ctrl->info->mappings, list) {
+		list_for_each_entry(map, &ctrl->info.mappings, list) {
 			if ((map->id == v4l2_id) && !next) {
 				*control = ctrl;
 				*mapping = map;
@@ -815,36 +816,36 @@
 {
 	int ret;
 
-	if (ctrl->info->flags & UVC_CONTROL_GET_DEF) {
+	if (ctrl->info.flags & UVC_CONTROL_GET_DEF) {
 		ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id,
-				     chain->dev->intfnum, ctrl->info->selector,
+				     chain->dev->intfnum, ctrl->info.selector,
 				     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
-				     ctrl->info->size);
+				     ctrl->info.size);
 		if (ret < 0)
 			return ret;
 	}
 
-	if (ctrl->info->flags & UVC_CONTROL_GET_MIN) {
+	if (ctrl->info.flags & UVC_CONTROL_GET_MIN) {
 		ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id,
-				     chain->dev->intfnum, ctrl->info->selector,
+				     chain->dev->intfnum, ctrl->info.selector,
 				     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN),
-				     ctrl->info->size);
+				     ctrl->info.size);
 		if (ret < 0)
 			return ret;
 	}
-	if (ctrl->info->flags & UVC_CONTROL_GET_MAX) {
+	if (ctrl->info.flags & UVC_CONTROL_GET_MAX) {
 		ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id,
-				     chain->dev->intfnum, ctrl->info->selector,
+				     chain->dev->intfnum, ctrl->info.selector,
 				     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX),
-				     ctrl->info->size);
+				     ctrl->info.size);
 		if (ret < 0)
 			return ret;
 	}
-	if (ctrl->info->flags & UVC_CONTROL_GET_RES) {
+	if (ctrl->info.flags & UVC_CONTROL_GET_RES) {
 		ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id,
-				     chain->dev->intfnum, ctrl->info->selector,
+				     chain->dev->intfnum, ctrl->info.selector,
 				     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES),
-				     ctrl->info->size);
+				     ctrl->info.size);
 		if (ret < 0)
 			return ret;
 	}
@@ -862,9 +863,15 @@
 	unsigned int i;
 	int ret;
 
+	ret = mutex_lock_interruptible(&chain->ctrl_mutex);
+	if (ret < 0)
+		return -ERESTARTSYS;
+
 	ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping);
-	if (ctrl == NULL)
-		return -EINVAL;
+	if (ctrl == NULL) {
+		ret = -EINVAL;
+		goto done;
+	}
 
 	memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl);
 	v4l2_ctrl->id = mapping->id;
@@ -872,18 +879,18 @@
 	strlcpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name);
 	v4l2_ctrl->flags = 0;
 
-	if (!(ctrl->info->flags & UVC_CONTROL_GET_CUR))
+	if (!(ctrl->info.flags & UVC_CONTROL_GET_CUR))
 		v4l2_ctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
-	if (!(ctrl->info->flags & UVC_CONTROL_SET_CUR))
+	if (!(ctrl->info.flags & UVC_CONTROL_SET_CUR))
 		v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
 	if (!ctrl->cached) {
 		ret = uvc_ctrl_populate_cache(chain, ctrl);
 		if (ret < 0)
-			return ret;
+			goto done;
 	}
 
-	if (ctrl->info->flags & UVC_CONTROL_GET_DEF) {
+	if (ctrl->info.flags & UVC_CONTROL_GET_DEF) {
 		v4l2_ctrl->default_value = mapping->get(mapping, UVC_GET_DEF,
 				uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF));
 	}
@@ -902,37 +909,39 @@
 			}
 		}
 
-		return 0;
+		goto done;
 
 	case V4L2_CTRL_TYPE_BOOLEAN:
 		v4l2_ctrl->minimum = 0;
 		v4l2_ctrl->maximum = 1;
 		v4l2_ctrl->step = 1;
-		return 0;
+		goto done;
 
 	case V4L2_CTRL_TYPE_BUTTON:
 		v4l2_ctrl->minimum = 0;
 		v4l2_ctrl->maximum = 0;
 		v4l2_ctrl->step = 0;
-		return 0;
+		goto done;
 
 	default:
 		break;
 	}
 
-	if (ctrl->info->flags & UVC_CONTROL_GET_MIN)
+	if (ctrl->info.flags & UVC_CONTROL_GET_MIN)
 		v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN,
 				     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
 
-	if (ctrl->info->flags & UVC_CONTROL_GET_MAX)
+	if (ctrl->info.flags & UVC_CONTROL_GET_MAX)
 		v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX,
 				     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
 
-	if (ctrl->info->flags & UVC_CONTROL_GET_RES)
+	if (ctrl->info.flags & UVC_CONTROL_GET_RES)
 		v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES,
 				  uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
 
-	return 0;
+done:
+	mutex_unlock(&chain->ctrl_mutex);
+	return ret;
 }
 
 
@@ -977,14 +986,14 @@
 
 	for (i = 0; i < entity->ncontrols; ++i) {
 		ctrl = &entity->controls[i];
-		if (ctrl->info == NULL)
+		if (!ctrl->initialized)
 			continue;
 
 		/* Reset the loaded flag for auto-update controls that were
 		 * marked as loaded in uvc_ctrl_get/uvc_ctrl_set to prevent
 		 * uvc_ctrl_get from using the cached value.
 		 */
-		if (ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE)
+		if (ctrl->info.flags & UVC_CONTROL_AUTO_UPDATE)
 			ctrl->loaded = 0;
 
 		if (!ctrl->dirty)
@@ -992,16 +1001,16 @@
 
 		if (!rollback)
 			ret = uvc_query_ctrl(dev, UVC_SET_CUR, ctrl->entity->id,
-				dev->intfnum, ctrl->info->selector,
+				dev->intfnum, ctrl->info.selector,
 				uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
-				ctrl->info->size);
+				ctrl->info.size);
 		else
 			ret = 0;
 
 		if (rollback || ret < 0)
 			memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
 			       uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
-			       ctrl->info->size);
+			       ctrl->info.size);
 
 		ctrl->dirty = 0;
 
@@ -1039,14 +1048,14 @@
 	int ret;
 
 	ctrl = uvc_find_control(chain, xctrl->id, &mapping);
-	if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0)
+	if (ctrl == NULL || (ctrl->info.flags & UVC_CONTROL_GET_CUR) == 0)
 		return -EINVAL;
 
 	if (!ctrl->loaded) {
 		ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id,
-				chain->dev->intfnum, ctrl->info->selector,
+				chain->dev->intfnum, ctrl->info.selector,
 				uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
-				ctrl->info->size);
+				ctrl->info.size);
 		if (ret < 0)
 			return ret;
 
@@ -1081,7 +1090,7 @@
 	int ret;
 
 	ctrl = uvc_find_control(chain, xctrl->id, &mapping);
-	if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0)
+	if (ctrl == NULL || (ctrl->info.flags & UVC_CONTROL_SET_CUR) == 0)
 		return -EINVAL;
 
 	/* Clamp out of range values. */
@@ -1127,16 +1136,16 @@
 	 * needs to be loaded from the device to perform the read-modify-write
 	 * operation.
 	 */
-	if (!ctrl->loaded && (ctrl->info->size * 8) != mapping->size) {
-		if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) {
+	if (!ctrl->loaded && (ctrl->info.size * 8) != mapping->size) {
+		if ((ctrl->info.flags & UVC_CONTROL_GET_CUR) == 0) {
 			memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
-				0, ctrl->info->size);
+				0, ctrl->info.size);
 		} else {
 			ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
 				ctrl->entity->id, chain->dev->intfnum,
-				ctrl->info->selector,
+				ctrl->info.selector,
 				uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
-				ctrl->info->size);
+				ctrl->info.size);
 			if (ret < 0)
 				return ret;
 		}
@@ -1148,7 +1157,7 @@
 	if (!ctrl->dirty) {
 		memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
 		       uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
-		       ctrl->info->size);
+		       ctrl->info.size);
 	}
 
 	mapping->set(mapping, value,
@@ -1163,12 +1172,138 @@
  * Dynamic controls
  */
 
+static void uvc_ctrl_fixup_xu_info(struct uvc_device *dev,
+	const struct uvc_control *ctrl, struct uvc_control_info *info)
+{
+	struct uvc_ctrl_fixup {
+		struct usb_device_id id;
+		u8 entity;
+		u8 selector;
+		u8 flags;
+	};
+
+	static const struct uvc_ctrl_fixup fixups[] = {
+		{ { USB_DEVICE(0x046d, 0x08c2) }, 9, 1,
+			UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX |
+			UVC_CONTROL_GET_DEF | UVC_CONTROL_SET_CUR |
+			UVC_CONTROL_AUTO_UPDATE },
+		{ { USB_DEVICE(0x046d, 0x08cc) }, 9, 1,
+			UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX |
+			UVC_CONTROL_GET_DEF | UVC_CONTROL_SET_CUR |
+			UVC_CONTROL_AUTO_UPDATE },
+		{ { USB_DEVICE(0x046d, 0x0994) }, 9, 1,
+			UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX |
+			UVC_CONTROL_GET_DEF | UVC_CONTROL_SET_CUR |
+			UVC_CONTROL_AUTO_UPDATE },
+	};
+
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(fixups); ++i) {
+		if (!usb_match_one_id(dev->intf, &fixups[i].id))
+			continue;
+
+		if (fixups[i].entity == ctrl->entity->id &&
+		    fixups[i].selector == info->selector) {
+			info->flags = fixups[i].flags;
+			return;
+		}
+	}
+}
+
+/*
+ * Query control information (size and flags) for XU controls.
+ */
+static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
+	const struct uvc_control *ctrl, struct uvc_control_info *info)
+{
+	u8 *data;
+	int ret;
+
+	data = kmalloc(2, GFP_KERNEL);
+	if (data == NULL)
+		return -ENOMEM;
+
+	memcpy(info->entity, ctrl->entity->extension.guidExtensionCode,
+	       sizeof(info->entity));
+	info->index = ctrl->index;
+	info->selector = ctrl->index + 1;
+
+	/* Query and verify the control length (GET_LEN) */
+	ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum,
+			     info->selector, data, 2);
+	if (ret < 0) {
+		uvc_trace(UVC_TRACE_CONTROL,
+			  "GET_LEN failed on control %pUl/%u (%d).\n",
+			   info->entity, info->selector, ret);
+		goto done;
+	}
+
+	info->size = le16_to_cpup((__le16 *)data);
+
+	/* Query the control information (GET_INFO) */
+	ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum,
+			     info->selector, data, 1);
+	if (ret < 0) {
+		uvc_trace(UVC_TRACE_CONTROL,
+			  "GET_INFO failed on control %pUl/%u (%d).\n",
+			  info->entity, info->selector, ret);
+		goto done;
+	}
+
+	info->flags = UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX
+		    | UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF
+		    | (data[0] & UVC_CONTROL_CAP_GET ? UVC_CONTROL_GET_CUR : 0)
+		    | (data[0] & UVC_CONTROL_CAP_SET ? UVC_CONTROL_SET_CUR : 0)
+		    | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ?
+		       UVC_CONTROL_AUTO_UPDATE : 0);
+
+	uvc_ctrl_fixup_xu_info(dev, ctrl, info);
+
+	uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, "
+		  "flags { get %u set %u auto %u }.\n",
+		  info->entity, info->selector, info->size,
+		  (info->flags & UVC_CONTROL_GET_CUR) ? 1 : 0,
+		  (info->flags & UVC_CONTROL_SET_CUR) ? 1 : 0,
+		  (info->flags & UVC_CONTROL_AUTO_UPDATE) ? 1 : 0);
+
+done:
+	kfree(data);
+	return ret;
+}
+
+static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
+	const struct uvc_control_info *info);
+
+static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
+	struct uvc_control *ctrl)
+{
+	struct uvc_control_info info;
+	int ret;
+
+	if (ctrl->initialized)
+		return 0;
+
+	ret = uvc_ctrl_fill_xu_info(dev, ctrl, &info);
+	if (ret < 0)
+		return ret;
+
+	ret = uvc_ctrl_add_info(dev, ctrl, &info);
+	if (ret < 0)
+		uvc_trace(UVC_TRACE_CONTROL, "Failed to initialize control "
+			  "%pUl/%u on device %s entity %u\n", info.entity,
+			  info.selector, dev->udev->devpath, ctrl->entity->id);
+
+	return ret;
+}
+
 int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
 	struct uvc_xu_control *xctrl, int set)
 {
 	struct uvc_entity *entity;
 	struct uvc_control *ctrl = NULL;
 	unsigned int i, found = 0;
+	int restore = 0;
 	__u8 *data;
 	int ret;
 
@@ -1185,13 +1320,10 @@
 		return -EINVAL;
 	}
 
-	/* Find the control. */
+	/* Find the control and perform delayed initialization if needed. */
 	for (i = 0; i < entity->ncontrols; ++i) {
 		ctrl = &entity->controls[i];
-		if (ctrl->info == NULL)
-			continue;
-
-		if (ctrl->info->selector == xctrl->selector) {
+		if (ctrl->index == xctrl->selector - 1) {
 			found = 1;
 			break;
 		}
@@ -1203,40 +1335,48 @@
 		return -EINVAL;
 	}
 
-	/* Validate control data size. */
-	if (ctrl->info->size != xctrl->size)
-		return -EINVAL;
-
-	if ((set && !(ctrl->info->flags & UVC_CONTROL_SET_CUR)) ||
-	    (!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR)))
-		return -EINVAL;
-
 	if (mutex_lock_interruptible(&chain->ctrl_mutex))
 		return -ERESTARTSYS;
 
+	ret = uvc_ctrl_init_xu_ctrl(chain->dev, ctrl);
+	if (ret < 0) {
+		ret = -ENOENT;
+		goto done;
+	}
+
+	/* Validate control data size. */
+	if (ctrl->info.size != xctrl->size) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if ((set && !(ctrl->info.flags & UVC_CONTROL_SET_CUR)) ||
+	    (!set && !(ctrl->info.flags & UVC_CONTROL_GET_CUR))) {
+		ret = -EINVAL;
+		goto done;
+	}
+
 	memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
 	       uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
-	       xctrl->size);
+	       ctrl->info.size);
 	data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT);
+	restore = set;
 
 	if (set && copy_from_user(data, xctrl->data, xctrl->size)) {
 		ret = -EFAULT;
-		goto out;
+		goto done;
 	}
 
 	ret = uvc_query_ctrl(chain->dev, set ? UVC_SET_CUR : UVC_GET_CUR,
 			     xctrl->unit, chain->dev->intfnum, xctrl->selector,
 			     data, xctrl->size);
 	if (ret < 0)
-		goto out;
+		goto done;
 
-	if (!set && copy_to_user(xctrl->data, data, xctrl->size)) {
+	if (!set && copy_to_user(xctrl->data, data, xctrl->size))
 		ret = -EFAULT;
-		goto out;
-	}
-
-out:
-	if (ret)
+done:
+	if (ret && restore)
 		memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
 		       uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
 		       xctrl->size);
@@ -1271,13 +1411,13 @@
 		for (i = 0; i < entity->ncontrols; ++i) {
 			ctrl = &entity->controls[i];
 
-			if (ctrl->info == NULL || !ctrl->modified ||
-			    (ctrl->info->flags & UVC_CONTROL_RESTORE) == 0)
+			if (!ctrl->initialized || !ctrl->modified ||
+			    (ctrl->info.flags & UVC_CONTROL_RESTORE) == 0)
 				continue;
 
 			printk(KERN_INFO "restoring control %pUl/%u/%u\n",
-				ctrl->info->entity, ctrl->info->index,
-				ctrl->info->selector);
+				ctrl->info.entity, ctrl->info.index,
+				ctrl->info.selector);
 			ctrl->dirty = 1;
 		}
 
@@ -1293,23 +1433,103 @@
  * Control and mapping handling
  */
 
-static int uvc_ctrl_add_ctrl(struct uvc_device *dev,
-	struct uvc_control_info *info)
+/*
+ * Add control information to a given control.
+ */
+static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
+	const struct uvc_control_info *info)
 {
-	struct uvc_entity *entity;
-	struct uvc_control *ctrl = NULL;
-	int ret = 0, found = 0;
-	unsigned int i;
-	u8 *uvc_info;
-	u8 *uvc_data;
+	int ret = 0;
 
+	memcpy(&ctrl->info, info, sizeof(*info));
+	INIT_LIST_HEAD(&ctrl->info.mappings);
+
+	/* Allocate an array to save control values (cur, def, max, etc.) */
+	ctrl->uvc_data = kzalloc(ctrl->info.size * UVC_CTRL_DATA_LAST + 1,
+				 GFP_KERNEL);
+	if (ctrl->uvc_data == NULL) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	ctrl->initialized = 1;
+
+	uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s "
+		"entity %u\n", ctrl->info.entity, ctrl->info.selector,
+		dev->udev->devpath, ctrl->entity->id);
+
+done:
+	if (ret < 0)
+		kfree(ctrl->uvc_data);
+	return ret;
+}
+
+/*
+ * Add a control mapping to a given control.
+ */
+static int __uvc_ctrl_add_mapping(struct uvc_device *dev,
+	struct uvc_control *ctrl, const struct uvc_control_mapping *mapping)
+{
+	struct uvc_control_mapping *map;
+	unsigned int size;
+
+	/* Most mappings come from static kernel data and need to be duplicated.
+	 * Mappings that come from userspace will be unnecessarily duplicated,
+	 * this could be optimized.
+	 */
+	map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL);
+	if (map == NULL)
+		return -ENOMEM;
+
+	size = sizeof(*mapping->menu_info) * mapping->menu_count;
+	map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL);
+	if (map->menu_info == NULL) {
+		kfree(map);
+		return -ENOMEM;
+	}
+
+	if (map->get == NULL)
+		map->get = uvc_get_le_value;
+	if (map->set == NULL)
+		map->set = uvc_set_le_value;
+
+	map->ctrl = &ctrl->info;
+	list_add_tail(&map->list, &ctrl->info.mappings);
+	uvc_trace(UVC_TRACE_CONTROL,
+		"Adding mapping '%s' to control %pUl/%u.\n",
+		map->name, ctrl->info.entity, ctrl->info.selector);
+
+	return 0;
+}
+
+int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
+	const struct uvc_control_mapping *mapping)
+{
+	struct uvc_device *dev = chain->dev;
+	struct uvc_control_mapping *map;
+	struct uvc_entity *entity;
+	struct uvc_control *ctrl;
+	int found = 0;
+	int ret;
+
+	if (mapping->id & ~V4L2_CTRL_ID_MASK) {
+		uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', control "
+			"id 0x%08x is invalid.\n", mapping->name,
+			mapping->id);
+		return -EINVAL;
+	}
+
+	/* Search for the matching (GUID/CS) control in the given device */
 	list_for_each_entry(entity, &dev->entities, list) {
-		if (!uvc_entity_match_guid(entity, info->entity))
+		unsigned int i;
+
+		if (UVC_ENTITY_TYPE(entity) != UVC_VC_EXTENSION_UNIT ||
+		    !uvc_entity_match_guid(entity, mapping->entity))
 			continue;
 
 		for (i = 0; i < entity->ncontrols; ++i) {
 			ctrl = &entity->controls[i];
-			if (ctrl->index == info->index) {
+			if (ctrl->index == mapping->selector - 1) {
 				found = 1;
 				break;
 			}
@@ -1318,176 +1538,45 @@
 		if (found)
 			break;
 	}
-
 	if (!found)
-		return 0;
+		return -ENOENT;
 
-	uvc_data = kmalloc(info->size * UVC_CTRL_DATA_LAST + 1, GFP_KERNEL);
-	if (uvc_data == NULL)
-		return -ENOMEM;
+	if (mutex_lock_interruptible(&chain->ctrl_mutex))
+		return -ERESTARTSYS;
 
-	uvc_info = uvc_data + info->size * UVC_CTRL_DATA_LAST;
+	/* Perform delayed initialization of XU controls */
+	ret = uvc_ctrl_init_xu_ctrl(dev, ctrl);
+	if (ret < 0) {
+		ret = -ENOENT;
+		goto done;
+	}
 
-	if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
-		/* Check if the device control information and length match
-		 * the user supplied information.
-		 */
-		ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id,
-				     dev->intfnum, info->selector, uvc_data, 2);
-		if (ret < 0) {
-			uvc_trace(UVC_TRACE_CONTROL,
-				"GET_LEN failed on control %pUl/%u (%d).\n",
-				info->entity, info->selector, ret);
-			goto done;
-		}
-
-		if (info->size != le16_to_cpu(*(__le16 *)uvc_data)) {
-			uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u size "
-				"doesn't match user supplied value.\n",
-				info->entity, info->selector);
-			ret = -EINVAL;
-			goto done;
-		}
-
-		ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id,
-				     dev->intfnum, info->selector, uvc_info, 1);
-		if (ret < 0) {
-			uvc_trace(UVC_TRACE_CONTROL,
-				"GET_INFO failed on control %pUl/%u (%d).\n",
-				info->entity, info->selector, ret);
-			goto done;
-		}
-
-		if (((info->flags & UVC_CONTROL_GET_CUR) &&
-		    !(*uvc_info & UVC_CONTROL_CAP_GET)) ||
-		    ((info->flags & UVC_CONTROL_SET_CUR) &&
-		    !(*uvc_info & UVC_CONTROL_CAP_SET))) {
-			uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u flags "
-				"don't match supported operations.\n",
-				info->entity, info->selector);
-			ret = -EINVAL;
+	list_for_each_entry(map, &ctrl->info.mappings, list) {
+		if (mapping->id == map->id) {
+			uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', "
+				"control id 0x%08x already exists.\n",
+				mapping->name, mapping->id);
+			ret = -EEXIST;
 			goto done;
 		}
 	}
 
-	ctrl->info = info;
-	ctrl->uvc_data = uvc_data;
-	ctrl->uvc_info = uvc_info;
+	/* Prevent excess memory consumption */
+	if (atomic_inc_return(&dev->nmappings) > UVC_MAX_CONTROL_MAPPINGS) {
+		atomic_dec(&dev->nmappings);
+		uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', maximum "
+			"mappings count (%u) exceeded.\n", mapping->name,
+			UVC_MAX_CONTROL_MAPPINGS);
+		ret = -ENOMEM;
+		goto done;
+	}
 
-	uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s "
-		"entity %u\n", ctrl->info->entity, ctrl->info->selector,
-		dev->udev->devpath, entity->id);
+	ret = __uvc_ctrl_add_mapping(dev, ctrl, mapping);
+	if (ret < 0)
+		atomic_dec(&dev->nmappings);
 
 done:
-	if (ret < 0)
-		kfree(uvc_data);
-
-	return ret;
-}
-
-/*
- * Add an item to the UVC control information list, and instantiate a control
- * structure for each device that supports the control.
- */
-int uvc_ctrl_add_info(struct uvc_control_info *info)
-{
-	struct uvc_control_info *ctrl;
-	struct uvc_device *dev;
-	int ret = 0;
-
-	/* Find matching controls by walking the devices, entities and
-	 * controls list.
-	 */
-	mutex_lock(&uvc_driver.ctrl_mutex);
-
-	/* First check if the list contains a control matching the new one.
-	 * Bail out if it does.
-	 */
-	list_for_each_entry(ctrl, &uvc_driver.controls, list) {
-		if (memcmp(ctrl->entity, info->entity, 16))
-			continue;
-
-		if (ctrl->selector == info->selector) {
-			uvc_trace(UVC_TRACE_CONTROL,
-				"Control %pUl/%u is already defined.\n",
-				info->entity, info->selector);
-			ret = -EEXIST;
-			goto end;
-		}
-		if (ctrl->index == info->index) {
-			uvc_trace(UVC_TRACE_CONTROL,
-				"Control %pUl/%u would overwrite index %d.\n",
-				info->entity, info->selector, info->index);
-			ret = -EEXIST;
-			goto end;
-		}
-	}
-
-	list_for_each_entry(dev, &uvc_driver.devices, list)
-		uvc_ctrl_add_ctrl(dev, info);
-
-	INIT_LIST_HEAD(&info->mappings);
-	list_add_tail(&info->list, &uvc_driver.controls);
-end:
-	mutex_unlock(&uvc_driver.ctrl_mutex);
-	return ret;
-}
-
-int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping)
-{
-	struct uvc_control_info *info;
-	struct uvc_control_mapping *map;
-	int ret = -EINVAL;
-
-	if (mapping->get == NULL)
-		mapping->get = uvc_get_le_value;
-	if (mapping->set == NULL)
-		mapping->set = uvc_set_le_value;
-
-	if (mapping->id & ~V4L2_CTRL_ID_MASK) {
-		uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with "
-			"invalid control id 0x%08x\n", mapping->name,
-			mapping->id);
-		return -EINVAL;
-	}
-
-	mutex_lock(&uvc_driver.ctrl_mutex);
-	list_for_each_entry(info, &uvc_driver.controls, list) {
-		if (memcmp(info->entity, mapping->entity, 16) ||
-			info->selector != mapping->selector)
-			continue;
-
-		if (info->size * 8 < mapping->size + mapping->offset) {
-			uvc_trace(UVC_TRACE_CONTROL,
-				"Mapping '%s' would overflow control %pUl/%u\n",
-				mapping->name, info->entity, info->selector);
-			ret = -EOVERFLOW;
-			goto end;
-		}
-
-		/* Check if the list contains a mapping matching the new one.
-		 * Bail out if it does.
-		 */
-		list_for_each_entry(map, &info->mappings, list) {
-			if (map->id == mapping->id) {
-				uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' is "
-					"already defined.\n", mapping->name);
-				ret = -EEXIST;
-				goto end;
-			}
-		}
-
-		mapping->ctrl = info;
-		list_add_tail(&mapping->list, &info->mappings);
-		uvc_trace(UVC_TRACE_CONTROL,
-			"Adding mapping %s to control %pUl/%u.\n",
-			mapping->name, info->entity, info->selector);
-
-		ret = 0;
-		break;
-	}
-end:
-	mutex_unlock(&uvc_driver.ctrl_mutex);
+	mutex_unlock(&chain->ctrl_mutex);
 	return ret;
 }
 
@@ -1496,29 +1585,49 @@
  * are currently the ones that crash the camera or unconditionally return an
  * error when queried.
  */
-static void
-uvc_ctrl_prune_entity(struct uvc_device *dev, struct uvc_entity *entity)
+static void uvc_ctrl_prune_entity(struct uvc_device *dev,
+	struct uvc_entity *entity)
 {
-	static const struct {
+	struct uvc_ctrl_blacklist {
 		struct usb_device_id id;
 		u8 index;
-	} blacklist[] = {
+	};
+
+	static const struct uvc_ctrl_blacklist processing_blacklist[] = {
 		{ { USB_DEVICE(0x13d3, 0x509b) }, 9 }, /* Gain */
 		{ { USB_DEVICE(0x1c4f, 0x3000) }, 6 }, /* WB Temperature */
 		{ { USB_DEVICE(0x5986, 0x0241) }, 2 }, /* Hue */
 	};
+	static const struct uvc_ctrl_blacklist camera_blacklist[] = {
+		{ { USB_DEVICE(0x06f8, 0x3005) }, 9 }, /* Zoom, Absolute */
+	};
 
-	u8 *controls;
+	const struct uvc_ctrl_blacklist *blacklist;
 	unsigned int size;
+	unsigned int count;
 	unsigned int i;
+	u8 *controls;
 
-	if (UVC_ENTITY_TYPE(entity) != UVC_VC_PROCESSING_UNIT)
+	switch (UVC_ENTITY_TYPE(entity)) {
+	case UVC_VC_PROCESSING_UNIT:
+		blacklist = processing_blacklist;
+		count = ARRAY_SIZE(processing_blacklist);
+		controls = entity->processing.bmControls;
+		size = entity->processing.bControlSize;
+		break;
+
+	case UVC_ITT_CAMERA:
+		blacklist = camera_blacklist;
+		count = ARRAY_SIZE(camera_blacklist);
+		controls = entity->camera.bmControls;
+		size = entity->camera.bControlSize;
+		break;
+
+	default:
 		return;
+	}
 
-	controls = entity->processing.bmControls;
-	size = entity->processing.bControlSize;
-
-	for (i = 0; i < ARRAY_SIZE(blacklist); ++i) {
+	for (i = 0; i < count; ++i) {
 		if (!usb_match_one_id(dev->intf, &blacklist[i].id))
 			continue;
 
@@ -1534,17 +1643,54 @@
 }
 
 /*
+ * Add control information and hardcoded stock control mappings to the given
+ * device.
+ */
+static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl)
+{
+	const struct uvc_control_info *info = uvc_ctrls;
+	const struct uvc_control_info *iend = info + ARRAY_SIZE(uvc_ctrls);
+	const struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
+	const struct uvc_control_mapping *mend =
+		mapping + ARRAY_SIZE(uvc_ctrl_mappings);
+
+	/* XU controls initialization requires querying the device for control
+	 * information. As some buggy UVC devices will crash when queried
+	 * repeatedly in a tight loop, delay XU controls initialization until
+	 * first use.
+	 */
+	if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT)
+		return;
+
+	for (; info < iend; ++info) {
+		if (uvc_entity_match_guid(ctrl->entity, info->entity) &&
+		    ctrl->index == info->index) {
+			uvc_ctrl_add_info(dev, ctrl, info);
+			break;
+		 }
+	}
+
+	if (!ctrl->initialized)
+		return;
+
+	for (; mapping < mend; ++mapping) {
+		if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
+		    ctrl->info.selector == mapping->selector)
+			__uvc_ctrl_add_mapping(dev, ctrl, mapping);
+	}
+}
+
+/*
  * Initialize device controls.
  */
 int uvc_ctrl_init_device(struct uvc_device *dev)
 {
-	struct uvc_control_info *info;
-	struct uvc_control *ctrl;
 	struct uvc_entity *entity;
 	unsigned int i;
 
 	/* Walk the entities list and instantiate controls */
 	list_for_each_entry(entity, &dev->entities, list) {
+		struct uvc_control *ctrl;
 		unsigned int bControlSize = 0, ncontrols = 0;
 		__u8 *bmControls = NULL;
 
@@ -1559,20 +1705,22 @@
 			bControlSize = entity->camera.bControlSize;
 		}
 
+		/* Remove bogus/blacklisted controls */
 		uvc_ctrl_prune_entity(dev, entity);
 
+		/* Count supported controls and allocate the controls array */
 		for (i = 0; i < bControlSize; ++i)
 			ncontrols += hweight8(bmControls[i]);
-
 		if (ncontrols == 0)
 			continue;
 
-		entity->controls = kzalloc(ncontrols*sizeof *ctrl, GFP_KERNEL);
+		entity->controls = kzalloc(ncontrols * sizeof(*ctrl),
+					   GFP_KERNEL);
 		if (entity->controls == NULL)
 			return -ENOMEM;
-
 		entity->ncontrols = ncontrols;
 
+		/* Initialize all supported controls */
 		ctrl = entity->controls;
 		for (i = 0; i < bControlSize * 8; ++i) {
 			if (uvc_test_bit(bmControls, i) == 0)
@@ -1580,81 +1728,47 @@
 
 			ctrl->entity = entity;
 			ctrl->index = i;
+
+			uvc_ctrl_init_ctrl(dev, ctrl);
 			ctrl++;
 		}
 	}
 
-	/* Walk the controls info list and associate them with the device
-	 * controls, then add the device to the global device list. This has
-	 * to be done while holding the controls lock, to make sure
-	 * uvc_ctrl_add_info() will not get called in-between.
-	 */
-	mutex_lock(&uvc_driver.ctrl_mutex);
-	list_for_each_entry(info, &uvc_driver.controls, list)
-		uvc_ctrl_add_ctrl(dev, info);
-
-	list_add_tail(&dev->list, &uvc_driver.devices);
-	mutex_unlock(&uvc_driver.ctrl_mutex);
-
 	return 0;
 }
 
 /*
  * Cleanup device controls.
  */
+static void uvc_ctrl_cleanup_mappings(struct uvc_device *dev,
+	struct uvc_control *ctrl)
+{
+	struct uvc_control_mapping *mapping, *nm;
+
+	list_for_each_entry_safe(mapping, nm, &ctrl->info.mappings, list) {
+		list_del(&mapping->list);
+		kfree(mapping->menu_info);
+		kfree(mapping);
+	}
+}
+
 void uvc_ctrl_cleanup_device(struct uvc_device *dev)
 {
 	struct uvc_entity *entity;
 	unsigned int i;
 
-	/* Remove the device from the global devices list */
-	mutex_lock(&uvc_driver.ctrl_mutex);
-	if (dev->list.next != NULL)
-		list_del(&dev->list);
-	mutex_unlock(&uvc_driver.ctrl_mutex);
-
+	/* Free controls and control mappings for all entities. */
 	list_for_each_entry(entity, &dev->entities, list) {
-		for (i = 0; i < entity->ncontrols; ++i)
-			kfree(entity->controls[i].uvc_data);
+		for (i = 0; i < entity->ncontrols; ++i) {
+			struct uvc_control *ctrl = &entity->controls[i];
+
+			if (!ctrl->initialized)
+				continue;
+
+			uvc_ctrl_cleanup_mappings(dev, ctrl);
+			kfree(ctrl->uvc_data);
+		}
 
 		kfree(entity->controls);
 	}
 }
-
-void uvc_ctrl_cleanup(void)
-{
-	struct uvc_control_info *info;
-	struct uvc_control_info *ni;
-	struct uvc_control_mapping *mapping;
-	struct uvc_control_mapping *nm;
-
-	list_for_each_entry_safe(info, ni, &uvc_driver.controls, list) {
-		if (!(info->flags & UVC_CONTROL_EXTENSION))
-			continue;
-
-		list_for_each_entry_safe(mapping, nm, &info->mappings, list) {
-			list_del(&mapping->list);
-			kfree(mapping->menu_info);
-			kfree(mapping);
-		}
-
-		list_del(&info->list);
-		kfree(info);
-	}
-}
-
-void uvc_ctrl_init(void)
-{
-	struct uvc_control_info *ctrl = uvc_ctrls;
-	struct uvc_control_info *cend = ctrl + ARRAY_SIZE(uvc_ctrls);
-	struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
-	struct uvc_control_mapping *mend =
-		mapping + ARRAY_SIZE(uvc_ctrl_mappings);
-
-	for (; ctrl < cend; ++ctrl)
-		uvc_ctrl_add_info(ctrl);
-
-	for (; mapping < mend; ++mapping)
-		uvc_ctrl_add_mapping(mapping);
-}
-
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index 2ac85d8..a1e9dfb 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -1,8 +1,8 @@
 /*
  *      uvc_driver.c  --  USB Video Class driver
  *
- *      Copyright (C) 2005-2009
- *          Laurent Pinchart (laurent.pinchart@skynet.be)
+ *      Copyright (C) 2005-2010
+ *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
@@ -38,11 +38,9 @@
 
 #include "uvcvideo.h"
 
-#define DRIVER_AUTHOR		"Laurent Pinchart <laurent.pinchart@skynet.be>"
+#define DRIVER_AUTHOR		"Laurent Pinchart " \
+				"<laurent.pinchart@ideasonboard.com>"
 #define DRIVER_DESC		"USB Video Class driver"
-#ifndef DRIVER_VERSION
-#define DRIVER_VERSION		"v0.1.0"
-#endif
 
 unsigned int uvc_clock_param = CLOCK_MONOTONIC;
 unsigned int uvc_no_drop_param;
@@ -1762,6 +1760,7 @@
 	INIT_LIST_HEAD(&dev->streams);
 	atomic_set(&dev->nstreams, 0);
 	atomic_set(&dev->users, 0);
+	atomic_set(&dev->nmappings, 0);
 
 	dev->udev = usb_get_dev(udev);
 	dev->intf = usb_get_intf(intf);
@@ -1820,6 +1819,7 @@
 	}
 
 	uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
+	usb_enable_autosuspend(udev);
 	return 0;
 
 error:
@@ -2287,12 +2287,6 @@
 {
 	int result;
 
-	INIT_LIST_HEAD(&uvc_driver.devices);
-	INIT_LIST_HEAD(&uvc_driver.controls);
-	mutex_init(&uvc_driver.ctrl_mutex);
-
-	uvc_ctrl_init();
-
 	result = usb_register(&uvc_driver.driver);
 	if (result == 0)
 		printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
@@ -2302,7 +2296,6 @@
 static void __exit uvc_cleanup(void)
 {
 	usb_deregister(&uvc_driver.driver);
-	uvc_ctrl_cleanup();
 }
 
 module_init(uvc_init);
diff --git a/drivers/media/video/uvc/uvc_isight.c b/drivers/media/video/uvc/uvc_isight.c
index a9285b5..74bbe8f 100644
--- a/drivers/media/video/uvc/uvc_isight.c
+++ b/drivers/media/video/uvc/uvc_isight.c
@@ -4,7 +4,7 @@
  *	Copyright (C) 2006-2007
  *		Ivan N. Zlatev <contact@i-nz.net>
  *	Copyright (C) 2008-2009
- *		Laurent Pinchart <laurent.pinchart@skynet.be>
+ *		Laurent Pinchart <laurent.pinchart@ideasonboard.com>
  *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
index e9928a4..ed6d544 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -1,8 +1,8 @@
 /*
  *      uvc_queue.c  --  USB Video Class driver - Buffers management
  *
- *      Copyright (C) 2005-2009
- *          Laurent Pinchart (laurent.pinchart@skynet.be)
+ *      Copyright (C) 2005-2010
+ *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
@@ -135,7 +135,6 @@
 		queue->buffer[i].buf.m.offset = i * bufsize;
 		queue->buffer[i].buf.length = buflength;
 		queue->buffer[i].buf.type = queue->type;
-		queue->buffer[i].buf.sequence = 0;
 		queue->buffer[i].buf.field = V4L2_FIELD_NONE;
 		queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;
 		queue->buffer[i].buf.flags = 0;
@@ -410,8 +409,7 @@
  * state can be properly initialized before buffers are accessed from the
  * interrupt handler.
  *
- * Enabling the video queue initializes parameters (such as sequence number,
- * sync pattern, ...). If the queue is already enabled, return -EBUSY.
+ * Enabling the video queue returns -EBUSY if the queue is already enabled.
  *
  * Disabling the video queue cancels the queue and removes all buffers from
  * the main queue.
@@ -430,7 +428,6 @@
 			ret = -EBUSY;
 			goto done;
 		}
-		queue->sequence = 0;
 		queue->flags |= UVC_QUEUE_STREAMING;
 		queue->buf_used = 0;
 	} else {
@@ -510,8 +507,6 @@
 		nextbuf = NULL;
 	spin_unlock_irqrestore(&queue->irqlock, flags);
 
-	buf->buf.sequence = queue->sequence++;
-
 	wake_up(&buf->wait);
 	return nextbuf;
 }
diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c
index 85019bd..b749277 100644
--- a/drivers/media/video/uvc/uvc_status.c
+++ b/drivers/media/video/uvc/uvc_status.c
@@ -1,8 +1,8 @@
 /*
  *      uvc_status.c  --  USB Video Class driver - Status endpoint
  *
- *      Copyright (C) 2007-2009
- *          Laurent Pinchart (laurent.pinchart@skynet.be)
+ *      Copyright (C) 2005-2009
+ *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index 86db326..6d15de9 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -1,8 +1,8 @@
 /*
  *      uvc_v4l2.c  --  USB Video Class driver - V4L2 API
  *
- *      Copyright (C) 2005-2009
- *          Laurent Pinchart (laurent.pinchart@skynet.be)
+ *      Copyright (C) 2005-2010
+ *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
@@ -31,7 +31,8 @@
 /* ------------------------------------------------------------------------
  * UVC ioctls
  */
-static int uvc_ioctl_ctrl_map(struct uvc_xu_control_mapping *xmap, int old)
+static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
+	struct uvc_xu_control_mapping *xmap, int old)
 {
 	struct uvc_control_mapping *map;
 	unsigned int size;
@@ -58,6 +59,8 @@
 
 	case V4L2_CTRL_TYPE_MENU:
 		if (old) {
+			uvc_trace(UVC_TRACE_CONTROL, "V4L2_CTRL_TYPE_MENU not "
+				  "supported for UVCIOC_CTRL_MAP_OLD.\n");
 			ret = -EINVAL;
 			goto done;
 		}
@@ -78,17 +81,17 @@
 		break;
 
 	default:
+		uvc_trace(UVC_TRACE_CONTROL, "Unsupported V4L2 control type "
+			  "%u.\n", xmap->v4l2_type);
 		ret = -EINVAL;
 		goto done;
 	}
 
-	ret = uvc_ctrl_add_mapping(map);
+	ret = uvc_ctrl_add_mapping(chain, map);
 
 done:
-	if (ret < 0) {
-		kfree(map->menu_info);
-		kfree(map);
-	}
+	kfree(map->menu_info);
+	kfree(map);
 
 	return ret;
 }
@@ -1021,42 +1024,13 @@
 
 	/* Dynamic controls. */
 	case UVCIOC_CTRL_ADD:
-	{
-		struct uvc_xu_control_info *xinfo = arg;
-		struct uvc_control_info *info;
-
-		if (!capable(CAP_SYS_ADMIN))
-			return -EPERM;
-
-		if (xinfo->size == 0)
-			return -EINVAL;
-
-		info = kzalloc(sizeof *info, GFP_KERNEL);
-		if (info == NULL)
-			return -ENOMEM;
-
-		memcpy(info->entity, xinfo->entity, sizeof info->entity);
-		info->index = xinfo->index;
-		info->selector = xinfo->selector;
-		info->size = xinfo->size;
-		info->flags = xinfo->flags;
-
-		info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX |
-			       UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF |
-			       UVC_CONTROL_EXTENSION;
-
-		ret = uvc_ctrl_add_info(info);
-		if (ret < 0)
-			kfree(info);
-		break;
-	}
+		/* Legacy ioctl, kept for API compatibility reasons */
+		return -EEXIST;
 
 	case UVCIOC_CTRL_MAP_OLD:
 	case UVCIOC_CTRL_MAP:
-		if (!capable(CAP_SYS_ADMIN))
-			return -EPERM;
-
-		return uvc_ioctl_ctrl_map(arg, cmd == UVCIOC_CTRL_MAP_OLD);
+		return uvc_ioctl_ctrl_map(chain, arg,
+					  cmd == UVCIOC_CTRL_MAP_OLD);
 
 	case UVCIOC_CTRL_GET:
 		return uvc_xu_ctrl_query(chain, arg, 0);
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index e27cf0d..5555f01 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -1,8 +1,8 @@
 /*
  *      uvc_video.c  --  USB Video Class driver - Video handling
  *
- *      Copyright (C) 2005-2009
- *          Laurent Pinchart (laurent.pinchart@skynet.be)
+ *      Copyright (C) 2005-2010
+ *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
@@ -45,6 +45,30 @@
 			unit << 8 | intfnum, data, size, timeout);
 }
 
+static const char *uvc_query_name(__u8 query)
+{
+	switch (query) {
+	case UVC_SET_CUR:
+		return "SET_CUR";
+	case UVC_GET_CUR:
+		return "GET_CUR";
+	case UVC_GET_MIN:
+		return "GET_MIN";
+	case UVC_GET_MAX:
+		return "GET_MAX";
+	case UVC_GET_RES:
+		return "GET_RES";
+	case UVC_GET_LEN:
+		return "GET_LEN";
+	case UVC_GET_INFO:
+		return "GET_INFO";
+	case UVC_GET_DEF:
+		return "GET_DEF";
+	default:
+		return "<invalid>";
+	}
+}
+
 int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
 			__u8 intfnum, __u8 cs, void *data, __u16 size)
 {
@@ -53,9 +77,9 @@
 	ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
 				UVC_CTRL_CONTROL_TIMEOUT);
 	if (ret != size) {
-		uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u "
-			"(unit %u) : %d (exp. %u).\n", query, cs, unit, ret,
-			size);
+		uvc_printk(KERN_ERR, "Failed to query (%s) UVC control %u on "
+			"unit %u: %d (exp. %u).\n", uvc_query_name(query), cs,
+			unit, ret, size);
 		return -EIO;
 	}
 
@@ -114,6 +138,15 @@
 			bandwidth /= 8;
 		bandwidth += 12;
 
+		/* The bandwidth estimate is too low for many cameras. Don't use
+		 * maximum packet sizes lower than 1024 bytes to try and work
+		 * around the problem. According to measurements done on two
+		 * different camera models, the value is high enough to get most
+		 * resolutions working while not preventing two simultaneous
+		 * VGA streams at 15 fps.
+		 */
+		bandwidth = max_t(u32, bandwidth, 1024);
+
 		ctrl->dwMaxPayloadTransferSize = bandwidth;
 	}
 }
@@ -394,6 +427,12 @@
 
 	fid = data[1] & UVC_STREAM_FID;
 
+	/* Increase the sequence number regardless of any buffer states, so
+	 * that discontinuous sequence numbers always indicate lost frames.
+	 */
+	if (stream->last_fid != fid)
+		stream->sequence++;
+
 	/* Store the payload FID bit and return immediately when the buffer is
 	 * NULL.
 	 */
@@ -427,6 +466,7 @@
 		else
 			ktime_get_real_ts(&ts);
 
+		buf->buf.sequence = stream->sequence;
 		buf->buf.timestamp.tv_sec = ts.tv_sec;
 		buf->buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
 
@@ -688,6 +728,7 @@
 		if (buf->buf.bytesused == stream->queue.buf_used) {
 			stream->queue.buf_used = 0;
 			buf->state = UVC_BUF_STATE_READY;
+			buf->buf.sequence = ++stream->sequence;
 			uvc_queue_next_buffer(&stream->queue, buf);
 			stream->last_fid ^= UVC_STREAM_FID;
 		}
@@ -946,6 +987,7 @@
 	unsigned int i;
 	int ret;
 
+	stream->sequence = -1;
 	stream->last_fid = -1;
 	stream->bulk.header_size = 0;
 	stream->bulk.skip_payload = 0;
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index 892e0e5..d97cf6d 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -27,8 +27,6 @@
 #define UVC_CONTROL_RESTORE	(1 << 6)
 /* Control can be updated by the camera. */
 #define UVC_CONTROL_AUTO_UPDATE	(1 << 7)
-/* Control is an extension unit control. */
-#define UVC_CONTROL_EXTENSION	(1 << 8)
 
 #define UVC_CONTROL_GET_RANGE	(UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \
 				 UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \
@@ -159,7 +157,8 @@
  * Driver specific constants.
  */
 
-#define DRIVER_VERSION_NUMBER	KERNEL_VERSION(0, 1, 0)
+#define DRIVER_VERSION_NUMBER	KERNEL_VERSION(1, 0, 0)
+#define DRIVER_VERSION		"v1.0.0"
 
 /* Number of isochronous URBs. */
 #define UVC_URBS		5
@@ -173,6 +172,9 @@
 #define UVC_CTRL_CONTROL_TIMEOUT	300
 #define UVC_CTRL_STREAMING_TIMEOUT	5000
 
+/* Maximum allowed number of control mappings per device */
+#define UVC_MAX_CONTROL_MAPPINGS	1024
+
 /* Devices quirks */
 #define UVC_QUIRK_STATUS_INTERVAL	0x00000001
 #define UVC_QUIRK_PROBE_MINMAX		0x00000002
@@ -198,11 +200,10 @@
  * structures to maximize cache efficiency.
  */
 struct uvc_control_info {
-	struct list_head list;
 	struct list_head mappings;
 
 	__u8 entity[16];
-	__u8 index;
+	__u8 index;	/* Bit index in bmControls */
 	__u8 selector;
 
 	__u16 size;
@@ -235,17 +236,17 @@
 
 struct uvc_control {
 	struct uvc_entity *entity;
-	struct uvc_control_info *info;
+	struct uvc_control_info info;
 
 	__u8 index;	/* Used to match the uvc_control entry with a
 			   uvc_control_info. */
-	__u8 dirty : 1,
-	     loaded : 1,
-	     modified : 1,
-	     cached : 1;
+	__u8 dirty:1,
+	     loaded:1,
+	     modified:1,
+	     cached:1,
+	     initialized:1;
 
 	__u8 *uvc_data;
-	__u8 *uvc_info;
 };
 
 struct uvc_format_desc {
@@ -392,7 +393,6 @@
 
 	void *mem;
 	unsigned int flags;
-	__u32 sequence;
 
 	unsigned int count;
 	unsigned int buf_size;
@@ -413,7 +413,7 @@
 	struct uvc_entity *processing;		/* Processing unit */
 	struct uvc_entity *selector;		/* Selector unit */
 
-	struct mutex ctrl_mutex;
+	struct mutex ctrl_mutex;		/* Protects ctrl.info */
 };
 
 struct uvc_streaming {
@@ -458,6 +458,7 @@
 	dma_addr_t urb_dma[UVC_URBS];
 	unsigned int urb_size;
 
+	__u32 sequence;
 	__u8 last_fid;
 };
 
@@ -474,8 +475,8 @@
 	char name[32];
 
 	enum uvc_device_state state;
-	struct list_head list;
 	atomic_t users;
+	atomic_t nmappings;
 
 	/* Video control interface */
 	__u16 uvc_version;
@@ -509,11 +510,6 @@
 
 struct uvc_driver {
 	struct usb_driver driver;
-
-	struct list_head devices;	/* struct uvc_device list */
-	struct list_head controls;	/* struct uvc_control_info list */
-	struct mutex ctrl_mutex;	/* protects controls and devices
-					   lists */
 };
 
 /* ------------------------------------------------------------------------
@@ -615,13 +611,11 @@
 extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
 		struct v4l2_queryctrl *v4l2_ctrl);
 
-extern int uvc_ctrl_add_info(struct uvc_control_info *info);
-extern int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping);
+extern int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
+		const struct uvc_control_mapping *mapping);
 extern int uvc_ctrl_init_device(struct uvc_device *dev);
 extern void uvc_ctrl_cleanup_device(struct uvc_device *dev);
 extern int uvc_ctrl_resume_device(struct uvc_device *dev);
-extern void uvc_ctrl_init(void);
-extern void uvc_ctrl_cleanup(void);
 
 extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
 extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback);
diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c
index 0c2105c..d4ac751 100644
--- a/drivers/media/video/v4l1-compat.c
+++ b/drivers/media/video/v4l1-compat.c
@@ -645,9 +645,16 @@
 		goto done;
 	}
 
-	pict->depth   = ((fmt->fmt.pix.bytesperline << 3)
-			 + (fmt->fmt.pix.width - 1))
-			 / fmt->fmt.pix.width;
+	if (fmt->fmt.pix.width)
+	{
+		pict->depth   = ((fmt->fmt.pix.bytesperline << 3)
+				 + (fmt->fmt.pix.width - 1))
+				 / fmt->fmt.pix.width;
+	} else {
+		err = -EINVAL;
+		goto done;
+	}
+
 	pict->palette = pixelformat_to_palette(
 		fmt->fmt.pix.pixelformat);
 done:
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 8ee1179..9294282 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -378,6 +378,8 @@
 
 	if (module_name)
 		request_module(module_name);
+	else
+		request_module(I2C_MODULE_PREFIX "%s", info->type);
 
 	/* Create the i2c client */
 	if (info->addr == 0 && probe_addrs)
@@ -676,3 +678,28 @@
 	return 0;
 }
 EXPORT_SYMBOL_GPL(v4l_fill_dv_preset_info);
+
+const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
+		const struct v4l2_discrete_probe *probe,
+		s32 width, s32 height)
+{
+	int i;
+	u32 error, min_error = UINT_MAX;
+	const struct v4l2_frmsize_discrete *size, *best = NULL;
+
+	if (!probe)
+		return best;
+
+	for (i = 0, size = probe->sizes; i < probe->num_sizes; i++, size++) {
+		error = abs(size->width - width) + abs(size->height - height);
+		if (error < min_error) {
+			min_error = error;
+			best = size;
+		}
+		if (!error)
+			break;
+	}
+
+	return best;
+}
+EXPORT_SYMBOL_GPL(v4l2_find_nearest_format);
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index ea8d32c..9d2502c 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -305,6 +305,8 @@
 	case V4L2_CID_ROTATE:			return "Rotate";
 	case V4L2_CID_BG_COLOR:			return "Background Color";
 	case V4L2_CID_CHROMA_GAIN:		return "Chroma Gain";
+	case V4L2_CID_ILLUMINATORS_1:		return "Illuminator 1";
+	case V4L2_CID_ILLUMINATORS_2:		return "Illuminator 2";
 
 	/* MPEG controls */
 	/* Keep the order of the 'case's the same as in videodev2.h! */
@@ -419,6 +421,8 @@
 	case V4L2_CID_AUDIO_LIMITER_ENABLED:
 	case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
 	case V4L2_CID_PILOT_TONE_ENABLED:
+	case V4L2_CID_ILLUMINATORS_1:
+	case V4L2_CID_ILLUMINATORS_2:
 		*type = V4L2_CTRL_TYPE_BOOLEAN;
 		*min = 0;
 		*max = *step = 1;
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index cb77197..0ca79786 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -81,7 +81,7 @@
 	/* Any types not assigned to fixed minor ranges must be mapped to
 	   one single bitmap for the purposes of finding a free node number
 	   since all those unassigned types use the same minor range. */
-	int idx = (vfl_type > VFL_TYPE_VTX) ? VFL_TYPE_MAX - 1 : vfl_type;
+	int idx = (vfl_type > VFL_TYPE_RADIO) ? VFL_TYPE_MAX - 1 : vfl_type;
 
 	return devnode_nums[idx];
 }
@@ -187,48 +187,69 @@
 		size_t sz, loff_t *off)
 {
 	struct video_device *vdev = video_devdata(filp);
+	int ret = -EIO;
 
 	if (!vdev->fops->read)
 		return -EINVAL;
-	if (!video_is_registered(vdev))
-		return -EIO;
-	return vdev->fops->read(filp, buf, sz, off);
+	if (vdev->lock)
+		mutex_lock(vdev->lock);
+	if (video_is_registered(vdev))
+		ret = vdev->fops->read(filp, buf, sz, off);
+	if (vdev->lock)
+		mutex_unlock(vdev->lock);
+	return ret;
 }
 
 static ssize_t v4l2_write(struct file *filp, const char __user *buf,
 		size_t sz, loff_t *off)
 {
 	struct video_device *vdev = video_devdata(filp);
+	int ret = -EIO;
 
 	if (!vdev->fops->write)
 		return -EINVAL;
-	if (!video_is_registered(vdev))
-		return -EIO;
-	return vdev->fops->write(filp, buf, sz, off);
+	if (vdev->lock)
+		mutex_lock(vdev->lock);
+	if (video_is_registered(vdev))
+		ret = vdev->fops->write(filp, buf, sz, off);
+	if (vdev->lock)
+		mutex_unlock(vdev->lock);
+	return ret;
 }
 
 static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
 {
 	struct video_device *vdev = video_devdata(filp);
+	int ret = DEFAULT_POLLMASK;
 
-	if (!vdev->fops->poll || !video_is_registered(vdev))
-		return DEFAULT_POLLMASK;
-	return vdev->fops->poll(filp, poll);
+	if (!vdev->fops->poll)
+		return ret;
+	if (vdev->lock)
+		mutex_lock(vdev->lock);
+	if (video_is_registered(vdev))
+		ret = vdev->fops->poll(filp, poll);
+	if (vdev->lock)
+		mutex_unlock(vdev->lock);
+	return ret;
 }
 
 static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct video_device *vdev = video_devdata(filp);
-	int ret;
+	int ret = -ENODEV;
 
-	/* Allow ioctl to continue even if the device was unregistered.
-	   Things like dequeueing buffers might still be useful. */
 	if (vdev->fops->unlocked_ioctl) {
-		ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
+		if (vdev->lock)
+			mutex_lock(vdev->lock);
+		if (video_is_registered(vdev))
+			ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
+		if (vdev->lock)
+			mutex_unlock(vdev->lock);
 	} else if (vdev->fops->ioctl) {
 		/* TODO: convert all drivers to unlocked_ioctl */
 		lock_kernel();
-		ret = vdev->fops->ioctl(filp, cmd, arg);
+		if (video_is_registered(vdev))
+			ret = vdev->fops->ioctl(filp, cmd, arg);
 		unlock_kernel();
 	} else
 		ret = -ENOTTY;
@@ -236,30 +257,20 @@
 	return ret;
 }
 
-#ifdef CONFIG_MMU
-#define v4l2_get_unmapped_area NULL
-#else
-static unsigned long v4l2_get_unmapped_area(struct file *filp,
-		unsigned long addr, unsigned long len, unsigned long pgoff,
-		unsigned long flags)
-{
-	struct video_device *vdev = video_devdata(filp);
-
-	if (!vdev->fops->get_unmapped_area)
-		return -ENOSYS;
-	if (!video_is_registered(vdev))
-		return -ENODEV;
-	return vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
-}
-#endif
-
 static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
 {
 	struct video_device *vdev = video_devdata(filp);
+	int ret = -ENODEV;
 
-	if (!vdev->fops->mmap || !video_is_registered(vdev))
-		return -ENODEV;
-	return vdev->fops->mmap(filp, vm);
+	if (!vdev->fops->mmap)
+		return ret;
+	if (vdev->lock)
+		mutex_lock(vdev->lock);
+	if (video_is_registered(vdev))
+		ret = vdev->fops->mmap(filp, vm);
+	if (vdev->lock)
+		mutex_unlock(vdev->lock);
+	return ret;
 }
 
 /* Override for the open function */
@@ -271,17 +282,24 @@
 	/* Check if the video device is available */
 	mutex_lock(&videodev_lock);
 	vdev = video_devdata(filp);
-	/* return ENODEV if the video device has been removed
-	   already or if it is not registered anymore. */
-	if (vdev == NULL || !video_is_registered(vdev)) {
+	/* return ENODEV if the video device has already been removed. */
+	if (vdev == NULL) {
 		mutex_unlock(&videodev_lock);
 		return -ENODEV;
 	}
 	/* and increase the device refcount */
 	video_get(vdev);
 	mutex_unlock(&videodev_lock);
-	if (vdev->fops->open)
-		ret = vdev->fops->open(filp);
+	if (vdev->fops->open) {
+		if (vdev->lock)
+			mutex_lock(vdev->lock);
+		if (video_is_registered(vdev))
+			ret = vdev->fops->open(filp);
+		else
+			ret = -ENODEV;
+		if (vdev->lock)
+			mutex_unlock(vdev->lock);
+	}
 
 	/* decrease the refcount in case of an error */
 	if (ret)
@@ -295,8 +313,13 @@
 	struct video_device *vdev = video_devdata(filp);
 	int ret = 0;
 
-	if (vdev->fops->release)
+	if (vdev->fops->release) {
+		if (vdev->lock)
+			mutex_lock(vdev->lock);
 		vdev->fops->release(filp);
+		if (vdev->lock)
+			mutex_unlock(vdev->lock);
+	}
 
 	/* decrease the refcount unconditionally since the release()
 	   return value is ignored. */
@@ -309,7 +332,6 @@
 	.read = v4l2_read,
 	.write = v4l2_write,
 	.open = v4l2_open,
-	.get_unmapped_area = v4l2_get_unmapped_area,
 	.mmap = v4l2_mmap,
 	.unlocked_ioctl = v4l2_ioctl,
 #ifdef CONFIG_COMPAT
@@ -377,8 +399,6 @@
  *
  *	%VFL_TYPE_GRABBER - A frame grabber
  *
- *	%VFL_TYPE_VTX - A teletext device
- *
  *	%VFL_TYPE_VBI - Vertical blank data (undecoded)
  *
  *	%VFL_TYPE_RADIO - A radio card
@@ -411,9 +431,6 @@
 	case VFL_TYPE_GRABBER:
 		name_base = "video";
 		break;
-	case VFL_TYPE_VTX:
-		name_base = "vtx";
-		break;
 	case VFL_TYPE_VBI:
 		name_base = "vbi";
 		break;
@@ -451,10 +468,6 @@
 		minor_offset = 64;
 		minor_cnt = 64;
 		break;
-	case VFL_TYPE_VTX:
-		minor_offset = 192;
-		minor_cnt = 32;
-		break;
 	case VFL_TYPE_VBI:
 		minor_offset = 224;
 		minor_cnt = 32;
diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
index de74ce0..69fd343 100644
--- a/drivers/media/video/v4l2-event.c
+++ b/drivers/media/video/v4l2-event.c
@@ -134,15 +134,22 @@
 	if (nonblocking)
 		return __v4l2_event_dequeue(fh, event);
 
+	/* Release the vdev lock while waiting */
+	if (fh->vdev->lock)
+		mutex_unlock(fh->vdev->lock);
+
 	do {
 		ret = wait_event_interruptible(events->wait,
 					       events->navailable != 0);
 		if (ret < 0)
-			return ret;
+			break;
 
 		ret = __v4l2_event_dequeue(fh, event);
 	} while (ret == -ENOENT);
 
+	if (fh->vdev->lock)
+		mutex_lock(fh->vdev->lock);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c
index f45f940..ac832a2 100644
--- a/drivers/media/video/v4l2-mem2mem.c
+++ b/drivers/media/video/v4l2-mem2mem.c
@@ -421,8 +421,8 @@
 	src_q = v4l2_m2m_get_src_vq(m2m_ctx);
 	dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
 
-	mutex_lock(&src_q->vb_lock);
-	mutex_lock(&dst_q->vb_lock);
+	videobuf_queue_lock(src_q);
+	videobuf_queue_lock(dst_q);
 
 	if (src_q->streaming && !list_empty(&src_q->stream))
 		src_vb = list_first_entry(&src_q->stream,
@@ -450,8 +450,8 @@
 	}
 
 end:
-	mutex_unlock(&dst_q->vb_lock);
-	mutex_unlock(&src_q->vb_lock);
+	videobuf_queue_unlock(dst_q);
+	videobuf_queue_unlock(src_q);
 	return rc;
 }
 EXPORT_SYMBOL_GPL(v4l2_m2m_poll);
diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c
new file mode 100644
index 0000000..02a21bc
--- /dev/null
+++ b/drivers/media/video/via-camera.c
@@ -0,0 +1,1474 @@
+/*
+ * Driver for the VIA Chrome integrated camera controller.
+ *
+ * Copyright 2009,2010 Jonathan Corbet <corbet@lwn.net>
+ * Distributable under the terms of the GNU General Public License, version 2
+ *
+ * This work was supported by the One Laptop Per Child project
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/videobuf-dma-sg.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_qos_params.h>
+#include <linux/via-core.h>
+#include <linux/via-gpio.h>
+#include <linux/via_i2c.h>
+
+#include "via-camera.h"
+
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+MODULE_DESCRIPTION("VIA framebuffer-based camera controller driver");
+MODULE_LICENSE("GPL");
+
+static int flip_image;
+module_param(flip_image, bool, 0444);
+MODULE_PARM_DESC(flip_image,
+		"If set, the sensor will be instructed to flip the image "
+		"vertically.");
+
+#ifdef CONFIG_OLPC_XO_1_5
+static int override_serial;
+module_param(override_serial, bool, 0444);
+MODULE_PARM_DESC(override_serial,
+		"The camera driver will normally refuse to load if "
+		"the XO 1.5 serial port is enabled.  Set this option "
+		"to force the issue.");
+#endif
+
+/*
+ * Basic window sizes.
+ */
+#define VGA_WIDTH	640
+#define VGA_HEIGHT	480
+#define QCIF_WIDTH	176
+#define	QCIF_HEIGHT	144
+
+/*
+ * The structure describing our camera.
+ */
+enum viacam_opstate { S_IDLE = 0, S_RUNNING = 1 };
+
+struct via_camera {
+	struct v4l2_device v4l2_dev;
+	struct video_device vdev;
+	struct v4l2_subdev *sensor;
+	struct platform_device *platdev;
+	struct viafb_dev *viadev;
+	struct mutex lock;
+	enum viacam_opstate opstate;
+	unsigned long flags;
+	struct pm_qos_request_list qos_request;
+	/*
+	 * GPIO info for power/reset management
+	 */
+	int power_gpio;
+	int reset_gpio;
+	/*
+	 * I/O memory stuff.
+	 */
+	void __iomem *mmio;	/* Where the registers live */
+	void __iomem *fbmem;	/* Frame buffer memory */
+	u32 fb_offset;		/* Reserved memory offset (FB) */
+	/*
+	 * Capture buffers and related.	 The controller supports
+	 * up to three, so that's what we have here.  These buffers
+	 * live in frame buffer memory, so we don't call them "DMA".
+	 */
+	unsigned int cb_offsets[3];	/* offsets into fb mem */
+	u8 *cb_addrs[3];		/* Kernel-space addresses */
+	int n_cap_bufs;			/* How many are we using? */
+	int next_buf;
+	struct videobuf_queue vb_queue;
+	struct list_head buffer_queue;	/* prot. by reg_lock */
+	/*
+	 * User tracking.
+	 */
+	int users;
+	struct file *owner;
+	/*
+	 * Video format information.  sensor_format is kept in a form
+	 * that we can use to pass to the sensor.  We always run the
+	 * sensor in VGA resolution, though, and let the controller
+	 * downscale things if need be.	 So we keep the "real*
+	 * dimensions separately.
+	 */
+	struct v4l2_pix_format sensor_format;
+	struct v4l2_pix_format user_format;
+	enum v4l2_mbus_pixelcode mbus_code;
+};
+
+/*
+ * Yes, this is a hack, but there's only going to be one of these
+ * on any system we know of.
+ */
+static struct via_camera *via_cam_info;
+
+/*
+ * Flag values, manipulated with bitops
+ */
+#define CF_DMA_ACTIVE	 0	/* A frame is incoming */
+#define CF_CONFIG_NEEDED 1	/* Must configure hardware */
+
+
+/*
+ * Nasty ugly v4l2 boilerplate.
+ */
+#define sensor_call(cam, optype, func, args...) \
+	v4l2_subdev_call(cam->sensor, optype, func, ##args)
+
+/*
+ * Debugging and related.
+ */
+#define cam_err(cam, fmt, arg...) \
+	dev_err(&(cam)->platdev->dev, fmt, ##arg);
+#define cam_warn(cam, fmt, arg...) \
+	dev_warn(&(cam)->platdev->dev, fmt, ##arg);
+#define cam_dbg(cam, fmt, arg...) \
+	dev_dbg(&(cam)->platdev->dev, fmt, ##arg);
+
+/*
+ * Format handling.  This is ripped almost directly from Hans's changes
+ * to cafe_ccic.c.  It's a little unfortunate; until this change, we
+ * didn't need to know anything about the format except its byte depth;
+ * now this information must be managed at this level too.
+ */
+static struct via_format {
+	__u8 *desc;
+	__u32 pixelformat;
+	int bpp;   /* Bytes per pixel */
+	enum v4l2_mbus_pixelcode mbus_code;
+} via_formats[] = {
+	{
+		.desc		= "YUYV 4:2:2",
+		.pixelformat	= V4L2_PIX_FMT_YUYV,
+		.mbus_code	= V4L2_MBUS_FMT_YUYV8_2X8,
+		.bpp		= 2,
+	},
+	{
+		.desc		= "RGB 565",
+		.pixelformat	= V4L2_PIX_FMT_RGB565,
+		.mbus_code	= V4L2_MBUS_FMT_RGB565_2X8_LE,
+		.bpp		= 2,
+	},
+	/* RGB444 and Bayer should be doable, but have never been
+	   tested with this driver. */
+};
+#define N_VIA_FMTS ARRAY_SIZE(via_formats)
+
+static struct via_format *via_find_format(u32 pixelformat)
+{
+	unsigned i;
+
+	for (i = 0; i < N_VIA_FMTS; i++)
+		if (via_formats[i].pixelformat == pixelformat)
+			return via_formats + i;
+	/* Not found? Then return the first format. */
+	return via_formats;
+}
+
+
+/*--------------------------------------------------------------------------*/
+/*
+ * Sensor power/reset management.  This piece is OLPC-specific for
+ * sure; other configurations will have things connected differently.
+ */
+static int via_sensor_power_setup(struct via_camera *cam)
+{
+	int ret;
+
+	cam->power_gpio = viafb_gpio_lookup("VGPIO3");
+	cam->reset_gpio = viafb_gpio_lookup("VGPIO2");
+	if (cam->power_gpio < 0 || cam->reset_gpio < 0) {
+		dev_err(&cam->platdev->dev, "Unable to find GPIO lines\n");
+		return -EINVAL;
+	}
+	ret = gpio_request(cam->power_gpio, "viafb-camera");
+	if (ret) {
+		dev_err(&cam->platdev->dev, "Unable to request power GPIO\n");
+		return ret;
+	}
+	ret = gpio_request(cam->reset_gpio, "viafb-camera");
+	if (ret) {
+		dev_err(&cam->platdev->dev, "Unable to request reset GPIO\n");
+		gpio_free(cam->power_gpio);
+		return ret;
+	}
+	gpio_direction_output(cam->power_gpio, 0);
+	gpio_direction_output(cam->reset_gpio, 0);
+	return 0;
+}
+
+/*
+ * Power up the sensor and perform the reset dance.
+ */
+static void via_sensor_power_up(struct via_camera *cam)
+{
+	gpio_set_value(cam->power_gpio, 1);
+	gpio_set_value(cam->reset_gpio, 0);
+	msleep(20);  /* Probably excessive */
+	gpio_set_value(cam->reset_gpio, 1);
+	msleep(20);
+}
+
+static void via_sensor_power_down(struct via_camera *cam)
+{
+	gpio_set_value(cam->power_gpio, 0);
+	gpio_set_value(cam->reset_gpio, 0);
+}
+
+
+static void via_sensor_power_release(struct via_camera *cam)
+{
+	via_sensor_power_down(cam);
+	gpio_free(cam->power_gpio);
+	gpio_free(cam->reset_gpio);
+}
+
+/* --------------------------------------------------------------------------*/
+/* Sensor ops */
+
+/*
+ * Manage the ov7670 "flip" bit, which needs special help.
+ */
+static int viacam_set_flip(struct via_camera *cam)
+{
+	struct v4l2_control ctrl;
+
+	memset(&ctrl, 0, sizeof(ctrl));
+	ctrl.id = V4L2_CID_VFLIP;
+	ctrl.value = flip_image;
+	return sensor_call(cam, core, s_ctrl, &ctrl);
+}
+
+/*
+ * Configure the sensor.  It's up to the caller to ensure
+ * that the camera is in the correct operating state.
+ */
+static int viacam_configure_sensor(struct via_camera *cam)
+{
+	struct v4l2_mbus_framefmt mbus_fmt;
+	int ret;
+
+	v4l2_fill_mbus_format(&mbus_fmt, &cam->sensor_format, cam->mbus_code);
+	ret = sensor_call(cam, core, init, 0);
+	if (ret == 0)
+		ret = sensor_call(cam, video, s_mbus_fmt, &mbus_fmt);
+	/*
+	 * OV7670 does weird things if flip is set *before* format...
+	 */
+	if (ret == 0)
+		ret = viacam_set_flip(cam);
+	return ret;
+}
+
+
+
+/* --------------------------------------------------------------------------*/
+/*
+ * Some simple register accessors; they assume that the lock is held.
+ *
+ * Should we want to support the second capture engine, we could
+ * hide the register difference by adding 0x1000 to registers in the
+ * 0x300-350 range.
+ */
+static inline void viacam_write_reg(struct via_camera *cam,
+		int reg, int value)
+{
+	iowrite32(value, cam->mmio + reg);
+}
+
+static inline int viacam_read_reg(struct via_camera *cam, int reg)
+{
+	return ioread32(cam->mmio + reg);
+}
+
+static inline void viacam_write_reg_mask(struct via_camera *cam,
+		int reg, int value, int mask)
+{
+	int tmp = viacam_read_reg(cam, reg);
+
+	tmp = (tmp & ~mask) | (value & mask);
+	viacam_write_reg(cam, reg, tmp);
+}
+
+
+/* --------------------------------------------------------------------------*/
+/* Interrupt management and handling */
+
+static irqreturn_t viacam_quick_irq(int irq, void *data)
+{
+	struct via_camera *cam = data;
+	irqreturn_t ret = IRQ_NONE;
+	int icv;
+
+	/*
+	 * All we do here is to clear the interrupts and tell
+	 * the handler thread to wake up.
+	 */
+	spin_lock(&cam->viadev->reg_lock);
+	icv = viacam_read_reg(cam, VCR_INTCTRL);
+	if (icv & VCR_IC_EAV) {
+		icv |= VCR_IC_EAV|VCR_IC_EVBI|VCR_IC_FFULL;
+		viacam_write_reg(cam, VCR_INTCTRL, icv);
+		ret = IRQ_WAKE_THREAD;
+	}
+	spin_unlock(&cam->viadev->reg_lock);
+	return ret;
+}
+
+/*
+ * Find the next videobuf buffer which has somebody waiting on it.
+ */
+static struct videobuf_buffer *viacam_next_buffer(struct via_camera *cam)
+{
+	unsigned long flags;
+	struct videobuf_buffer *buf = NULL;
+
+	spin_lock_irqsave(&cam->viadev->reg_lock, flags);
+	if (cam->opstate != S_RUNNING)
+		goto out;
+	if (list_empty(&cam->buffer_queue))
+		goto out;
+	buf = list_entry(cam->buffer_queue.next, struct videobuf_buffer, queue);
+	if (!waitqueue_active(&buf->done)) {/* Nobody waiting */
+		buf = NULL;
+		goto out;
+	}
+	list_del(&buf->queue);
+	buf->state = VIDEOBUF_ACTIVE;
+out:
+	spin_unlock_irqrestore(&cam->viadev->reg_lock, flags);
+	return buf;
+}
+
+/*
+ * The threaded IRQ handler.
+ */
+static irqreturn_t viacam_irq(int irq, void *data)
+{
+	int bufn;
+	struct videobuf_buffer *vb;
+	struct via_camera *cam = data;
+	struct videobuf_dmabuf *vdma;
+
+	/*
+	 * If there is no place to put the data frame, don't bother
+	 * with anything else.
+	 */
+	vb = viacam_next_buffer(cam);
+	if (vb == NULL)
+		goto done;
+	/*
+	 * Figure out which buffer we just completed.
+	 */
+	bufn = (viacam_read_reg(cam, VCR_INTCTRL) & VCR_IC_ACTBUF) >> 3;
+	bufn -= 1;
+	if (bufn < 0)
+		bufn = cam->n_cap_bufs - 1;
+	/*
+	 * Copy over the data and let any waiters know.
+	 */
+	vdma = videobuf_to_dma(vb);
+	viafb_dma_copy_out_sg(cam->cb_offsets[bufn], vdma->sglist, vdma->sglen);
+	vb->state = VIDEOBUF_DONE;
+	vb->size = cam->user_format.sizeimage;
+	wake_up(&vb->done);
+done:
+	return IRQ_HANDLED;
+}
+
+
+/*
+ * These functions must mess around with the general interrupt
+ * control register, which is relevant to much more than just the
+ * camera.  Nothing else uses interrupts, though, as of this writing.
+ * Should that situation change, we'll have to improve support at
+ * the via-core level.
+ */
+static void viacam_int_enable(struct via_camera *cam)
+{
+	viacam_write_reg(cam, VCR_INTCTRL,
+			VCR_IC_INTEN|VCR_IC_EAV|VCR_IC_EVBI|VCR_IC_FFULL);
+	viafb_irq_enable(VDE_I_C0AVEN);
+}
+
+static void viacam_int_disable(struct via_camera *cam)
+{
+	viafb_irq_disable(VDE_I_C0AVEN);
+	viacam_write_reg(cam, VCR_INTCTRL, 0);
+}
+
+
+
+/* --------------------------------------------------------------------------*/
+/* Controller operations */
+
+/*
+ * Set up our capture buffers in framebuffer memory.
+ */
+static int viacam_ctlr_cbufs(struct via_camera *cam)
+{
+	int nbuf = cam->viadev->camera_fbmem_size/cam->sensor_format.sizeimage;
+	int i;
+	unsigned int offset;
+
+	/*
+	 * See how many buffers we can work with.
+	 */
+	if (nbuf >= 3) {
+		cam->n_cap_bufs = 3;
+		viacam_write_reg_mask(cam, VCR_CAPINTC, VCR_CI_3BUFS,
+				VCR_CI_3BUFS);
+	} else if (nbuf == 2) {
+		cam->n_cap_bufs = 2;
+		viacam_write_reg_mask(cam, VCR_CAPINTC, 0, VCR_CI_3BUFS);
+	} else {
+		cam_warn(cam, "Insufficient frame buffer memory\n");
+		return -ENOMEM;
+	}
+	/*
+	 * Set them up.
+	 */
+	offset = cam->fb_offset;
+	for (i = 0; i < cam->n_cap_bufs; i++) {
+		cam->cb_offsets[i] = offset;
+		cam->cb_addrs[i] = cam->fbmem + offset;
+		viacam_write_reg(cam, VCR_VBUF1 + i*4, offset & VCR_VBUF_MASK);
+		offset += cam->sensor_format.sizeimage;
+	}
+	return 0;
+}
+
+/*
+ * Set the scaling register for downscaling the image.
+ *
+ * This register works like this...  Vertical scaling is enabled
+ * by bit 26; if that bit is set, downscaling is controlled by the
+ * value in bits 16:25.	 Those bits are divided by 1024 to get
+ * the scaling factor; setting just bit 25 thus cuts the height
+ * in half.
+ *
+ * Horizontal scaling works about the same, but it's enabled by
+ * bit 11, with bits 0:10 giving the numerator of a fraction
+ * (over 2048) for the scaling value.
+ *
+ * This function is naive in that, if the user departs from
+ * the 3x4 VGA scaling factor, the image will distort.	We
+ * could work around that if it really seemed important.
+ */
+static void viacam_set_scale(struct via_camera *cam)
+{
+	unsigned int avscale;
+	int sf;
+
+	if (cam->user_format.width == VGA_WIDTH)
+		avscale = 0;
+	else {
+		sf = (cam->user_format.width*2048)/VGA_WIDTH;
+		avscale = VCR_AVS_HEN | sf;
+	}
+	if (cam->user_format.height < VGA_HEIGHT) {
+		sf = (1024*cam->user_format.height)/VGA_HEIGHT;
+		avscale |= VCR_AVS_VEN | (sf << 16);
+	}
+	viacam_write_reg(cam, VCR_AVSCALE, avscale);
+}
+
+
+/*
+ * Configure image-related information into the capture engine.
+ */
+static void viacam_ctlr_image(struct via_camera *cam)
+{
+	int cicreg;
+
+	/*
+	 * Disable clock before messing with stuff - from the via
+	 * sample driver.
+	 */
+	viacam_write_reg(cam, VCR_CAPINTC, ~(VCR_CI_ENABLE|VCR_CI_CLKEN));
+	/*
+	 * Set up the controller for VGA resolution, modulo magic
+	 * offsets from the via sample driver.
+	 */
+	viacam_write_reg(cam, VCR_HORRANGE, 0x06200120);
+	viacam_write_reg(cam, VCR_VERTRANGE, 0x01de0000);
+	viacam_set_scale(cam);
+	/*
+	 * Image size info.
+	 */
+	viacam_write_reg(cam, VCR_MAXDATA,
+			(cam->sensor_format.height << 16) |
+			(cam->sensor_format.bytesperline >> 3));
+	viacam_write_reg(cam, VCR_MAXVBI, 0);
+	viacam_write_reg(cam, VCR_VSTRIDE,
+			cam->user_format.bytesperline & VCR_VS_STRIDE);
+	/*
+	 * Set up the capture interface control register,
+	 * everything but the "go" bit.
+	 *
+	 * The FIFO threshold is a bit of a magic number; 8 is what
+	 * VIA's sample code uses.
+	 */
+	cicreg = VCR_CI_CLKEN |
+		0x08000000 |		/* FIFO threshold */
+		VCR_CI_FLDINV |		/* OLPC-specific? */
+		VCR_CI_VREFINV |	/* OLPC-specific? */
+		VCR_CI_DIBOTH |		/* Capture both fields */
+		VCR_CI_CCIR601_8;
+	if (cam->n_cap_bufs == 3)
+		cicreg |= VCR_CI_3BUFS;
+	/*
+	 * YUV formats need different byte swapping than RGB.
+	 */
+	if (cam->user_format.pixelformat == V4L2_PIX_FMT_YUYV)
+		cicreg |= VCR_CI_YUYV;
+	else
+		cicreg |= VCR_CI_UYVY;
+	viacam_write_reg(cam, VCR_CAPINTC, cicreg);
+}
+
+
+static int viacam_config_controller(struct via_camera *cam)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cam->viadev->reg_lock, flags);
+	ret = viacam_ctlr_cbufs(cam);
+	if (!ret)
+		viacam_ctlr_image(cam);
+	spin_unlock_irqrestore(&cam->viadev->reg_lock, flags);
+	clear_bit(CF_CONFIG_NEEDED, &cam->flags);
+	return ret;
+}
+
+/*
+ * Make it start grabbing data.
+ */
+static void viacam_start_engine(struct via_camera *cam)
+{
+	spin_lock_irq(&cam->viadev->reg_lock);
+	cam->next_buf = 0;
+	viacam_write_reg_mask(cam, VCR_CAPINTC, VCR_CI_ENABLE, VCR_CI_ENABLE);
+	viacam_int_enable(cam);
+	(void) viacam_read_reg(cam, VCR_CAPINTC); /* Force post */
+	cam->opstate = S_RUNNING;
+	spin_unlock_irq(&cam->viadev->reg_lock);
+}
+
+
+static void viacam_stop_engine(struct via_camera *cam)
+{
+	spin_lock_irq(&cam->viadev->reg_lock);
+	viacam_int_disable(cam);
+	viacam_write_reg_mask(cam, VCR_CAPINTC, 0, VCR_CI_ENABLE);
+	(void) viacam_read_reg(cam, VCR_CAPINTC); /* Force post */
+	cam->opstate = S_IDLE;
+	spin_unlock_irq(&cam->viadev->reg_lock);
+}
+
+
+/* --------------------------------------------------------------------------*/
+/* Videobuf callback ops */
+
+/*
+ * buffer_setup.  The purpose of this one would appear to be to tell
+ * videobuf how big a single image is.	It's also evidently up to us
+ * to put some sort of limit on the maximum number of buffers allowed.
+ */
+static int viacam_vb_buf_setup(struct videobuf_queue *q,
+		unsigned int *count, unsigned int *size)
+{
+	struct via_camera *cam = q->priv_data;
+
+	*size = cam->user_format.sizeimage;
+	if (*count == 0 || *count > 6)	/* Arbitrary number */
+		*count = 6;
+	return 0;
+}
+
+/*
+ * Prepare a buffer.
+ */
+static int viacam_vb_buf_prepare(struct videobuf_queue *q,
+		struct videobuf_buffer *vb, enum v4l2_field field)
+{
+	struct via_camera *cam = q->priv_data;
+
+	vb->size = cam->user_format.sizeimage;
+	vb->width = cam->user_format.width; /* bytesperline???? */
+	vb->height = cam->user_format.height;
+	vb->field = field;
+	if (vb->state == VIDEOBUF_NEEDS_INIT) {
+		int ret = videobuf_iolock(q, vb, NULL);
+		if (ret)
+			return ret;
+	}
+	vb->state = VIDEOBUF_PREPARED;
+	return 0;
+}
+
+/*
+ * We've got a buffer to put data into.
+ *
+ * FIXME: check for a running engine and valid buffers?
+ */
+static void viacam_vb_buf_queue(struct videobuf_queue *q,
+		struct videobuf_buffer *vb)
+{
+	struct via_camera *cam = q->priv_data;
+
+	/*
+	 * Note that videobuf holds the lock when it calls
+	 * us, so we need not (indeed, cannot) take it here.
+	 */
+	vb->state = VIDEOBUF_QUEUED;
+	list_add_tail(&vb->queue, &cam->buffer_queue);
+}
+
+/*
+ * Free a buffer.
+ */
+static void viacam_vb_buf_release(struct videobuf_queue *q,
+		struct videobuf_buffer *vb)
+{
+	struct via_camera *cam = q->priv_data;
+
+	videobuf_dma_unmap(&cam->platdev->dev, videobuf_to_dma(vb));
+	videobuf_dma_free(videobuf_to_dma(vb));
+	vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static const struct videobuf_queue_ops viacam_vb_ops = {
+	.buf_setup	= viacam_vb_buf_setup,
+	.buf_prepare	= viacam_vb_buf_prepare,
+	.buf_queue	= viacam_vb_buf_queue,
+	.buf_release	= viacam_vb_buf_release,
+};
+
+/* --------------------------------------------------------------------------*/
+/* File operations */
+
+static int viacam_open(struct file *filp)
+{
+	struct via_camera *cam = video_drvdata(filp);
+
+	filp->private_data = cam;
+	/*
+	 * Note the new user.  If this is the first one, we'll also
+	 * need to power up the sensor.
+	 */
+	mutex_lock(&cam->lock);
+	if (cam->users == 0) {
+		int ret = viafb_request_dma();
+
+		if (ret) {
+			mutex_unlock(&cam->lock);
+			return ret;
+		}
+		via_sensor_power_up(cam);
+		set_bit(CF_CONFIG_NEEDED, &cam->flags);
+		/*
+		 * Hook into videobuf.	Evidently this cannot fail.
+		 */
+		videobuf_queue_sg_init(&cam->vb_queue, &viacam_vb_ops,
+				&cam->platdev->dev, &cam->viadev->reg_lock,
+				V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+				sizeof(struct videobuf_buffer), cam, NULL);
+	}
+	(cam->users)++;
+	mutex_unlock(&cam->lock);
+	return 0;
+}
+
+static int viacam_release(struct file *filp)
+{
+	struct via_camera *cam = video_drvdata(filp);
+
+	mutex_lock(&cam->lock);
+	(cam->users)--;
+	/*
+	 * If the "owner" is closing, shut down any ongoing
+	 * operations.
+	 */
+	if (filp == cam->owner) {
+		videobuf_stop(&cam->vb_queue);
+		/*
+		 * We don't hold the spinlock here, but, if release()
+		 * is being called by the owner, nobody else will
+		 * be changing the state.  And an extra stop would
+		 * not hurt anyway.
+		 */
+		if (cam->opstate != S_IDLE)
+			viacam_stop_engine(cam);
+		cam->owner = NULL;
+	}
+	/*
+	 * Last one out needs to turn out the lights.
+	 */
+	if (cam->users == 0) {
+		videobuf_mmap_free(&cam->vb_queue);
+		via_sensor_power_down(cam);
+		viafb_release_dma();
+	}
+	mutex_unlock(&cam->lock);
+	return 0;
+}
+
+/*
+ * Read a frame from the device.
+ */
+static ssize_t viacam_read(struct file *filp, char __user *buffer,
+		size_t len, loff_t *pos)
+{
+	struct via_camera *cam = video_drvdata(filp);
+	int ret;
+
+	mutex_lock(&cam->lock);
+	/*
+	 * Enforce the V4l2 "only one owner gets to read data" rule.
+	 */
+	if (cam->owner && cam->owner != filp) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+	cam->owner = filp;
+	/*
+	 * Do we need to configure the hardware?
+	 */
+	if (test_bit(CF_CONFIG_NEEDED, &cam->flags)) {
+		ret = viacam_configure_sensor(cam);
+		if (!ret)
+			ret = viacam_config_controller(cam);
+		if (ret)
+			goto out_unlock;
+	}
+	/*
+	 * Fire up the capture engine, then have videobuf do
+	 * the heavy lifting.  Someday it would be good to avoid
+	 * stopping and restarting the engine each time.
+	 */
+	INIT_LIST_HEAD(&cam->buffer_queue);
+	viacam_start_engine(cam);
+	ret = videobuf_read_stream(&cam->vb_queue, buffer, len, pos, 0,
+			filp->f_flags & O_NONBLOCK);
+	viacam_stop_engine(cam);
+	/* videobuf_stop() ?? */
+
+out_unlock:
+	mutex_unlock(&cam->lock);
+	return ret;
+}
+
+
+static unsigned int viacam_poll(struct file *filp, struct poll_table_struct *pt)
+{
+	struct via_camera *cam = video_drvdata(filp);
+
+	return videobuf_poll_stream(filp, &cam->vb_queue, pt);
+}
+
+
+static int viacam_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct via_camera *cam = video_drvdata(filp);
+
+	return videobuf_mmap_mapper(&cam->vb_queue, vma);
+}
+
+
+
+static const struct v4l2_file_operations viacam_fops = {
+	.owner		= THIS_MODULE,
+	.open		= viacam_open,
+	.release	= viacam_release,
+	.read		= viacam_read,
+	.poll		= viacam_poll,
+	.mmap		= viacam_mmap,
+	.unlocked_ioctl	= video_ioctl2,
+};
+
+/*----------------------------------------------------------------------------*/
+/*
+ * The long list of v4l2 ioctl ops
+ */
+
+static int viacam_g_chip_ident(struct file *file, void *priv,
+		struct v4l2_dbg_chip_ident *ident)
+{
+	struct via_camera *cam = priv;
+
+	ident->ident = V4L2_IDENT_NONE;
+	ident->revision = 0;
+	if (v4l2_chip_match_host(&ident->match)) {
+		ident->ident = V4L2_IDENT_VIA_VX855;
+		return 0;
+	}
+	return sensor_call(cam, core, g_chip_ident, ident);
+}
+
+/*
+ * Control ops are passed through to the sensor.
+ */
+static int viacam_queryctrl(struct file *filp, void *priv,
+		struct v4l2_queryctrl *qc)
+{
+	struct via_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->lock);
+	ret = sensor_call(cam, core, queryctrl, qc);
+	mutex_unlock(&cam->lock);
+	return ret;
+}
+
+
+static int viacam_g_ctrl(struct file *filp, void *priv,
+		struct v4l2_control *ctrl)
+{
+	struct via_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->lock);
+	ret = sensor_call(cam, core, g_ctrl, ctrl);
+	mutex_unlock(&cam->lock);
+	return ret;
+}
+
+
+static int viacam_s_ctrl(struct file *filp, void *priv,
+		struct v4l2_control *ctrl)
+{
+	struct via_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->lock);
+	ret = sensor_call(cam, core, s_ctrl, ctrl);
+	mutex_unlock(&cam->lock);
+	return ret;
+}
+
+/*
+ * Only one input.
+ */
+static int viacam_enum_input(struct file *filp, void *priv,
+		struct v4l2_input *input)
+{
+	if (input->index != 0)
+		return -EINVAL;
+
+	input->type = V4L2_INPUT_TYPE_CAMERA;
+	input->std = V4L2_STD_ALL; /* Not sure what should go here */
+	strcpy(input->name, "Camera");
+	return 0;
+}
+
+static int viacam_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int viacam_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int viacam_s_std(struct file *filp, void *priv, v4l2_std_id *std)
+{
+	return 0;
+}
+
+/*
+ * Video format stuff.	Here is our default format until
+ * user space messes with things.
+ */
+static const struct v4l2_pix_format viacam_def_pix_format = {
+	.width		= VGA_WIDTH,
+	.height		= VGA_HEIGHT,
+	.pixelformat	= V4L2_PIX_FMT_YUYV,
+	.field		= V4L2_FIELD_NONE,
+	.bytesperline	= VGA_WIDTH * 2,
+	.sizeimage	= VGA_WIDTH * VGA_HEIGHT * 2,
+};
+
+static const enum v4l2_mbus_pixelcode via_def_mbus_code = V4L2_MBUS_FMT_YUYV8_2X8;
+
+static int viacam_enum_fmt_vid_cap(struct file *filp, void *priv,
+		struct v4l2_fmtdesc *fmt)
+{
+	if (fmt->index >= N_VIA_FMTS)
+		return -EINVAL;
+	strlcpy(fmt->description, via_formats[fmt->index].desc,
+			sizeof(fmt->description));
+	fmt->pixelformat = via_formats[fmt->index].pixelformat;
+	return 0;
+}
+
+/*
+ * Figure out proper image dimensions, but always force the
+ * sensor to VGA.
+ */
+static void viacam_fmt_pre(struct v4l2_pix_format *userfmt,
+		struct v4l2_pix_format *sensorfmt)
+{
+	*sensorfmt = *userfmt;
+	if (userfmt->width < QCIF_WIDTH || userfmt->height < QCIF_HEIGHT) {
+		userfmt->width = QCIF_WIDTH;
+		userfmt->height = QCIF_HEIGHT;
+	}
+	if (userfmt->width > VGA_WIDTH || userfmt->height > VGA_HEIGHT) {
+		userfmt->width = VGA_WIDTH;
+		userfmt->height = VGA_HEIGHT;
+	}
+	sensorfmt->width = VGA_WIDTH;
+	sensorfmt->height = VGA_HEIGHT;
+}
+
+static void viacam_fmt_post(struct v4l2_pix_format *userfmt,
+		struct v4l2_pix_format *sensorfmt)
+{
+	struct via_format *f = via_find_format(userfmt->pixelformat);
+
+	sensorfmt->bytesperline = sensorfmt->width * f->bpp;
+	sensorfmt->sizeimage = sensorfmt->height * sensorfmt->bytesperline;
+	userfmt->pixelformat = sensorfmt->pixelformat;
+	userfmt->field = sensorfmt->field;
+	userfmt->bytesperline = 2 * userfmt->width;
+	userfmt->sizeimage = userfmt->bytesperline * userfmt->height;
+}
+
+
+/*
+ * The real work of figuring out a workable format.
+ */
+static int viacam_do_try_fmt(struct via_camera *cam,
+		struct v4l2_pix_format *upix, struct v4l2_pix_format *spix)
+{
+	int ret;
+	struct v4l2_mbus_framefmt mbus_fmt;
+	struct via_format *f = via_find_format(upix->pixelformat);
+
+	upix->pixelformat = f->pixelformat;
+	viacam_fmt_pre(upix, spix);
+	v4l2_fill_mbus_format(&mbus_fmt, upix, f->mbus_code);
+	ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt);
+	v4l2_fill_pix_format(spix, &mbus_fmt);
+	viacam_fmt_post(upix, spix);
+	return ret;
+}
+
+
+
+static int viacam_try_fmt_vid_cap(struct file *filp, void *priv,
+		struct v4l2_format *fmt)
+{
+	struct via_camera *cam = priv;
+	struct v4l2_format sfmt;
+	int ret;
+
+	mutex_lock(&cam->lock);
+	ret = viacam_do_try_fmt(cam, &fmt->fmt.pix, &sfmt.fmt.pix);
+	mutex_unlock(&cam->lock);
+	return ret;
+}
+
+
+static int viacam_g_fmt_vid_cap(struct file *filp, void *priv,
+		struct v4l2_format *fmt)
+{
+	struct via_camera *cam = priv;
+
+	mutex_lock(&cam->lock);
+	fmt->fmt.pix = cam->user_format;
+	mutex_unlock(&cam->lock);
+	return 0;
+}
+
+static int viacam_s_fmt_vid_cap(struct file *filp, void *priv,
+		struct v4l2_format *fmt)
+{
+	struct via_camera *cam = priv;
+	int ret;
+	struct v4l2_format sfmt;
+	struct via_format *f = via_find_format(fmt->fmt.pix.pixelformat);
+
+	/*
+	 * Camera must be idle or we can't mess with the
+	 * video setup.
+	 */
+	mutex_lock(&cam->lock);
+	if (cam->opstate != S_IDLE) {
+		ret = -EBUSY;
+		goto out;
+	}
+	/*
+	 * Let the sensor code look over and tweak the
+	 * requested formatting.
+	 */
+	ret = viacam_do_try_fmt(cam, &fmt->fmt.pix, &sfmt.fmt.pix);
+	if (ret)
+		goto out;
+	/*
+	 * OK, let's commit to the new format.
+	 */
+	cam->user_format = fmt->fmt.pix;
+	cam->sensor_format = sfmt.fmt.pix;
+	cam->mbus_code = f->mbus_code;
+	ret = viacam_configure_sensor(cam);
+	if (!ret)
+		ret = viacam_config_controller(cam);
+out:
+	mutex_unlock(&cam->lock);
+	return ret;
+}
+
+static int viacam_querycap(struct file *filp, void *priv,
+		struct v4l2_capability *cap)
+{
+	strcpy(cap->driver, "via-camera");
+	strcpy(cap->card, "via-camera");
+	cap->version = 1;
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+		V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+	return 0;
+}
+
+/*
+ * Streaming operations - pure videobuf stuff.
+ */
+static int viacam_reqbufs(struct file *filp, void *priv,
+		struct v4l2_requestbuffers *rb)
+{
+	struct via_camera *cam = priv;
+
+	return videobuf_reqbufs(&cam->vb_queue, rb);
+}
+
+static int viacam_querybuf(struct file *filp, void *priv,
+		struct v4l2_buffer *buf)
+{
+	struct via_camera *cam = priv;
+
+	return videobuf_querybuf(&cam->vb_queue, buf);
+}
+
+static int viacam_qbuf(struct file *filp, void *priv, struct v4l2_buffer *buf)
+{
+	struct via_camera *cam = priv;
+
+	return videobuf_qbuf(&cam->vb_queue, buf);
+}
+
+static int viacam_dqbuf(struct file *filp, void *priv, struct v4l2_buffer *buf)
+{
+	struct via_camera *cam = priv;
+
+	return videobuf_dqbuf(&cam->vb_queue, buf, filp->f_flags & O_NONBLOCK);
+}
+
+static int viacam_streamon(struct file *filp, void *priv, enum v4l2_buf_type t)
+{
+	struct via_camera *cam = priv;
+	int ret = 0;
+
+	if (t != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	mutex_lock(&cam->lock);
+	if (cam->opstate != S_IDLE) {
+		ret = -EBUSY;
+		goto out;
+	}
+	/*
+	 * Enforce the V4l2 "only one owner gets to read data" rule.
+	 */
+	if (cam->owner && cam->owner != filp) {
+		ret = -EBUSY;
+		goto out;
+	}
+	cam->owner = filp;
+	/*
+	 * Configure things if need be.
+	 */
+	if (test_bit(CF_CONFIG_NEEDED, &cam->flags)) {
+		ret = viacam_configure_sensor(cam);
+		if (ret)
+			goto out;
+		ret = viacam_config_controller(cam);
+		if (ret)
+			goto out;
+	}
+	/*
+	 * If the CPU goes into C3, the DMA transfer gets corrupted and
+	 * users start filing unsightly bug reports.  Put in a "latency"
+	 * requirement which will keep the CPU out of the deeper sleep
+	 * states.
+	 */
+	pm_qos_add_request(&cam->qos_request, PM_QOS_CPU_DMA_LATENCY, 50);
+	/*
+	 * Fire things up.
+	 */
+	INIT_LIST_HEAD(&cam->buffer_queue);
+	ret = videobuf_streamon(&cam->vb_queue);
+	if (!ret)
+		viacam_start_engine(cam);
+out:
+	mutex_unlock(&cam->lock);
+	return ret;
+}
+
+static int viacam_streamoff(struct file *filp, void *priv, enum v4l2_buf_type t)
+{
+	struct via_camera *cam = priv;
+	int ret;
+
+	if (t != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	mutex_lock(&cam->lock);
+	if (cam->opstate != S_RUNNING) {
+		ret = -EINVAL;
+		goto out;
+	}
+	pm_qos_remove_request(&cam->qos_request);
+	viacam_stop_engine(cam);
+	/*
+	 * Videobuf will recycle all of the outstanding buffers, but
+	 * we should be sure we don't retain any references to
+	 * any of them.
+	 */
+	ret = videobuf_streamoff(&cam->vb_queue);
+	INIT_LIST_HEAD(&cam->buffer_queue);
+out:
+	mutex_unlock(&cam->lock);
+	return ret;
+}
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+static int viacam_vidiocgmbuf(struct file *filp, void *priv,
+		struct video_mbuf *mbuf)
+{
+	struct via_camera *cam = priv;
+
+	return videobuf_cgmbuf(&cam->vb_queue, mbuf, 6);
+}
+#endif
+
+/* G/S_PARM */
+
+static int viacam_g_parm(struct file *filp, void *priv,
+		struct v4l2_streamparm *parm)
+{
+	struct via_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->lock);
+	ret = sensor_call(cam, video, g_parm, parm);
+	mutex_unlock(&cam->lock);
+	parm->parm.capture.readbuffers = cam->n_cap_bufs;
+	return ret;
+}
+
+static int viacam_s_parm(struct file *filp, void *priv,
+		struct v4l2_streamparm *parm)
+{
+	struct via_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->lock);
+	ret = sensor_call(cam, video, s_parm, parm);
+	mutex_unlock(&cam->lock);
+	parm->parm.capture.readbuffers = cam->n_cap_bufs;
+	return ret;
+}
+
+static int viacam_enum_framesizes(struct file *filp, void *priv,
+		struct v4l2_frmsizeenum *sizes)
+{
+	if (sizes->index != 0)
+		return -EINVAL;
+	sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+	sizes->stepwise.min_width = QCIF_WIDTH;
+	sizes->stepwise.min_height = QCIF_HEIGHT;
+	sizes->stepwise.max_width = VGA_WIDTH;
+	sizes->stepwise.max_height = VGA_HEIGHT;
+	sizes->stepwise.step_width = sizes->stepwise.step_height = 1;
+	return 0;
+}
+
+static int viacam_enum_frameintervals(struct file *filp, void *priv,
+		struct v4l2_frmivalenum *interval)
+{
+	struct via_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->lock);
+	ret = sensor_call(cam, video, enum_frameintervals, interval);
+	mutex_unlock(&cam->lock);
+	return ret;
+}
+
+
+
+static const struct v4l2_ioctl_ops viacam_ioctl_ops = {
+	.vidioc_g_chip_ident	= viacam_g_chip_ident,
+	.vidioc_queryctrl	= viacam_queryctrl,
+	.vidioc_g_ctrl		= viacam_g_ctrl,
+	.vidioc_s_ctrl		= viacam_s_ctrl,
+	.vidioc_enum_input	= viacam_enum_input,
+	.vidioc_g_input		= viacam_g_input,
+	.vidioc_s_input		= viacam_s_input,
+	.vidioc_s_std		= viacam_s_std,
+	.vidioc_enum_fmt_vid_cap = viacam_enum_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap = viacam_try_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap	= viacam_g_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap	= viacam_s_fmt_vid_cap,
+	.vidioc_querycap	= viacam_querycap,
+	.vidioc_reqbufs		= viacam_reqbufs,
+	.vidioc_querybuf	= viacam_querybuf,
+	.vidioc_qbuf		= viacam_qbuf,
+	.vidioc_dqbuf		= viacam_dqbuf,
+	.vidioc_streamon	= viacam_streamon,
+	.vidioc_streamoff	= viacam_streamoff,
+	.vidioc_g_parm		= viacam_g_parm,
+	.vidioc_s_parm		= viacam_s_parm,
+	.vidioc_enum_framesizes = viacam_enum_framesizes,
+	.vidioc_enum_frameintervals = viacam_enum_frameintervals,
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+	.vidiocgmbuf		= viacam_vidiocgmbuf,
+#endif
+};
+
+/*----------------------------------------------------------------------------*/
+
+/*
+ * Power management.
+ */
+
+/*
+ * Setup stuff.
+ */
+
+static struct video_device viacam_v4l_template = {
+	.name		= "via-camera",
+	.minor		= -1,
+	.tvnorms	= V4L2_STD_NTSC_M,
+	.current_norm	= V4L2_STD_NTSC_M,
+	.fops		= &viacam_fops,
+	.ioctl_ops	= &viacam_ioctl_ops,
+	.release	= video_device_release_empty, /* Check this */
+};
+
+
+static __devinit int viacam_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct i2c_adapter *sensor_adapter;
+	struct viafb_dev *viadev = pdev->dev.platform_data;
+
+	/*
+	 * Note that there are actually two capture channels on
+	 * the device.	We only deal with one for now.	That
+	 * is encoded here; nothing else assumes it's dealing with
+	 * a unique capture device.
+	 */
+	struct via_camera *cam;
+
+	/*
+	 * Ensure that frame buffer memory has been set aside for
+	 * this purpose.  As an arbitrary limit, refuse to work
+	 * with less than two frames of VGA 16-bit data.
+	 *
+	 * If we ever support the second port, we'll need to set
+	 * aside more memory.
+	 */
+	if (viadev->camera_fbmem_size < (VGA_HEIGHT*VGA_WIDTH*4)) {
+		printk(KERN_ERR "viacam: insufficient FB memory reserved\n");
+		return -ENOMEM;
+	}
+	if (viadev->engine_mmio == NULL) {
+		printk(KERN_ERR "viacam: No I/O memory, so no pictures\n");
+		return -ENOMEM;
+	}
+	/*
+	 * Basic structure initialization.
+	 */
+	cam = kzalloc (sizeof(struct via_camera), GFP_KERNEL);
+	if (cam == NULL)
+		return -ENOMEM;
+	via_cam_info = cam;
+	cam->platdev = pdev;
+	cam->viadev = viadev;
+	cam->users = 0;
+	cam->owner = NULL;
+	cam->opstate = S_IDLE;
+	cam->user_format = cam->sensor_format = viacam_def_pix_format;
+	mutex_init(&cam->lock);
+	INIT_LIST_HEAD(&cam->buffer_queue);
+	cam->mmio = viadev->engine_mmio;
+	cam->fbmem = viadev->fbmem;
+	cam->fb_offset = viadev->camera_fbmem_offset;
+	cam->flags = 1 << CF_CONFIG_NEEDED;
+	cam->mbus_code = via_def_mbus_code;
+	/*
+	 * Tell V4L that we exist.
+	 */
+	ret = v4l2_device_register(&pdev->dev, &cam->v4l2_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to register v4l2 device\n");
+		return ret;
+	}
+	/*
+	 * Convince the system that we can do DMA.
+	 */
+	pdev->dev.dma_mask = &viadev->pdev->dma_mask;
+	dma_set_mask(&pdev->dev, 0xffffffff);
+	/*
+	 * Fire up the capture port.  The write to 0x78 looks purely
+	 * OLPCish; any system will need to tweak 0x1e.
+	 */
+	via_write_reg_mask(VIASR, 0x78, 0, 0x80);
+	via_write_reg_mask(VIASR, 0x1e, 0xc0, 0xc0);
+	/*
+	 * Get the sensor powered up.
+	 */
+	ret = via_sensor_power_setup(cam);
+	if (ret)
+		goto out_unregister;
+	via_sensor_power_up(cam);
+
+	/*
+	 * See if we can't find it on the bus.	The VIA_PORT_31 assumption
+	 * is OLPC-specific.  0x42 assumption is ov7670-specific.
+	 */
+	sensor_adapter = viafb_find_i2c_adapter(VIA_PORT_31);
+	cam->sensor = v4l2_i2c_new_subdev(&cam->v4l2_dev, sensor_adapter,
+			"ov7670", "ov7670", 0x42 >> 1, NULL);
+	if (cam->sensor == NULL) {
+		dev_err(&pdev->dev, "Unable to find the sensor!\n");
+		ret = -ENODEV;
+		goto out_power_down;
+	}
+	/*
+	 * Get the IRQ.
+	 */
+	viacam_int_disable(cam);
+	ret = request_threaded_irq(viadev->pdev->irq, viacam_quick_irq,
+			viacam_irq, IRQF_SHARED, "via-camera", cam);
+	if (ret)
+		goto out_power_down;
+	/*
+	 * Tell V4l2 that we exist.
+	 */
+	cam->vdev = viacam_v4l_template;
+	cam->vdev.v4l2_dev = &cam->v4l2_dev;
+	ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
+	if (ret)
+		goto out_irq;
+	video_set_drvdata(&cam->vdev, cam);
+
+	/* Power the sensor down until somebody opens the device */
+	via_sensor_power_down(cam);
+	return 0;
+
+out_irq:
+	free_irq(viadev->pdev->irq, cam);
+out_power_down:
+	via_sensor_power_release(cam);
+out_unregister:
+	v4l2_device_unregister(&cam->v4l2_dev);
+	return ret;
+}
+
+static __devexit int viacam_remove(struct platform_device *pdev)
+{
+	struct via_camera *cam = via_cam_info;
+	struct viafb_dev *viadev = pdev->dev.platform_data;
+
+	video_unregister_device(&cam->vdev);
+	v4l2_device_unregister(&cam->v4l2_dev);
+	free_irq(viadev->pdev->irq, cam);
+	via_sensor_power_release(cam);
+	via_cam_info = NULL;
+	return 0;
+}
+
+
+static struct platform_driver viacam_driver = {
+	.driver = {
+		.name = "viafb-camera",
+	},
+	.probe = viacam_probe,
+	.remove = viacam_remove,
+};
+
+
+#ifdef CONFIG_OLPC_XO_1_5
+/*
+ * The OLPC folks put the serial port on the same pin as
+ * the camera.	They also get grumpy if we break the
+ * serial port and keep them from using it.  So we have
+ * to check the serial enable bit and not step on it.
+ */
+#define VIACAM_SERIAL_DEVFN 0x88
+#define VIACAM_SERIAL_CREG 0x46
+#define VIACAM_SERIAL_BIT 0x40
+
+static __devinit int viacam_check_serial_port(void)
+{
+	struct pci_bus *pbus = pci_find_bus(0, 0);
+	u8 cbyte;
+
+	pci_bus_read_config_byte(pbus, VIACAM_SERIAL_DEVFN,
+			VIACAM_SERIAL_CREG, &cbyte);
+	if ((cbyte & VIACAM_SERIAL_BIT) == 0)
+		return 0; /* Not enabled */
+	if (override_serial == 0) {
+		printk(KERN_NOTICE "Via camera: serial port is enabled, " \
+				"refusing to load.\n");
+		printk(KERN_NOTICE "Specify override_serial=1 to force " \
+				"module loading.\n");
+		return -EBUSY;
+	}
+	printk(KERN_NOTICE "Via camera: overriding serial port\n");
+	pci_bus_write_config_byte(pbus, VIACAM_SERIAL_DEVFN,
+			VIACAM_SERIAL_CREG, cbyte & ~VIACAM_SERIAL_BIT);
+	return 0;
+}
+#endif
+
+
+
+
+static int viacam_init(void)
+{
+#ifdef CONFIG_OLPC_XO_1_5
+	if (viacam_check_serial_port())
+		return -EBUSY;
+#endif
+	return platform_driver_register(&viacam_driver);
+}
+module_init(viacam_init);
+
+static void viacam_exit(void)
+{
+	platform_driver_unregister(&viacam_driver);
+}
+module_exit(viacam_exit);
diff --git a/drivers/media/video/via-camera.h b/drivers/media/video/via-camera.h
new file mode 100644
index 0000000..b12a4b3
--- /dev/null
+++ b/drivers/media/video/via-camera.h
@@ -0,0 +1,93 @@
+/*
+ * VIA Camera register definitions.
+ */
+#define VCR_INTCTRL	0x300	/* Capture interrupt control */
+#define   VCR_IC_EAV	  0x0001   /* End of active video status */
+#define	  VCR_IC_EVBI	  0x0002   /* End of VBI status */
+#define   VCR_IC_FBOTFLD  0x0004   /* "flipping" Bottom field is active */
+#define   VCR_IC_ACTBUF	  0x0018   /* Active video buffer  */
+#define   VCR_IC_VSYNC	  0x0020   /* 0 = VB, 1 = active video */
+#define   VCR_IC_BOTFLD	  0x0040   /* Bottom field is active */
+#define   VCR_IC_FFULL	  0x0080   /* FIFO full */
+#define   VCR_IC_INTEN	  0x0100   /* End of active video int. enable */
+#define   VCR_IC_VBIINT	  0x0200   /* End of VBI int enable */
+#define   VCR_IC_VBIBUF	  0x0400   /* Current VBI buffer */
+
+#define VCR_TSC		0x308	/* Transport stream control */
+#define VCR_TSC_ENABLE    0x000001   /* Transport stream input enable */
+#define VCR_TSC_DROPERR   0x000002   /* Drop error packets */
+#define VCR_TSC_METHOD    0x00000c   /* DMA method (non-functional) */
+#define VCR_TSC_COUNT     0x07fff0   /* KByte or packet count */
+#define VCR_TSC_CBMODE	  0x080000   /* Change buffer by byte count */
+#define VCR_TSC_PSSIG	  0x100000   /* Packet starting signal disable */
+#define VCR_TSC_BE	  0x200000   /* MSB first (serial mode) */
+#define VCR_TSC_SERIAL	  0x400000   /* Serial input (0 = parallel) */
+
+#define VCR_CAPINTC	0x310	/* Capture interface control */
+#define   VCR_CI_ENABLE   0x00000001  /* Capture enable */
+#define   VCR_CI_BSS	  0x00000002  /* WTF "bit stream selection" */
+#define   VCR_CI_3BUFS	  0x00000004  /* 1 = 3 buffers, 0 = 2 buffers */
+#define   VCR_CI_VIPEN	  0x00000008  /* VIP enable */
+#define   VCR_CI_CCIR601_8  0	        /* CCIR601 input stream, 8 bit */
+#define   VCR_CI_CCIR656_8  0x00000010  /* ... CCIR656, 8 bit */
+#define   VCR_CI_CCIR601_16 0x00000020  /* ... CCIR601, 16 bit */
+#define   VCR_CI_CCIR656_16 0x00000030  /* ... CCIR656, 16 bit */
+#define   VCR_CI_HDMODE   0x00000040  /* CCIR656-16 hdr decode mode; 1=16b */
+#define   VCR_CI_BSWAP    0x00000080  /* Swap bytes (16-bit) */
+#define   VCR_CI_YUYV	  0	      /* Byte order 0123 */
+#define   VCR_CI_UYVY	  0x00000100  /* Byte order 1032 */
+#define   VCR_CI_YVYU	  0x00000200  /* Byte order 0321 */
+#define   VCR_CI_VYUY	  0x00000300  /* Byte order 3012 */
+#define   VCR_CI_VIPTYPE  0x00000400  /* VIP type */
+#define   VCR_CI_IFSEN    0x00000800  /* Input field signal enable */
+#define   VCR_CI_DIODD	  0	      /* De-interlace odd, 30fps */
+#define   VCR_CI_DIEVEN   0x00001000  /*    ...even field, 30fps */
+#define   VCR_CI_DIBOTH   0x00002000  /*    ...both fields, 60fps */
+#define   VCR_CI_DIBOTH30 0x00003000  /*    ...both fields, 30fps interlace */
+#define   VCR_CI_CONVTYPE 0x00004000  /* 4:2:2 to 4:4:4; 1 = interpolate */
+#define   VCR_CI_CFC	  0x00008000  /* Capture flipping control */
+#define   VCR_CI_FILTER   0x00070000  /* Horiz filter mode select
+					 000 = none
+					 001 = 2 tap
+					 010 = 3 tap
+					 011 = 4 tap
+					 100 = 5 tap */
+#define   VCR_CI_CLKINV   0x00080000  /* Input CLK inverted */
+#define   VCR_CI_VREFINV  0x00100000  /* VREF inverted */
+#define   VCR_CI_HREFINV  0x00200000  /* HREF inverted */
+#define   VCR_CI_FLDINV   0x00400000  /* Field inverted */
+#define   VCR_CI_CLKPIN	  0x00800000  /* Capture clock pin */
+#define   VCR_CI_THRESH   0x0f000000  /* Capture fifo threshold */
+#define   VCR_CI_HRLE     0x10000000  /* Positive edge of HREF */
+#define   VCR_CI_VRLE     0x20000000  /* Positive edge of VREF */
+#define   VCR_CI_OFLDINV  0x40000000  /* Field output inverted */
+#define   VCR_CI_CLKEN    0x80000000  /* Capture clock enable */
+
+#define VCR_HORRANGE	0x314	/* Active video horizontal range */
+#define VCR_VERTRANGE	0x318	/* Active video vertical range */
+#define VCR_AVSCALE	0x31c	/* Active video scaling control */
+#define   VCR_AVS_HEN	  0x00000800   /* Horizontal scale enable */
+#define   VCR_AVS_VEN	  0x04000000   /* Vertical enable */
+#define VCR_VBIHOR	0x320	/* VBI Data horizontal range */
+#define VCR_VBIVERT	0x324	/* VBI data vertical range */
+#define VCR_VBIBUF1	0x328	/* First VBI buffer */
+#define VCR_VBISTRIDE	0x32c	/* VBI stride */
+#define VCR_ANCDATACNT	0x330	/* Ancillary data count setting */
+#define VCR_MAXDATA	0x334	/* Active data count of active video */
+#define VCR_MAXVBI	0x338	/* Maximum data count of VBI */
+#define VCR_CAPDATA	0x33c	/* Capture data count */
+#define VCR_VBUF1	0x340	/* First video buffer */
+#define VCR_VBUF2	0x344	/* Second video buffer */
+#define VCR_VBUF3	0x348	/* Third video buffer */
+#define VCR_VBUF_MASK	0x1ffffff0	/* Bits 28:4 */
+#define VCR_VBIBUF2	0x34c	/* Second VBI buffer */
+#define VCR_VSTRIDE	0x350	/* Stride of video + coring control */
+#define   VCR_VS_STRIDE_SHIFT 4
+#define   VCR_VS_STRIDE   0x00001ff0  /* Stride (8-byte units) */
+#define   VCR_VS_CCD	  0x007f0000  /* Coring compare data */
+#define   VCR_VS_COREEN   0x00800000  /* Coring enable */
+#define VCR_TS0ERR	0x354	/* TS buffer 0 error indicator */
+#define VCR_TS1ERR	0x358	/* TS buffer 0 error indicator */
+#define VCR_TS2ERR	0x35c	/* TS buffer 0 error indicator */
+
+/* Add 0x1000 for the second capture engine registers */
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c
index ce1595b..8979f91 100644
--- a/drivers/media/video/videobuf-core.c
+++ b/drivers/media/video/videobuf-core.c
@@ -73,25 +73,46 @@
 }
 EXPORT_SYMBOL_GPL(videobuf_alloc_vb);
 
-#define WAITON_CONDITION (vb->state != VIDEOBUF_ACTIVE &&\
-				vb->state != VIDEOBUF_QUEUED)
-int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr)
+static int is_state_active_or_queued(struct videobuf_queue *q, struct videobuf_buffer *vb)
 {
+	unsigned long flags;
+	bool rc;
+
+	spin_lock_irqsave(q->irqlock, flags);
+	rc = vb->state != VIDEOBUF_ACTIVE && vb->state != VIDEOBUF_QUEUED;
+	spin_unlock_irqrestore(q->irqlock, flags);
+	return rc;
+};
+
+int videobuf_waiton(struct videobuf_queue *q, struct videobuf_buffer *vb,
+		int non_blocking, int intr)
+{
+	bool is_ext_locked;
+	int ret = 0;
+
 	MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
 
 	if (non_blocking) {
-		if (WAITON_CONDITION)
+		if (is_state_active_or_queued(q, vb))
 			return 0;
-		else
-			return -EAGAIN;
+		return -EAGAIN;
 	}
 
-	if (intr)
-		return wait_event_interruptible(vb->done, WAITON_CONDITION);
-	else
-		wait_event(vb->done, WAITON_CONDITION);
+	is_ext_locked = q->ext_lock && mutex_is_locked(q->ext_lock);
 
-	return 0;
+	/* Release vdev lock to prevent this wait from blocking outside access to
+	   the device. */
+	if (is_ext_locked)
+		mutex_unlock(q->ext_lock);
+	if (intr)
+		ret = wait_event_interruptible(vb->done, is_state_active_or_queued(q, vb));
+	else
+		wait_event(vb->done, is_state_active_or_queued(q, vb));
+	/* Relock */
+	if (is_ext_locked)
+		mutex_lock(q->ext_lock);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(videobuf_waiton);
 
@@ -125,11 +146,13 @@
 			 enum v4l2_field field,
 			 unsigned int msize,
 			 void *priv,
-			 struct videobuf_qtype_ops *int_ops)
+			 struct videobuf_qtype_ops *int_ops,
+			 struct mutex *ext_lock)
 {
 	BUG_ON(!q);
 	memset(q, 0, sizeof(*q));
 	q->irqlock   = irqlock;
+	q->ext_lock  = ext_lock;
 	q->dev       = dev;
 	q->type      = type;
 	q->field     = field;
@@ -350,9 +373,9 @@
 int videobuf_mmap_free(struct videobuf_queue *q)
 {
 	int ret;
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 	ret = __videobuf_free(q);
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(videobuf_mmap_free);
@@ -407,9 +430,9 @@
 			enum v4l2_memory memory)
 {
 	int ret;
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 	ret = __videobuf_mmap_setup(q, bcount, bsize, memory);
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(videobuf_mmap_setup);
@@ -432,7 +455,7 @@
 		return -EINVAL;
 	}
 
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 	if (req->type != q->type) {
 		dprintk(1, "reqbufs: queue type invalid\n");
 		retval = -EINVAL;
@@ -469,7 +492,7 @@
 	retval = 0;
 
  done:
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 	return retval;
 }
 EXPORT_SYMBOL_GPL(videobuf_reqbufs);
@@ -478,7 +501,7 @@
 {
 	int ret = -EINVAL;
 
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 	if (unlikely(b->type != q->type)) {
 		dprintk(1, "querybuf: Wrong type.\n");
 		goto done;
@@ -496,7 +519,7 @@
 
 	ret = 0;
 done:
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(videobuf_querybuf);
@@ -513,7 +536,7 @@
 	if (b->memory == V4L2_MEMORY_MMAP)
 		down_read(&current->mm->mmap_sem);
 
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 	retval = -EBUSY;
 	if (q->reading) {
 		dprintk(1, "qbuf: Reading running...\n");
@@ -605,7 +628,7 @@
 	wake_up_interruptible_sync(&q->wait);
 
 done:
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 
 	if (b->memory == V4L2_MEMORY_MMAP)
 		up_read(&current->mm->mmap_sem);
@@ -635,14 +658,14 @@
 			dprintk(2, "next_buffer: waiting on buffer\n");
 
 			/* Drop lock to avoid deadlock with qbuf */
-			mutex_unlock(&q->vb_lock);
+			videobuf_queue_unlock(q);
 
 			/* Checking list_empty and streaming is safe without
 			 * locks because we goto checks to validate while
 			 * holding locks before proceeding */
 			retval = wait_event_interruptible(q->wait,
 				!list_empty(&q->stream) || !q->streaming);
-			mutex_lock(&q->vb_lock);
+			videobuf_queue_lock(q);
 
 			if (retval)
 				goto done;
@@ -669,7 +692,7 @@
 		goto done;
 
 	buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
-	retval = videobuf_waiton(buf, nonblocking, 1);
+	retval = videobuf_waiton(q, buf, nonblocking, 1);
 	if (retval < 0)
 		goto done;
 
@@ -687,7 +710,7 @@
 	MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
 
 	memset(b, 0, sizeof(*b));
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 
 	retval = stream_next_buffer(q, &buf, nonblocking);
 	if (retval < 0) {
@@ -713,7 +736,7 @@
 	buf->state = VIDEOBUF_IDLE;
 	b->flags &= ~V4L2_BUF_FLAG_DONE;
 done:
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 	return retval;
 }
 EXPORT_SYMBOL_GPL(videobuf_dqbuf);
@@ -724,7 +747,7 @@
 	unsigned long flags = 0;
 	int retval;
 
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 	retval = -EBUSY;
 	if (q->reading)
 		goto done;
@@ -740,7 +763,7 @@
 
 	wake_up_interruptible_sync(&q->wait);
 done:
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 	return retval;
 }
 EXPORT_SYMBOL_GPL(videobuf_streamon);
@@ -760,9 +783,9 @@
 {
 	int retval;
 
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 	retval = __videobuf_streamoff(q);
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 
 	return retval;
 }
@@ -797,7 +820,7 @@
 	spin_lock_irqsave(q->irqlock, flags);
 	q->ops->buf_queue(q, q->read_buf);
 	spin_unlock_irqrestore(q->irqlock, flags);
-	retval = videobuf_waiton(q->read_buf, 0, 0);
+	retval = videobuf_waiton(q, q->read_buf, 0, 0);
 	if (0 == retval) {
 		CALL(q, sync, q, q->read_buf);
 		if (VIDEOBUF_ERROR == q->read_buf->state)
@@ -868,7 +891,7 @@
 
 	MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
 
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 
 	q->ops->buf_setup(q, &nbufs, &size);
 
@@ -909,7 +932,7 @@
 	}
 
 	/* wait until capture is done */
-	retval = videobuf_waiton(q->read_buf, nonblocking, 1);
+	retval = videobuf_waiton(q, q->read_buf, nonblocking, 1);
 	if (0 != retval)
 		goto done;
 
@@ -938,7 +961,7 @@
 	}
 
 done:
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 	return retval;
 }
 EXPORT_SYMBOL_GPL(videobuf_read_one);
@@ -999,9 +1022,9 @@
 {
 	int rc;
 
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 	rc = __videobuf_read_start(q);
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 
 	return rc;
 }
@@ -1009,15 +1032,15 @@
 
 void videobuf_read_stop(struct videobuf_queue *q)
 {
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 	__videobuf_read_stop(q);
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 }
 EXPORT_SYMBOL_GPL(videobuf_read_stop);
 
 void videobuf_stop(struct videobuf_queue *q)
 {
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 
 	if (q->streaming)
 		__videobuf_streamoff(q);
@@ -1025,7 +1048,7 @@
 	if (q->reading)
 		__videobuf_read_stop(q);
 
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 }
 EXPORT_SYMBOL_GPL(videobuf_stop);
 
@@ -1039,7 +1062,7 @@
 	MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
 
 	dprintk(2, "%s\n", __func__);
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 	retval = -EBUSY;
 	if (q->streaming)
 		goto done;
@@ -1059,7 +1082,7 @@
 			list_del(&q->read_buf->stream);
 			q->read_off = 0;
 		}
-		rc = videobuf_waiton(q->read_buf, nonblocking, 1);
+		rc = videobuf_waiton(q, q->read_buf, nonblocking, 1);
 		if (rc < 0) {
 			if (0 == retval)
 				retval = rc;
@@ -1097,7 +1120,7 @@
 	}
 
 done:
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 	return retval;
 }
 EXPORT_SYMBOL_GPL(videobuf_read_stream);
@@ -1109,7 +1132,7 @@
 	struct videobuf_buffer *buf = NULL;
 	unsigned int rc = 0;
 
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 	if (q->streaming) {
 		if (!list_empty(&q->stream))
 			buf = list_entry(q->stream.next,
@@ -1147,7 +1170,7 @@
 			}
 		}
 	}
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 	return rc;
 }
 EXPORT_SYMBOL_GPL(videobuf_poll_stream);
@@ -1164,7 +1187,7 @@
 		return -EINVAL;
 	}
 
-	mutex_lock(&q->vb_lock);
+	videobuf_queue_lock(q);
 	for (i = 0; i < VIDEO_MAX_FRAME; i++) {
 		struct videobuf_buffer *buf = q->bufs[i];
 
@@ -1174,7 +1197,7 @@
 			break;
 		}
 	}
-	mutex_unlock(&q->vb_lock);
+	videobuf_queue_unlock(q);
 
 	return rc;
 }
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
index 6ff9e4b..c969111 100644
--- a/drivers/media/video/videobuf-dma-contig.c
+++ b/drivers/media/video/videobuf-dma-contig.c
@@ -28,7 +28,6 @@
 	void *vaddr;
 	dma_addr_t dma_handle;
 	unsigned long size;
-	int is_userptr;
 };
 
 #define MAGIC_DC_MEM 0x0733ac61
@@ -63,7 +62,7 @@
 		struct videobuf_dma_contig_memory *mem;
 
 		dev_dbg(q->dev, "munmap %p q=%p\n", map, q);
-		mutex_lock(&q->vb_lock);
+		videobuf_queue_lock(q);
 
 		/* We need first to cancel streams, before unmapping */
 		if (q->streaming)
@@ -103,7 +102,7 @@
 
 		kfree(map);
 
-		mutex_unlock(&q->vb_lock);
+		videobuf_queue_unlock(q);
 	}
 }
 
@@ -120,7 +119,6 @@
  */
 static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem)
 {
-	mem->is_userptr = 0;
 	mem->dma_handle = 0;
 	mem->size = 0;
 }
@@ -147,7 +145,6 @@
 
 	offset = vb->baddr & ~PAGE_MASK;
 	mem->size = PAGE_ALIGN(vb->size + offset);
-	mem->is_userptr = 0;
 	ret = -EINVAL;
 
 	down_read(&mm->mmap_sem);
@@ -181,9 +178,6 @@
 		pages_done++;
 	}
 
-	if (!ret)
-		mem->is_userptr = 1;
-
  out_up:
 	up_read(&current->mm->mmap_sem);
 
@@ -349,10 +343,11 @@
 				    enum v4l2_buf_type type,
 				    enum v4l2_field field,
 				    unsigned int msize,
-				    void *priv)
+				    void *priv,
+				    struct mutex *ext_lock)
 {
 	videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
-				 priv, &qops);
+				 priv, &qops, ext_lock);
 }
 EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init);
 
diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c
index 2ad0bc2..20f227e 100644
--- a/drivers/media/video/videobuf-dma-sg.c
+++ b/drivers/media/video/videobuf-dma-sg.c
@@ -116,8 +116,8 @@
 			goto nopage;
 		if (PageHighMem(pages[i]))
 			goto highmem;
-		sg_set_page(&sglist[i], pages[i], min(PAGE_SIZE, size), 0);
-		size -= min(PAGE_SIZE, size);
+		sg_set_page(&sglist[i], pages[i], min_t(size_t, PAGE_SIZE, size), 0);
+		size -= min_t(size_t, PAGE_SIZE, size);
 	}
 	return sglist;
 
@@ -358,7 +358,7 @@
 	map->count--;
 	if (0 == map->count) {
 		dprintk(1, "munmap %p q=%p\n", map, q);
-		mutex_lock(&q->vb_lock);
+		videobuf_queue_lock(q);
 		for (i = 0; i < VIDEO_MAX_FRAME; i++) {
 			if (NULL == q->bufs[i])
 				continue;
@@ -374,7 +374,7 @@
 			q->bufs[i]->baddr = 0;
 			q->ops->buf_release(q, q->bufs[i]);
 		}
-		mutex_unlock(&q->vb_lock);
+		videobuf_queue_unlock(q);
 		kfree(map);
 	}
 	return;
@@ -654,10 +654,11 @@
 			 enum v4l2_buf_type type,
 			 enum v4l2_field field,
 			 unsigned int msize,
-			 void *priv)
+			 void *priv,
+			 struct mutex *ext_lock)
 {
 	videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
-				 priv, &sg_ops);
+				 priv, &sg_ops, ext_lock);
 }
 EXPORT_SYMBOL_GPL(videobuf_queue_sg_init);
 
diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c
index 3f76398..3de7c7e 100644
--- a/drivers/media/video/videobuf-dvb.c
+++ b/drivers/media/video/videobuf-dvb.c
@@ -57,7 +57,7 @@
 		buf = list_entry(dvb->dvbq.stream.next,
 				 struct videobuf_buffer, stream);
 		list_del(&buf->stream);
-		err = videobuf_waiton(buf,0,1);
+		err = videobuf_waiton(&dvb->dvbq, buf, 0, 1);
 
 		/* no more feeds left or stop_feed() asked us to quit */
 		if (0 == dvb->nfeeds)
diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c
index e7fe31d..df14258 100644
--- a/drivers/media/video/videobuf-vmalloc.c
+++ b/drivers/media/video/videobuf-vmalloc.c
@@ -75,7 +75,7 @@
 		struct videobuf_vmalloc_memory *mem;
 
 		dprintk(1, "munmap %p q=%p\n", map, q);
-		mutex_lock(&q->vb_lock);
+		videobuf_queue_lock(q);
 
 		/* We need first to cancel streams, before unmapping */
 		if (q->streaming)
@@ -114,7 +114,7 @@
 
 		kfree(map);
 
-		mutex_unlock(&q->vb_lock);
+		videobuf_queue_unlock(q);
 	}
 
 	return;
@@ -304,10 +304,11 @@
 			 enum v4l2_buf_type type,
 			 enum v4l2_field field,
 			 unsigned int msize,
-			 void *priv)
+			 void *priv,
+			 struct mutex *ext_lock)
 {
 	videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
-				 priv, &qops);
+				 priv, &qops, ext_lock);
 }
 EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init);
 
diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c
index 3eb15f7..e5e005d 100644
--- a/drivers/media/video/vino.c
+++ b/drivers/media/video/vino.c
@@ -4334,10 +4334,10 @@
 
 	vino_drvdata->decoder =
 		v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter,
-			       "saa7191", "saa7191", 0, I2C_ADDRS(0x45));
+			       NULL, "saa7191", 0, I2C_ADDRS(0x45));
 	vino_drvdata->camera =
 		v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter,
-			       "indycam", "indycam", 0, I2C_ADDRS(0x2b));
+			       NULL, "indycam", 0, I2C_ADDRS(0x2b));
 
 	dprintk("init complete!\n");
 
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index e17b6fe..9797e5a 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -820,14 +820,11 @@
 					struct v4l2_format *f)
 {
 	struct vivi_dev *dev = video_drvdata(file);
-	struct videobuf_queue *q = &dev->vb_vidq;
 
 	int ret = vidioc_try_fmt_vid_cap(file, priv, f);
 	if (ret < 0)
 		return ret;
 
-	mutex_lock(&q->vb_lock);
-
 	if (vivi_is_generating(dev)) {
 		dprintk(dev, 1, "%s device busy\n", __func__);
 		ret = -EBUSY;
@@ -840,7 +837,6 @@
 	dev->vb_vidq.field = f->fmt.pix.field;
 	ret = 0;
 out:
-	mutex_unlock(&q->vb_lock);
 	return ret;
 }
 
@@ -1086,7 +1082,7 @@
 	.release        = vivi_close,
 	.read           = vivi_read,
 	.poll		= vivi_poll,
-	.ioctl          = video_ioctl2, /* V4L2 ioctl handler */
+	.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
 	.mmap           = vivi_mmap,
 };
 
@@ -1173,19 +1169,19 @@
 	dev->saturation = 127;
 	dev->hue = 0;
 
+	/* initialize locks */
+	spin_lock_init(&dev->slock);
+	mutex_init(&dev->mutex);
+
 	videobuf_queue_vmalloc_init(&dev->vb_vidq, &vivi_video_qops,
 			NULL, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
 			V4L2_FIELD_INTERLACED,
-			sizeof(struct vivi_buffer), dev);
+			sizeof(struct vivi_buffer), dev, &dev->mutex);
 
 	/* init video dma queues */
 	INIT_LIST_HEAD(&dev->vidq.active);
 	init_waitqueue_head(&dev->vidq.wq);
 
-	/* initialize locks */
-	spin_lock_init(&dev->slock);
-	mutex_init(&dev->mutex);
-
 	ret = -ENOMEM;
 	vfd = video_device_alloc();
 	if (!vfd)
@@ -1194,6 +1190,7 @@
 	*vfd = vivi_template;
 	vfd->debug = debug;
 	vfd->v4l2_dev = &dev->v4l2_dev;
+	vfd->lock = &dev->mutex;
 
 	ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
 	if (ret < 0)
diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c
index ca8303b..c15efb6 100644
--- a/drivers/media/video/vp27smpx.c
+++ b/drivers/media/video/vp27smpx.c
@@ -27,11 +27,9 @@
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("vp27smpx driver");
 MODULE_AUTHOR("Hans Verkuil");
@@ -200,9 +198,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, vp27smpx_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "vp27smpx",
-	.probe = vp27smpx_probe,
-	.remove = vp27smpx_remove,
-	.id_table = vp27smpx_id,
+static struct i2c_driver vp27smpx_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "vp27smpx",
+	},
+	.probe		= vp27smpx_probe,
+	.remove		= vp27smpx_remove,
+	.id_table	= vp27smpx_id,
 };
+
+static __init int init_vp27smpx(void)
+{
+	return i2c_add_driver(&vp27smpx_driver);
+}
+
+static __exit void exit_vp27smpx(void)
+{
+	i2c_del_driver(&vp27smpx_driver);
+}
+
+module_init(init_vp27smpx);
+module_exit(exit_vp27smpx);
diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c
index 77ebcea..91a01b3 100644
--- a/drivers/media/video/vpx3220.c
+++ b/drivers/media/video/vpx3220.c
@@ -28,7 +28,6 @@
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver");
 MODULE_AUTHOR("Laurent Pinchart");
@@ -614,9 +613,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, vpx3220_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "vpx3220",
-	.probe = vpx3220_probe,
-	.remove = vpx3220_remove,
-	.id_table = vpx3220_id,
+static struct i2c_driver vpx3220_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "vpx3220",
+	},
+	.probe		= vpx3220_probe,
+	.remove		= vpx3220_remove,
+	.id_table	= vpx3220_id,
 };
+
+static __init int init_vpx3220(void)
+{
+	return i2c_add_driver(&vpx3220_driver);
+}
+
+static __exit void exit_vpx3220(void)
+{
+	i2c_del_driver(&vpx3220_driver);
+}
+
+module_init(init_vpx3220);
+module_exit(exit_vpx3220);
diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c
index d596554..a22f765 100644
--- a/drivers/media/video/wm8739.c
+++ b/drivers/media/video/wm8739.c
@@ -30,7 +30,6 @@
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/v4l2-i2c-drv.h>
 #include <media/v4l2-ctrls.h>
 
 MODULE_DESCRIPTION("wm8739 driver");
@@ -282,9 +281,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, wm8739_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "wm8739",
-	.probe = wm8739_probe,
-	.remove = wm8739_remove,
-	.id_table = wm8739_id,
+static struct i2c_driver wm8739_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "wm8739",
+	},
+	.probe		= wm8739_probe,
+	.remove		= wm8739_remove,
+	.id_table	= wm8739_id,
 };
+
+static __init int init_wm8739(void)
+{
+	return i2c_add_driver(&wm8739_driver);
+}
+
+static __exit void exit_wm8739(void)
+{
+	i2c_del_driver(&wm8739_driver);
+}
+
+module_init(init_wm8739);
+module_exit(exit_wm8739);
diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c
index 23bad3f..1355256 100644
--- a/drivers/media/video/wm8775.c
+++ b/drivers/media/video/wm8775.c
@@ -31,12 +31,11 @@
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-ctrls.h>
-#include <media/v4l2-i2c-drv.h>
+#include <media/wm8775.h>
 
 MODULE_DESCRIPTION("wm8775 driver");
 MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
@@ -52,10 +51,16 @@
 	TOT_REGS
 };
 
+#define ALC_HOLD 0x85 /* R17: use zero cross detection, ALC hold time 42.6 ms */
+#define ALC_EN 0x100  /* R17: ALC enable */
+
 struct wm8775_state {
 	struct v4l2_subdev sd;
 	struct v4l2_ctrl_handler hdl;
 	struct v4l2_ctrl *mute;
+	struct v4l2_ctrl *vol;
+	struct v4l2_ctrl *bal;
+	struct v4l2_ctrl *loud;
 	u8 input;		/* Last selected input (0-0xf) */
 };
 
@@ -87,6 +92,30 @@
 	return -1;
 }
 
+static void wm8775_set_audio(struct v4l2_subdev *sd, int quietly)
+{
+	struct wm8775_state *state = to_state(sd);
+	u8 vol_l, vol_r;
+	int muted = 0 != state->mute->val;
+	u16 volume = (u16)state->vol->val;
+	u16 balance = (u16)state->bal->val;
+
+	/* normalize ( 65535 to 0 -> 255 to 0 (+24dB to -103dB) ) */
+	vol_l = (min(65536 - balance, 32768) * volume) >> 23;
+	vol_r = (min(balance, (u16)32768) * volume) >> 23;
+
+	/* Mute */
+	if (muted || quietly)
+		wm8775_write(sd, R21, 0x0c0 | state->input);
+
+	wm8775_write(sd, R14, vol_l | 0x100); /* 0x100= Left channel ADC zero cross enable */
+	wm8775_write(sd, R15, vol_r | 0x100); /* 0x100= Right channel ADC zero cross enable */
+
+	/* Un-mute */
+	if (!muted)
+		wm8775_write(sd, R21, state->input);
+}
+
 static int wm8775_s_routing(struct v4l2_subdev *sd,
 			    u32 input, u32 output, u32 config)
 {
@@ -104,25 +133,26 @@
 	state->input = input;
 	if (!v4l2_ctrl_g_ctrl(state->mute))
 		return 0;
-	wm8775_write(sd, R21, 0x0c0);
-	wm8775_write(sd, R14, 0x1d4);
-	wm8775_write(sd, R15, 0x1d4);
-	wm8775_write(sd, R21, 0x100 + state->input);
+	if (!v4l2_ctrl_g_ctrl(state->vol))
+		return 0;
+	if (!v4l2_ctrl_g_ctrl(state->bal))
+		return 0;
+	wm8775_set_audio(sd, 1);
 	return 0;
 }
 
 static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl)
 {
 	struct v4l2_subdev *sd = to_sd(ctrl);
-	struct wm8775_state *state = to_state(sd);
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUDIO_MUTE:
-		wm8775_write(sd, R21, 0x0c0);
-		wm8775_write(sd, R14, 0x1d4);
-		wm8775_write(sd, R15, 0x1d4);
-		if (!ctrl->val)
-			wm8775_write(sd, R21, 0x100 + state->input);
+	case V4L2_CID_AUDIO_VOLUME:
+	case V4L2_CID_AUDIO_BALANCE:
+		wm8775_set_audio(sd, 0);
+		return 0;
+	case V4L2_CID_AUDIO_LOUDNESS:
+		wm8775_write(sd, R17, (ctrl->val ? ALC_EN : 0) | ALC_HOLD);
 		return 0;
 	}
 	return -EINVAL;
@@ -146,16 +176,7 @@
 
 static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
 {
-	struct wm8775_state *state = to_state(sd);
-
-	/* If I remove this, then it can happen that I have no
-	   sound the first time I tune from static to a valid channel.
-	   It's difficult to reproduce and is almost certainly related
-	   to the zero cross detect circuit. */
-	wm8775_write(sd, R21, 0x0c0);
-	wm8775_write(sd, R14, 0x1d4);
-	wm8775_write(sd, R15, 0x1d4);
-	wm8775_write(sd, R21, 0x100 + state->input);
+	wm8775_set_audio(sd, 0);
 	return 0;
 }
 
@@ -205,6 +226,7 @@
 {
 	struct wm8775_state *state;
 	struct v4l2_subdev *sd;
+	int err;
 
 	/* Check if the adapter supports the needed features */
 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -218,15 +240,21 @@
 		return -ENOMEM;
 	sd = &state->sd;
 	v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
+	sd->grp_id = WM8775_GID; /* subdev group id */
 	state->input = 2;
 
-	v4l2_ctrl_handler_init(&state->hdl, 1);
+	v4l2_ctrl_handler_init(&state->hdl, 4);
 	state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
 			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+	state->vol = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
+			V4L2_CID_AUDIO_VOLUME, 0, 65535, (65535+99)/100, 0xCF00); /* 0dB*/
+	state->bal = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
+			V4L2_CID_AUDIO_BALANCE, 0, 65535, (65535+99)/100, 32768);
+	state->loud = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
+			V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 1);
 	sd->ctrl_handler = &state->hdl;
-	if (state->hdl.error) {
-		int err = state->hdl.error;
-
+	err = state->hdl.error;
+	if (err) {
 		v4l2_ctrl_handler_free(&state->hdl);
 		kfree(state);
 		return err;
@@ -238,29 +266,25 @@
 	wm8775_write(sd, R23, 0x000);
 	/* Disable zero cross detect timeout */
 	wm8775_write(sd, R7, 0x000);
-	/* Left justified, 24-bit mode */
-	wm8775_write(sd, R11, 0x021);
+	/* HPF enable, I2S mode, 24-bit */
+	wm8775_write(sd, R11, 0x022);
 	/* Master mode, clock ratio 256fs */
 	wm8775_write(sd, R12, 0x102);
 	/* Powered up */
 	wm8775_write(sd, R13, 0x000);
-	/* ADC gain +2.5dB, enable zero cross */
-	wm8775_write(sd, R14, 0x1d4);
-	/* ADC gain +2.5dB, enable zero cross */
-	wm8775_write(sd, R15, 0x1d4);
-	/* ALC Stereo, ALC target level -1dB FS max gain +8dB */
-	wm8775_write(sd, R16, 0x1bf);
-	/* Enable gain control, use zero cross detection,
-	   ALC hold time 42.6 ms */
-	wm8775_write(sd, R17, 0x185);
+	/* ALC stereo, ALC target level -5dB FS, ALC max gain +8dB */
+	wm8775_write(sd, R16, 0x1bb);
+	/* Set ALC mode and hold time */
+	wm8775_write(sd, R17, (state->loud->val ? ALC_EN : 0) | ALC_HOLD);
 	/* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */
 	wm8775_write(sd, R18, 0x0a2);
 	/* Enable noise gate, threshold -72dBfs */
 	wm8775_write(sd, R19, 0x005);
-	/* Transient window 4ms, lower PGA gain limit -1dB */
-	wm8775_write(sd, R20, 0x07a);
-	/* LRBOTH = 1, use input 2. */
-	wm8775_write(sd, R21, 0x102);
+	/* Transient window 4ms, ALC min gain -5dB  */
+	wm8775_write(sd, R20, 0x0fb);
+
+	wm8775_set_audio(sd, 1);      /* set volume/mute/mux */
+
 	return 0;
 }
 
@@ -281,9 +305,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, wm8775_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "wm8775",
-	.probe = wm8775_probe,
-	.remove = wm8775_remove,
-	.id_table = wm8775_id,
+static struct i2c_driver wm8775_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "wm8775",
+	},
+	.probe		= wm8775_probe,
+	.remove		= wm8775_remove,
+	.id_table	= wm8775_id,
 };
+
+static __init int init_wm8775(void)
+{
+	return i2c_add_driver(&wm8775_driver);
+}
+
+static __exit void exit_wm8775(void)
+{
+	i2c_del_driver(&wm8775_driver);
+}
+
+module_init(init_wm8775);
+module_exit(exit_wm8775);
diff --git a/drivers/media/video/zoran/zoran.h b/drivers/media/video/zoran/zoran.h
index 307e847..37fe1618 100644
--- a/drivers/media/video/zoran/zoran.h
+++ b/drivers/media/video/zoran/zoran.h
@@ -341,10 +341,8 @@
 	enum card_type type;
 	char name[32];
 	const char *i2c_decoder;	/* i2c decoder device */
-	const char *mod_decoder;	/* i2c decoder module */
 	const unsigned short *addrs_decoder;
 	const char *i2c_encoder;	/* i2c encoder device */
-	const char *mod_encoder;	/* i2c encoder module */
 	const unsigned short *addrs_encoder;
 	u16 video_vfe, video_codec;			/* videocodec types */
 	u16 audio_chip;					/* audio type */
diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c
index bfcd3ae..0aac376 100644
--- a/drivers/media/video/zoran/zoran_card.c
+++ b/drivers/media/video/zoran/zoran_card.c
@@ -379,7 +379,6 @@
 		.type = DC10_old,
 		.name = "DC10(old)",
 		.i2c_decoder = "vpx3220a",
-		.mod_decoder = "vpx3220",
 		.addrs_decoder = vpx3220_addrs,
 		.video_codec = CODEC_TYPE_ZR36050,
 		.video_vfe = CODEC_TYPE_ZR36016,
@@ -409,10 +408,8 @@
 		.type = DC10_new,
 		.name = "DC10(new)",
 		.i2c_decoder = "saa7110",
-		.mod_decoder = "saa7110",
 		.addrs_decoder = saa7110_addrs,
 		.i2c_encoder = "adv7175",
-		.mod_encoder = "adv7175",
 		.addrs_encoder = adv717x_addrs,
 		.video_codec = CODEC_TYPE_ZR36060,
 
@@ -440,10 +437,8 @@
 		.type = DC10plus,
 		.name = "DC10plus",
 		.i2c_decoder = "saa7110",
-		.mod_decoder = "saa7110",
 		.addrs_decoder = saa7110_addrs,
 		.i2c_encoder = "adv7175",
-		.mod_encoder = "adv7175",
 		.addrs_encoder = adv717x_addrs,
 		.video_codec = CODEC_TYPE_ZR36060,
 
@@ -472,10 +467,8 @@
 		.type = DC30,
 		.name = "DC30",
 		.i2c_decoder = "vpx3220a",
-		.mod_decoder = "vpx3220",
 		.addrs_decoder = vpx3220_addrs,
 		.i2c_encoder = "adv7175",
-		.mod_encoder = "adv7175",
 		.addrs_encoder = adv717x_addrs,
 		.video_codec = CODEC_TYPE_ZR36050,
 		.video_vfe = CODEC_TYPE_ZR36016,
@@ -505,10 +498,8 @@
 		.type = DC30plus,
 		.name = "DC30plus",
 		.i2c_decoder = "vpx3220a",
-		.mod_decoder = "vpx3220",
 		.addrs_decoder = vpx3220_addrs,
 		.i2c_encoder = "adv7175",
-		.mod_encoder = "adv7175",
 		.addrs_encoder = adv717x_addrs,
 		.video_codec = CODEC_TYPE_ZR36050,
 		.video_vfe = CODEC_TYPE_ZR36016,
@@ -538,10 +529,8 @@
 		.type = LML33,
 		.name = "LML33",
 		.i2c_decoder = "bt819a",
-		.mod_decoder = "bt819",
 		.addrs_decoder = bt819_addrs,
 		.i2c_encoder = "bt856",
-		.mod_encoder = "bt856",
 		.addrs_encoder = bt856_addrs,
 		.video_codec = CODEC_TYPE_ZR36060,
 
@@ -569,10 +558,8 @@
 		.type = LML33R10,
 		.name = "LML33R10",
 		.i2c_decoder = "saa7114",
-		.mod_decoder = "saa7115",
 		.addrs_decoder = saa7114_addrs,
 		.i2c_encoder = "adv7170",
-		.mod_encoder = "adv7170",
 		.addrs_encoder = adv717x_addrs,
 		.video_codec = CODEC_TYPE_ZR36060,
 
@@ -600,10 +587,8 @@
 		.type = BUZ,
 		.name = "Buz",
 		.i2c_decoder = "saa7111",
-		.mod_decoder = "saa7115",
 		.addrs_decoder = saa7111_addrs,
 		.i2c_encoder = "saa7185",
-		.mod_encoder = "saa7185",
 		.addrs_encoder = saa7185_addrs,
 		.video_codec = CODEC_TYPE_ZR36060,
 
@@ -633,10 +618,8 @@
 		/* AverMedia chose not to brand the 6-Eyes. Thus it
 		   can't be autodetected, and requires card=x. */
 		.i2c_decoder = "ks0127",
-		.mod_decoder = "ks0127",
 		.addrs_decoder = ks0127_addrs,
 		.i2c_encoder = "bt866",
-		.mod_encoder = "bt866",
 		.addrs_encoder = bt866_addrs,
 		.video_codec = CODEC_TYPE_ZR36060,
 
@@ -1359,13 +1342,13 @@
 	}
 
 	zr->decoder = v4l2_i2c_new_subdev(&zr->v4l2_dev,
-		&zr->i2c_adapter, zr->card.mod_decoder, zr->card.i2c_decoder,
+		&zr->i2c_adapter, NULL, zr->card.i2c_decoder,
 		0, zr->card.addrs_decoder);
 
-	if (zr->card.mod_encoder)
+	if (zr->card.i2c_encoder)
 		zr->encoder = v4l2_i2c_new_subdev(&zr->v4l2_dev,
 			&zr->i2c_adapter,
-			zr->card.mod_encoder, zr->card.i2c_encoder,
+			NULL, zr->card.i2c_encoder,
 			0, zr->card.addrs_encoder);
 
 	dprintk(2,
diff --git a/drivers/media/video/zoran/zoran_device.c b/drivers/media/video/zoran/zoran_device.c
index 6f846ab..b02007e 100644
--- a/drivers/media/video/zoran/zoran_device.c
+++ b/drivers/media/video/zoran/zoran_device.c
@@ -1470,8 +1470,7 @@
 		    (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
 		     zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)) {
 			if (zr36067_debug > 1 && (!zr->frame_num || zr->JPEG_error)) {
-				char sc[] = "0000";
-				char sv[5];
+				char sv[BUZ_NUM_STAT_COM + 1];
 				int i;
 
 				printk(KERN_INFO
@@ -1481,12 +1480,9 @@
 				       zr->jpg_settings.field_per_buff,
 				       zr->JPEG_missed);
 
-				strcpy(sv, sc);
-				for (i = 0; i < 4; i++) {
-					if (le32_to_cpu(zr->stat_com[i]) & 1)
-						sv[i] = '1';
-				}
-				sv[4] = 0;
+				for (i = 0; i < BUZ_NUM_STAT_COM; i++)
+					sv[i] = le32_to_cpu(zr->stat_com[i]) & 1 ? '1' : '0';
+				sv[BUZ_NUM_STAT_COM] = 0;
 				printk(KERN_INFO
 				       "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n",
 				       ZR_DEVNAME(zr), sv,
diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c
index 3c471a4..401082b 100644
--- a/drivers/media/video/zoran/zoran_driver.c
+++ b/drivers/media/video/zoran/zoran_driver.c
@@ -3322,7 +3322,7 @@
 mmap_unlock_and_return:
 	mutex_unlock(&zr->resource_lock);
 
-	return 0;
+	return res;
 }
 
 static const struct v4l2_ioctl_ops zoran_ioctl_ops = {
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
index a82b5bd..7dfb01e 100644
--- a/drivers/media/video/zr364xx.c
+++ b/drivers/media/video/zr364xx.c
@@ -572,7 +572,7 @@
 	DBG("wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i);
 unlock:
 	spin_unlock_irqrestore(&cam->slock, flags);
-	return 0;
+	return rc;
 }
 
 /* this function moves the usb stream read pipe data
@@ -1304,7 +1304,7 @@
 				    NULL, &cam->slock,
 				    cam->type,
 				    V4L2_FIELD_NONE,
-				    sizeof(struct zr364xx_buffer), cam);
+				    sizeof(struct zr364xx_buffer), cam, NULL);
 
 	/* Added some delay here, since opening/closing the camera quickly,
 	 * like Ekiga does during its startup, can crash the webcam
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 8e03e76..2ea91e0 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -51,6 +51,10 @@
 
 source "drivers/staging/tm6000/Kconfig"
 
+source "drivers/staging/cpia/Kconfig"
+
+source "drivers/staging/stradis/Kconfig"
+
 source "drivers/staging/usbip/Kconfig"
 
 source "drivers/staging/winbond/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 0e7d755..c359fbb 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -8,6 +8,8 @@
 obj-$(CONFIG_VIDEO_GO7007)	+= go7007/
 obj-$(CONFIG_VIDEO_CX25821)	+= cx25821/
 obj-$(CONFIG_VIDEO_TM6000)	+= tm6000/
+obj-$(CONFIG_VIDEO_CPIA)	+= cpia/
+obj-$(CONFIG_VIDEO_STRADIS)	+= stradis/
 obj-$(CONFIG_LIRC_STAGING)	+= lirc/
 obj-$(CONFIG_USB_IP_COMMON)	+= usbip/
 obj-$(CONFIG_W35UND)		+= winbond/
diff --git a/drivers/staging/cpia/Kconfig b/drivers/staging/cpia/Kconfig
new file mode 100644
index 0000000..205d247
--- /dev/null
+++ b/drivers/staging/cpia/Kconfig
@@ -0,0 +1,39 @@
+config VIDEO_CPIA
+	tristate "CPiA Video For Linux (DEPRECATED)"
+	depends on VIDEO_V4L1
+	default n
+	---help---
+	  This driver is DEPRECATED please use the gspca cpia1 module
+	  instead. Note that you need atleast version 0.6.4 of libv4l for
+	  the cpia1 gspca module.
+
+	  This is the video4linux driver for cameras based on Vision's CPiA
+	  (Colour Processor Interface ASIC), such as the Creative Labs Video
+	  Blaster Webcam II. If you have one of these cameras, say Y here
+	  and select parallel port and/or USB lowlevel support below,
+	  otherwise say N. This will not work with the Creative Webcam III.
+
+	  Please read <file:Documentation/video4linux/README.cpia> for more
+	  information.
+
+	  This driver is also available as a module (cpia).
+
+config VIDEO_CPIA_PP
+	tristate "CPiA Parallel Port Lowlevel Support"
+	depends on PARPORT_1284 && VIDEO_CPIA && PARPORT
+	help
+	  This is the lowlevel parallel port support for cameras based on
+	  Vision's CPiA (Colour Processor Interface ASIC), such as the
+	  Creative Webcam II. If you have the parallel port version of one
+	  of these cameras, say Y here, otherwise say N. It is also available
+	  as a module (cpia_pp).
+
+config VIDEO_CPIA_USB
+	tristate "CPiA USB Lowlevel Support"
+	depends on VIDEO_CPIA && USB
+	help
+	  This is the lowlevel USB support for cameras based on Vision's CPiA
+	  (Colour Processor Interface ASIC), such as the Creative Webcam II.
+	  If you have the USB version of one of these cameras, say Y here,
+	  otherwise say N. This will not work with the Creative Webcam III.
+	  It is also available as a module (cpia_usb).
diff --git a/drivers/staging/cpia/Makefile b/drivers/staging/cpia/Makefile
new file mode 100644
index 0000000..89e52f1
--- /dev/null
+++ b/drivers/staging/cpia/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_VIDEO_CPIA) += cpia.o
+obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o
+obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o
+
+EXTRA_CFLAGS += -Idrivers/media/video
diff --git a/drivers/staging/cpia/TODO b/drivers/staging/cpia/TODO
new file mode 100644
index 0000000..ccb1c07
--- /dev/null
+++ b/drivers/staging/cpia/TODO
@@ -0,0 +1,8 @@
+This is an obsolete driver for some cpia-based webcams that use the parallel port.
+We couldn't find anyone with this hardware in order to port it to use V4L2.
+
+Also, parallel-port webcams are obsolete nowadays.
+
+If nobody take care on it, the driver will be removed for 2.6.38.
+
+Please send patches to linux-media@vger.kernel.org
diff --git a/drivers/media/video/cpia.c b/drivers/staging/cpia/cpia.c
similarity index 100%
rename from drivers/media/video/cpia.c
rename to drivers/staging/cpia/cpia.c
diff --git a/drivers/media/video/cpia.h b/drivers/staging/cpia/cpia.h
similarity index 100%
rename from drivers/media/video/cpia.h
rename to drivers/staging/cpia/cpia.h
diff --git a/drivers/media/video/cpia_pp.c b/drivers/staging/cpia/cpia_pp.c
similarity index 100%
rename from drivers/media/video/cpia_pp.c
rename to drivers/staging/cpia/cpia_pp.c
diff --git a/drivers/media/video/cpia_usb.c b/drivers/staging/cpia/cpia_usb.c
similarity index 100%
rename from drivers/media/video/cpia_usb.c
rename to drivers/staging/cpia/cpia_usb.c
diff --git a/drivers/staging/cx25821/Kconfig b/drivers/staging/cx25821/Kconfig
index 813cb35..1d73334 100644
--- a/drivers/staging/cx25821/Kconfig
+++ b/drivers/staging/cx25821/Kconfig
@@ -5,7 +5,7 @@
 	select I2C_ALGOBIT
 	select VIDEO_BTCX
 	select VIDEO_TVEEPROM
-	select VIDEO_IR
+	depends on VIDEO_IR
 	select VIDEOBUF_DVB
 	select VIDEOBUF_DMA_SG
 	select VIDEO_CX25840
diff --git a/drivers/staging/cx25821/cx25821-alsa.c b/drivers/staging/cx25821/cx25821-alsa.c
index bbe3643..2a01dc0 100644
--- a/drivers/staging/cx25821/cx25821-alsa.c
+++ b/drivers/staging/cx25821/cx25821-alsa.c
@@ -629,7 +629,7 @@
  * Only boards with eeprom and byte 1 at eeprom=1 have it
  */
 
-static struct pci_device_id cx25821_audio_pci_tbl[] __devinitdata = {
+static const struct pci_device_id cx25821_audio_pci_tbl[] __devinitdata = {
 	{0x14f1, 0x0920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
 	{0,}
 };
diff --git a/drivers/staging/cx25821/cx25821-audio-upstream.c b/drivers/staging/cx25821/cx25821-audio-upstream.c
index cdff49f..6f32006 100644
--- a/drivers/staging/cx25821/cx25821-audio-upstream.c
+++ b/drivers/staging/cx25821/cx25821-audio-upstream.c
@@ -40,8 +40,8 @@
 MODULE_LICENSE("GPL");
 
 static int _intr_msk =
-    FLD_AUD_SRC_RISCI1 | FLD_AUD_SRC_OF | FLD_AUD_SRC_SYNC |
-    FLD_AUD_SRC_OPC_ERR;
+	FLD_AUD_SRC_RISCI1 | FLD_AUD_SRC_OF | FLD_AUD_SRC_SYNC |
+	FLD_AUD_SRC_OPC_ERR;
 
 int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev,
 					      struct sram_channel *ch,
@@ -506,7 +506,7 @@
 {
 	int i = 0;
 	u32 int_msk_tmp;
-       struct sram_channel *channel = dev->channels[chan_num].sram_channels;
+	struct sram_channel *channel = dev->channels[chan_num].sram_channels;
 	dma_addr_t risc_phys_jump_addr;
 	__le32 *rp;
 
@@ -608,7 +608,7 @@
 	if (!dev)
 		return -1;
 
-       sram_ch = dev->channels[dev->_audio_upstream_channel_select].
+	sram_ch = dev->channels[dev->_audio_upstream_channel_select].
 				       sram_channels;
 
 	msk_stat = cx_read(sram_ch->int_mstat);
@@ -733,7 +733,7 @@
 	}
 
 	dev->_audio_upstream_channel_select = channel_select;
-       sram_ch = dev->channels[channel_select].sram_channels;
+	sram_ch = dev->channels[channel_select].sram_channels;
 
 	/* Work queue */
 	INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler);
@@ -764,9 +764,8 @@
 		       str_length + 1);
 
 		/* Default if filename is empty string */
-		if (strcmp(dev->input_audiofilename, "") == 0) {
+		if (strcmp(dev->input_audiofilename, "") == 0)
 			dev->_audiofilename = "/root/audioGOOD.wav";
-		}
 	} else {
 		str_length = strlen(_defaultAudioName);
 		dev->_audiofilename = kmalloc(str_length + 1, GFP_KERNEL);
diff --git a/drivers/staging/cx25821/cx25821-audio-upstream.h b/drivers/staging/cx25821/cx25821-audio-upstream.h
index ca987ad..668a4f1 100644
--- a/drivers/staging/cx25821/cx25821-audio-upstream.h
+++ b/drivers/staging/cx25821/cx25821-audio-upstream.h
@@ -46,11 +46,11 @@
 #define USE_RISC_NOOP_AUDIO   1
 
 #ifdef USE_RISC_NOOP_AUDIO
-#define AUDIO_RISC_DMA_BUF_SIZE    ( LINES_PER_AUDIO_BUFFER*RISC_READ_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE + RISC_JUMP_INSTRUCTION_SIZE)
+#define AUDIO_RISC_DMA_BUF_SIZE    (LINES_PER_AUDIO_BUFFER*RISC_READ_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE + RISC_JUMP_INSTRUCTION_SIZE)
 #endif
 
 #ifndef USE_RISC_NOOP_AUDIO
-#define AUDIO_RISC_DMA_BUF_SIZE    ( LINES_PER_AUDIO_BUFFER*RISC_READ_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + RISC_JUMP_INSTRUCTION_SIZE)
+#define AUDIO_RISC_DMA_BUF_SIZE    (LINES_PER_AUDIO_BUFFER*RISC_READ_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + RISC_JUMP_INSTRUCTION_SIZE)
 #endif
 
 static int _line_size;
diff --git a/drivers/staging/cx25821/cx25821-audio.h b/drivers/staging/cx25821/cx25821-audio.h
index 434b2a3..a702a0d 100644
--- a/drivers/staging/cx25821/cx25821-audio.h
+++ b/drivers/staging/cx25821/cx25821-audio.h
@@ -31,18 +31,18 @@
 #define NUMBER_OF_PROGRAMS  8
 
 /*
-  Max size of the RISC program for a buffer. - worst case is 2 writes per line
-  Space is also added for the 4 no-op instructions added on the end.
-*/
+ * Max size of the RISC program for a buffer. - worst case is 2 writes per line
+ * Space is also added for the 4 no-op instructions added on the end.
+ */
 #ifndef USE_RISC_NOOP
 #define MAX_BUFFER_PROGRAM_SIZE     \
-    (2*LINES_PER_BUFFER*RISC_WRITE_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE*4)
+	(2*LINES_PER_BUFFER*RISC_WRITE_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE*4)
 #endif
 
 /* MAE 12 July 2005 Try to use NOOP RISC instruction instead */
 #ifdef USE_RISC_NOOP
 #define MAX_BUFFER_PROGRAM_SIZE     \
-    (2*LINES_PER_BUFFER*RISC_WRITE_INSTRUCTION_SIZE + RISC_NOOP_INSTRUCTION_SIZE*4)
+	(2*LINES_PER_BUFFER*RISC_WRITE_INSTRUCTION_SIZE + RISC_NOOP_INSTRUCTION_SIZE*4)
 #endif
 
 /* Sizes of various instructions in bytes.  Used when adding instructions. */
diff --git a/drivers/staging/cx25821/cx25821-core.c b/drivers/staging/cx25821/cx25821-core.c
index c487c19..ca1eece 100644
--- a/drivers/staging/cx25821/cx25821-core.c
+++ b/drivers/staging/cx25821/cx25821-core.c
@@ -42,7 +42,7 @@
 module_param_array(card, int, NULL, 0444);
 MODULE_PARM_DESC(card, "card type");
 
-static unsigned int cx25821_devcount = 0;
+static unsigned int cx25821_devcount;
 
 static DEFINE_MUTEX(devlist);
 LIST_HEAD(cx25821_devlist);
@@ -781,14 +781,14 @@
 
 	/* Disable Video A/B activity */
 	for (i = 0; i < VID_CHANNEL_NUM; i++) {
-	       cx_write(dev->channels[i].sram_channels->dma_ctl, 0);
-	       cx_write(dev->channels[i].sram_channels->int_msk, 0);
+		cx_write(dev->channels[i].sram_channels->dma_ctl, 0);
+		cx_write(dev->channels[i].sram_channels->int_msk, 0);
 	}
 
-	for (i = VID_UPSTREAM_SRAM_CHANNEL_I; i <= VID_UPSTREAM_SRAM_CHANNEL_J;
-	     i++) {
-	       cx_write(dev->channels[i].sram_channels->dma_ctl, 0);
-	       cx_write(dev->channels[i].sram_channels->int_msk, 0);
+	for (i = VID_UPSTREAM_SRAM_CHANNEL_I;
+		i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) {
+		cx_write(dev->channels[i].sram_channels->dma_ctl, 0);
+		cx_write(dev->channels[i].sram_channels->int_msk, 0);
 	}
 
 	/* Disable Audio activity */
@@ -806,9 +806,9 @@
 			      u32 format)
 {
 	if (channel_select <= 7 && channel_select >= 0) {
-	       cx_write(dev->channels[channel_select].
-			       sram_channels->pix_frmt, format);
-	       dev->channels[channel_select].pixel_formats = format;
+		cx_write(dev->channels[channel_select].
+			sram_channels->pix_frmt, format);
+		dev->channels[channel_select].pixel_formats = format;
 	}
 }
 
@@ -829,7 +829,7 @@
 	cx_write(PCI_INT_STAT, 0xffffffff);
 
 	for (i = 0; i < VID_CHANNEL_NUM; i++)
-	       cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff);
+		cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff);
 
 	cx_write(AUD_A_INT_STAT, 0xffffffff);
 	cx_write(AUD_B_INT_STAT, 0xffffffff);
@@ -843,22 +843,22 @@
 	mdelay(100);
 
 	for (i = 0; i < VID_CHANNEL_NUM; i++) {
-	       cx25821_set_vip_mode(dev, dev->channels[i].sram_channels);
-	       cx25821_sram_channel_setup(dev, dev->channels[i].sram_channels,
-					       1440, 0);
-	       dev->channels[i].pixel_formats = PIXEL_FRMT_422;
-	       dev->channels[i].use_cif_resolution = FALSE;
+		cx25821_set_vip_mode(dev, dev->channels[i].sram_channels);
+		cx25821_sram_channel_setup(dev, dev->channels[i].sram_channels,
+						1440, 0);
+		dev->channels[i].pixel_formats = PIXEL_FRMT_422;
+		dev->channels[i].use_cif_resolution = FALSE;
 	}
 
 	/* Probably only affect Downstream */
-	for (i = VID_UPSTREAM_SRAM_CHANNEL_I; i <= VID_UPSTREAM_SRAM_CHANNEL_J;
-	     i++) {
-	       cx25821_set_vip_mode(dev, dev->channels[i].sram_channels);
+	for (i = VID_UPSTREAM_SRAM_CHANNEL_I;
+		i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) {
+		cx25821_set_vip_mode(dev, dev->channels[i].sram_channels);
 	}
 
-       cx25821_sram_channel_setup_audio(dev,
-			       dev->channels[SRAM_CH08].sram_channels,
-			       128, 0);
+	cx25821_sram_channel_setup_audio(dev,
+				dev->channels[SRAM_CH08].sram_channels,
+				128, 0);
 
 	cx25821_gpio_init(dev);
 }
@@ -931,8 +931,8 @@
 
 	/* Apply a sensible clock frequency for the PCIe bridge */
 	dev->clk_freq = 28000000;
-       for (i = 0; i < MAX_VID_CHANNEL_NUM; i++)
-	       dev->channels[i].sram_channels = &cx25821_sram_channels[i];
+	for (i = 0; i < MAX_VID_CHANNEL_NUM; i++)
+		dev->channels[i].sram_channels = &cx25821_sram_channels[i];
 
 	if (dev->nr > 1)
 		CX25821_INFO("dev->nr > 1!");
@@ -1003,11 +1003,11 @@
 
 	cx25821_card_setup(dev);
 
-       if (medusa_video_init(dev) < 0)
-	       CX25821_ERR("%s() Failed to initialize medusa!\n"
-	       , __func__);
+	if (medusa_video_init(dev) < 0)
+		CX25821_ERR("%s() Failed to initialize medusa!\n"
+		, __func__);
 
-       cx25821_video_register(dev);
+	cx25821_video_register(dev);
 
 	/* register IOCTL device */
 	dev->ioctl_dev =
@@ -1319,7 +1319,7 @@
 	struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
 
 	BUG_ON(in_interrupt());
-	videobuf_waiton(&buf->vb, 0, 0);
+	videobuf_waiton(q, &buf->vb, 0, 0);
 	videobuf_dma_unmap(q->dev, dma);
 	videobuf_dma_free(dma);
 	btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
@@ -1342,12 +1342,12 @@
 
 	for (i = 0; i < VID_CHANNEL_NUM; i++) {
 		if (pci_status & mask[i]) {
-		       vid_status = cx_read(dev->channels[i].
-			       sram_channels->int_stat);
+			vid_status = cx_read(dev->channels[i].
+				sram_channels->int_stat);
 
 			if (vid_status)
 				handled +=
-				    cx25821_video_irq(dev, i, vid_status);
+				cx25821_video_irq(dev, i, vid_status);
 
 			cx_write(PCI_INT_STAT, mask[i]);
 		}
diff --git a/drivers/staging/cx25821/cx25821-i2c.c b/drivers/staging/cx25821/cx25821-i2c.c
index e43572e..2b14bcc 100644
--- a/drivers/staging/cx25821/cx25821-i2c.c
+++ b/drivers/staging/cx25821/cx25821-i2c.c
@@ -283,7 +283,7 @@
 	.master_xfer = i2c_xfer,
 	.functionality = cx25821_functionality,
 #ifdef NEED_ALGO_CONTROL
-       .algo_control = dummy_algo_control,
+	.algo_control = dummy_algo_control,
 #endif
 };
 
diff --git a/drivers/staging/cx25821/cx25821-medusa-reg.h b/drivers/staging/cx25821/cx25821-medusa-reg.h
index f7f33b3..1c1c228 100644
--- a/drivers/staging/cx25821/cx25821-medusa-reg.h
+++ b/drivers/staging/cx25821/cx25821-medusa-reg.h
@@ -443,13 +443,13 @@
 /*****************************************************************************/
 /* LUMA_CTRL register fields */
 #define VDEC_A_BRITE_CTRL				0x1014
-#define VDEC_A_CNTRST_CTRL       		0x1015
-#define VDEC_A_PEAK_SEL          		0x1016
+#define VDEC_A_CNTRST_CTRL			0x1015
+#define VDEC_A_PEAK_SEL				0x1016
 
 /*****************************************************************************/
 /* CHROMA_CTRL register fields */
-#define VDEC_A_USAT_CTRL         		0x1018
-#define VDEC_A_VSAT_CTRL         		0x1019
-#define VDEC_A_HUE_CTRL          		0x101A
+#define VDEC_A_USAT_CTRL			0x1018
+#define VDEC_A_VSAT_CTRL			0x1019
+#define VDEC_A_HUE_CTRL				0x101A
 
 #endif
diff --git a/drivers/staging/cx25821/cx25821-medusa-video.c b/drivers/staging/cx25821/cx25821-medusa-video.c
index ef9f2b8..1e11e0c 100644
--- a/drivers/staging/cx25821/cx25821-medusa-video.c
+++ b/drivers/staging/cx25821/cx25821-medusa-video.c
@@ -778,9 +778,9 @@
 
 int medusa_video_init(struct cx25821_dev *dev)
 {
-       u32 value = 0, tmp = 0;
-       int ret_val = 0;
-       int i = 0;
+	u32 value = 0, tmp = 0;
+	int ret_val = 0;
+	int i = 0;
 
 	mutex_lock(&dev->lock);
 
@@ -829,7 +829,7 @@
 	/* select AFE clock to output mode */
 	value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp);
 	value &= 0x83FFFFFF;
-       ret_val =
+	ret_val =
 	   cx25821_i2c_write(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL,
 			     value | 0x10000000);
 
diff --git a/drivers/staging/cx25821/cx25821-reg.h b/drivers/staging/cx25821/cx25821-reg.h
index cfe0f32..a3fc25a 100644
--- a/drivers/staging/cx25821/cx25821-reg.h
+++ b/drivers/staging/cx25821/cx25821-reg.h
@@ -163,8 +163,8 @@
 #define  FLD_VID_DST_RISC2         0x00000010
 #define  FLD_VID_SRC_RISC1         0x00000002
 #define  FLD_VID_DST_RISC1         0x00000001
-#define  FLD_VID_SRC_ERRORS		FLD_VID_SRC_OPC_ERR | FLD_VID_SRC_SYNC | FLD_VID_SRC_UF
-#define  FLD_VID_DST_ERRORS		FLD_VID_DST_OPC_ERR | FLD_VID_DST_SYNC | FLD_VID_DST_OF
+#define  FLD_VID_SRC_ERRORS		(FLD_VID_SRC_OPC_ERR | FLD_VID_SRC_SYNC | FLD_VID_SRC_UF)
+#define  FLD_VID_DST_ERRORS		(FLD_VID_DST_OPC_ERR | FLD_VID_DST_SYNC | FLD_VID_DST_OF)
 
 /* ***************************************************************************** */
 #define  AUD_A_INT_MSK             0x0400C0	/* Audio Int interrupt mask */
diff --git a/drivers/staging/cx25821/cx25821-video-upstream-ch2.c b/drivers/staging/cx25821/cx25821-video-upstream-ch2.c
index d12dbb5..405e2db 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream-ch2.c
+++ b/drivers/staging/cx25821/cx25821-video-upstream-ch2.c
@@ -32,17 +32,17 @@
 #include <linux/file.h>
 #include <linux/fcntl.h>
 #include <linux/slab.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 
 MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
 MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
 MODULE_LICENSE("GPL");
 
 static int _intr_msk =
-    FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | FLD_VID_SRC_OPC_ERR;
+	FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | FLD_VID_SRC_OPC_ERR;
 
 static __le32 *cx25821_update_riscprogram_ch2(struct cx25821_dev *dev,
-					      __le32 * rp, unsigned int offset,
+					      __le32 *rp, unsigned int offset,
 					      unsigned int bpl, u32 sync_line,
 					      unsigned int lines,
 					      int fifo_enable, int field_type)
@@ -53,9 +53,8 @@
 	*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
 
 	if (USE_RISC_NOOP_VIDEO) {
-		for (i = 0; i < NUM_NO_OPS; i++) {
+		for (i = 0; i < NUM_NO_OPS; i++)
 			*(rp++) = cpu_to_le32(RISC_NOOP);
-		}
 	}
 
 	/* scan lines */
@@ -75,7 +74,7 @@
 }
 
 static __le32 *cx25821_risc_field_upstream_ch2(struct cx25821_dev *dev,
-					       __le32 * rp,
+					       __le32 *rp,
 					       dma_addr_t databuf_phys_addr,
 					       unsigned int offset,
 					       u32 sync_line, unsigned int bpl,
@@ -88,14 +87,12 @@
 	int dist_betwn_starts = bpl * 2;
 
 	/* sync instruction */
-	if (sync_line != NO_SYNC_LINE) {
+	if (sync_line != NO_SYNC_LINE)
 		*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
-	}
 
 	if (USE_RISC_NOOP_VIDEO) {
-		for (i = 0; i < NUM_NO_OPS; i++) {
+		for (i = 0; i < NUM_NO_OPS; i++)
 			*(rp++) = cpu_to_le32(RISC_NOOP);
-		}
 	}
 
 	/* scan lines */
@@ -133,7 +130,7 @@
 {
 	__le32 *rp;
 	int fifo_enable = 0;
-       int singlefield_lines = lines >> 1; /*get line count for single field */
+	int singlefield_lines = lines >> 1; /*get line count for single field */
 	int odd_num_lines = singlefield_lines;
 	int frame = 0;
 	int frame_size = 0;
@@ -218,15 +215,15 @@
 		    ("cx25821: No video file is currently running so return!\n");
 		return;
 	}
-       /* Disable RISC interrupts */
+	/* Disable RISC interrupts */
 	tmp = cx_read(sram_ch->int_msk);
 	cx_write(sram_ch->int_msk, tmp & ~_intr_msk);
 
-       /* Turn OFF risc and fifo */
+	/* Turn OFF risc and fifo */
 	tmp = cx_read(sram_ch->dma_ctl);
 	cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN));
 
-       /* Clear data buffer memory */
+	/* Clear data buffer memory */
 	if (dev->_data_buf_virt_addr_ch2)
 		memset(dev->_data_buf_virt_addr_ch2, 0,
 		       dev->_data_buf_size_ch2);
@@ -250,9 +247,8 @@
 
 void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev)
 {
-	if (dev->_is_running_ch2) {
+	if (dev->_is_running_ch2)
 		cx25821_stop_upstream_video_ch2(dev);
-	}
 
 	if (dev->_dma_virt_addr_ch2) {
 		pci_free_consistent(dev->pci, dev->_risc_size_ch2,
@@ -303,11 +299,10 @@
 	file_offset = dev->_frame_count_ch2 * frame_size;
 
 	myfile = filp_open(dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0);
-
 	if (IS_ERR(myfile)) {
 		const int open_errno = -PTR_ERR(myfile);
-		printk("%s(): ERROR opening file(%s) with errno = %d! \n",
-		       __func__, dev->_filename_ch2, open_errno);
+		printk("%s(): ERROR opening file(%s) with errno = %d!\n",
+			__func__, dev->_filename_ch2, open_errno);
 		return PTR_ERR(myfile);
 	} else {
 		if (!(myfile->f_op)) {
@@ -371,8 +366,8 @@
 	    container_of(work, struct cx25821_dev, _irq_work_entry_ch2);
 
 	if (!dev) {
-		printk("ERROR %s(): since container_of(work_struct) FAILED! \n",
-		       __func__);
+		printk("ERROR %s(): since container_of(work_struct) FAILED!\n",
+			__func__);
 		return;
 	}
 
@@ -398,8 +393,8 @@
 
 	if (IS_ERR(myfile)) {
 		const int open_errno = -PTR_ERR(myfile);
-		printk("%s(): ERROR opening file(%s) with errno = %d! \n",
-		       __func__, dev->_filename_ch2, open_errno);
+		printk("%s(): ERROR opening file(%s) with errno = %d!\n",
+			__func__, dev->_filename_ch2, open_errno);
 		return PTR_ERR(myfile);
 	} else {
 		if (!(myfile->f_op)) {
@@ -450,9 +445,8 @@
 			if (i > 0)
 				dev->_frame_count_ch2++;
 
-			if (vfs_read_retval < line_size) {
+			if (vfs_read_retval < line_size)
 				break;
-			}
 		}
 
 		dev->_file_status_ch2 =
@@ -494,7 +488,7 @@
 		return -ENOMEM;
 	}
 
-       /* Iniitize at this address until n bytes to 0 */
+	/* Iniitize at this address until n bytes to 0 */
 	memset(dev->_dma_virt_addr_ch2, 0, dev->_risc_size_ch2);
 
 	if (dev->_data_buf_virt_addr_ch2 != NULL) {
@@ -502,7 +496,7 @@
 				    dev->_data_buf_virt_addr_ch2,
 				    dev->_data_buf_phys_addr_ch2);
 	}
-       /* For Video Data buffer allocation */
+	/* For Video Data buffer allocation */
 	dev->_data_buf_virt_addr_ch2 =
 	    pci_alloc_consistent(dev->pci, dev->upstream_databuf_size_ch2,
 				 &data_dma_addr);
@@ -515,26 +509,26 @@
 		return -ENOMEM;
 	}
 
-       /* Initialize at this address until n bytes to 0 */
+	/* Initialize at this address until n bytes to 0 */
 	memset(dev->_data_buf_virt_addr_ch2, 0, dev->_data_buf_size_ch2);
 
 	ret = cx25821_openfile_ch2(dev, sram_ch);
 	if (ret < 0)
 		return ret;
 
-       /* Creating RISC programs */
+	/* Creating RISC programs */
 	ret =
 	    cx25821_risc_buffer_upstream_ch2(dev, dev->pci, 0, bpl,
 					     dev->_lines_count_ch2);
 	if (ret < 0) {
 		printk(KERN_INFO
-		       "cx25821: Failed creating Video Upstream Risc programs! \n");
+			"cx25821: Failed creating Video Upstream Risc programs!\n");
 		goto error;
 	}
 
 	return 0;
 
-      error:
+	error:
 	return ret;
 }
 
@@ -542,7 +536,7 @@
 				   u32 status)
 {
 	u32 int_msk_tmp;
-       struct sram_channel *channel = dev->channels[chan_num].sram_channels;
+	struct sram_channel *channel = dev->channels[chan_num].sram_channels;
 	int singlefield_lines = NTSC_FIELD_HEIGHT;
 	int line_size_in_bytes = Y422_LINE_SZ;
 	int odd_risc_prog_size = 0;
@@ -550,13 +544,13 @@
 	__le32 *rp;
 
 	if (status & FLD_VID_SRC_RISC1) {
-	       /* We should only process one program per call */
+		/* We should only process one program per call */
 		u32 prog_cnt = cx_read(channel->gpcnt);
 
-	       /*
-		  Since we've identified our IRQ, clear our bits from the
-		  interrupt mask and interrupt status registers
-	       */
+		/*
+		 *  Since we've identified our IRQ, clear our bits from the
+		 *  interrupt mask and interrupt status registers
+		 */
 		int_msk_tmp = cx_read(channel->int_msk);
 		cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk);
 		cx_write(channel->int_stat, _intr_msk);
@@ -612,7 +606,7 @@
 		       dev->_frame_count_ch2);
 		return -1;
 	}
-       /* ElSE, set the interrupt mask register, re-enable irq. */
+	/* ElSE, set the interrupt mask register, re-enable irq. */
 	int_msk_tmp = cx_read(channel->int_msk);
 	cx_write(channel->int_msk, int_msk_tmp |= _intr_msk);
 
@@ -631,24 +625,22 @@
 		return -1;
 
 	channel_num = VID_UPSTREAM_SRAM_CHANNEL_J;
-
-       sram_ch = dev->channels[channel_num].sram_channels;
+	sram_ch = dev->channels[channel_num].sram_channels;
 
 	msk_stat = cx_read(sram_ch->int_mstat);
 	vid_status = cx_read(sram_ch->int_stat);
 
-       /* Only deal with our interrupt */
+	/* Only deal with our interrupt */
 	if (vid_status) {
 		handled =
 		    cx25821_video_upstream_irq_ch2(dev, channel_num,
 						   vid_status);
 	}
 
-	if (handled < 0) {
+	if (handled < 0)
 		cx25821_stop_upstream_video_ch2(dev);
-	} else {
+	else
 		handled += handled;
-	}
 
 	return IRQ_RETVAL(handled);
 }
@@ -667,22 +659,21 @@
 	value |= dev->_isNTSC_ch2 ? 0 : 0x10;
 	cx_write(ch->vid_fmt_ctl, value);
 
-       /*
-	  set number of active pixels in each line. Default is 720
-	  pixels in both NTSC and PAL format
-       */
+	/*
+	 *  set number of active pixels in each line. Default is 720
+	 * pixels in both NTSC and PAL format
+	 */
 	cx_write(ch->vid_active_ctl1, width);
 
 	num_lines = (height / 2) & 0x3FF;
 	odd_num_lines = num_lines;
 
-	if (dev->_isNTSC_ch2) {
+	if (dev->_isNTSC_ch2)
 		odd_num_lines += 1;
-	}
 
 	value = (num_lines << 16) | odd_num_lines;
 
-       /* set number of active lines in field 0 (top) and field 1 (bottom) */
+	/* set number of active lines in field 0 (top) and field 1 (bottom) */
 	cx_write(ch->vid_active_ctl2, value);
 
 	cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3);
@@ -694,27 +685,27 @@
 	u32 tmp = 0;
 	int err = 0;
 
-       /*
-	  656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface
-	  for channel A-C
-       */
+	/*
+	 *  656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface
+	 * for channel A-C
+	 */
 	tmp = cx_read(VID_CH_MODE_SEL);
 	cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF);
 
-       /*
-	  Set the physical start address of the RISC program in the initial
-	  program counter(IPC) member of the cmds.
-       */
+	/*
+	 *  Set the physical start address of the RISC program in the initial
+	 *  program counter(IPC) member of the cmds.
+	 */
 	cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr_ch2);
-       cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */
+	cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */
 
 	/* reset counter */
 	cx_write(sram_ch->gpcnt_ctl, 3);
 
-       /* Clear our bits from the interrupt status register. */
+	/* Clear our bits from the interrupt status register. */
 	cx_write(sram_ch->int_stat, _intr_msk);
 
-       /* Set the interrupt mask register, enable irq. */
+	/* Set the interrupt mask register, enable irq. */
 	cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit));
 	tmp = cx_read(sram_ch->int_msk);
 	cx_write(sram_ch->int_msk, tmp |= _intr_msk);
@@ -727,7 +718,7 @@
 		       dev->pci->irq);
 		goto fail_irq;
 	}
-       /* Start the DMA  engine */
+	/* Start the DMA  engine */
 	tmp = cx_read(sram_ch->dma_ctl);
 	cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN);
 
@@ -736,7 +727,7 @@
 
 	return 0;
 
-      fail_irq:
+	fail_irq:
 	cx25821_dev_unregister(dev);
 	return err;
 }
@@ -758,7 +749,7 @@
 	}
 
 	dev->_channel2_upstream_select = channel_select;
-       sram_ch = dev->channels[channel_select].sram_channels;
+	sram_ch = dev->channels[channel_select].sram_channels;
 
 	INIT_WORK(&dev->_irq_work_entry_ch2, cx25821_vidups_handler_ch2);
 	dev->_irq_queues_ch2 =
@@ -769,10 +760,10 @@
 		    ("cx25821: create_singlethread_workqueue() for Video FAILED!\n");
 		return -ENOMEM;
 	}
-       /*
-	  656/VIP SRC Upstream Channel I & J and 7 -
-	  Host Bus Interface for channel A-C
-       */
+	/*
+	 * 656/VIP SRC Upstream Channel I & J and 7 -
+	 * Host Bus Interface for channel A-C
+	 */
 	tmp = cx_read(VID_CH_MODE_SEL);
 	cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF);
 
@@ -808,7 +799,7 @@
 		       str_length + 1);
 	}
 
-       /* Default if filename is empty string */
+	/* Default if filename is empty string */
 	if (strcmp(dev->input_filename_ch2, "") == 0) {
 		if (dev->_isNTSC_ch2) {
 			dev->_filename_ch2 =
@@ -833,7 +824,7 @@
 	dev->upstream_riscbuf_size_ch2 = risc_buffer_size * 2;
 	dev->upstream_databuf_size_ch2 = data_frame_size * 2;
 
-       /* Allocating buffers and prepare RISC program */
+	/* Allocating buffers and prepare RISC program */
 	retval =
 	    cx25821_upstream_buffer_prepare_ch2(dev, sram_ch,
 						dev->_line_size_ch2);
@@ -848,7 +839,7 @@
 
 	return 0;
 
-      error:
+	error:
 	cx25821_dev_unregister(dev);
 
 	return err;
diff --git a/drivers/staging/cx25821/cx25821-video-upstream-ch2.h b/drivers/staging/cx25821/cx25821-video-upstream-ch2.h
index 6234063..029e830 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream-ch2.h
+++ b/drivers/staging/cx25821/cx25821-video-upstream-ch2.h
@@ -88,14 +88,14 @@
 #endif
 
 #ifndef USE_RISC_NOOP_VIDEO
-#define PAL_US_VID_PROG_SIZE      ((PAL_FIELD_HEIGHT + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE )
-#define PAL_RISC_BUF_SIZE         ( 2 * (RISC_SYNC_INSTRUCTION_SIZE + PAL_US_VID_PROG_SIZE) )
+#define PAL_US_VID_PROG_SIZE      ((PAL_FIELD_HEIGHT + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+#define PAL_RISC_BUF_SIZE         (2 * (RISC_SYNC_INSTRUCTION_SIZE + PAL_US_VID_PROG_SIZE))
 #define PAL_VID_PROG_SIZE         ((PAL_FIELD_HEIGHT*2) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
-				    RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE )
-#define ODD_FLD_PAL_PROG_SIZE     ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE )
-#define ODD_FLD_NTSC_PROG_SIZE    ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE )
+				    RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
+#define ODD_FLD_PAL_PROG_SIZE     ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+#define ODD_FLD_NTSC_PROG_SIZE    ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
 #define NTSC_US_VID_PROG_SIZE     ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
-#define NTSC_RISC_BUF_SIZE        (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE) )
+#define NTSC_RISC_BUF_SIZE        (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
 #define FRAME1_VID_PROG_SIZE      ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
-				    RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE )
+				    RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
 #endif
diff --git a/drivers/staging/cx25821/cx25821-video-upstream.c b/drivers/staging/cx25821/cx25821-video-upstream.c
index 756a820..16bf74d 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream.c
+++ b/drivers/staging/cx25821/cx25821-video-upstream.c
@@ -39,7 +39,7 @@
 MODULE_LICENSE("GPL");
 
 static int _intr_msk =
-    FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | FLD_VID_SRC_OPC_ERR;
+	FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | FLD_VID_SRC_OPC_ERR;
 
 int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev,
 					struct sram_channel *ch,
@@ -346,13 +346,13 @@
 
 	if (IS_ERR(myfile)) {
 		const int open_errno = -PTR_ERR(myfile);
-	       printk(KERN_ERR
+		printk(KERN_ERR
 		   "%s(): ERROR opening file(%s) with errno = %d!\n",
 		   __func__, dev->_filename, open_errno);
 		return PTR_ERR(myfile);
 	} else {
 		if (!(myfile->f_op)) {
-		       printk(KERN_ERR
+			printk(KERN_ERR
 			   "%s: File has no file operations registered!",
 			   __func__);
 			filp_close(myfile, NULL);
@@ -360,7 +360,7 @@
 		}
 
 		if (!myfile->f_op->read) {
-		       printk(KERN_ERR
+			printk(KERN_ERR
 			   "%s: File has no READ operations registered!",
 			   __func__);
 			filp_close(myfile, NULL);
@@ -415,7 +415,7 @@
 	    container_of(work, struct cx25821_dev, _irq_work_entry);
 
 	if (!dev) {
-	       printk(KERN_ERR
+		printk(KERN_ERR
 		   "ERROR %s(): since container_of(work_struct) FAILED!\n",
 		   __func__);
 		return;
@@ -448,7 +448,7 @@
 		return PTR_ERR(myfile);
 	} else {
 		if (!(myfile->f_op)) {
-		       printk(KERN_ERR
+			printk(KERN_ERR
 			   "%s: File has no file operations registered!",
 			   __func__);
 			filp_close(myfile, NULL);
@@ -456,7 +456,7 @@
 		}
 
 		if (!myfile->f_op->read) {
-		       printk(KERN_ERR
+			printk(KERN_ERR
 			   "%s: File has no READ operations registered!  \
 			   Returning.",
 			     __func__);
@@ -589,7 +589,7 @@
 			       u32 status)
 {
 	u32 int_msk_tmp;
-       struct sram_channel *channel = dev->channels[chan_num].sram_channels;
+	struct sram_channel *channel = dev->channels[chan_num].sram_channels;
 	int singlefield_lines = NTSC_FIELD_HEIGHT;
 	int line_size_in_bytes = Y422_LINE_SZ;
 	int odd_risc_prog_size = 0;
@@ -657,12 +657,12 @@
 			   Interrupt!\n", __func__);
 
 		if (status & FLD_VID_SRC_SYNC)
-		       printk(KERN_ERR "%s: Video Received Sync Error \
-		       Interrupt!\n", __func__);
+			printk(KERN_ERR "%s: Video Received Sync Error \
+				Interrupt!\n", __func__);
 
 		if (status & FLD_VID_SRC_OPC_ERR)
-		       printk(KERN_ERR "%s: Video Received OpCode Error \
-		       Interrupt!\n", __func__);
+			printk(KERN_ERR "%s: Video Received OpCode Error \
+				Interrupt!\n", __func__);
 	}
 
 	if (dev->_file_status == END_OF_FILE) {
@@ -690,7 +690,7 @@
 
 	channel_num = VID_UPSTREAM_SRAM_CHANNEL_I;
 
-       sram_ch = dev->channels[channel_num].sram_channels;
+	sram_ch = dev->channels[channel_num].sram_channels;
 
 	msk_stat = cx_read(sram_ch->int_mstat);
 	vid_status = cx_read(sram_ch->int_stat);
@@ -811,7 +811,7 @@
 	}
 
 	dev->_channel_upstream_select = channel_select;
-       sram_ch = dev->channels[channel_select].sram_channels;
+	sram_ch = dev->channels[channel_select].sram_channels;
 
 	INIT_WORK(&dev->_irq_work_entry, cx25821_vidups_handler);
 	dev->_irq_queues = create_singlethread_workqueue("cx25821_workqueue");
diff --git a/drivers/staging/cx25821/cx25821-video-upstream.h b/drivers/staging/cx25821/cx25821-video-upstream.h
index 10dee5c..f0b3ac0 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream.h
+++ b/drivers/staging/cx25821/cx25821-video-upstream.h
@@ -97,13 +97,13 @@
 #define PAL_RISC_BUF_SIZE           (2 * PAL_US_VID_PROG_SIZE)
 
 #define PAL_VID_PROG_SIZE           ((PAL_FIELD_HEIGHT*2) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
-				      RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE )
+				      RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
 
-#define ODD_FLD_PAL_PROG_SIZE       ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE )
-#define ODD_FLD_NTSC_PROG_SIZE      ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE )
+#define ODD_FLD_PAL_PROG_SIZE       ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+#define ODD_FLD_NTSC_PROG_SIZE      ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
 
 #define NTSC_US_VID_PROG_SIZE       ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
-#define NTSC_RISC_BUF_SIZE          ( 2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE) )
+#define NTSC_RISC_BUF_SIZE          (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
 #define FRAME1_VID_PROG_SIZE        ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \
-				      RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE )
+				      RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
 #endif
diff --git a/drivers/staging/cx25821/cx25821-video.c b/drivers/staging/cx25821/cx25821-video.c
index 1d5e879..e7f1d57 100644
--- a/drivers/staging/cx25821/cx25821-video.c
+++ b/drivers/staging/cx25821/cx25821-video.c
@@ -856,7 +856,7 @@
 			      &dev->pci->dev, &dev->slock,
 			      V4L2_BUF_TYPE_VIDEO_CAPTURE,
 			      V4L2_FIELD_INTERLACED,
-			      sizeof(struct cx25821_buffer), fh);
+			      sizeof(struct cx25821_buffer), fh, NULL);
 
        dprintk(1, "post videobuf_queue_init()\n");
        unlock_kernel();
@@ -993,6 +993,7 @@
 {
        struct cx25821_fh *fh = priv;
        struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+	struct v4l2_mbus_framefmt mbus_fmt;
        int err;
        int pix_format = PIXEL_FRMT_422;
 
@@ -1039,7 +1040,8 @@
 
        dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width,
 	       fh->height, fh->vidq.field);
-       cx25821_call_all(dev, video, s_fmt, f);
+	v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+	cx25821_call_all(dev, video, s_mbus_fmt, &mbus_fmt);
 
        return 0;
 }
diff --git a/drivers/staging/cx25821/cx25821.h b/drivers/staging/cx25821/cx25821.h
index 1b628f6..c940001 100644
--- a/drivers/staging/cx25821/cx25821.h
+++ b/drivers/staging/cx25821/cx25821.h
@@ -91,10 +91,10 @@
 
 /* Currently supported by the driver */
 #define CX25821_NORMS (\
-    V4L2_STD_NTSC_M |  V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_M_KR | \
-    V4L2_STD_PAL_BG |  V4L2_STD_PAL_DK    |  V4L2_STD_PAL_I    | \
-    V4L2_STD_PAL_M  |  V4L2_STD_PAL_N     |  V4L2_STD_PAL_H    | \
-    V4L2_STD_PAL_Nc )
+	V4L2_STD_NTSC_M |  V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_M_KR | \
+	V4L2_STD_PAL_BG |  V4L2_STD_PAL_DK    |  V4L2_STD_PAL_I    | \
+	V4L2_STD_PAL_M  |  V4L2_STD_PAL_N     |  V4L2_STD_PAL_H    | \
+	V4L2_STD_PAL_Nc)
 
 #define CX25821_BOARD_CONEXANT_ATHENA10 1
 #define MAX_VID_CHANNEL_NUM     12
@@ -139,7 +139,7 @@
 	/* video capture */
 	struct cx25821_fmt *fmt;
 	unsigned int width, height;
-    int channel_id;
+	int channel_id;
 
 	/* vbi capture */
 	struct videobuf_queue vidq;
@@ -238,26 +238,25 @@
 };
 
 struct cx25821_channel {
-       struct v4l2_prio_state prio;
+	struct v4l2_prio_state prio;
 
-       int ctl_bright;
-       int ctl_contrast;
-       int ctl_hue;
-       int ctl_saturation;
+	int ctl_bright;
+	int ctl_contrast;
+	int ctl_hue;
+	int ctl_saturation;
+	struct cx25821_data timeout_data;
 
-       struct cx25821_data timeout_data;
+	struct video_device *video_dev;
+	struct cx25821_dmaqueue vidq;
 
-       struct video_device *video_dev;
-       struct cx25821_dmaqueue vidq;
+	struct sram_channel *sram_channels;
 
-       struct sram_channel *sram_channels;
+	struct mutex lock;
+	int resources;
 
-       struct mutex lock;
-       int resources;
-
-       int pixel_formats;
-       int use_cif_resolution;
-       int cif_width;
+	int pixel_formats;
+	int use_cif_resolution;
+	int cif_width;
 };
 
 struct cx25821_dev {
@@ -283,7 +282,7 @@
 	int nr;
 	struct mutex lock;
 
-    struct cx25821_channel channels[MAX_VID_CHANNEL_NUM];
+	struct cx25821_channel channels[MAX_VID_CHANNEL_NUM];
 
 	/* board details */
 	unsigned int board;
@@ -311,7 +310,7 @@
 	int _audio_lines_count;
 	int _audioframe_count;
 	int _audio_upstream_channel_select;
-       int _last_index_irq;    /* The last interrupt index processed. */
+	int _last_index_irq;    /* The last interrupt index processed. */
 
 	__le32 *_risc_audio_jmp_addr;
 	__le32 *_risc_virt_start_addr;
@@ -443,7 +442,7 @@
 }
 
 #define cx25821_call_all(dev, o, f, args...) \
-    v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
+	v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
 
 extern struct list_head cx25821_devlist;
 extern struct cx25821_board cx25821_boards[];
@@ -491,7 +490,7 @@
 	u32 fld_aud_fifo_en;
 	u32 fld_aud_risc_en;
 
-       /* For Upstream Video */
+	/* For Upstream Video */
 	u32 vid_fmt_ctl;
 	u32 vid_active_ctl1;
 	u32 vid_active_ctl2;
@@ -511,8 +510,8 @@
 #define cx_write(reg, value)     writel((value), dev->lmmio + ((reg)>>2))
 
 #define cx_andor(reg, mask, value) \
-  writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
-  ((value) & (mask)), dev->lmmio+((reg)>>2))
+	writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
+	((value) & (mask)), dev->lmmio+((reg)>>2))
 
 #define cx_set(reg, bit)          cx_andor((reg), (bit), (bit))
 #define cx_clear(reg, bit)        cx_andor((reg), (bit), 0)
diff --git a/drivers/staging/dt3155v4l/dt3155v4l.c b/drivers/staging/dt3155v4l/dt3155v4l.c
index fd48b38..b996697 100644
--- a/drivers/staging/dt3155v4l/dt3155v4l.c
+++ b/drivers/staging/dt3155v4l/dt3155v4l.c
@@ -293,7 +293,7 @@
 dt3155_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
 {
 	if (vb->state == VIDEOBUF_ACTIVE)
-		videobuf_waiton(vb, 0, 0); /* FIXME: cannot be interrupted */
+		videobuf_waiton(q, vb, 0, 0); /* FIXME: cannot be interrupted */
 	videobuf_dma_contig_free(q, vb);
 	vb->state = VIDEOBUF_NEEDS_INIT;
 }
@@ -440,7 +440,7 @@
 		videobuf_queue_dma_contig_init(pd->vidq, &vbq_ops,
 				&pd->pdev->dev, &pd->lock,
 				V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
-				sizeof(struct videobuf_buffer), pd);
+				sizeof(struct videobuf_buffer), pd, NULL);
 		/* disable all irqs, clear all irq flags */
 		iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD,
 						pd->regs + INT_CSR);
@@ -494,7 +494,7 @@
 		tmp = pd->curr_buf;
 		spin_unlock_irqrestore(&pd->lock, flags);
 		if (tmp)
-			videobuf_waiton(tmp, 0, 1); /* block, interruptible */
+			videobuf_waiton(pd->vidq, tmp, 0, 1); /* block, interruptible */
 		dt3155_stop_acq(pd);
 		videobuf_stop(pd->vidq);
 		pd->acq_fp = NULL;
@@ -603,7 +603,7 @@
 	tmp = pd->curr_buf;
 	spin_unlock_irqrestore(&pd->lock, flags);
 	if (tmp)
-		videobuf_waiton(tmp, 0, 1); /* block, interruptible */
+		videobuf_waiton(pd->vidq, tmp, 0, 1); /* block, interruptible */
 	return ret;
 }
 
diff --git a/drivers/staging/go7007/Kconfig b/drivers/staging/go7007/Kconfig
index 75fa468..3aecd30 100644
--- a/drivers/staging/go7007/Kconfig
+++ b/drivers/staging/go7007/Kconfig
@@ -4,7 +4,7 @@
 	depends on BKL # please fix
 	depends on SND
 	select VIDEOBUF_DMA_SG
-	select VIDEO_IR
+	depends on VIDEO_IR
 	select VIDEO_TUNER
 	select VIDEO_TVEEPROM
 	select SND_PCM
diff --git a/drivers/staging/go7007/go7007-driver.c b/drivers/staging/go7007/go7007-driver.c
index 372a7c6..b3f42f3 100644
--- a/drivers/staging/go7007/go7007-driver.c
+++ b/drivers/staging/go7007/go7007-driver.c
@@ -194,51 +194,15 @@
  * Attempt to instantiate an I2C client by ID, probably loading a module.
  */
 static int init_i2c_module(struct i2c_adapter *adapter, const char *type,
-			   int id, int addr)
+			   int addr)
 {
 	struct go7007 *go = i2c_get_adapdata(adapter);
 	struct v4l2_device *v4l2_dev = &go->v4l2_dev;
-	char *modname;
 
-	switch (id) {
-	case I2C_DRIVERID_WIS_SAA7115:
-		modname = "wis-saa7115";
-		break;
-	case I2C_DRIVERID_WIS_SAA7113:
-		modname = "wis-saa7113";
-		break;
-	case I2C_DRIVERID_WIS_UDA1342:
-		modname = "wis-uda1342";
-		break;
-	case I2C_DRIVERID_WIS_SONY_TUNER:
-		modname = "wis-sony-tuner";
-		break;
-	case I2C_DRIVERID_WIS_TW9903:
-		modname = "wis-tw9903";
-		break;
-	case I2C_DRIVERID_WIS_TW2804:
-		modname = "wis-tw2804";
-		break;
-	case I2C_DRIVERID_WIS_OV7640:
-		modname = "wis-ov7640";
-		break;
-	case I2C_DRIVERID_S2250:
-		modname = "s2250";
-		break;
-	default:
-		modname = NULL;
-		break;
-	}
-
-	if (v4l2_i2c_new_subdev(v4l2_dev, adapter, modname, type, addr, NULL))
+	if (v4l2_i2c_new_subdev(v4l2_dev, adapter, NULL, type, addr, NULL))
 		return 0;
 
-	if (modname != NULL)
-		printk(KERN_INFO
-			"go7007: probing for module %s failed\n", modname);
-	else
-		printk(KERN_INFO
-			"go7007: sensor %u seems to be unsupported!\n", id);
+	printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", type);
 	return -1;
 }
 
@@ -277,7 +241,6 @@
 		for (i = 0; i < go->board_info->num_i2c_devs; ++i)
 			init_i2c_module(&go->i2c_adapter,
 					go->board_info->i2c_devs[i].type,
-					go->board_info->i2c_devs[i].id,
 					go->board_info->i2c_devs[i].addr);
 		if (go->board_id == GO7007_BOARDID_ADLINK_MPG24)
 			i2c_clients_command(&go->i2c_adapter,
@@ -393,7 +356,8 @@
 	for (i = 0; i < 16; ++i) {
 		y = (((go->parse_length - 1) << 3) + i) / (go->width >> 4);
 		x = (((go->parse_length - 1) << 3) + i) % (go->width >> 4);
-		go->active_map[stride * y + (x >> 3)] |=
+		if (stride * y + (x >> 3) < sizeof(go->active_map))
+			go->active_map[stride * y + (x >> 3)] |=
 					(go->modet_word & 1) << (x & 0x7);
 		go->modet_word >>= 1;
 	}
@@ -485,6 +449,15 @@
 			}
 			break;
 		case STATE_00_00_01:
+			if (buf[i] == 0xF8 && go->modet_enable == 0) {
+				/* MODET start code, but MODET not enabled */
+				store_byte(go->active_buf, 0x00);
+				store_byte(go->active_buf, 0x00);
+				store_byte(go->active_buf, 0x01);
+				store_byte(go->active_buf, 0xF8);
+				go->state = STATE_DATA;
+				break;
+			}
 			/* If this is the start of a new MPEG frame,
 			 * get a new buffer */
 			if ((go->format == GO7007_FORMAT_MPEG1 ||
diff --git a/drivers/staging/go7007/go7007-usb.c b/drivers/staging/go7007/go7007-usb.c
index 20ed930..bea9f4d 100644
--- a/drivers/staging/go7007/go7007-usb.c
+++ b/drivers/staging/go7007/go7007-usb.c
@@ -394,7 +394,7 @@
 		.num_i2c_devs	 = 1,
 		.i2c_devs	 = {
 			{
-				.type	= "wis_twTW2804",
+				.type	= "wis_tw2804",
 				.id	= I2C_DRIVERID_WIS_TW2804,
 				.addr	= 0x00, /* yes, really */
 			},
diff --git a/drivers/staging/go7007/go7007-v4l2.c b/drivers/staging/go7007/go7007-v4l2.c
index 46b4b9f..2b27d8d 100644
--- a/drivers/staging/go7007/go7007-v4l2.c
+++ b/drivers/staging/go7007/go7007-v4l2.c
@@ -252,23 +252,22 @@
 		go->modet_map[i] = 0;
 
 	if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) {
-		struct v4l2_format res;
+		struct v4l2_mbus_framefmt mbus_fmt;
 
-		if (fmt != NULL) {
-			res = *fmt;
-		} else {
-			res.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-			res.fmt.pix.width = width;
-		}
+		mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
+		if (fmt != NULL)
+			mbus_fmt.width = fmt->fmt.pix.width;
+		else
+			mbus_fmt.width = width;
 
 		if (height > sensor_height / 2) {
-			res.fmt.pix.height = height / 2;
+			mbus_fmt.height = height / 2;
 			go->encoder_v_halve = 0;
 		} else {
-			res.fmt.pix.height = height;
+			mbus_fmt.height = height;
 			go->encoder_v_halve = 1;
 		}
-		call_all(&go->v4l2_dev, video, s_fmt, &res);
+		call_all(&go->v4l2_dev, video, s_mbus_fmt, &mbus_fmt);
 	} else {
 		if (width <= sensor_width / 4) {
 			go->encoder_h_halve = 1;
diff --git a/drivers/staging/go7007/s2250-board.c b/drivers/staging/go7007/s2250-board.c
index 93f2604..e7736a9 100644
--- a/drivers/staging/go7007/s2250-board.c
+++ b/drivers/staging/go7007/s2250-board.c
@@ -23,7 +23,6 @@
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-common.h>
-#include <media/v4l2-i2c-drv.h>
 #include <media/v4l2-subdev.h>
 #include "go7007-priv.h"
 
@@ -479,12 +478,13 @@
 	return 0;
 }
 
-static int s2250_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+static int s2250_s_mbus_fmt(struct v4l2_subdev *sd,
+			struct v4l2_mbus_framefmt *fmt)
 {
 	struct s2250 *state = to_state(sd);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-	if (fmt->fmt.pix.height < 640) {
+	if (fmt->height < 640) {
 		write_reg_fp(client, 0x12b, state->reg12b_val | 0x400);
 		write_reg_fp(client, 0x140, 0x060);
 	} else {
@@ -555,7 +555,7 @@
 
 static const struct v4l2_subdev_video_ops s2250_video_ops = {
 	.s_routing = s2250_s_video_routing,
-	.s_fmt = s2250_s_fmt,
+	.s_mbus_fmt = s2250_s_mbus_fmt,
 };
 
 static const struct v4l2_subdev_ops s2250_ops = {
@@ -674,9 +674,25 @@
 };
 MODULE_DEVICE_TABLE(i2c, s2250_id);
 
-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
-	.name = "s2250",
-	.probe = s2250_probe,
-	.remove = s2250_remove,
-	.id_table = s2250_id,
+static struct i2c_driver s2250_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "s2250",
+	},
+	.probe		= s2250_probe,
+	.remove		= s2250_remove,
+	.id_table	= s2250_id,
 };
+
+static __init int init_s2250(void)
+{
+	return i2c_add_driver(&s2250_driver);
+}
+
+static __exit void exit_s2250(void)
+{
+	i2c_del_driver(&s2250_driver);
+}
+
+module_init(init_s2250);
+module_exit(exit_s2250);
diff --git a/drivers/staging/go7007/wis-ov7640.c b/drivers/staging/go7007/wis-ov7640.c
index 4f0cbdd..6bc9470 100644
--- a/drivers/staging/go7007/wis-ov7640.c
+++ b/drivers/staging/go7007/wis-ov7640.c
@@ -81,6 +81,7 @@
 	{ "wis_ov7640", 0 },
 	{ }
 };
+MODULE_DEVICE_TABLE(i2c, wis_ov7640_id);
 
 static struct i2c_driver wis_ov7640_driver = {
 	.driver = {
diff --git a/drivers/staging/go7007/wis-saa7113.c b/drivers/staging/go7007/wis-saa7113.c
index 72f5c1f..05e0e10 100644
--- a/drivers/staging/go7007/wis-saa7113.c
+++ b/drivers/staging/go7007/wis-saa7113.c
@@ -308,6 +308,7 @@
 	{ "wis_saa7113", 0 },
 	{ }
 };
+MODULE_DEVICE_TABLE(i2c, wis_saa7113_id);
 
 static struct i2c_driver wis_saa7113_driver = {
 	.driver = {
diff --git a/drivers/staging/go7007/wis-saa7115.c b/drivers/staging/go7007/wis-saa7115.c
index cd950b6..46cff59 100644
--- a/drivers/staging/go7007/wis-saa7115.c
+++ b/drivers/staging/go7007/wis-saa7115.c
@@ -441,6 +441,7 @@
 	{ "wis_saa7115", 0 },
 	{ }
 };
+MODULE_DEVICE_TABLE(i2c, wis_saa7115_id);
 
 static struct i2c_driver wis_saa7115_driver = {
 	.driver = {
diff --git a/drivers/staging/go7007/wis-sony-tuner.c b/drivers/staging/go7007/wis-sony-tuner.c
index 981c9b3..8f1b7d4 100644
--- a/drivers/staging/go7007/wis-sony-tuner.c
+++ b/drivers/staging/go7007/wis-sony-tuner.c
@@ -692,6 +692,7 @@
 	{ "wis_sony_tuner", 0 },
 	{ }
 };
+MODULE_DEVICE_TABLE(i2c, wis_sony_tuner_id);
 
 static struct i2c_driver wis_sony_tuner_driver = {
 	.driver = {
diff --git a/drivers/staging/go7007/wis-tw2804.c b/drivers/staging/go7007/wis-tw2804.c
index ee28a99..5b218c5 100644
--- a/drivers/staging/go7007/wis-tw2804.c
+++ b/drivers/staging/go7007/wis-tw2804.c
@@ -331,6 +331,7 @@
 	{ "wis_tw2804", 0 },
 	{ }
 };
+MODULE_DEVICE_TABLE(i2c, wis_tw2804_id);
 
 static struct i2c_driver wis_tw2804_driver = {
 	.driver = {
diff --git a/drivers/staging/go7007/wis-tw9903.c b/drivers/staging/go7007/wis-tw9903.c
index 80d4726..9230f4a 100644
--- a/drivers/staging/go7007/wis-tw9903.c
+++ b/drivers/staging/go7007/wis-tw9903.c
@@ -313,6 +313,7 @@
 	{ "wis_tw9903", 0 },
 	{ }
 };
+MODULE_DEVICE_TABLE(i2c, wis_tw9903_id);
 
 static struct i2c_driver wis_tw9903_driver = {
 	.driver = {
diff --git a/drivers/staging/go7007/wis-uda1342.c b/drivers/staging/go7007/wis-uda1342.c
index 5c4eb49..0127be2 100644
--- a/drivers/staging/go7007/wis-uda1342.c
+++ b/drivers/staging/go7007/wis-uda1342.c
@@ -86,6 +86,7 @@
 	{ "wis_uda1342", 0 },
 	{ }
 };
+MODULE_DEVICE_TABLE(i2c, wis_uda1342_id);
 
 static struct i2c_driver wis_uda1342_driver = {
 	.driver = {
diff --git a/drivers/staging/lirc/Kconfig b/drivers/staging/lirc/Kconfig
index 100c4d4..fa790db 100644
--- a/drivers/staging/lirc/Kconfig
+++ b/drivers/staging/lirc/Kconfig
@@ -53,7 +53,7 @@
 
 config LIRC_PARALLEL
 	tristate "Homebrew Parallel Port Receiver"
-	depends on LIRC_STAGING && PARPORT && !SMP
+	depends on LIRC_STAGING && PARPORT
 	help
 	  Driver for Homebrew Parallel Port Receivers
 
diff --git a/drivers/staging/lirc/lirc_igorplugusb.c b/drivers/staging/lirc/lirc_igorplugusb.c
index bce600e..0dc2c2b 100644
--- a/drivers/staging/lirc/lirc_igorplugusb.c
+++ b/drivers/staging/lirc/lirc_igorplugusb.c
@@ -54,10 +54,10 @@
 
 
 /* module identification */
-#define DRIVER_VERSION		"0.1"
+#define DRIVER_VERSION		"0.2"
 #define DRIVER_AUTHOR		\
 	"Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>"
-#define DRIVER_DESC		"USB remote driver for LIRC"
+#define DRIVER_DESC		"Igorplug USB remote driver for LIRC"
 #define DRIVER_NAME		"lirc_igorplugusb"
 
 /* debugging support */
@@ -201,7 +201,6 @@
 
 	/* usb */
 	struct usb_device *usbdev;
-	struct urb *urb_in;
 	int devnum;
 
 	unsigned char *buf_in;
@@ -216,28 +215,36 @@
 
 	/* handle sending (init strings) */
 	int send_flags;
-	wait_queue_head_t wait_out;
 };
 
 static int unregister_from_lirc(struct igorplug *ir)
 {
-	struct lirc_driver *d = ir->d;
+	struct lirc_driver *d;
 	int devnum;
 
-	if (!ir->d)
+	if (!ir) {
+		printk(KERN_ERR "%s: called with NULL device struct!\n",
+		       __func__);
 		return -EINVAL;
+	}
 
 	devnum = ir->devnum;
-	dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
+	d = ir->d;
 
+	if (!d) {
+		printk(KERN_ERR "%s: called with NULL lirc driver struct!\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	dprintk(DRIVER_NAME "[%d]: calling lirc_unregister_driver\n", devnum);
 	lirc_unregister_driver(d->minor);
 
-	printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
-
 	kfree(d);
 	ir->d = NULL;
 	kfree(ir);
-	return 0;
+
+	return devnum;
 }
 
 static int set_use_inc(void *data)
@@ -248,6 +255,7 @@
 		printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
 		return -EIO;
 	}
+
 	dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
 
 	if (!ir->usbdev)
@@ -264,9 +272,29 @@
 		printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
 		return;
 	}
+
 	dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
 }
 
+static void send_fragment(struct igorplug *ir, struct lirc_buffer *buf,
+			   int i, int max)
+{
+	int code;
+
+	/* MODE2: pulse/space (PULSE_BIT) in 1us units */
+	while (i < max) {
+		/* 1 Igor-tick = 85.333333 us */
+		code = (unsigned int)ir->buf_in[i] * 85 +
+			(unsigned int)ir->buf_in[i] / 3;
+		ir->last_time.tv_usec += code;
+		if (ir->in_space)
+			code |= PULSE_BIT;
+		lirc_buffer_write(buf, (unsigned char *)&code);
+		/* 1 chunk = CODE_LENGTH bytes */
+		ir->in_space ^= 1;
+		++i;
+	}
+}
 
 /**
  * Called in user context.
@@ -274,41 +302,32 @@
  * -ENODATA if none was available. This should add some number of bits
  * evenly divisible by code_length to the buffer
  */
-static int usb_remote_poll(void *data, struct lirc_buffer *buf)
+static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf)
 {
 	int ret;
 	struct igorplug *ir = (struct igorplug *)data;
 
-	if (!ir->usbdev)  /* Has the device been removed? */
+	if (!ir || !ir->usbdev)  /* Has the device been removed? */
 		return -ENODEV;
 
 	memset(ir->buf_in, 0, ir->len_in);
 
-	ret = usb_control_msg(
-	      ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
-	      GET_INFRACODE, USB_TYPE_VENDOR|USB_DIR_IN,
-	      0/* offset */, /*unused*/0,
-	      ir->buf_in, ir->len_in,
-	      /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
+	ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+			      GET_INFRACODE, USB_TYPE_VENDOR | USB_DIR_IN,
+			      0/* offset */, /*unused*/0,
+			      ir->buf_in, ir->len_in,
+			      /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
 	if (ret > 0) {
-		int i = DEVICE_HEADERLEN;
 		int code, timediff;
 		struct timeval now;
 
-		if (ret <= 1)  /* ACK packet has 1 byte --> ignore */
+		/* ACK packet has 1 byte --> ignore */
+		if (ret < DEVICE_HEADERLEN)
 			return -ENODATA;
 
 		dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n",
 			ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]);
 
-		if (ir->buf_in[2] != 0) {
-			printk(DRIVER_NAME "[%d]: Device buffer overrun.\n",
-				ir->devnum);
-			/* start at earliest byte */
-			i = DEVICE_HEADERLEN + ir->buf_in[2];
-			/* where are we now? space, gap or pulse? */
-		}
-
 		do_gettimeofday(&now);
 		timediff = now.tv_sec - ir->last_time.tv_sec;
 		if (timediff + 1 > PULSE_MASK / 1000000)
@@ -325,18 +344,20 @@
 		lirc_buffer_write(buf, (unsigned char *)&code);
 		ir->in_space = 1;   /* next comes a pulse */
 
-		/* MODE2: pulse/space (PULSE_BIT) in 1us units */
-
-		while (i < ret) {
-			/* 1 Igor-tick = 85.333333 us */
-			code = (unsigned int)ir->buf_in[i] * 85
-				+ (unsigned int)ir->buf_in[i] / 3;
-			if (ir->in_space)
-				code |= PULSE_BIT;
-			lirc_buffer_write(buf, (unsigned char *)&code);
-			/* 1 chunk = CODE_LENGTH bytes */
-			ir->in_space ^= 1;
-			++i;
+		if (ir->buf_in[2] == 0)
+			send_fragment(ir, buf, DEVICE_HEADERLEN, ret);
+		else {
+			printk(KERN_WARNING DRIVER_NAME
+			       "[%d]: Device buffer overrun.\n", ir->devnum);
+			/* HHHNNNNNNNNNNNOOOOOOOO H = header
+			      <---[2]--->         N = newer
+			   <---------ret--------> O = older */
+			ir->buf_in[2] %= ret - DEVICE_HEADERLEN; /* sanitize */
+			/* keep even-ness to not desync pulse/pause */
+			send_fragment(ir, buf, DEVICE_HEADERLEN +
+				      ir->buf_in[2] - (ir->buf_in[2] & 1), ret);
+			send_fragment(ir, buf, DEVICE_HEADERLEN,
+				      DEVICE_HEADERLEN + ir->buf_in[2]);
 		}
 
 		ret = usb_control_msg(
@@ -358,12 +379,12 @@
 
 
 
-static int usb_remote_probe(struct usb_interface *intf,
-				const struct usb_device_id *id)
+static int igorplugusb_remote_probe(struct usb_interface *intf,
+				    const struct usb_device_id *id)
 {
 	struct usb_device *dev = NULL;
 	struct usb_host_interface *idesc = NULL;
-	struct usb_host_endpoint *ep_ctl2;
+	struct usb_endpoint_descriptor *ep;
 	struct igorplug *ir = NULL;
 	struct lirc_driver *driver = NULL;
 	int devnum, pipe, maxp;
@@ -380,20 +401,21 @@
 
 	if (idesc->desc.bNumEndpoints != 1)
 		return -ENODEV;
-	ep_ctl2 = idesc->endpoint;
-	if (((ep_ctl2->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+
+	ep = &idesc->endpoint->desc;
+	if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
 	    != USB_DIR_IN)
-	    || (ep_ctl2->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+	    || (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
 	    != USB_ENDPOINT_XFER_CONTROL)
 		return -ENODEV;
-	pipe = usb_rcvctrlpipe(dev, ep_ctl2->desc.bEndpointAddress);
+
+	pipe = usb_rcvctrlpipe(dev, ep->bEndpointAddress);
 	devnum = dev->devnum;
 	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
 
-	dprintk(DRIVER_NAME "[%d]: bytes_in_key=%lu maxp=%d\n",
+	dprintk(DRIVER_NAME "[%d]: bytes_in_key=%zu maxp=%d\n",
 		devnum, CODE_LENGTH, maxp);
 
-
 	mem_failure = 0;
 	ir = kzalloc(sizeof(struct igorplug), GFP_KERNEL);
 	if (!ir) {
@@ -406,9 +428,8 @@
 		goto mem_failure_switch;
 	}
 
-	ir->buf_in = usb_alloc_coherent(dev,
-			      DEVICE_BUFLEN+DEVICE_HEADERLEN,
-			      GFP_ATOMIC, &ir->dma_in);
+	ir->buf_in = usb_alloc_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN,
+					GFP_ATOMIC, &ir->dma_in);
 	if (!ir->buf_in) {
 		mem_failure = 3;
 		goto mem_failure_switch;
@@ -424,12 +445,10 @@
 	driver->set_use_inc = &set_use_inc;
 	driver->set_use_dec = &set_use_dec;
 	driver->sample_rate = sample_rate;    /* per second */
-	driver->add_to_buf = &usb_remote_poll;
+	driver->add_to_buf = &igorplugusb_remote_poll;
 	driver->dev = &intf->dev;
 	driver->owner = THIS_MODULE;
 
-	init_waitqueue_head(&ir->wait_out);
-
 	minor = lirc_register_driver(driver);
 	if (minor < 0)
 		mem_failure = 9;
@@ -438,7 +457,7 @@
 
 	switch (mem_failure) {
 	case 9:
-		usb_free_coherent(dev, DEVICE_BUFLEN+DEVICE_HEADERLEN,
+		usb_free_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN,
 			ir->buf_in, ir->dma_in);
 	case 3:
 		kfree(driver);
@@ -454,7 +473,7 @@
 	ir->d = driver;
 	ir->devnum = devnum;
 	ir->usbdev = dev;
-	ir->len_in = DEVICE_BUFLEN+DEVICE_HEADERLEN;
+	ir->len_in = DEVICE_BUFLEN + DEVICE_HEADERLEN;
 	ir->in_space = 1; /* First mode2 event is a space. */
 	do_gettimeofday(&ir->last_time);
 
@@ -484,63 +503,64 @@
 }
 
 
-static void usb_remote_disconnect(struct usb_interface *intf)
+static void igorplugusb_remote_disconnect(struct usb_interface *intf)
 {
-	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_device *usbdev = interface_to_usbdev(intf);
 	struct igorplug *ir = usb_get_intfdata(intf);
+	struct device *dev = &intf->dev;
+	int devnum;
+
 	usb_set_intfdata(intf, NULL);
 
 	if (!ir || !ir->d)
 		return;
 
 	ir->usbdev = NULL;
-	wake_up_all(&ir->wait_out);
 
-	usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in);
+	usb_free_coherent(usbdev, ir->len_in, ir->buf_in, ir->dma_in);
 
-	unregister_from_lirc(ir);
+	devnum = unregister_from_lirc(ir);
+
+	dev_info(dev, DRIVER_NAME "[%d]: %s done\n", devnum, __func__);
 }
 
-static struct usb_device_id usb_remote_id_table[] = {
+static struct usb_device_id igorplugusb_remote_id_table[] = {
 	/* Igor Plug USB (Atmel's Manufact. ID) */
 	{ USB_DEVICE(0x03eb, 0x0002) },
+	/* Fit PC2 Infrared Adapter */
+	{ USB_DEVICE(0x03eb, 0x21fe) },
 
 	/* Terminating entry */
 	{ }
 };
 
-static struct usb_driver usb_remote_driver = {
+static struct usb_driver igorplugusb_remote_driver = {
 	.name =		DRIVER_NAME,
-	.probe =	usb_remote_probe,
-	.disconnect =	usb_remote_disconnect,
-	.id_table =	usb_remote_id_table
+	.probe =	igorplugusb_remote_probe,
+	.disconnect =	igorplugusb_remote_disconnect,
+	.id_table =	igorplugusb_remote_id_table
 };
 
-static int __init usb_remote_init(void)
+static int __init igorplugusb_remote_init(void)
 {
-	int i;
+	int ret = 0;
 
-	printk(KERN_INFO "\n"
-	       DRIVER_NAME ": " DRIVER_DESC " v" DRIVER_VERSION "\n");
-	printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
-	dprintk(DRIVER_NAME ": debug mode enabled\n");
+	dprintk(DRIVER_NAME ": loaded, debug mode enabled\n");
 
-	i = usb_register(&usb_remote_driver);
-	if (i < 0) {
-		printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
-		return -ENODEV;
-	}
+	ret = usb_register(&igorplugusb_remote_driver);
+	if (ret)
+		printk(KERN_ERR DRIVER_NAME ": usb register failed!\n");
 
-	return 0;
+	return ret;
 }
 
-static void __exit usb_remote_exit(void)
+static void __exit igorplugusb_remote_exit(void)
 {
-	usb_deregister(&usb_remote_driver);
+	usb_deregister(&igorplugusb_remote_driver);
 }
 
-module_init(usb_remote_init);
-module_exit(usb_remote_exit);
+module_init(igorplugusb_remote_init);
+module_exit(igorplugusb_remote_exit);
 
 #include <linux/vermagic.h>
 MODULE_INFO(vermagic, VERMAGIC_STRING);
@@ -548,8 +568,10 @@
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(usb, usb_remote_id_table);
+MODULE_DEVICE_TABLE(usb, igorplugusb_remote_id_table);
 
 module_param(sample_rate, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(sample_rate, "Sampling rate in Hz (default: 100)");
 
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/staging/lirc/lirc_it87.c b/drivers/staging/lirc/lirc_it87.c
index 543c5c3b..929ae57 100644
--- a/drivers/staging/lirc/lirc_it87.c
+++ b/drivers/staging/lirc/lirc_it87.c
@@ -239,8 +239,7 @@
 static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
 {
 	int retval = 0;
-	unsigned long value = 0;
-	unsigned int ivalue;
+	__u32 value = 0;
 	unsigned long hw_flags;
 
 	if (cmd == LIRC_GET_FEATURES)
@@ -256,24 +255,24 @@
 	case LIRC_GET_FEATURES:
 	case LIRC_GET_SEND_MODE:
 	case LIRC_GET_REC_MODE:
-		retval = put_user(value, (unsigned long *) arg);
+		retval = put_user(value, (__u32 *) arg);
 		break;
 
 	case LIRC_SET_SEND_MODE:
 	case LIRC_SET_REC_MODE:
-		retval = get_user(value, (unsigned long *) arg);
+		retval = get_user(value, (__u32 *) arg);
 		break;
 
 	case LIRC_SET_SEND_CARRIER:
-		retval = get_user(ivalue, (unsigned int *) arg);
+		retval = get_user(value, (__u32 *) arg);
 		if (retval)
 			return retval;
-		ivalue /= 1000;
-		if (ivalue > IT87_CIR_FREQ_MAX ||
-		    ivalue < IT87_CIR_FREQ_MIN)
+		value /= 1000;
+		if (value > IT87_CIR_FREQ_MAX ||
+		    value < IT87_CIR_FREQ_MIN)
 			return -EINVAL;
 
-		it87_freq = ivalue;
+		it87_freq = value;
 
 		spin_lock_irqsave(&hardware_lock, hw_flags);
 		outb(((inb(io + IT87_CIR_TCR2) & IT87_CIR_TCR2_TXMPW) |
@@ -340,6 +339,9 @@
 	.write		= lirc_write,
 	.poll		= lirc_poll,
 	.unlocked_ioctl	= lirc_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= lirc_ioctl,
+#endif
 	.open		= lirc_open,
 	.release	= lirc_close,
 	.llseek		= noop_llseek,
@@ -964,10 +966,11 @@
 	printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
 }
 
-/* SECTION: PNP for ITE8704/18 */
+/* SECTION: PNP for ITE8704/13/18 */
 
 static const struct pnp_device_id pnp_dev_table[] = {
 	{"ITE8704", 0},
+	{"ITE8713", 0},
 	{}
 };
 
diff --git a/drivers/staging/lirc/lirc_ite8709.c b/drivers/staging/lirc/lirc_ite8709.c
index 9352f45..cb20cfd 100644
--- a/drivers/staging/lirc/lirc_ite8709.c
+++ b/drivers/staging/lirc/lirc_ite8709.c
@@ -102,8 +102,8 @@
 	int io;
 	int irq;
 	spinlock_t hardware_lock;
-	unsigned long long acc_pulse;
-	unsigned long long acc_space;
+	__u64 acc_pulse;
+	__u64 acc_space;
 	char lastbit;
 	struct timeval last_tv;
 	struct lirc_driver driver;
@@ -220,7 +220,7 @@
 }
 
 static void ite8709_add_read_queue(struct ite8709_device *dev, int flag,
-					unsigned long long val)
+				   __u64 val)
 {
 	int value;
 
diff --git a/drivers/staging/lirc/lirc_parallel.c b/drivers/staging/lirc/lirc_parallel.c
index 6da4a8c..884904c 100644
--- a/drivers/staging/lirc/lirc_parallel.c
+++ b/drivers/staging/lirc/lirc_parallel.c
@@ -24,10 +24,6 @@
 
 /*** Includes ***/
 
-#ifdef CONFIG_SMP
-#error "--- Sorry, this driver is not SMP safe. ---"
-#endif
-
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
@@ -301,9 +297,9 @@
 
 	if (signal != 0) {
 		/* ajust value to usecs */
-		unsigned long long helper;
+		__u64 helper;
 
-		helper = ((unsigned long long) signal)*1000000;
+		helper = ((__u64) signal)*1000000;
 		do_div(helper, timer);
 		signal = (long) helper;
 
@@ -404,9 +400,9 @@
 
 	/* adjust values from usecs */
 	for (i = 0; i < count; i++) {
-		unsigned long long helper;
+		__u64 helper;
 
-		helper = ((unsigned long long) wbuf[i])*timer;
+		helper = ((__u64) wbuf[i])*timer;
 		do_div(helper, 1000000);
 		wbuf[i] = (int) helper;
 	}
@@ -464,48 +460,48 @@
 static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
 {
 	int result;
-	unsigned long features = LIRC_CAN_SET_TRANSMITTER_MASK |
-				 LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
-	unsigned long mode;
-	unsigned int ivalue;
+	__u32 features = LIRC_CAN_SET_TRANSMITTER_MASK |
+			 LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
+	__u32 mode;
+	__u32 value;
 
 	switch (cmd) {
 	case LIRC_GET_FEATURES:
-		result = put_user(features, (unsigned long *) arg);
+		result = put_user(features, (__u32 *) arg);
 		if (result)
 			return result;
 		break;
 	case LIRC_GET_SEND_MODE:
-		result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
+		result = put_user(LIRC_MODE_PULSE, (__u32 *) arg);
 		if (result)
 			return result;
 		break;
 	case LIRC_GET_REC_MODE:
-		result = put_user(LIRC_MODE_MODE2, (unsigned long *) arg);
+		result = put_user(LIRC_MODE_MODE2, (__u32 *) arg);
 		if (result)
 			return result;
 		break;
 	case LIRC_SET_SEND_MODE:
-		result = get_user(mode, (unsigned long *) arg);
+		result = get_user(mode, (__u32 *) arg);
 		if (result)
 			return result;
 		if (mode != LIRC_MODE_PULSE)
 			return -EINVAL;
 		break;
 	case LIRC_SET_REC_MODE:
-		result = get_user(mode, (unsigned long *) arg);
+		result = get_user(mode, (__u32 *) arg);
 		if (result)
 			return result;
 		if (mode != LIRC_MODE_MODE2)
 			return -ENOSYS;
 		break;
 	case LIRC_SET_TRANSMITTER_MASK:
-		result = get_user(ivalue, (unsigned int *) arg);
+		result = get_user(value, (__u32 *) arg);
 		if (result)
 			return result;
-		if ((ivalue & LIRC_PARALLEL_TRANSMITTER_MASK) != ivalue)
+		if ((value & LIRC_PARALLEL_TRANSMITTER_MASK) != value)
 			return LIRC_PARALLEL_MAX_TRANSMITTERS;
-		tx_mask = ivalue;
+		tx_mask = value;
 		break;
 	default:
 		return -ENOIOCTLCMD;
@@ -546,6 +542,9 @@
 	.write		= lirc_write,
 	.poll		= lirc_poll,
 	.unlocked_ioctl	= lirc_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= lirc_ioctl,
+#endif
 	.open		= lirc_open,
 	.release	= lirc_close
 };
@@ -576,28 +575,6 @@
 static int pf(void *handle);
 static void kf(void *handle);
 
-static struct timer_list poll_timer;
-static void poll_state(unsigned long ignored);
-
-static void poll_state(unsigned long ignored)
-{
-	printk(KERN_NOTICE "%s: time\n",
-	       LIRC_DRIVER_NAME);
-	del_timer(&poll_timer);
-	if (is_claimed)
-		return;
-	kf(NULL);
-	if (!is_claimed) {
-		printk(KERN_NOTICE "%s: could not claim port, giving up\n",
-		       LIRC_DRIVER_NAME);
-		init_timer(&poll_timer);
-		poll_timer.expires = jiffies + HZ;
-		poll_timer.data = (unsigned long)current;
-		poll_timer.function = poll_state;
-		add_timer(&poll_timer);
-	}
-}
-
 static int pf(void *handle)
 {
 	parport_disable_irq(pport);
diff --git a/drivers/staging/lirc/lirc_serial.c b/drivers/staging/lirc/lirc_serial.c
index 8da3824..971844b 100644
--- a/drivers/staging/lirc/lirc_serial.c
+++ b/drivers/staging/lirc/lirc_serial.c
@@ -372,7 +372,7 @@
 static int init_timing_params(unsigned int new_duty_cycle,
 		unsigned int new_freq)
 {
-	unsigned long long loops_per_sec, work;
+	__u64 loops_per_sec, work;
 
 	duty_cycle = new_duty_cycle;
 	freq = new_freq;
@@ -987,8 +987,7 @@
 static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
 {
 	int result;
-	unsigned long value;
-	unsigned int ivalue;
+	__u32 value;
 
 	switch (cmd) {
 	case LIRC_GET_SEND_MODE:
@@ -997,7 +996,7 @@
 
 		result = put_user(LIRC_SEND2MODE
 				  (hardware[type].features&LIRC_CAN_SEND_MASK),
-				  (unsigned long *) arg);
+				  (__u32 *) arg);
 		if (result)
 			return result;
 		break;
@@ -1006,7 +1005,7 @@
 		if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
 			return -ENOIOCTLCMD;
 
-		result = get_user(value, (unsigned long *) arg);
+		result = get_user(value, (__u32 *) arg);
 		if (result)
 			return result;
 		/* only LIRC_MODE_PULSE supported */
@@ -1023,12 +1022,12 @@
 		if (!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
 			return -ENOIOCTLCMD;
 
-		result = get_user(ivalue, (unsigned int *) arg);
+		result = get_user(value, (__u32 *) arg);
 		if (result)
 			return result;
-		if (ivalue <= 0 || ivalue > 100)
+		if (value <= 0 || value > 100)
 			return -EINVAL;
-		return init_timing_params(ivalue, freq);
+		return init_timing_params(value, freq);
 		break;
 
 	case LIRC_SET_SEND_CARRIER:
@@ -1036,12 +1035,12 @@
 		if (!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER))
 			return -ENOIOCTLCMD;
 
-		result = get_user(ivalue, (unsigned int *) arg);
+		result = get_user(value, (__u32 *) arg);
 		if (result)
 			return result;
-		if (ivalue > 500000 || ivalue < 20000)
+		if (value > 500000 || value < 20000)
 			return -EINVAL;
-		return init_timing_params(duty_cycle, ivalue);
+		return init_timing_params(duty_cycle, value);
 		break;
 
 	default:
@@ -1054,6 +1053,9 @@
 	.owner		= THIS_MODULE,
 	.write		= lirc_write,
 	.unlocked_ioctl	= lirc_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= lirc_ioctl,
+#endif
 	.read		= lirc_dev_fop_read,
 	.poll		= lirc_dev_fop_poll,
 	.open		= lirc_dev_fop_open,
diff --git a/drivers/staging/lirc/lirc_sir.c b/drivers/staging/lirc/lirc_sir.c
index 2478871..c553ab6 100644
--- a/drivers/staging/lirc/lirc_sir.c
+++ b/drivers/staging/lirc/lirc_sir.c
@@ -336,9 +336,8 @@
 static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
 {
 	int retval = 0;
-	unsigned long value = 0;
+	__u32 value = 0;
 #ifdef LIRC_ON_SA1100
-	unsigned int ivalue;
 
 	if (cmd == LIRC_GET_FEATURES)
 		value = LIRC_CAN_SEND_PULSE |
@@ -362,22 +361,22 @@
 	case LIRC_GET_FEATURES:
 	case LIRC_GET_SEND_MODE:
 	case LIRC_GET_REC_MODE:
-		retval = put_user(value, (unsigned long *) arg);
+		retval = put_user(value, (__u32 *) arg);
 		break;
 
 	case LIRC_SET_SEND_MODE:
 	case LIRC_SET_REC_MODE:
-		retval = get_user(value, (unsigned long *) arg);
+		retval = get_user(value, (__u32 *) arg);
 		break;
 #ifdef LIRC_ON_SA1100
 	case LIRC_SET_SEND_DUTY_CYCLE:
-		retval = get_user(ivalue, (unsigned int *) arg);
+		retval = get_user(value, (__u32 *) arg);
 		if (retval)
 			return retval;
-		if (ivalue <= 0 || ivalue > 100)
+		if (value <= 0 || value > 100)
 			return -EINVAL;
-		/* (ivalue/100)*(1000000/freq) */
-		duty_cycle = ivalue;
+		/* (value/100)*(1000000/freq) */
+		duty_cycle = value;
 		pulse_width = (unsigned long) duty_cycle*10000/freq;
 		space_width = (unsigned long) 1000000L/freq-pulse_width;
 		if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
@@ -386,12 +385,12 @@
 			space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
 		break;
 	case LIRC_SET_SEND_CARRIER:
-		retval = get_user(ivalue, (unsigned int *) arg);
+		retval = get_user(value, (__u32 *) arg);
 		if (retval)
 			return retval;
-		if (ivalue > 500000 || ivalue < 20000)
+		if (value > 500000 || value < 20000)
 			return -EINVAL;
-		freq = ivalue;
+		freq = value;
 		pulse_width = (unsigned long) duty_cycle*10000/freq;
 		space_width = (unsigned long) 1000000L/freq-pulse_width;
 		if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
@@ -457,6 +456,9 @@
 	.write		= lirc_write,
 	.poll		= lirc_poll,
 	.unlocked_ioctl	= lirc_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= lirc_ioctl,
+#endif
 	.open		= lirc_dev_fop_open,
 	.release	= lirc_dev_fop_close,
 	.llseek		= no_llseek,
diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c
index 100caab1..d920644 100644
--- a/drivers/staging/lirc/lirc_zilog.c
+++ b/drivers/staging/lirc/lirc_zilog.c
@@ -1139,6 +1139,9 @@
 	.write		= write,
 	.poll		= poll,
 	.unlocked_ioctl	= ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= ioctl,
+#endif
 	.open		= open,
 	.release	= close
 };
diff --git a/drivers/staging/stradis/Kconfig b/drivers/staging/stradis/Kconfig
new file mode 100644
index 0000000..92e8911
--- /dev/null
+++ b/drivers/staging/stradis/Kconfig
@@ -0,0 +1,7 @@
+config VIDEO_STRADIS
+        tristate "Stradis 4:2:2 MPEG-2 video driver (DEPRECATED)"
+        depends on EXPERIMENTAL && PCI && VIDEO_V4L1 && VIRT_TO_BUS
+        help
+          Say Y here to enable support for the Stradis 4:2:2 MPEG-2 video
+          driver for PCI.  There is a product page at
+          <http://www.stradis.com/>.
diff --git a/drivers/staging/stradis/Makefile b/drivers/staging/stradis/Makefile
new file mode 100644
index 0000000..0f1feab
--- /dev/null
+++ b/drivers/staging/stradis/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_VIDEO_STRADIS) += stradis.o
+
+EXTRA_CFLAGS += -Idrivers/media/video
diff --git a/drivers/staging/stradis/TODO b/drivers/staging/stradis/TODO
new file mode 100644
index 0000000..f48150f
--- /dev/null
+++ b/drivers/staging/stradis/TODO
@@ -0,0 +1,6 @@
+This is an obsolete driver for ancient stradis hardware.
+We couldn't find anyone with this hardware in order to port it to use V4L2.
+
+If nobody take care on it, the driver will be removed for 2.6.38.
+
+Please send patches to linux-media@vger.kernel.org
diff --git a/drivers/media/video/stradis.c b/drivers/staging/stradis/stradis.c
similarity index 100%
rename from drivers/media/video/stradis.c
rename to drivers/staging/stradis/stradis.c
diff --git a/drivers/staging/tm6000/TODO b/drivers/staging/tm6000/TODO
new file mode 100644
index 0000000..34780fc
--- /dev/null
+++ b/drivers/staging/tm6000/TODO
@@ -0,0 +1,6 @@
+There a few things to do before putting this driver in production:
+	- CodingStyle;
+	- Fix audio;
+	- Fix some panic/OOPS conditions.
+
+Please send patches to linux-media@vger.kernel.org
diff --git a/drivers/staging/tm6000/tm6000-alsa.c b/drivers/staging/tm6000/tm6000-alsa.c
index 087137d..e379e3e 100644
--- a/drivers/staging/tm6000/tm6000-alsa.c
+++ b/drivers/staging/tm6000/tm6000-alsa.c
@@ -160,15 +160,15 @@
 		SNDRV_PCM_INFO_MMAP_VALID,
 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
 
-	.rates =		SNDRV_PCM_RATE_48000,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS,
 	.rate_min =		48000,
 	.rate_max =		48000,
 	.channels_min = 2,
 	.channels_max = 2,
-	.period_bytes_min = 62720,
-	.period_bytes_max = 62720,
+	.period_bytes_min = 64,
+	.period_bytes_max = 12544,
 	.periods_min = 1,
-	.periods_max = 1024,
+	.periods_max = 98,
 	.buffer_bytes_max = 62720 * 8,
 };
 
@@ -201,6 +201,14 @@
  */
 static int snd_tm6000_close(struct snd_pcm_substream *substream)
 {
+	struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+	struct tm6000_core *core = chip->core;
+
+	if (atomic_read(&core->stream_started) > 0) {
+		atomic_set(&core->stream_started, 0);
+		schedule_work(&core->wq_trigger);
+	}
+
 	return 0;
 }
 
@@ -211,38 +219,67 @@
 	struct snd_pcm_runtime *runtime;
 	int period_elapsed = 0;
 	unsigned int stride, buf_pos;
+	int length;
 
-	if (!size || !substream)
+	if (atomic_read(&core->stream_started) == 0)
+		return 0;
+
+	if (!size || !substream) {
+		dprintk(1, "substream was NULL\n");
 		return -EINVAL;
+	}
 
 	runtime = substream->runtime;
-	if (!runtime || !runtime->dma_area)
+	if (!runtime || !runtime->dma_area) {
+		dprintk(1, "runtime was NULL\n");
 		return -EINVAL;
+	}
 
 	buf_pos = chip->buf_pos;
 	stride = runtime->frame_bits >> 3;
 
+	if (stride == 0) {
+		dprintk(1, "stride is zero\n");
+		return -EINVAL;
+	}
+
+	length = size / stride;
+	if (length == 0) {
+		dprintk(1, "%s: length was zero\n", __func__);
+		return -EINVAL;
+	}
+
 	dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size,
 		runtime->dma_area, buf_pos,
 		(unsigned int)runtime->buffer_size, stride);
 
-	if (buf_pos + size >= runtime->buffer_size * stride) {
-		unsigned int cnt = runtime->buffer_size * stride - buf_pos;
-		memcpy(runtime->dma_area + buf_pos, buf, cnt);
-		memcpy(runtime->dma_area, buf + cnt, size - cnt);
+	if (buf_pos + length >= runtime->buffer_size) {
+		unsigned int cnt = runtime->buffer_size - buf_pos;
+		memcpy(runtime->dma_area + buf_pos * stride, buf, cnt * stride);
+		memcpy(runtime->dma_area, buf + cnt * stride,
+			length * stride - cnt * stride);
 	} else
-		memcpy(runtime->dma_area + buf_pos, buf, size);
+		memcpy(runtime->dma_area + buf_pos * stride, buf,
+			length * stride);
 
-	chip->buf_pos += size;
-	if (chip->buf_pos >= runtime->buffer_size * stride)
-		chip->buf_pos -= runtime->buffer_size * stride;
+#ifndef NO_PCM_LOCK
+       snd_pcm_stream_lock(substream);
+#endif
 
-	chip->period_pos += size;
+	chip->buf_pos += length;
+	if (chip->buf_pos >= runtime->buffer_size)
+		chip->buf_pos -= runtime->buffer_size;
+
+	chip->period_pos += length;
 	if (chip->period_pos >= runtime->period_size) {
 		chip->period_pos -= runtime->period_size;
 		period_elapsed = 1;
 	}
 
+#ifndef NO_PCM_LOCK
+       snd_pcm_stream_unlock(substream);
+#endif
+
 	if (period_elapsed)
 		snd_pcm_period_elapsed(substream);
 
@@ -272,8 +309,12 @@
 static int snd_tm6000_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+	struct tm6000_core *core = chip->core;
 
-	_tm6000_stop_audio_dma(chip);
+	if (atomic_read(&core->stream_started) > 0) {
+		atomic_set(&core->stream_started, 0);
+		schedule_work(&core->wq_trigger);
+	}
 
 	return 0;
 }
@@ -295,30 +336,42 @@
 /*
  * trigger callback
  */
+static void audio_trigger(struct work_struct *work)
+{
+	struct tm6000_core *core = container_of(work, struct tm6000_core,
+						wq_trigger);
+	struct snd_tm6000_card *chip = core->adev;
+
+	if (atomic_read(&core->stream_started)) {
+		dprintk(1, "starting capture");
+		_tm6000_start_audio_dma(chip);
+	} else {
+		dprintk(1, "stopping capture");
+		_tm6000_stop_audio_dma(chip);
+	}
+}
+
 static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 	struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
-	int err;
-
-	spin_lock(&chip->reg_lock);
+	struct tm6000_core *core = chip->core;
+	int err = 0;
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
-		err = _tm6000_start_audio_dma(chip);
+		atomic_set(&core->stream_started, 1);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
-		err = _tm6000_stop_audio_dma(chip);
+		atomic_set(&core->stream_started, 0);
 		break;
 	default:
 		err = -EINVAL;
 		break;
 	}
-
-	spin_unlock(&chip->reg_lock);
+	schedule_work(&core->wq_trigger);
 
 	return err;
 }
-
 /*
  * pointer callback
  */
@@ -411,6 +464,7 @@
 
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops);
 
+	INIT_WORK(&dev->wq_trigger, audio_trigger);
 	rc = snd_card_register(card);
 	if (rc < 0)
 		goto error;
diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c
index 9d091c3..664e603 100644
--- a/drivers/staging/tm6000/tm6000-cards.c
+++ b/drivers/staging/tm6000/tm6000-cards.c
@@ -1,20 +1,20 @@
 /*
-   tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices
-
-   Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation version 2
-
-   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.
+ *  tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ *  Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/init.h>
@@ -349,7 +349,7 @@
 			       dev->gpio.tuner_reset, 0x01);
 		break;
 	}
-	return (rc);
+	return rc;
 }
 EXPORT_SYMBOL_GPL(tm6000_xc5000_callback);
 
@@ -545,7 +545,7 @@
 
 	/* Load tuner module */
 	v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
-		"tuner", "tuner", dev->tuner_addr, NULL);
+		NULL, "tuner", dev->tuner_addr, NULL);
 
 	memset(&tun_setup, 0, sizeof(tun_setup));
 	tun_setup.type = dev->tuner_type;
@@ -683,7 +683,7 @@
 
 	if (dev->caps.has_tda9874)
 		v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
-			"tvaudio", "tvaudio", I2C_ADDR_TDA9874, NULL);
+			NULL, "tvaudio", I2C_ADDR_TDA9874, NULL);
 
 	/* register and initialize V4L2 */
 	rc = tm6000_v4l2_register(dev);
@@ -909,8 +909,6 @@
 
 	printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name);
 
-	mutex_lock(&dev->lock);
-
 	tm6000_ir_fini(dev);
 
 	if (dev->gpio.power_led) {
@@ -945,7 +943,6 @@
 	tm6000_close_extension(dev);
 	tm6000_remove_from_devlist(dev);
 
-	mutex_unlock(&dev->lock);
 	kfree(dev);
 }
 
diff --git a/drivers/staging/tm6000/tm6000-core.c b/drivers/staging/tm6000/tm6000-core.c
index cded411..df3f187 100644
--- a/drivers/staging/tm6000/tm6000-core.c
+++ b/drivers/staging/tm6000/tm6000-core.c
@@ -1,23 +1,23 @@
 /*
-   tm6000-core.c - driver for TM5600/TM6000/TM6010 USB video capture devices
-
-   Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
-
-   Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
-       - DVB-T support
-
-   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 version 2
-
-   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.
+ *  tm6000-core.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ *  Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ *  Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
+ *      - DVB-T support
+ *
+ *  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 version 2
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/module.h>
@@ -30,14 +30,13 @@
 #include <media/v4l2-common.h>
 #include <media/tuner.h>
 
-#define USB_TIMEOUT	5*HZ /* ms */
+#define USB_TIMEOUT	(5 * HZ) /* ms */
 
 int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req,
 			  u16 value, u16 index, u8 *buf, u16 len)
 {
 	int          ret, i;
 	unsigned int pipe;
-	static int   ini = 0, last = 0, n = 0;
 	u8	     *data = NULL;
 
 	if (len)
@@ -52,19 +51,12 @@
 	}
 
 	if (tm6000_debug & V4L2_DEBUG_I2C) {
-		if (!ini)
-			last = ini = jiffies;
+		printk("(dev %p, pipe %08x): ", dev->udev, pipe);
 
-		printk("%06i (dev %p, pipe %08x): ", n, dev->udev, pipe);
-
-		printk("%s: %06u ms %06u ms %02x %02x %02x %02x %02x %02x %02x %02x ",
+		printk("%s: %02x %02x %02x %02x %02x %02x %02x %02x ",
 			(req_type & USB_DIR_IN) ? " IN" : "OUT",
-			jiffies_to_msecs(jiffies-last),
-			jiffies_to_msecs(jiffies-ini),
 			req_type, req, value&0xff, value>>8, index&0xff,
 			index>>8, len&0xff, len>>8);
-		last = jiffies;
-		n++;
 
 		if (!(req_type & USB_DIR_IN)) {
 			printk(">>> ");
@@ -186,21 +178,17 @@
 	}
 }
 
-int tm6000_init_analog_mode(struct tm6000_core *dev)
+static void tm6000_set_vbi(struct tm6000_core *dev)
 {
+	/*
+	 * FIXME:
+	 * VBI lines and start/end are different between 60Hz and 50Hz
+	 * So, it is very likely that we need to change the config to
+	 * something that takes it into account, doing something different
+	 * if (dev->norm & V4L2_STD_525_60)
+	 */
+
 	if (dev->dev_type == TM6010) {
-		int val;
-
-		/* Enable video */
-		val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0);
-		val |= 0x60;
-		tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
-		val = tm6000_get_reg(dev,
-			TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0);
-		val &= ~0x40;
-		tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, val);
-
-		/* Init teletext */
 		tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01);
 		tm6000_set_reg(dev, TM6010_REQ07_R41_TELETEXT_VBI_CODE1, 0x27);
 		tm6000_set_reg(dev, TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55);
@@ -249,44 +237,26 @@
 		tm6000_set_reg(dev, TM6010_REQ07_R5B_VBI_TELETEXT_DTO0, 0x4c);
 		tm6000_set_reg(dev, TM6010_REQ07_R40_TELETEXT_VBI_CODE0, 0x01);
 		tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00);
+	}
+}
 
+int tm6000_init_analog_mode(struct tm6000_core *dev)
+{
+	struct v4l2_frequency f;
 
-		/* Init audio */
-		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
-		tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
-		tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
-		tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0xa0);
-		tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x05);
-		tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x06);
-		tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, 0x00);
-		tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, 0x00);
-		tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x08);
-		tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91);
-		tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1, 0x20);
-		tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x12);
-		tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x20);
-		tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1, 0xf0);
-		tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2, 0x80);
-		tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1, 0xc0);
-		tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2, 0x80);
-		tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12);
-		tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T, 0xfe);
-		tm6000_set_reg(dev, TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20);
-		tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR, 0x14);
-		tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe);
-		tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01);
-		tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL, 0xa0);
-		tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN, 0x32);
-		tm6000_set_reg(dev, TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64);
-		tm6000_set_reg(dev, TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20);
-		tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c, 0x00);
-		tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d, 0x00);
-		tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
-		tm6000_set_reg(dev, TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00);
-		tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL, 0x00);
-		tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3);
-		tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00);
-		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
+	if (dev->dev_type == TM6010) {
+		int val;
+
+		/* Enable video */
+		val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0);
+		val |= 0x60;
+		tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
+		val = tm6000_get_reg(dev,
+			TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0);
+		val &= ~0x40;
+		tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, val);
+
+		tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc);
 
 	} else {
 		/* Enables soft reset */
@@ -325,15 +295,22 @@
 
 	/* Tuner firmware can now be loaded */
 
-	/*FIXME: Hack!!! */
-	struct v4l2_frequency f;
-	mutex_lock(&dev->lock);
+	/*
+	 * FIXME: This is a hack! xc3028 "sleeps" when no channel is detected
+	 * for more than a few seconds. Not sure why, as this behavior does
+	 * not happen on other devices with xc3028. So, I suspect that it
+	 * is yet another bug at tm6000. After start sleeping, decoding 
+	 * doesn't start automatically. Instead, it requires some
+	 * I2C commands to wake it up. As we want to have image at the
+	 * beginning, we needed to add this hack. The better would be to
+	 * discover some way to make tm6000 to wake up without this hack.
+	 */
 	f.frequency = dev->freq;
 	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
-	mutex_unlock(&dev->lock);
 
 	msleep(100);
 	tm6000_set_standard(dev, &dev->norm);
+	tm6000_set_vbi(dev);
 	tm6000_set_audio_bitrate(dev, 48000);
 
 	/* switch dvb led off */
@@ -361,7 +338,6 @@
 		tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0x28);
 		tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xfc);
 		tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0xff);
-		tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe);
 		tm6000_read_write_usb(dev, 0xc0, 0x0e, 0x00c2, 0x0008, buf, 2);
 		printk(KERN_INFO"buf %#x %#x\n", buf[0], buf[1]);
 	} else  {
@@ -660,7 +636,6 @@
  */
 
 static LIST_HEAD(tm6000_extension_devlist);
-static DEFINE_MUTEX(tm6000_extension_devlist_lock);
 
 int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type,
 			char *buf, int size)
@@ -684,14 +659,12 @@
 	struct tm6000_core *dev = NULL;
 
 	mutex_lock(&tm6000_devlist_mutex);
-	mutex_lock(&tm6000_extension_devlist_lock);
 	list_add_tail(&ops->next, &tm6000_extension_devlist);
 	list_for_each_entry(dev, &tm6000_devlist, devlist) {
 		ops->init(dev);
 		printk(KERN_INFO "%s: Initialized (%s) extension\n",
 		       dev->name, ops->name);
 	}
-	mutex_unlock(&tm6000_extension_devlist_lock);
 	mutex_unlock(&tm6000_devlist_mutex);
 	return 0;
 }
@@ -707,10 +680,8 @@
 			ops->fini(dev);
 	}
 
-	mutex_lock(&tm6000_extension_devlist_lock);
 	printk(KERN_INFO "tm6000: Remove (%s) extension\n", ops->name);
 	list_del(&ops->next);
-	mutex_unlock(&tm6000_extension_devlist_lock);
 	mutex_unlock(&tm6000_devlist_mutex);
 }
 EXPORT_SYMBOL(tm6000_unregister_extension);
@@ -719,26 +690,26 @@
 {
 	struct tm6000_ops *ops = NULL;
 
-	mutex_lock(&tm6000_extension_devlist_lock);
+	mutex_lock(&tm6000_devlist_mutex);
 	if (!list_empty(&tm6000_extension_devlist)) {
 		list_for_each_entry(ops, &tm6000_extension_devlist, next) {
 			if (ops->init)
 				ops->init(dev);
 		}
 	}
-	mutex_unlock(&tm6000_extension_devlist_lock);
+	mutex_unlock(&tm6000_devlist_mutex);
 }
 
 void tm6000_close_extension(struct tm6000_core *dev)
 {
 	struct tm6000_ops *ops = NULL;
 
-	mutex_lock(&tm6000_extension_devlist_lock);
+	mutex_lock(&tm6000_devlist_mutex);
 	if (!list_empty(&tm6000_extension_devlist)) {
 		list_for_each_entry(ops, &tm6000_extension_devlist, next) {
 			if (ops->fini)
 				ops->fini(dev);
 		}
 	}
-	mutex_unlock(&tm6000_extension_devlist_lock);
+	mutex_lock(&tm6000_devlist_mutex);
 }
diff --git a/drivers/staging/tm6000/tm6000-dvb.c b/drivers/staging/tm6000/tm6000-dvb.c
index f501edc..ff04c89 100644
--- a/drivers/staging/tm6000/tm6000-dvb.c
+++ b/drivers/staging/tm6000/tm6000-dvb.c
@@ -1,20 +1,20 @@
 /*
-   tm6000-dvb.c - dvb-t support for TM5600/TM6000/TM6010 USB video capture devices
-
-   Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation version 2
-
-   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.
+ *  tm6000-dvb.c - dvb-t support for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ *  Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/kernel.h>
diff --git a/drivers/staging/tm6000/tm6000-i2c.c b/drivers/staging/tm6000/tm6000-i2c.c
index 79bc67f..3e46866 100644
--- a/drivers/staging/tm6000/tm6000-i2c.c
+++ b/drivers/staging/tm6000/tm6000-i2c.c
@@ -1,23 +1,23 @@
 /*
-   tm6000-i2c.c - driver for TM5600/TM6000/TM6010 USB video capture devices
-
-   Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
-
-   Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
-	- Fix SMBus Read Byte command
-
-   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 version 2
-
-   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.
+ *  tm6000-i2c.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ *  Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ *  Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
+ *	- Fix SMBus Read Byte command
+ *
+ *  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 version 2
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/module.h>
@@ -32,11 +32,9 @@
 #include "tuner-xc2028.h"
 
 
-/*FIXME: Hack to avoid needing to patch i2c-id.h */
-#define I2C_HW_B_TM6000 I2C_HW_B_EM28XX
 /* ----------------------------------------------------------- */
 
-static unsigned int i2c_debug = 0;
+static unsigned int i2c_debug;
 module_param(i2c_debug, int, 0644);
 MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
 
@@ -324,7 +322,6 @@
 	.owner = THIS_MODULE,
 	.class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL,
 	.name = "tm6000",
-	.id = I2C_HW_B_TM6000,
 	.algo = &tm6000_algo,
 };
 
diff --git a/drivers/staging/tm6000/tm6000-input.c b/drivers/staging/tm6000/tm6000-input.c
index 54f7667..6022caa 100644
--- a/drivers/staging/tm6000/tm6000-input.c
+++ b/drivers/staging/tm6000/tm6000-input.c
@@ -1,20 +1,20 @@
 /*
-   tm6000-input.c - driver for TM5600/TM6000/TM6010 USB video capture devices
-
-   Copyright (C) 2010 Stefan Ringel <stefan.ringel@arcor.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 version 2
-
-   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.
+ *  tm6000-input.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ *  Copyright (C) 2010 Stefan Ringel <stefan.ringel@arcor.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 version 2
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/module.h>
@@ -36,7 +36,7 @@
 
 static unsigned int enable_ir = 1;
 module_param(enable_ir, int, 0644);
-MODULE_PARM_DESC(enable_ir, "enable ir (default is enable");
+MODULE_PARM_DESC(enable_ir, "enable ir (default is enable)");
 
 #undef dprintk
 
diff --git a/drivers/staging/tm6000/tm6000-regs.h b/drivers/staging/tm6000/tm6000-regs.h
index 1c5289c..1f0ced8 100644
--- a/drivers/staging/tm6000/tm6000-regs.h
+++ b/drivers/staging/tm6000/tm6000-regs.h
@@ -1,20 +1,20 @@
 /*
-   tm6000-regs.h - driver for TM5600/TM6000/TM6010 USB video capture devices
-
-   Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation version 2
-
-   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.
+ *  tm6000-regs.h - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ *  Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2
+ *
+ *  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.
  */
 
 /*
diff --git a/drivers/staging/tm6000/tm6000-stds.c b/drivers/staging/tm6000/tm6000-stds.c
index 6bf4a73..cc7b866 100644
--- a/drivers/staging/tm6000/tm6000-stds.c
+++ b/drivers/staging/tm6000/tm6000-stds.c
@@ -1,20 +1,20 @@
 /*
-   tm6000-stds.c - driver for TM5600/TM6000/TM6010 USB video capture devices
-
-   Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@redhat.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation version 2
-
-   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.
+ *  tm6000-stds.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ *  Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/module.h>
@@ -28,21 +28,37 @@
 	unsigned char value;
 };
 
+enum tm6000_audio_std {
+	BG_NICAM,
+	BTSC,
+	BG_A2,
+	DK_NICAM,
+	EIAJ,
+	FM_RADIO,
+	I_NICAM,
+	KOREA_A2,
+	L_NICAM,
+};
+
 struct tm6000_std_tv_settings {
 	v4l2_std_id id;
+	enum tm6000_audio_std audio_default_std;
+
 	struct tm6000_reg_settings sif[12];
 	struct tm6000_reg_settings nosif[12];
-	struct tm6000_reg_settings common[25];
+	struct tm6000_reg_settings common[26];
 };
 
 struct tm6000_std_settings {
 	v4l2_std_id id;
+	enum tm6000_audio_std audio_default_std;
 	struct tm6000_reg_settings common[37];
 };
 
 static struct tm6000_std_tv_settings tv_stds[] = {
 	{
 		.id = V4L2_STD_PAL_M,
+		.audio_default_std = BTSC,
 		.sif = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8},
@@ -96,11 +112,14 @@
 
 			{TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc},
 			{TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
+
 			{TM6010_REQ07_R3F_RESET, 0x00},
+
 			{0, 0, 0},
 		},
 	}, {
 		.id = V4L2_STD_PAL_Nc,
+		.audio_default_std = BTSC,
 		.sif = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8},
@@ -154,11 +173,14 @@
 
 			{TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc},
 			{TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
+
 			{TM6010_REQ07_R3F_RESET, 0x00},
+
 			{0, 0, 0},
 		},
 	}, {
 		.id = V4L2_STD_PAL,
+		.audio_default_std = BG_A2,
 		.sif = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8},
@@ -212,11 +234,14 @@
 
 			{TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc},
 			{TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
+
 			{TM6010_REQ07_R3F_RESET, 0x00},
+
 			{0, 0, 0},
 		},
 	}, {
-		.id = V4L2_STD_SECAM,
+		.id = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G,
+		.audio_default_std = BG_NICAM,
 		.sif = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8},
@@ -269,11 +294,72 @@
 			{TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xFF},
 
 			{TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
+
+			{TM6010_REQ07_R3F_RESET, 0x00},
+			{0, 0, 0},
+		},
+	}, {
+		.id = V4L2_STD_SECAM_DK,
+		.audio_default_std = DK_NICAM,
+		.sif = {
+			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2},
+			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8},
+			{TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3},
+			{TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x08},
+			{TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1},
+			{TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0},
+			{TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2},
+			{TM6010_REQ08_RED_GAIN_SEL, 0xe8},
+			{TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x62},
+			{TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe},
+			{TM6010_REQ07_RFE_POWER_DOWN, 0xcb},
+			{0, 0, 0},
+		},
+		.nosif = {
+			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
+			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8},
+			{TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3},
+			{TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x0f},
+			{TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1},
+			{TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0},
+			{TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2},
+			{TM6010_REQ08_RED_GAIN_SEL, 0xe8},
+			{TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60},
+			{TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc},
+			{TM6010_REQ07_RFE_POWER_DOWN, 0x8b},
+			{0, 0, 0},
+		},
+		.common = {
+			{TM6010_REQ07_R3F_RESET, 0x01},
+			{TM6010_REQ07_R00_VIDEO_CONTROL0, 0x38},
+			{TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e},
+			{TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f},
+			{TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02},
+			{TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31},
+			{TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24},
+			{TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92},
+			{TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8},
+			{TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed},
+			{TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c},
+			{TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc},
+			{TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc},
+			{TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd},
+			{TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c},
+			{TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c},
+			{TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1},
+			{TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c},
+			{TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18},
+			{TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42},
+			{TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xFF},
+
+			{TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
+
 			{TM6010_REQ07_R3F_RESET, 0x00},
 			{0, 0, 0},
 		},
 	}, {
 		.id = V4L2_STD_NTSC,
+		.audio_default_std = BTSC,
 		.sif = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8},
@@ -327,7 +413,9 @@
 
 			{TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd},
 			{TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
+
 			{TM6010_REQ07_R3F_RESET, 0x00},
+
 			{0, 0, 0},
 		},
 	},
@@ -336,6 +424,7 @@
 static struct tm6000_std_settings composite_stds[] = {
 	{
 		.id = V4L2_STD_PAL_M,
+		.audio_default_std = BTSC,
 		.common = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4},
@@ -378,6 +467,7 @@
 		},
 	 }, {
 		.id = V4L2_STD_PAL_Nc,
+		.audio_default_std = BTSC,
 		.common = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4},
@@ -420,6 +510,7 @@
 		},
 	}, {
 		.id = V4L2_STD_PAL,
+		.audio_default_std = BG_A2,
 		.common = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4},
@@ -462,6 +553,49 @@
 		},
 	 }, {
 		.id = V4L2_STD_SECAM,
+		.audio_default_std = BG_NICAM,
+		.common = {
+			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
+			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4},
+			{TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3},
+			{TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x0f},
+			{TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1},
+			{TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0},
+			{TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2},
+			{TM6010_REQ08_RED_GAIN_SEL, 0xe8},
+			{TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x68},
+			{TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc},
+			{TM6010_REQ07_RFE_POWER_DOWN, 0x8b},
+
+			{TM6010_REQ07_R3F_RESET, 0x01},
+			{TM6010_REQ07_R00_VIDEO_CONTROL0, 0x38},
+			{TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e},
+			{TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f},
+			{TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02},
+			{TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31},
+			{TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24},
+			{TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92},
+			{TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8},
+			{TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed},
+			{TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c},
+			{TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc},
+			{TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc},
+			{TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd},
+			{TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c},
+			{TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c},
+			{TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1},
+			{TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c},
+			{TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18},
+			{TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42},
+			{TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xFF},
+
+			{TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
+			{TM6010_REQ07_R3F_RESET, 0x00},
+			{0, 0, 0},
+		},
+	}, {
+		.id = V4L2_STD_SECAM_DK,
+		.audio_default_std = DK_NICAM,
 		.common = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4},
@@ -503,6 +637,7 @@
 		},
 	}, {
 		.id = V4L2_STD_NTSC,
+		.audio_default_std = BTSC,
 		.common = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4},
@@ -549,6 +684,7 @@
 static struct tm6000_std_settings svideo_stds[] = {
 	{
 		.id = V4L2_STD_PAL_M,
+		.audio_default_std = BTSC,
 		.common = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc},
@@ -591,6 +727,7 @@
 		},
 	}, {
 		.id = V4L2_STD_PAL_Nc,
+		.audio_default_std = BTSC,
 		.common = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc},
@@ -633,6 +770,7 @@
 		},
 	}, {
 		.id = V4L2_STD_PAL,
+		.audio_default_std = BG_A2,
 		.common = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc},
@@ -675,6 +813,49 @@
 		},
 	 }, {
 		.id = V4L2_STD_SECAM,
+		.audio_default_std = BG_NICAM,
+		.common = {
+			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
+			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc},
+			{TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8},
+			{TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00},
+			{TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2},
+			{TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0},
+			{TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2},
+			{TM6010_REQ08_RED_GAIN_SEL, 0xe0},
+			{TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x68},
+			{TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc},
+			{TM6010_REQ07_RFE_POWER_DOWN, 0x8a},
+
+			{TM6010_REQ07_R3F_RESET, 0x01},
+			{TM6010_REQ07_R00_VIDEO_CONTROL0, 0x39},
+			{TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e},
+			{TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f},
+			{TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03},
+			{TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31},
+			{TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24},
+			{TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92},
+			{TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8},
+			{TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed},
+			{TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c},
+			{TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc},
+			{TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc},
+			{TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd},
+			{TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c},
+			{TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a},
+			{TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1},
+			{TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c},
+			{TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18},
+			{TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42},
+			{TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xFF},
+
+			{TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
+			{TM6010_REQ07_R3F_RESET, 0x00},
+			{0, 0, 0},
+		},
+	}, {
+		.id = V4L2_STD_SECAM_DK,
+		.audio_default_std = DK_NICAM,
 		.common = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc},
@@ -716,6 +897,7 @@
 		},
 	}, {
 		.id = V4L2_STD_NTSC,
+		.audio_default_std = BTSC,
 		.common = {
 			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
 			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc},
@@ -760,6 +942,133 @@
 	},
 };
 
+
+static int tm6000_set_audio_std(struct tm6000_core *dev,
+				enum tm6000_audio_std std)
+{
+	uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */
+	uint8_t areg_05 = 0x09; /* Auto 4.5 = M Japan, Auto 6.5 = DK */
+	uint8_t areg_06 = 0x02; /* Auto de-emphasis, mannual channel mode */
+	uint8_t mono_flag = 0;  /* No mono */
+	uint8_t nicam_flag = 0; /* No NICAM */
+
+	switch (std) {
+#if 0
+	case DK_MONO:
+		mono_flag = 1;
+		break;
+	case DK_A2_1:
+		break;
+	case DK_A2_3:
+		areg_05 = 0x0b;
+		break;
+	case BG_MONO:
+		mono_flag = 1;
+		areg_05 = 0x05;
+		break;
+#endif
+	case BG_NICAM:
+		areg_05 = 0x07;
+		nicam_flag = 1;
+		break;
+	case BTSC:
+		areg_05 = 0x02;
+		break;
+	case BG_A2:
+		areg_05 = 0x05;
+		break;
+	case DK_NICAM:
+		areg_05 = 0x06;
+		nicam_flag = 1;
+		break;
+	case EIAJ:
+		areg_05 = 0x02;
+		break;
+	case FM_RADIO:
+		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
+		tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
+		tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
+		tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c);
+		tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00);
+		tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18);
+		tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91);
+		tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe);
+		tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01);
+		tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
+		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
+		return 0;
+		break;
+	case I_NICAM:
+		areg_05 = 0x08;
+		nicam_flag = 1;
+		break;
+	case KOREA_A2:
+		areg_05 = 0x04;
+		break;
+	case L_NICAM:
+		areg_02 = 0x02; /* GC1 Fixed gain +12dB */
+		areg_05 = 0x0a;
+		nicam_flag = 1;
+		break;
+	}
+
+#if 0
+	switch (tv_audio_mode) {
+	case TV_MONO:
+		areg_06 = (nicam_flag) ? 0x03 : 0x00;
+		break;
+	case TV_LANG_A:
+		areg_06 = 0x00;
+		break;
+	case TV_LANG_B:
+		areg_06 = 0x01;
+		break;
+	}
+#endif
+
+	if (mono_flag)
+		areg_06 = 0x00;
+
+	tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
+	tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, areg_02);
+	tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
+	tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0xa0);
+	tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, areg_05);
+	tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, areg_06);
+	tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, 0x00);
+	tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, 0x00);
+	tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x08);
+	tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91);
+	tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1, 0x20);
+	tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x12);
+	tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x20);
+	tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1, 0xf0);
+	tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2, 0x80);
+	tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1, 0xc0);
+	tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2, 0x80);
+	tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12);
+	tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T, 0xfe);
+	tm6000_set_reg(dev, TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20);
+	tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR, 0x14);
+	tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe);
+	tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01);
+	tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL, 0xa0);
+	tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN, 0x32);
+	tm6000_set_reg(dev, TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64);
+	tm6000_set_reg(dev, TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20);
+	tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c, 0x00);
+	tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d, 0x00);
+	tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
+	tm6000_set_reg(dev, TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00);
+	tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL, 0x00);
+	tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3);
+	tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00);
+	tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc);
+	tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
+
+	return 0;
+}
+
 void tm6000_get_std_res(struct tm6000_core *dev)
 {
 	/* Currently, those are the only supported resoltions */
@@ -820,6 +1129,8 @@
 	rc = tm6000_load_std(dev, tv_stds[pos].common,
 			     sizeof(tv_stds[pos].common));
 
+	tm6000_set_audio_std(dev, tv_stds[pos].audio_default_std);
+
 	return rc;
 }
 
@@ -845,6 +1156,8 @@
 				rc = tm6000_load_std(dev, svideo_stds[i].common,
 						     sizeof(svideo_stds[i].
 							    common));
+				tm6000_set_audio_std(dev, svideo_stds[i].audio_default_std);
+
 				goto ret;
 			}
 		}
@@ -856,6 +1169,7 @@
 						     composite_stds[i].common,
 						     sizeof(composite_stds[i].
 							    common));
+				tm6000_set_audio_std(dev, composite_stds[i].audio_default_std);
 				goto ret;
 			}
 		}
diff --git a/drivers/staging/tm6000/tm6000-usb-isoc.h b/drivers/staging/tm6000/tm6000-usb-isoc.h
index 138716a..a9e61d9 100644
--- a/drivers/staging/tm6000/tm6000-usb-isoc.h
+++ b/drivers/staging/tm6000/tm6000-usb-isoc.h
@@ -1,20 +1,20 @@
 /*
-   tm6000-buf.c - driver for TM5600/TM6000/TM6010 USB video capture devices
-
-   Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation version 2
-
-   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.
+ *  tm6000-buf.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ *  Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/videodev2.h>
diff --git a/drivers/staging/tm6000/tm6000-video.c b/drivers/staging/tm6000/tm6000-video.c
index ce0a089..9ec8279 100644
--- a/drivers/staging/tm6000/tm6000-video.c
+++ b/drivers/staging/tm6000/tm6000-video.c
@@ -1,23 +1,23 @@
 /*
-   tm6000-video.c - driver for TM5600/TM6000/TM6010 USB video capture devices
-
-   Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
-
-   Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
-	- Fixed module load/unload
-
-   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 version 2
-
-   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.
+ *   tm6000-video.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ *  Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ *  Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
+ *	- Fixed module load/unload
+ *
+ *  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 version 2
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 #include <linux/module.h>
 #include <linux/delay.h>
@@ -118,8 +118,9 @@
 };
 
 /* ------------------------------------------------------------------
-	DMA and thread functions
-   ------------------------------------------------------------------*/
+ *	DMA and thread functions
+ * ------------------------------------------------------------------
+ */
 
 #define norm_maxw(a) 720
 #define norm_maxh(a) 576
@@ -189,17 +190,17 @@
 			struct urb *urb)
 {
 	struct tm6000_dmaqueue  *dma_q = urb->context;
-	struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq);
-	u8 *ptr=data, *endp=data+len, c;
-	unsigned long header=0;
-	int rc=0;
+	struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
+	u8 *ptr = data, *endp = data+len, c;
+	unsigned long header = 0;
+	int rc = 0;
 	unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0;
 	struct tm6000_buffer *vbuf;
 	char *voutp = NULL;
 	unsigned int linewidth;
 
 	/* get video buffer */
-	get_next_buf (dma_q, &vbuf);
+	get_next_buf(dma_q, &vbuf);
 	if (!vbuf)
 		return rc;
 	voutp = videobuf_to_vmalloc(&vbuf->vb);
@@ -213,7 +214,7 @@
 				/* from last urb or packet */
 				header = dev->isoc_ctl.tmp_buf;
 				if (4 - dev->isoc_ctl.tmp_buf_len > 0) {
-					memcpy ((u8 *)&header +
+					memcpy((u8 *)&header +
 						dev->isoc_ctl.tmp_buf_len,
 						ptr,
 						4 - dev->isoc_ctl.tmp_buf_len);
@@ -224,7 +225,7 @@
 				if (ptr + 3 >= endp) {
 					/* have incomplete header */
 					dev->isoc_ctl.tmp_buf_len = endp - ptr;
-					memcpy (&dev->isoc_ctl.tmp_buf, ptr,
+					memcpy(&dev->isoc_ctl.tmp_buf, ptr,
 						dev->isoc_ctl.tmp_buf_len);
 					return rc;
 				}
@@ -261,13 +262,13 @@
 					/* Announces that a new buffer
 					 * were filled
 					 */
-					buffer_filled (dev, dma_q, vbuf);
-					dprintk (dev, V4L2_DEBUG_ISOC,
+					buffer_filled(dev, dma_q, vbuf);
+					dprintk(dev, V4L2_DEBUG_ISOC,
 							"new buffer filled\n");
-					get_next_buf (dma_q, &vbuf);
+					get_next_buf(dma_q, &vbuf);
 					if (!vbuf)
 						return rc;
-					voutp = videobuf_to_vmalloc (&vbuf->vb);
+					voutp = videobuf_to_vmalloc(&vbuf->vb);
 					if (!voutp)
 						return rc;
 					memset(voutp, 0, vbuf->vb.size);
@@ -301,9 +302,17 @@
 			case TM6000_URB_MSG_VIDEO:
 				/* Fills video buffer */
 				if (vbuf)
-					memcpy (&voutp[pos], ptr, cpysize);
+					memcpy(&voutp[pos], ptr, cpysize);
 				break;
 			case TM6000_URB_MSG_AUDIO:
+				/* Need some code to copy audio buffer */
+				if (dev->fourcc == V4L2_PIX_FMT_YUYV) {
+					/* Swap word bytes */
+					int i;
+
+					for (i = 0; i < cpysize; i += 2)
+						swab16s((u16 *)(ptr + i));
+				}
 				tm6000_call_fillbuf(dev, TM6000_AUDIO, ptr, cpysize);
 				break;
 			case TM6000_URB_MSG_VBI:
@@ -338,9 +347,9 @@
 			struct urb *urb)
 {
 	struct tm6000_dmaqueue  *dma_q = urb->context;
-	struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq);
-	unsigned int pos=dev->isoc_ctl.pos,cpysize;
-	int rc=1;
+	struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
+	unsigned int pos = dev->isoc_ctl.pos, cpysize;
+	int rc = 1;
 	struct tm6000_buffer *buf;
 	char *outp = NULL;
 
@@ -351,19 +360,18 @@
 	if (!outp)
 		return 0;
 
-	while (len>0) {
-		cpysize=min(len,buf->vb.size-pos);
-		//printk("Copying %d bytes (max=%lu) from %p to %p[%u]\n",cpysize,(*buf)->vb.size,ptr,out_p,pos);
+	while (len > 0) {
+		cpysize = min(len, buf->vb.size-pos);
 		memcpy(&outp[pos], ptr, cpysize);
-		pos+=cpysize;
-		ptr+=cpysize;
-		len-=cpysize;
+		pos += cpysize;
+		ptr += cpysize;
+		len -= cpysize;
 		if (pos >= buf->vb.size) {
-			pos=0;
+			pos = 0;
 			/* Announces that a new buffer were filled */
-			buffer_filled (dev, dma_q, buf);
+			buffer_filled(dev, dma_q, buf);
 			dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n");
-			get_next_buf (dma_q, &buf);
+			get_next_buf(dma_q, &buf);
 			if (!buf)
 				break;
 			outp = videobuf_to_vmalloc(&(buf->vb));
@@ -373,16 +381,16 @@
 		}
 	}
 
-	dev->isoc_ctl.pos=pos;
+	dev->isoc_ctl.pos = pos;
 	return rc;
 }
 
-static void inline print_err_status (struct tm6000_core *dev,
+static inline void print_err_status(struct tm6000_core *dev,
 				     int packet, int status)
 {
 	char *errmsg = "Unknown";
 
-	switch(status) {
+	switch (status) {
 	case -ENOENT:
 		errmsg = "unlinked synchronuously";
 		break;
@@ -408,7 +416,7 @@
 		errmsg = "Device does not respond";
 		break;
 	}
-	if (packet<0) {
+	if (packet < 0) {
 		dprintk(dev, V4L2_DEBUG_QUEUE, "URB status %d [%s].\n",
 			status, errmsg);
 	} else {
@@ -424,20 +432,20 @@
 static inline int tm6000_isoc_copy(struct urb *urb)
 {
 	struct tm6000_dmaqueue  *dma_q = urb->context;
-	struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq);
-	int i, len=0, rc=1, status;
+	struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
+	int i, len = 0, rc = 1, status;
 	char *p;
 
 	if (urb->status < 0) {
-		print_err_status (dev, -1, urb->status);
+		print_err_status(dev, -1, urb->status);
 		return 0;
 	}
 
 	for (i = 0; i < urb->number_of_packets; i++) {
 		status = urb->iso_frame_desc[i].status;
 
-		if (status<0) {
-			print_err_status (dev,i,status);
+		if (status < 0) {
+			print_err_status(dev, i, status);
 			continue;
 		}
 
@@ -446,9 +454,9 @@
 		if (len > 0) {
 			p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
 			if (!urb->iso_frame_desc[i].status) {
-				if ((dev->fourcc)==V4L2_PIX_FMT_TM6000) {
-					rc=copy_multiplexed(p, len, urb);
-					if (rc<=0)
+				if ((dev->fourcc) == V4L2_PIX_FMT_TM6000) {
+					rc = copy_multiplexed(p, len, urb);
+					if (rc <= 0)
 						return rc;
 				} else {
 					copy_streams(p, len, urb);
@@ -460,8 +468,9 @@
 }
 
 /* ------------------------------------------------------------------
-	URB control
-   ------------------------------------------------------------------*/
+ *	URB control
+ * ------------------------------------------------------------------
+ */
 
 /*
  * IRQ callback, called by URB callback
@@ -501,7 +510,7 @@
 
 	dev->isoc_ctl.buf = NULL;
 	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
-		urb=dev->isoc_ctl.urb[i];
+		urb = dev->isoc_ctl.urb[i];
 		if (urb) {
 			usb_kill_urb(urb);
 			usb_unlink_urb(urb);
@@ -517,11 +526,11 @@
 		dev->isoc_ctl.transfer_buffer[i] = NULL;
 	}
 
-	kfree (dev->isoc_ctl.urb);
-	kfree (dev->isoc_ctl.transfer_buffer);
+	kfree(dev->isoc_ctl.urb);
+	kfree(dev->isoc_ctl.transfer_buffer);
 
-	dev->isoc_ctl.urb=NULL;
-	dev->isoc_ctl.transfer_buffer=NULL;
+	dev->isoc_ctl.urb = NULL;
+	dev->isoc_ctl.transfer_buffer = NULL;
 	dev->isoc_ctl.num_bufs = 0;
 }
 
@@ -552,7 +561,7 @@
 
 	dev->isoc_ctl.max_pkt_size = size;
 
-	max_packets = ( framesize + size - 1) / size;
+	max_packets = (framesize + size - 1) / size;
 
 	if (max_packets > TM6000_MAX_ISO_PACKETS)
 		max_packets = TM6000_MAX_ISO_PACKETS;
@@ -594,10 +603,10 @@
 		dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev,
 			sb_size, GFP_KERNEL, &urb->transfer_dma);
 		if (!dev->isoc_ctl.transfer_buffer[i]) {
-			tm6000_err ("unable to allocate %i bytes for transfer"
+			tm6000_err("unable to allocate %i bytes for transfer"
 					" buffer %i%s\n",
 					sb_size, i,
-					in_interrupt()?" while in int":"");
+					in_interrupt() ? " while in int" : "");
 			tm6000_uninit_isoc(dev);
 			return -ENOMEM;
 		}
@@ -619,13 +628,13 @@
 	return 0;
 }
 
-static int tm6000_start_thread( struct tm6000_core *dev)
+static int tm6000_start_thread(struct tm6000_core *dev)
 {
 	struct tm6000_dmaqueue *dma_q = &dev->vidq;
 	int i;
 
-	dma_q->frame=0;
-	dma_q->ini_jiffies=jiffies;
+	dma_q->frame = 0;
+	dma_q->ini_jiffies = jiffies;
 
 	init_waitqueue_head(&dma_q->wq);
 
@@ -644,8 +653,9 @@
 }
 
 /* ------------------------------------------------------------------
-	Videobuf operations
-   ------------------------------------------------------------------*/
+ *	Videobuf operations
+ * ------------------------------------------------------------------
+ */
 
 static int
 buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
@@ -656,9 +666,8 @@
 	if (0 == *count)
 		*count = TM6000_DEF_BUF;
 
-	if (*count < TM6000_MIN_BUF) {
-		*count=TM6000_MIN_BUF;
-	}
+	if (*count < TM6000_MIN_BUF)
+		*count = TM6000_MIN_BUF;
 
 	while (*size * *count > vid_limit * 1024 * 1024)
 		(*count)--;
@@ -698,7 +707,7 @@
 						enum v4l2_field field)
 {
 	struct tm6000_fh     *fh  = vq->priv_data;
-	struct tm6000_buffer *buf = container_of(vb,struct tm6000_buffer,vb);
+	struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb);
 	struct tm6000_core   *dev = fh->dev;
 	int rc = 0, urb_init = 0;
 
@@ -753,7 +762,7 @@
 static void
 buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
 {
-	struct tm6000_buffer    *buf     = container_of(vb,struct tm6000_buffer,vb);
+	struct tm6000_buffer    *buf     = container_of(vb, struct tm6000_buffer, vb);
 	struct tm6000_fh        *fh      = vq->priv_data;
 	struct tm6000_core      *dev     = fh->dev;
 	struct tm6000_dmaqueue  *vidq    = &dev->vidq;
@@ -764,9 +773,9 @@
 
 static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb)
 {
-	struct tm6000_buffer   *buf  = container_of(vb,struct tm6000_buffer,vb);
+	struct tm6000_buffer   *buf  = container_of(vb, struct tm6000_buffer, vb);
 
-	free_buffer(vq,buf);
+	free_buffer(vq, buf);
 }
 
 static struct videobuf_queue_ops tm6000_video_qops = {
@@ -777,49 +786,66 @@
 };
 
 /* ------------------------------------------------------------------
-	IOCTL handling
-   ------------------------------------------------------------------*/
+ *	IOCTL handling
+ * ------------------------------------------------------------------
+ */
 
-static int res_get(struct tm6000_core *dev, struct tm6000_fh *fh)
+static bool is_res_read(struct tm6000_core *dev, struct tm6000_fh *fh)
 {
-	/* is it free? */
-	mutex_lock(&dev->lock);
-	if (dev->resources) {
-		/* no, someone else uses it */
-		mutex_unlock(&dev->lock);
-		return 0;
-	}
-	/* it's free, grab it */
-	dev->resources =1;
-	dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n");
-	mutex_unlock(&dev->lock);
-	return 1;
+	/* Is the current fh handling it? if so, that's OK */
+	if (dev->resources == fh && dev->is_res_read)
+		return true;
+
+	return false;
 }
 
-static int res_locked(struct tm6000_core *dev)
+static bool is_res_streaming(struct tm6000_core *dev, struct tm6000_fh *fh)
 {
-	return (dev->resources);
+	/* Is the current fh handling it? if so, that's OK */
+	if (dev->resources == fh)
+		return true;
+
+	return false;
+}
+
+static bool res_get(struct tm6000_core *dev, struct tm6000_fh *fh,
+		   bool is_res_read)
+{
+	/* Is the current fh handling it? if so, that's OK */
+	if (dev->resources == fh && dev->is_res_read == is_res_read)
+		return true;
+
+	/* is it free? */
+	if (dev->resources)
+		return false;
+
+	/* grab it */
+	dev->resources = fh;
+	dev->is_res_read = is_res_read;
+	dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n");
+	return true;
 }
 
 static void res_free(struct tm6000_core *dev, struct tm6000_fh *fh)
 {
-	mutex_lock(&dev->lock);
-	dev->resources = 0;
+	/* Is the current fh handling it? if so, that's OK */
+	if (dev->resources != fh)
+		return;
+
+	dev->resources = NULL;
 	dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: put\n");
-	mutex_unlock(&dev->lock);
 }
 
 /* ------------------------------------------------------------------
-	IOCTL vidioc handling
-   ------------------------------------------------------------------*/
-static int vidioc_querycap (struct file *file, void  *priv,
+ *	IOCTL vidioc handling
+ * ------------------------------------------------------------------
+ */
+static int vidioc_querycap(struct file *file, void  *priv,
 					struct v4l2_capability *cap)
 {
-	//	struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev;
 
 	strlcpy(cap->driver, "tm6000", sizeof(cap->driver));
-	strlcpy(cap->card,"Trident TVMaster TM5600/6000/6010", sizeof(cap->card));
-	//	strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
+	strlcpy(cap->card, "Trident TVMaster TM5600/6000/6010", sizeof(cap->card));
 	cap->version = TM6000_VERSION;
 	cap->capabilities =	V4L2_CAP_VIDEO_CAPTURE |
 				V4L2_CAP_STREAMING     |
@@ -828,21 +854,21 @@
 	return 0;
 }
 
-static int vidioc_enum_fmt_vid_cap (struct file *file, void  *priv,
+static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
 					struct v4l2_fmtdesc *f)
 {
 	if (unlikely(f->index >= ARRAY_SIZE(format)))
 		return -EINVAL;
 
-	strlcpy(f->description,format[f->index].name,sizeof(f->description));
+	strlcpy(f->description, format[f->index].name, sizeof(f->description));
 	f->pixelformat = format[f->index].fourcc;
 	return 0;
 }
 
-static int vidioc_g_fmt_vid_cap (struct file *file, void *priv,
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
 					struct v4l2_format *f)
 {
-	struct tm6000_fh  *fh=priv;
+	struct tm6000_fh  *fh = priv;
 
 	f->fmt.pix.width        = fh->width;
 	f->fmt.pix.height       = fh->height;
@@ -853,10 +879,10 @@
 	f->fmt.pix.sizeimage =
 		f->fmt.pix.height * f->fmt.pix.bytesperline;
 
-	return (0);
+	return 0;
 }
 
-static struct tm6000_fmt* format_by_fourcc(unsigned int fourcc)
+static struct tm6000_fmt *format_by_fourcc(unsigned int fourcc)
 {
 	unsigned int i;
 
@@ -866,7 +892,7 @@
 	return NULL;
 }
 
-static int vidioc_try_fmt_vid_cap (struct file *file, void *priv,
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
 			struct v4l2_format *f)
 {
 	struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev;
@@ -882,15 +908,14 @@
 
 	field = f->fmt.pix.field;
 
-	if (field == V4L2_FIELD_ANY) {
-//		field=V4L2_FIELD_INTERLACED;
-		field=V4L2_FIELD_SEQ_TB;
-	} else if (V4L2_FIELD_INTERLACED != field) {
+	if (field == V4L2_FIELD_ANY)
+		field = V4L2_FIELD_SEQ_TB;
+	else if (V4L2_FIELD_INTERLACED != field) {
 		dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Field type invalid.\n");
 		return -EINVAL;
 	}
 
-	tm6000_get_std_res (dev);
+	tm6000_get_std_res(dev);
 
 	f->fmt.pix.width  = dev->width;
 	f->fmt.pix.height = dev->height;
@@ -908,14 +933,14 @@
 }
 
 /*FIXME: This seems to be generic enough to be at videodev2 */
-static int vidioc_s_fmt_vid_cap (struct file *file, void *priv,
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
 					struct v4l2_format *f)
 {
-	struct tm6000_fh  *fh=priv;
+	struct tm6000_fh  *fh = priv;
 	struct tm6000_core *dev = fh->dev;
-	int ret = vidioc_try_fmt_vid_cap(file,fh,f);
+	int ret = vidioc_try_fmt_vid_cap(file, fh, f);
 	if (ret < 0)
-		return (ret);
+		return ret;
 
 	fh->fmt           = format_by_fourcc(f->fmt.pix.pixelformat);
 	fh->width         = f->fmt.pix.width;
@@ -927,52 +952,52 @@
 
 	tm6000_set_fourcc_format(dev);
 
-	return (0);
+	return 0;
 }
 
-static int vidioc_reqbufs (struct file *file, void *priv,
+static int vidioc_reqbufs(struct file *file, void *priv,
 			   struct v4l2_requestbuffers *p)
 {
-	struct tm6000_fh  *fh=priv;
+	struct tm6000_fh  *fh = priv;
 
-	return (videobuf_reqbufs(&fh->vb_vidq, p));
+	return videobuf_reqbufs(&fh->vb_vidq, p);
 }
 
-static int vidioc_querybuf (struct file *file, void *priv,
+static int vidioc_querybuf(struct file *file, void *priv,
 			    struct v4l2_buffer *p)
 {
-	struct tm6000_fh  *fh=priv;
+	struct tm6000_fh  *fh = priv;
 
-	return (videobuf_querybuf(&fh->vb_vidq, p));
+	return videobuf_querybuf(&fh->vb_vidq, p);
 }
 
-static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
 {
-	struct tm6000_fh  *fh=priv;
+	struct tm6000_fh  *fh = priv;
 
-	return (videobuf_qbuf(&fh->vb_vidq, p));
+	return videobuf_qbuf(&fh->vb_vidq, p);
 }
 
-static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
 {
-	struct tm6000_fh  *fh=priv;
+	struct tm6000_fh  *fh = priv;
 
-	return (videobuf_dqbuf(&fh->vb_vidq, p,
-				file->f_flags & O_NONBLOCK));
+	return videobuf_dqbuf(&fh->vb_vidq, p,
+				file->f_flags & O_NONBLOCK);
 }
 
 #ifdef CONFIG_VIDEO_V4L1_COMPAT
-static int vidiocgmbuf (struct file *file, void *priv, struct video_mbuf *mbuf)
+static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
 {
-	struct tm6000_fh  *fh=priv;
+	struct tm6000_fh  *fh = priv;
 
-	return videobuf_cgmbuf (&fh->vb_vidq, mbuf, 8);
+	return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
 }
 #endif
 
 static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
 {
-	struct tm6000_fh  *fh=priv;
+	struct tm6000_fh  *fh = priv;
 	struct tm6000_core *dev    = fh->dev;
 
 	if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -980,7 +1005,7 @@
 	if (i != fh->type)
 		return -EINVAL;
 
-	if (!res_get(dev,fh))
+	if (!res_get(dev, fh, false))
 		return -EBUSY;
 	return (videobuf_streamon(&fh->vb_vidq));
 }
@@ -1007,7 +1032,7 @@
 	struct tm6000_fh   *fh=priv;
 	struct tm6000_core *dev = fh->dev;
 
-	rc=tm6000_set_standard (dev, norm);
+	rc = tm6000_init_analog_mode(dev);
 
 	fh->width  = dev->width;
 	fh->height = dev->height;
@@ -1020,21 +1045,21 @@
 	return 0;
 }
 
-static int vidioc_enum_input (struct file *file, void *priv,
+static int vidioc_enum_input(struct file *file, void *priv,
 				struct v4l2_input *inp)
 {
 	switch (inp->index) {
 	case TM6000_INPUT_TV:
 		inp->type = V4L2_INPUT_TYPE_TUNER;
-		strcpy(inp->name,"Television");
+		strcpy(inp->name, "Television");
 		break;
 	case TM6000_INPUT_COMPOSITE:
 		inp->type = V4L2_INPUT_TYPE_CAMERA;
-		strcpy(inp->name,"Composite");
+		strcpy(inp->name, "Composite");
 		break;
 	case TM6000_INPUT_SVIDEO:
 		inp->type = V4L2_INPUT_TYPE_CAMERA;
-		strcpy(inp->name,"S-Video");
+		strcpy(inp->name, "S-Video");
 		break;
 	default:
 		return -EINVAL;
@@ -1044,48 +1069,48 @@
 	return 0;
 }
 
-static int vidioc_g_input (struct file *file, void *priv, unsigned int *i)
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
 {
-	struct tm6000_fh   *fh=priv;
+	struct tm6000_fh   *fh = priv;
 	struct tm6000_core *dev = fh->dev;
 
-	*i=dev->input;
+	*i = dev->input;
 
 	return 0;
 }
-static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
 {
-	struct tm6000_fh   *fh=priv;
+	struct tm6000_fh   *fh = priv;
 	struct tm6000_core *dev = fh->dev;
-	int rc=0;
+	int rc = 0;
 	char buf[1];
 
 	switch (i) {
 	case TM6000_INPUT_TV:
-		dev->input=i;
-		*buf=0;
+		dev->input = i;
+		*buf = 0;
 		break;
 	case TM6000_INPUT_COMPOSITE:
 	case TM6000_INPUT_SVIDEO:
-		dev->input=i;
-		*buf=1;
+		dev->input = i;
+		*buf = 1;
 		break;
 	default:
 		return -EINVAL;
 	}
-	rc=tm6000_read_write_usb (dev, USB_DIR_OUT | USB_TYPE_VENDOR,
+	rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR,
 			       REQ_03_SET_GET_MCU_PIN, 0x03, 1, buf, 1);
 
 	if (!rc) {
-		dev->input=i;
-		rc=vidioc_s_std (file, priv, &dev->vfd->current_norm);
+		dev->input = i;
+		rc = vidioc_s_std(file, priv, &dev->vfd->current_norm);
 	}
 
-	return (rc);
+	return rc;
 }
 
 	/* --- controls ---------------------------------------------- */
-static int vidioc_queryctrl (struct file *file, void *priv,
+static int vidioc_queryctrl(struct file *file, void *priv,
 				struct v4l2_queryctrl *qc)
 {
 	int i;
@@ -1094,16 +1119,16 @@
 		if (qc->id && qc->id == tm6000_qctrl[i].id) {
 			memcpy(qc, &(tm6000_qctrl[i]),
 				sizeof(*qc));
-			return (0);
+			return 0;
 		}
 
 	return -EINVAL;
 }
 
-static int vidioc_g_ctrl (struct file *file, void *priv,
+static int vidioc_g_ctrl(struct file *file, void *priv,
 				struct v4l2_control *ctrl)
 {
-	struct tm6000_fh  *fh=priv;
+	struct tm6000_fh  *fh = priv;
 	struct tm6000_core *dev    = fh->dev;
 	int  val;
 
@@ -1125,41 +1150,41 @@
 		return -EINVAL;
 	}
 
-	if (val<0)
+	if (val < 0)
 		return val;
 
-	ctrl->value=val;
+	ctrl->value = val;
 
 	return 0;
 }
-static int vidioc_s_ctrl (struct file *file, void *priv,
+static int vidioc_s_ctrl(struct file *file, void *priv,
 				struct v4l2_control *ctrl)
 {
-	struct tm6000_fh   *fh  =priv;
+	struct tm6000_fh   *fh  = priv;
 	struct tm6000_core *dev = fh->dev;
-	u8  val=ctrl->value;
+	u8  val = ctrl->value;
 
 	switch (ctrl->id) {
 	case V4L2_CID_CONTRAST:
-  tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val);
+		tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val);
 		return 0;
 	case V4L2_CID_BRIGHTNESS:
-  tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val);
+		tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val);
 		return 0;
 	case V4L2_CID_SATURATION:
-  tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val);
+		tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val);
 		return 0;
 	case V4L2_CID_HUE:
-  tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val);
+		tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val);
 		return 0;
 	}
 	return -EINVAL;
 }
 
-static int vidioc_g_tuner (struct file *file, void *priv,
+static int vidioc_g_tuner(struct file *file, void *priv,
 				struct v4l2_tuner *t)
 {
-	struct tm6000_fh   *fh  =priv;
+	struct tm6000_fh   *fh  = priv;
 	struct tm6000_core *dev = fh->dev;
 
 	if (unlikely(UNSET == dev->tuner_type))
@@ -1176,10 +1201,10 @@
 	return 0;
 }
 
-static int vidioc_s_tuner (struct file *file, void *priv,
+static int vidioc_s_tuner(struct file *file, void *priv,
 				struct v4l2_tuner *t)
 {
-	struct tm6000_fh   *fh  =priv;
+	struct tm6000_fh   *fh  = priv;
 	struct tm6000_core *dev = fh->dev;
 
 	if (UNSET == dev->tuner_type)
@@ -1190,10 +1215,10 @@
 	return 0;
 }
 
-static int vidioc_g_frequency (struct file *file, void *priv,
+static int vidioc_g_frequency(struct file *file, void *priv,
 				struct v4l2_frequency *f)
 {
-	struct tm6000_fh   *fh  =priv;
+	struct tm6000_fh   *fh  = priv;
 	struct tm6000_core *dev = fh->dev;
 
 	if (unlikely(UNSET == dev->tuner_type))
@@ -1207,10 +1232,10 @@
 	return 0;
 }
 
-static int vidioc_s_frequency (struct file *file, void *priv,
+static int vidioc_s_frequency(struct file *file, void *priv,
 				struct v4l2_frequency *f)
 {
-	struct tm6000_fh   *fh  =priv;
+	struct tm6000_fh   *fh  = priv;
 	struct tm6000_core *dev = fh->dev;
 
 	if (unlikely(f->type != V4L2_TUNER_ANALOG_TV))
@@ -1221,10 +1246,8 @@
 	if (unlikely(f->tuner != 0))
 		return -EINVAL;
 
-//	mutex_lock(&dev->lock);
 	dev->freq = f->frequency;
 	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f);
-//	mutex_unlock(&dev->lock);
 
 	return 0;
 }
@@ -1239,7 +1262,7 @@
 	struct tm6000_core *dev = video_drvdata(file);
 	struct tm6000_fh *fh;
 	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	int i,rc;
+	int i, rc;
 
 	printk(KERN_INFO "tm6000: open called (dev=%s)\n",
 		video_device_node_name(vdev));
@@ -1256,7 +1279,7 @@
 		dev->users);
 
 	/* allocate + initialize per filehandle data */
-	fh = kzalloc(sizeof(*fh),GFP_KERNEL);
+	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
 	if (NULL == fh) {
 		dev->users--;
 		return -ENOMEM;
@@ -1284,23 +1307,23 @@
 				"active=%d\n",list_empty(&dev->vidq.active));
 
 	/* initialize hardware on analog mode */
-	if (dev->mode!=TM6000_MODE_ANALOG) {
-		rc=tm6000_init_analog_mode (dev);
-		if (rc<0)
-			return rc;
+	rc = tm6000_init_analog_mode(dev);
+	if (rc < 0)
+		return rc;
 
+	if (dev->mode != TM6000_MODE_ANALOG) {
 		/* Put all controls at a sane state */
 		for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++)
-			qctl_regs[i] =tm6000_qctrl[i].default_value;
+			qctl_regs[i] = tm6000_qctrl[i].default_value;
 
-		dev->mode=TM6000_MODE_ANALOG;
+		dev->mode = TM6000_MODE_ANALOG;
 	}
 
 	videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops,
 			NULL, &dev->slock,
 			fh->type,
 			V4L2_FIELD_INTERLACED,
-			sizeof(struct tm6000_buffer),fh);
+			sizeof(struct tm6000_buffer), fh, &dev->lock);
 
 	return 0;
 }
@@ -1311,7 +1334,7 @@
 	struct tm6000_fh        *fh = file->private_data;
 
 	if (fh->type==V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-		if (res_locked(fh->dev))
+		if (!res_get(fh->dev, fh, true))
 			return -EBUSY;
 
 		return videobuf_read_stream(&fh->vb_vidq, data, count, pos, 0,
@@ -1329,7 +1352,10 @@
 	if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
 		return POLLERR;
 
-	if (res_get(fh->dev,fh)) {
+	if (!!is_res_streaming(fh->dev, fh))
+		return POLLERR;
+
+	if (!is_res_read(fh->dev, fh)) {
 		/* streaming capture */
 		if (list_empty(&fh->vb_vidq.stream))
 			return POLLERR;
@@ -1342,7 +1368,7 @@
 	poll_wait(file, &buf->vb.done, wait);
 	if (buf->vb.state == VIDEOBUF_DONE ||
 	    buf->vb.state == VIDEOBUF_ERROR)
-		return POLLIN|POLLRDNORM;
+		return POLLIN | POLLRDNORM;
 	return 0;
 }
 
@@ -1357,12 +1383,13 @@
 
 	dev->users--;
 
+	res_free(dev, fh);
 	if (!dev->users) {
 		tm6000_uninit_isoc(dev);
 		videobuf_mmap_free(&fh->vb_vidq);
 	}
 
-	kfree (fh);
+	kfree(fh);
 
 	return 0;
 }
@@ -1372,7 +1399,7 @@
 	struct tm6000_fh        *fh = file->private_data;
 	int ret;
 
-	ret=videobuf_mmap_mapper(&fh->vb_vidq, vma);
+	ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
 
 	return ret;
 }
@@ -1381,7 +1408,7 @@
 	.owner		= THIS_MODULE,
 	.open           = tm6000_open,
 	.release        = tm6000_release,
-	.ioctl          = video_ioctl2, /* V4L2 ioctl handler */
+	.unlocked_ioctl	= video_ioctl2, /* V4L2 ioctl handler */
 	.read           = tm6000_read,
 	.poll		= tm6000_poll,
 	.mmap		= tm6000_mmap,
@@ -1425,8 +1452,9 @@
 };
 
 /* -----------------------------------------------------------------
-	Initialization and module stuff
-   ------------------------------------------------------------------*/
+ *	Initialization and module stuff
+ * ------------------------------------------------------------------
+ */
 
 int tm6000_v4l2_register(struct tm6000_core *dev)
 {
@@ -1443,8 +1471,10 @@
 	INIT_LIST_HEAD(&dev->vidq.active);
 	INIT_LIST_HEAD(&dev->vidq.queued);
 
-	memcpy (dev->vfd, &tm6000_template, sizeof(*(dev->vfd)));
-	dev->vfd->debug=tm6000_debug;
+	memcpy(dev->vfd, &tm6000_template, sizeof(*(dev->vfd)));
+	dev->vfd->debug = tm6000_debug;
+	dev->vfd->lock = &dev->lock;
+
 	vfd->v4l2_dev = &dev->v4l2_dev;
 	video_set_drvdata(vfd, dev);
 
@@ -1466,11 +1496,11 @@
 }
 
 module_param(video_nr, int, 0);
-MODULE_PARM_DESC(video_nr,"Allow changing video device number");
+MODULE_PARM_DESC(video_nr, "Allow changing video device number");
 
-module_param_named (debug, tm6000_debug, int, 0444);
-MODULE_PARM_DESC(debug,"activates debug info");
+module_param_named(debug, tm6000_debug, int, 0444);
+MODULE_PARM_DESC(debug, "activates debug info");
 
-module_param(vid_limit,int,0644);
-MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes");
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
 
diff --git a/drivers/staging/tm6000/tm6000.h b/drivers/staging/tm6000/tm6000.h
index 1ec1bff..46017b60 100644
--- a/drivers/staging/tm6000/tm6000.h
+++ b/drivers/staging/tm6000/tm6000.h
@@ -1,23 +1,23 @@
 /*
-   tm6000.h - driver for TM5600/TM6000/TM6010 USB video capture devices
-
-   Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
-
-   Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
-	- DVB-T support
-
-   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 version 2
-
-   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.
+ *  tm6000.h - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ *  Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ *  Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
+ *	- DVB-T support
+ *
+ *  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 version 2
+ *
+ *  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.
  */
 
 /* Use the tm6000-hack, instead of the proper initialization code i*/
@@ -54,8 +54,9 @@
 };
 
 /* ------------------------------------------------------------------
-	Basic structures
-   ------------------------------------------------------------------*/
+ *	Basic structures
+ * ------------------------------------------------------------------
+ */
 
 struct tm6000_fmt {
 	char  *name;
@@ -189,7 +190,9 @@
 	int				users;
 
 	/* various device info */
-	unsigned int			resources;
+	struct tm6000_fh		*resources;	/* Points to fh that is streaming */
+	bool				is_res_read;
+
 	struct video_device		*vfd;
 	struct tm6000_dmaqueue		vidq;
 	struct v4l2_device		v4l2_dev;
@@ -205,6 +208,9 @@
 
 	/* audio support */
 	struct snd_tm6000_card		*adev;
+	struct work_struct		wq_trigger;   /* Trigger to start/stop audio for alsa module */
+	atomic_t			stream_started;  /* stream should be running if true */
+
 
 	struct tm6000_IR		*ir;
 
@@ -251,9 +257,9 @@
 	enum v4l2_buf_type           type;
 };
 
-#define TM6000_STD	V4L2_STD_PAL|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc|    \
+#define TM6000_STD	(V4L2_STD_PAL|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc|    \
 			V4L2_STD_PAL_M|V4L2_STD_PAL_60|V4L2_STD_NTSC_M| \
-			V4L2_STD_NTSC_M_JP|V4L2_STD_SECAM
+			V4L2_STD_NTSC_M_JP|V4L2_STD_SECAM)
 
 /* In tm6000-cards.c */
 
diff --git a/drivers/video/via/accel.c b/drivers/video/via/accel.c
index 3c969cd..4b67b8e 100644
--- a/drivers/video/via/accel.c
+++ b/drivers/video/via/accel.c
@@ -358,7 +358,7 @@
 	viapar->shared->vq_vram_addr = viapar->fbmem_free;
 	viapar->fbmem_used += VQ_SIZE;
 
-#if defined(CONFIG_FB_VIA_CAMERA) || defined(CONFIG_FB_VIA_CAMERA_MODULE)
+#if defined(CONFIG_VIDEO_VIA_CAMERA) || defined(CONFIG_VIDEO_VIA_CAMERA_MODULE)
 	/*
 	 * Set aside a chunk of framebuffer memory for the camera
 	 * driver.  Someday this driver probably needs a proper allocator
diff --git a/drivers/video/via/via-core.c b/drivers/video/via/via-core.c
index 31e3033..a3aa917 100644
--- a/drivers/video/via/via-core.c
+++ b/drivers/video/via/via-core.c
@@ -95,6 +95,13 @@
 
 /* ---------------------------------------------------------------------- */
 /*
+ * Currently, the camera driver is the only user of the DMA code, so we
+ * only compile it in if the camera driver is being built.  Chances are,
+ * most viafb systems will not need to have this extra code for a while.
+ * As soon as another user comes long, the ifdef can be removed.
+ */
+#if defined(CONFIG_VIDEO_VIA_CAMERA) || defined(CONFIG_VIDEO_VIA_CAMERA_MODULE)
+/*
  * Access to the DMA engine.  This currently provides what the camera
  * driver needs (i.e. outgoing only) but is easily expandable if need
  * be.
@@ -322,7 +329,7 @@
 	return 0;
 }
 EXPORT_SYMBOL_GPL(viafb_dma_copy_out_sg);
-
+#endif /* CONFIG_VIDEO_VIA_CAMERA */
 
 /* ---------------------------------------------------------------------- */
 /*
@@ -511,7 +518,12 @@
 	},
 	{
 		.name = "viafb-i2c",
-	}
+	},
+#if defined(CONFIG_VIDEO_VIA_CAMERA) || defined(CONFIG_VIDEO_VIA_CAMERA_MODULE)
+	{
+		.name = "viafb-camera",
+	},
+#endif
 };
 #define N_SUBDEVS ARRAY_SIZE(viafb_subdevs)
 
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 831c463..05a59f0 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -372,7 +372,6 @@
 header-y += vhost.h
 header-y += videodev.h
 header-y += videodev2.h
-header-y += videotext.h
 header-y += virtio_9p.h
 header-y += virtio_balloon.h
 header-y += virtio_blk.h
diff --git a/include/linux/via-core.h b/include/linux/via-core.h
index 7ffb521..38bffd8 100644
--- a/include/linux/via-core.h
+++ b/include/linux/via-core.h
@@ -81,7 +81,7 @@
 	unsigned long fbmem_start;
 	long fbmem_len;
 	void __iomem *fbmem;
-#if defined(CONFIG_FB_VIA_CAMERA) || defined(CONFIG_FB_VIA_CAMERA_MODULE)
+#if defined(CONFIG_VIDEO_VIA_CAMERA) || defined(CONFIG_VIDEO_VIA_CAMERA_MODULE)
 	long camera_fbmem_offset;
 	long camera_fbmem_size;
 #endif
@@ -138,6 +138,7 @@
 #define   VDE_I_LVDSSIEN  0x40000000  /* LVDS Sense enable */
 #define   VDE_I_ENABLE	  0x80000000  /* Global interrupt enable */
 
+#if defined(CONFIG_VIDEO_VIA_CAMERA) || defined(CONFIG_VIDEO_VIA_CAMERA_MODULE)
 /*
  * DMA management.
  */
@@ -172,6 +173,7 @@
  */
 #define VGA_WIDTH	640
 #define VGA_HEIGHT	480
+#endif /* CONFIG_VIDEO_VIA_CAMERA */
 
 /*
  * Indexed port operations.  Note that these are all multi-op
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 61490c6..5f6f470 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -363,6 +363,8 @@
 #define V4L2_PIX_FMT_OV518    v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */
 #define V4L2_PIX_FMT_STV0680  v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */
 #define V4L2_PIX_FMT_TM6000   v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */
+#define V4L2_PIX_FMT_CIT_YYVYUY v4l2_fourcc('C', 'I', 'T', 'V') /* one line of Y then 1 line of VYUY */
+#define V4L2_PIX_FMT_KONICA420  v4l2_fourcc('K', 'O', 'N', 'I') /* YUV420 planar in blocks of 256 pixels */
 
 /*
  *	F O R M A T   E N U M E R A T I O N
@@ -1045,8 +1047,11 @@
 
 #define V4L2_CID_CHROMA_GAIN                    (V4L2_CID_BASE+36)
 
+#define V4L2_CID_ILLUMINATORS_1			(V4L2_CID_BASE+37)
+#define V4L2_CID_ILLUMINATORS_2			(V4L2_CID_BASE+38)
+
 /* last CID + 1 */
-#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+37)
+#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+39)
 
 /*  MPEG-class control IDs defined by V4L2 */
 #define V4L2_CID_MPEG_BASE 			(V4L2_CTRL_CLASS_MPEG | 0x900)
@@ -1363,6 +1368,8 @@
 #define V4L2_TUNER_CAP_SAP		0x0020
 #define V4L2_TUNER_CAP_LANG1		0x0040
 #define V4L2_TUNER_CAP_RDS		0x0080
+#define V4L2_TUNER_CAP_RDS_BLOCK_IO	0x0100
+#define V4L2_TUNER_CAP_RDS_CONTROLS	0x0200
 
 /*  Flags for the 'rxsubchans' field */
 #define V4L2_TUNER_SUB_MONO		0x0001
@@ -1392,7 +1399,8 @@
 	enum v4l2_tuner_type  type;
 	__u32		      seek_upward;
 	__u32		      wrap_around;
-	__u32		      reserved[8];
+	__u32		      spacing;
+	__u32		      reserved[7];
 };
 
 /*
diff --git a/include/linux/videotext.h b/include/linux/videotext.h
deleted file mode 100644
index 3e68c8d..0000000
--- a/include/linux/videotext.h
+++ /dev/null
@@ -1,125 +0,0 @@
-#ifndef _VTX_H
-#define _VTX_H
-
-/*
- * Teletext (=Videotext) hardware decoders using interface /dev/vtx
- * Do not confuse with drivers using /dev/vbi which decode videotext by software
- *
- * Videotext IOCTLs changed in order to use _IO() macros defined in <linux/ioctl.h>,
- * unused tuner IOCTLs cleaned up by
- * Michael Geng <linux@MichaelGeng.de>
- *
- * Copyright (c) 1994-97 Martin Buck  <martin-2.buck@student.uni-ulm.de>
- * Read COPYING for more information
- *
- */
-
-
-/*
- *	Videotext ioctls
- */
-#define VTXIOCGETINFO	_IOR  (0x81,  1, vtx_info_t)
-#define VTXIOCCLRPAGE	_IOW  (0x81,  2, vtx_pagereq_t)
-#define VTXIOCCLRFOUND	_IOW  (0x81,  3, vtx_pagereq_t)
-#define VTXIOCPAGEREQ	_IOW  (0x81,  4, vtx_pagereq_t)
-#define VTXIOCGETSTAT	_IOW  (0x81,  5, vtx_pagereq_t)
-#define VTXIOCGETPAGE	_IOW  (0x81,  6, vtx_pagereq_t)
-#define VTXIOCSTOPDAU	_IOW  (0x81,  7, vtx_pagereq_t)
-#define VTXIOCPUTPAGE	_IO   (0x81,  8)
-#define VTXIOCSETDISP	_IO   (0x81,  9)
-#define VTXIOCPUTSTAT	_IO   (0x81, 10)
-#define VTXIOCCLRCACHE	_IO   (0x81, 11)
-#define VTXIOCSETVIRT	_IOW  (0x81, 12, long)
-
-/* for compatibility, will go away some day */
-#define VTXIOCGETINFO_OLD  0x7101  /* get version of driver & capabilities of vtx-chipset */
-#define VTXIOCCLRPAGE_OLD  0x7102  /* clear page-buffer */
-#define VTXIOCCLRFOUND_OLD 0x7103  /* clear bits indicating that page was found */
-#define VTXIOCPAGEREQ_OLD  0x7104  /* search for page */
-#define VTXIOCGETSTAT_OLD  0x7105  /* get status of page-buffer */
-#define VTXIOCGETPAGE_OLD  0x7106  /* get contents of page-buffer */
-#define VTXIOCSTOPDAU_OLD  0x7107  /* stop data acquisition unit */
-#define VTXIOCPUTPAGE_OLD  0x7108  /* display page on TV-screen */
-#define VTXIOCSETDISP_OLD  0x7109  /* set TV-mode */
-#define VTXIOCPUTSTAT_OLD  0x710a  /* set status of TV-output-buffer */
-#define VTXIOCCLRCACHE_OLD 0x710b  /* clear cache on VTX-interface (if avail.) */
-#define VTXIOCSETVIRT_OLD  0x710c  /* turn on virtual mode (this disables TV-display) */
-
-/*
- *	Definitions for VTXIOCGETINFO
- */
-
-#define SAA5243 0
-#define SAA5246 1
-#define SAA5249 2
-#define SAA5248 3
-#define XSTV5346 4
-
-typedef struct {
-	int version_major, version_minor;	/* version of driver; if version_major changes, driver */
-						/* is not backward compatible!!! CHECK THIS!!! */
-	int numpages;				/* number of page-buffers of vtx-chipset */
-	int cct_type;				/* type of vtx-chipset (SAA5243, SAA5246, SAA5248 or
-						 * SAA5249) */
-}
-vtx_info_t;
-
-
-/*
- *	Definitions for VTXIOC{CLRPAGE,CLRFOUND,PAGEREQ,GETSTAT,GETPAGE,STOPDAU,PUTPAGE,SETDISP}
- */
-
-#define MIN_UNIT   (1<<0)
-#define MIN_TEN    (1<<1)
-#define HR_UNIT    (1<<2)
-#define HR_TEN     (1<<3)
-#define PG_UNIT    (1<<4)
-#define PG_TEN     (1<<5)
-#define PG_HUND    (1<<6)
-#define PGMASK_MAX (1<<7)
-#define PGMASK_PAGE (PG_HUND | PG_TEN | PG_UNIT)
-#define PGMASK_HOUR (HR_TEN | HR_UNIT)
-#define PGMASK_MINUTE (MIN_TEN | MIN_UNIT)
-
-typedef struct
-{
-	int page;	/* number of requested page (hexadecimal) */
-	int hour;	/* requested hour (hexadecimal) */
-	int minute;	/* requested minute (hexadecimal) */
-	int pagemask;	/* mask defining which values of the above are set */
-	int pgbuf;	/* buffer where page will be stored */
-	int start;	/* start of requested part of page */
-	int end;	/* end of requested part of page */
-	void __user *buffer;	/* pointer to beginning of destination buffer */
-}
-vtx_pagereq_t;
-
-
-/*
- *	Definitions for VTXIOC{GETSTAT,PUTSTAT}
- */
-
-#define VTX_PAGESIZE (40 * 24)
-#define VTX_VIRTUALSIZE (40 * 49)
-
-typedef struct
-{
-	int pagenum;			/* number of page (hexadecimal) */
-	int hour;			/* hour (hexadecimal) */
-	int minute;			/* minute (hexadecimal) */
-	int charset;			/* national charset */
-	unsigned delete : 1;		/* delete page (C4) */
-	unsigned headline : 1;		/* insert headline (C5) */
-	unsigned subtitle : 1;		/* insert subtitle (C6) */
-	unsigned supp_header : 1;	/* suppress header (C7) */
-	unsigned update : 1;		/* update page (C8) */
-	unsigned inter_seq : 1;		/* interrupted sequence (C9) */
-	unsigned dis_disp : 1;		/* disable/suppress display (C10) */
-	unsigned serial : 1;		/* serial mode (C11) */
-	unsigned notfound : 1;		/* /FOUND */
-	unsigned pblf : 1;		/* PBLF */
-	unsigned hamming : 1;		/* hamming-error occurred */
-}
-vtx_pageinfo_t;
-
-#endif /* _VTX_H */
diff --git a/include/media/ir-core.h b/include/media/ir-core.h
index eb7fddf..6dc37fa 100644
--- a/include/media/ir-core.h
+++ b/include/media/ir-core.h
@@ -60,6 +60,7 @@
  * @s_idle: optional: enable/disable hardware idle mode, upon which,
 	device doesn't interrupt host until it sees IR pulses
  * @s_learning_mode: enable wide band receiver used for learning
+ * @s_carrier_report: enable carrier reports
  */
 struct ir_dev_props {
 	enum rc_driver_type	driver_type;
@@ -82,8 +83,9 @@
 	int			(*s_tx_duty_cycle)(void *priv, u32 duty_cycle);
 	int			(*s_rx_carrier_range)(void *priv, u32 min, u32 max);
 	int			(*tx_ir)(void *priv, int *txbuf, u32 n);
-	void			(*s_idle)(void *priv, int enable);
+	void			(*s_idle)(void *priv, bool enable);
 	int			(*s_learning_mode)(void *priv, int enable);
+	int			(*s_carrier_report) (void *priv, int enable);
 };
 
 struct ir_input_dev {
@@ -157,27 +159,54 @@
 
 void ir_repeat(struct input_dev *dev);
 void ir_keydown(struct input_dev *dev, int scancode, u8 toggle);
+void ir_keyup(struct ir_input_dev *ir);
 u32 ir_g_keycode_from_table(struct input_dev *input_dev, u32 scancode);
 
 /* From ir-raw-event.c */
 
 struct ir_raw_event {
-	unsigned                        pulse:1;
-	unsigned                        duration:31;
+	union {
+		u32             duration;
+
+		struct {
+			u32     carrier;
+			u8      duty_cycle;
+		};
+	};
+
+	unsigned                pulse:1;
+	unsigned                reset:1;
+	unsigned                timeout:1;
+	unsigned                carrier_report:1;
 };
 
-#define IR_MAX_DURATION                 0x7FFFFFFF      /* a bit more than 2 seconds */
+#define DEFINE_IR_RAW_EVENT(event) \
+	struct ir_raw_event event = { \
+		{ .duration = 0 } , \
+		.pulse = 0, \
+		.reset = 0, \
+		.timeout = 0, \
+		.carrier_report = 0 }
+
+static inline void init_ir_raw_event(struct ir_raw_event *ev)
+{
+	memset(ev, 0, sizeof(*ev));
+}
+
+#define IR_MAX_DURATION         0xFFFFFFFF      /* a bit more than 4 seconds */
 
 void ir_raw_event_handle(struct input_dev *input_dev);
 int ir_raw_event_store(struct input_dev *input_dev, struct ir_raw_event *ev);
 int ir_raw_event_store_edge(struct input_dev *input_dev, enum raw_event_type type);
 int ir_raw_event_store_with_filter(struct input_dev *input_dev,
 				struct ir_raw_event *ev);
-void ir_raw_event_set_idle(struct input_dev *input_dev, int idle);
+void ir_raw_event_set_idle(struct input_dev *input_dev, bool idle);
 
 static inline void ir_raw_event_reset(struct input_dev *input_dev)
 {
-	struct ir_raw_event ev = { .pulse = false, .duration = 0 };
+	DEFINE_IR_RAW_EVENT(ev);
+	ev.reset = true;
+
 	ir_raw_event_store(input_dev, &ev);
 	ir_raw_event_handle(input_dev);
 }
diff --git a/include/media/ir-kbd-i2c.h b/include/media/ir-kbd-i2c.h
index 5e96d7a..557c676 100644
--- a/include/media/ir-kbd-i2c.h
+++ b/include/media/ir-kbd-i2c.h
@@ -3,6 +3,8 @@
 
 #include <media/ir-common.h>
 
+#define DEFAULT_POLLING_INTERVAL	100	/* ms */
+
 struct IR_i2c;
 
 struct IR_i2c {
@@ -15,6 +17,8 @@
 	/* Used to avoid fast repeating */
 	unsigned char          old;
 
+	u32                    polling_interval; /* in ms */
+
 	struct delayed_work    work;
 	char                   name[32];
 	char                   phys[32];
@@ -24,7 +28,6 @@
 enum ir_kbd_get_key_fn {
 	IR_KBD_GET_KEY_CUSTOM = 0,
 	IR_KBD_GET_KEY_PIXELVIEW,
-	IR_KBD_GET_KEY_PV951,
 	IR_KBD_GET_KEY_HAUP,
 	IR_KBD_GET_KEY_KNC1,
 	IR_KBD_GET_KEY_FUSIONHDTV,
@@ -35,8 +38,9 @@
 /* Can be passed when instantiating an ir_video i2c device */
 struct IR_i2c_init_data {
 	char			*ir_codes;
-	const char             *name;
-	u64          type; /* IR_TYPE_RC5, etc */
+	const char		*name;
+	u64			type; /* IR_TYPE_RC5, etc */
+	u32			polling_interval; /* 0 means DEFAULT_POLLING_INTERVAL */
 	/*
 	 * Specify either a function pointer or a value indicating one of
 	 * ir_kbd_i2c's internal get_key functions
diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h
index b1f6066..54780a5 100644
--- a/include/media/lirc_dev.h
+++ b/include/media/lirc_dev.h
@@ -125,10 +125,10 @@
 struct lirc_driver {
 	char name[40];
 	int minor;
-	unsigned long code_length;
+	__u32 code_length;
 	unsigned int buffer_size; /* in chunks holding one code each */
 	int sample_rate;
-	unsigned long features;
+	__u32 features;
 
 	unsigned int chunk_size;
 
@@ -139,7 +139,7 @@
 	struct lirc_buffer *rbuf;
 	int (*set_use_inc) (void *data);
 	void (*set_use_dec) (void *data);
-	struct file_operations *fops;
+	const struct file_operations *fops;
 	struct device *dev;
 	struct module *owner;
 };
diff --git a/include/media/omap1_camera.h b/include/media/omap1_camera.h
new file mode 100644
index 0000000..819767c
--- /dev/null
+++ b/include/media/omap1_camera.h
@@ -0,0 +1,35 @@
+/*
+ * Header for V4L2 SoC Camera driver for OMAP1 Camera Interface
+ *
+ * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MEDIA_OMAP1_CAMERA_H_
+#define __MEDIA_OMAP1_CAMERA_H_
+
+#include <linux/bitops.h>
+
+#define OMAP1_CAMERA_IOSIZE		0x1c
+
+enum omap1_cam_vb_mode {
+	OMAP1_CAM_DMA_CONTIG = 0,
+	OMAP1_CAM_DMA_SG,
+};
+
+#define OMAP1_CAMERA_MIN_BUF_COUNT(x)	((x) == OMAP1_CAM_DMA_CONTIG ? 3 : 2)
+
+struct omap1_cam_platform_data {
+	unsigned long	camexclk_khz;
+	unsigned long	lclk_khz_max;
+	unsigned long	flags;
+};
+
+#define OMAP1_CAMERA_LCLK_RISING	BIT(0)
+#define OMAP1_CAMERA_RST_LOW		BIT(1)
+#define OMAP1_CAMERA_RST_HIGH		BIT(2)
+
+#endif /* __MEDIA_OMAP1_CAMERA_H_ */
diff --git a/include/media/rc-map.h b/include/media/rc-map.h
index 9b201ec..e0f17ed 100644
--- a/include/media/rc-map.h
+++ b/include/media/rc-map.h
@@ -17,12 +17,13 @@
 #define IR_TYPE_RC6	(1  << 2)	/* Philips RC6 protocol */
 #define IR_TYPE_JVC	(1  << 3)	/* JVC protocol */
 #define IR_TYPE_SONY	(1  << 4)	/* Sony12/15/20 protocol */
+#define IR_TYPE_RC5_SZ	(1  << 5)	/* RC5 variant used by Streamzap */
 #define IR_TYPE_LIRC	(1  << 30)	/* Pass raw IR to lirc userspace */
 #define IR_TYPE_OTHER	(1u << 31)
 
 #define IR_TYPE_ALL (IR_TYPE_RC5 | IR_TYPE_NEC  | IR_TYPE_RC6  | \
 		     IR_TYPE_JVC | IR_TYPE_SONY | IR_TYPE_LIRC | \
-		     IR_TYPE_OTHER)
+		     IR_TYPE_RC5_SZ | IR_TYPE_OTHER)
 
 struct ir_scancode {
 	u32	scancode;
@@ -54,6 +55,8 @@
 /* Names of the several keytables defined in-kernel */
 
 #define RC_MAP_ADSTECH_DVB_T_PCI         "rc-adstech-dvb-t-pci"
+#define RC_MAP_ALINK_DTU_M               "rc-alink-dtu-m"
+#define RC_MAP_ANYSEE                    "rc-anysee"
 #define RC_MAP_APAC_VIEWCOMP             "rc-apac-viewcomp"
 #define RC_MAP_ASUS_PC39                 "rc-asus-pc39"
 #define RC_MAP_ATI_TV_WONDER_HD_600      "rc-ati-tv-wonder-hd-600"
@@ -62,8 +65,10 @@
 #define RC_MAP_AVERMEDIA_DVBT            "rc-avermedia-dvbt"
 #define RC_MAP_AVERMEDIA_M135A           "rc-avermedia-m135a"
 #define RC_MAP_AVERMEDIA_M733A_RM_K6     "rc-avermedia-m733a-rm-k6"
+#define RC_MAP_AVERMEDIA_RM_KS           "rc-avermedia-rm-ks"
 #define RC_MAP_AVERMEDIA                 "rc-avermedia"
 #define RC_MAP_AVERTV_303                "rc-avertv-303"
+#define RC_MAP_AZUREWAVE_AD_TU700        "rc-azurewave-ad-tu700"
 #define RC_MAP_BEHOLD_COLUMBUS           "rc-behold-columbus"
 #define RC_MAP_BEHOLD                    "rc-behold"
 #define RC_MAP_BUDGET_CI_OLD             "rc-budget-ci-old"
@@ -71,6 +76,8 @@
 #define RC_MAP_CINERGY                   "rc-cinergy"
 #define RC_MAP_DIB0700_NEC_TABLE         "rc-dib0700-nec"
 #define RC_MAP_DIB0700_RC5_TABLE         "rc-dib0700-rc5"
+#define RC_MAP_DIGITALNOW_TINYTWIN       "rc-digitalnow-tinytwin"
+#define RC_MAP_DIGITTRADE                "rc-digittrade"
 #define RC_MAP_DM1105_NEC                "rc-dm1105-nec"
 #define RC_MAP_DNTV_LIVE_DVBT_PRO        "rc-dntv-live-dvbt-pro"
 #define RC_MAP_DNTV_LIVE_DVB_T           "rc-dntv-live-dvb-t"
@@ -94,8 +101,12 @@
 #define RC_MAP_KAIOMY                    "rc-kaiomy"
 #define RC_MAP_KWORLD_315U               "rc-kworld-315u"
 #define RC_MAP_KWORLD_PLUS_TV_ANALOG     "rc-kworld-plus-tv-analog"
+#define RC_MAP_LEADTEK_Y04G0051          "rc-leadtek-y04g0051"
 #define RC_MAP_LIRC                      "rc-lirc"
+#define RC_MAP_LME2510                   "rc-lme2510"
 #define RC_MAP_MANLI                     "rc-manli"
+#define RC_MAP_MSI_DIGIVOX_II            "rc-msi-digivox-ii"
+#define RC_MAP_MSI_DIGIVOX_III           "rc-msi-digivox-iii"
 #define RC_MAP_MSI_TVANYWHERE_PLUS       "rc-msi-tvanywhere-plus"
 #define RC_MAP_MSI_TVANYWHERE            "rc-msi-tvanywhere"
 #define RC_MAP_NEBULA                    "rc-nebula"
@@ -114,14 +125,18 @@
 #define RC_MAP_PURPLETV                  "rc-purpletv"
 #define RC_MAP_PV951                     "rc-pv951"
 #define RC_MAP_RC5_HAUPPAUGE_NEW         "rc-rc5-hauppauge-new"
-#define RC_MAP_RC5_STREAMZAP             "rc-rc5-streamzap"
 #define RC_MAP_RC5_TV                    "rc-rc5-tv"
 #define RC_MAP_RC6_MCE                   "rc-rc6-mce"
 #define RC_MAP_REAL_AUDIO_220_32_KEYS    "rc-real-audio-220-32-keys"
+#define RC_MAP_STREAMZAP                 "rc-streamzap"
 #define RC_MAP_TBS_NEC                   "rc-tbs-nec"
 #define RC_MAP_TERRATEC_CINERGY_XS       "rc-terratec-cinergy-xs"
+#define RC_MAP_TERRATEC_SLIM             "rc-terratec-slim"
 #define RC_MAP_TEVII_NEC                 "rc-tevii-nec"
+#define RC_MAP_TOTAL_MEDIA_IN_HAND       "rc-total-media-in-hand"
+#define RC_MAP_TREKSTOR                  "rc-trekstor"
 #define RC_MAP_TT_1500                   "rc-tt-1500"
+#define RC_MAP_TWINHAN_VP1027_DVBS       "rc-twinhan1027"
 #define RC_MAP_VIDEOMATE_S350            "rc-videomate-s350"
 #define RC_MAP_VIDEOMATE_TV_PVR          "rc-videomate-tv-pvr"
 #define RC_MAP_WINFAST                   "rc-winfast"
diff --git a/include/media/s3c_fimc.h b/include/media/s3c_fimc.h
new file mode 100644
index 0000000..ca1b673
--- /dev/null
+++ b/include/media/s3c_fimc.h
@@ -0,0 +1,60 @@
+/*
+ * Samsung S5P SoC camera interface driver header
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd
+ * Author: Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef S3C_FIMC_H_
+#define S3C_FIMC_H_
+
+enum cam_bus_type {
+	FIMC_ITU_601 = 1,
+	FIMC_ITU_656,
+	FIMC_MIPI_CSI2,
+	FIMC_LCD_WB, /* FIFO link from LCD mixer */
+};
+
+#define FIMC_CLK_INV_PCLK	(1 << 0)
+#define FIMC_CLK_INV_VSYNC	(1 << 1)
+#define FIMC_CLK_INV_HREF	(1 << 2)
+#define FIMC_CLK_INV_HSYNC	(1 << 3)
+
+struct i2c_board_info;
+
+/**
+ * struct s3c_fimc_isp_info - image sensor information required for host
+ *			      interace configuration.
+ *
+ * @board_info: pointer to I2C subdevice's board info
+ * @bus_type: determines bus type, MIPI, ITU-R BT.601 etc.
+ * @i2c_bus_num: i2c control bus id the sensor is attached to
+ * @mux_id: FIMC camera interface multiplexer index (separate for MIPI and ITU)
+ * @bus_width: camera data bus width in bits
+ * @flags: flags defining bus signals polarity inversion (High by default)
+ */
+struct s3c_fimc_isp_info {
+	struct i2c_board_info *board_info;
+	enum cam_bus_type bus_type;
+	u16 i2c_bus_num;
+	u16 mux_id;
+	u16 bus_width;
+	u16 flags;
+};
+
+
+#define FIMC_MAX_CAMIF_CLIENTS	2
+
+/**
+ * struct s3c_platform_fimc - camera host interface platform data
+ *
+ * @isp_info: properties of camera sensor required for host interface setup
+ */
+struct s3c_platform_fimc {
+	struct s3c_fimc_isp_info *isp_info[FIMC_MAX_CAMIF_CLIENTS];
+};
+#endif /* S3C_FIMC_H_ */
diff --git a/include/media/sh_vou.h b/include/media/sh_vou.h
index a3ef302..ec3ba9a 100644
--- a/include/media/sh_vou.h
+++ b/include/media/sh_vou.h
@@ -28,7 +28,6 @@
 	int i2c_adap;
 	struct i2c_board_info *board_info;
 	unsigned long flags;
-	char *module_name;
 };
 
 #endif
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index 2ce9573..86e3631 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -21,6 +21,8 @@
 
 extern struct bus_type soc_camera_bus_type;
 
+struct file;
+
 struct soc_camera_device {
 	struct list_head list;
 	struct device dev;
@@ -41,10 +43,7 @@
 	/* soc_camera.c private count. Only accessed with .video_lock held */
 	int use_count;
 	struct mutex video_lock;	/* Protects device data */
-};
-
-struct soc_camera_file {
-	struct soc_camera_device *icd;
+	struct file *streamer;		/* stream owner */
 	struct videobuf_queue vb_vidq;
 };
 
@@ -79,7 +78,7 @@
 	int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *);
 	void (*init_videobuf)(struct videobuf_queue *,
 			      struct soc_camera_device *);
-	int (*reqbufs)(struct soc_camera_file *, struct v4l2_requestbuffers *);
+	int (*reqbufs)(struct soc_camera_device *, struct v4l2_requestbuffers *);
 	int (*querycap)(struct soc_camera_host *, struct v4l2_capability *);
 	int (*set_bus_param)(struct soc_camera_device *, __u32);
 	int (*get_ctrl)(struct soc_camera_device *, struct v4l2_control *);
diff --git a/include/media/sr030pc30.h b/include/media/sr030pc30.h
new file mode 100644
index 0000000..6f901a6
--- /dev/null
+++ b/include/media/sr030pc30.h
@@ -0,0 +1,21 @@
+/*
+ * Driver header for SR030PC30 camera sensor
+ *
+ * Copyright (c) 2010 Samsung Electronics, Co. Ltd
+ * Contact: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef SR030PC30_H
+#define SR030PC30_H
+
+struct sr030pc30_platform_data {
+	unsigned long clk_rate;	/* master clock frequency in Hz */
+	int (*set_power)(struct device *dev, int on);
+};
+
+#endif /* SR030PC30_H */
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index 21b4428..51e89f2 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -38,6 +38,9 @@
 	/* module tvaudio: reserved range 50-99 */
 	V4L2_IDENT_TVAUDIO = 50,	/* A tvaudio chip, unknown which it is exactly */
 
+	/* Sony IMX074 */
+	V4L2_IDENT_IMX074 = 74,
+
 	/* module saa7110: just ident 100 */
 	V4L2_IDENT_SAA7110 = 100,
 
@@ -70,6 +73,7 @@
 	V4L2_IDENT_OV9655 = 255,
 	V4L2_IDENT_SOI968 = 256,
 	V4L2_IDENT_OV9640 = 257,
+	V4L2_IDENT_OV6650 = 258,
 
 	/* module saa7146: reserved range 300-309 */
 	V4L2_IDENT_SAA7146 = 300,
@@ -111,6 +115,10 @@
 	V4L2_IDENT_VPX3216B = 3216,
 	V4L2_IDENT_VPX3220A = 3220,
 
+	/* VX855 just ident 3409 */
+	/* Other via devs could use 3314, 3324, 3327, 3336, 3364, 3353 */
+	V4L2_IDENT_VIA_VX855 = 3409,
+
 	/* module tvp5150 */
 	V4L2_IDENT_TVP5150 = 5150,
 
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index 98b3264..41dd480 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -232,4 +232,14 @@
 			   unsigned int hmax, unsigned int halign,
 			   unsigned int salign);
 int v4l_fill_dv_preset_info(u32 preset, struct v4l2_dv_enum_preset *info);
+
+struct v4l2_discrete_probe {
+	const struct v4l2_frmsize_discrete	*sizes;
+	int					num_sizes;
+};
+
+const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
+		const struct v4l2_discrete_probe *probe,
+		s32 width, s32 height);
+
 #endif /* V4L2_COMMON_H_ */
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index 1efcacb..15802a0 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -21,8 +21,7 @@
 #define VFL_TYPE_GRABBER	0
 #define VFL_TYPE_VBI		1
 #define VFL_TYPE_RADIO		2
-#define VFL_TYPE_VTX		3
-#define VFL_TYPE_MAX		4
+#define VFL_TYPE_MAX		3
 
 struct v4l2_ioctl_callbacks;
 struct video_device;
@@ -42,8 +41,6 @@
 	unsigned int (*poll) (struct file *, struct poll_table_struct *);
 	long (*ioctl) (struct file *, unsigned int, unsigned long);
 	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
-	unsigned long (*get_unmapped_area) (struct file *, unsigned long,
-				unsigned long, unsigned long, unsigned long);
 	int (*mmap) (struct file *, struct vm_area_struct *);
 	int (*open) (struct file *);
 	int (*release) (struct file *);
@@ -97,6 +94,9 @@
 
 	/* ioctl callbacks */
 	const struct v4l2_ioctl_ops *ioctl_ops;
+
+	/* serialization lock */
+	struct mutex *lock;
 };
 
 /* dev to video-device */
diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h
index 8bcbd7a..6648036 100644
--- a/include/media/v4l2-device.h
+++ b/include/media/v4l2-device.h
@@ -101,46 +101,67 @@
 /* Call the specified callback for all subdevs matching the condition.
    Ignore any errors. Note that you cannot add or delete a subdev
    while walking the subdevs list. */
-#define __v4l2_device_call_subdevs(v4l2_dev, cond, o, f, args...) 	\
+#define __v4l2_device_call_subdevs_p(v4l2_dev, sd, cond, o, f, args...)	\
 	do { 								\
-		struct v4l2_subdev *sd; 				\
+		list_for_each_entry((sd), &(v4l2_dev)->subdevs, list)	\
+			if ((cond) && (sd)->ops->o && (sd)->ops->o->f)	\
+				(sd)->ops->o->f((sd) , ##args);		\
+	} while (0)
+
+#define __v4l2_device_call_subdevs(v4l2_dev, cond, o, f, args...)	\
+	do {								\
+		struct v4l2_subdev *__sd;				\
 									\
-		list_for_each_entry(sd, &(v4l2_dev)->subdevs, list)   	\
-			if ((cond) && sd->ops->o && sd->ops->o->f) 	\
-				sd->ops->o->f(sd , ##args); 		\
+		__v4l2_device_call_subdevs_p(v4l2_dev, __sd, cond, o,	\
+						f , ##args);		\
 	} while (0)
 
 /* Call the specified callback for all subdevs matching the condition.
    If the callback returns an error other than 0 or -ENOIOCTLCMD, then
    return with that error code. Note that you cannot add or delete a
    subdev while walking the subdevs list. */
-#define __v4l2_device_call_subdevs_until_err(v4l2_dev, cond, o, f, args...) \
+#define __v4l2_device_call_subdevs_until_err_p(v4l2_dev, sd, cond, o, f, args...) \
 ({ 									\
-	struct v4l2_subdev *sd; 					\
-	long err = 0; 							\
+	long __err = 0;							\
 									\
-	list_for_each_entry(sd, &(v4l2_dev)->subdevs, list) { 		\
-		if ((cond) && sd->ops->o && sd->ops->o->f) 		\
-			err = sd->ops->o->f(sd , ##args); 		\
-		if (err && err != -ENOIOCTLCMD)				\
+	list_for_each_entry((sd), &(v4l2_dev)->subdevs, list) {		\
+		if ((cond) && (sd)->ops->o && (sd)->ops->o->f)		\
+			__err = (sd)->ops->o->f((sd) , ##args);		\
+		if (__err && __err != -ENOIOCTLCMD)			\
 			break; 						\
 	} 								\
-	(err == -ENOIOCTLCMD) ? 0 : err; 				\
+	(__err == -ENOIOCTLCMD) ? 0 : __err;				\
+})
+
+#define __v4l2_device_call_subdevs_until_err(v4l2_dev, cond, o, f, args...) \
+({									\
+	struct v4l2_subdev *__sd;					\
+	__v4l2_device_call_subdevs_until_err_p(v4l2_dev, __sd, cond, o,	\
+						f, args...);		\
 })
 
 /* Call the specified callback for all subdevs matching grp_id (if 0, then
    match them all). Ignore any errors. Note that you cannot add or delete
    a subdev while walking the subdevs list. */
-#define v4l2_device_call_all(v4l2_dev, grpid, o, f, args...) 		\
-	__v4l2_device_call_subdevs(v4l2_dev, 				\
-			!(grpid) || sd->grp_id == (grpid), o, f , ##args)
+#define v4l2_device_call_all(v4l2_dev, grpid, o, f, args...)		\
+	do {								\
+		struct v4l2_subdev *__sd;				\
+									\
+		__v4l2_device_call_subdevs_p(v4l2_dev, __sd,		\
+			!(grpid) || __sd->grp_id == (grpid), o, f ,	\
+			##args);					\
+	} while (0)
 
 /* Call the specified callback for all subdevs matching grp_id (if 0, then
    match them all). If the callback returns an error other than 0 or
    -ENOIOCTLCMD, then return with that error code. Note that you cannot
    add or delete a subdev while walking the subdevs list. */
 #define v4l2_device_call_until_err(v4l2_dev, grpid, o, f, args...) 	\
-	__v4l2_device_call_subdevs_until_err(v4l2_dev,			\
-		       !(grpid) || sd->grp_id == (grpid), o, f , ##args)
+({									\
+	struct v4l2_subdev *__sd;					\
+	__v4l2_device_call_subdevs_until_err_p(v4l2_dev, __sd,		\
+			!(grpid) || __sd->grp_id == (grpid), o, f ,	\
+			##args);					\
+})
 
 #endif
diff --git a/include/media/v4l2-i2c-drv.h b/include/media/v4l2-i2c-drv.h
deleted file mode 100644
index 74bf741..0000000
--- a/include/media/v4l2-i2c-drv.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * v4l2-i2c-drv.h - contains I2C handling code that's identical for
- *		    all V4L2 I2C drivers. Use this header if the
- *		    I2C driver is only used by drivers converted
- *		    to the bus-based I2C API.
- *
- * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* NOTE: the full version of this header is in the v4l-dvb repository
- * and allows v4l i2c drivers to be compiled on pre-2.6.26 kernels.
- * The version of this header as it appears in the kernel is a stripped
- * version (without all the backwards compatibility stuff) and so it
- * looks a bit odd.
- *
- * If you look at the full version then you will understand the reason
- * for introducing this header since you really don't want to have all
- * the tricky backwards compatibility code in each and every i2c driver.
- *
- * If the i2c driver will never be compiled for pre-2.6.26 kernels, then
- * DO NOT USE this header! Just write it as a regular i2c driver.
- */
-
-#ifndef __V4L2_I2C_DRV_H__
-#define __V4L2_I2C_DRV_H__
-
-#include <media/v4l2-common.h>
-
-struct v4l2_i2c_driver_data {
-	const char * const name;
-	int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
-	int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
-	int (*remove)(struct i2c_client *client);
-	int (*suspend)(struct i2c_client *client, pm_message_t state);
-	int (*resume)(struct i2c_client *client);
-	const struct i2c_device_id *id_table;
-};
-
-static struct v4l2_i2c_driver_data v4l2_i2c_data;
-static struct i2c_driver v4l2_i2c_driver;
-
-
-/* Bus-based I2C implementation for kernels >= 2.6.26 */
-
-static int __init v4l2_i2c_drv_init(void)
-{
-	v4l2_i2c_driver.driver.name = v4l2_i2c_data.name;
-	v4l2_i2c_driver.command = v4l2_i2c_data.command;
-	v4l2_i2c_driver.probe = v4l2_i2c_data.probe;
-	v4l2_i2c_driver.remove = v4l2_i2c_data.remove;
-	v4l2_i2c_driver.suspend = v4l2_i2c_data.suspend;
-	v4l2_i2c_driver.resume = v4l2_i2c_data.resume;
-	v4l2_i2c_driver.id_table = v4l2_i2c_data.id_table;
-	return i2c_add_driver(&v4l2_i2c_driver);
-}
-
-
-static void __exit v4l2_i2c_drv_cleanup(void)
-{
-	i2c_del_driver(&v4l2_i2c_driver);
-}
-
-module_init(v4l2_i2c_drv_init);
-module_exit(v4l2_i2c_drv_cleanup);
-
-#endif /* __V4L2_I2C_DRV_H__ */
diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h
index f0cf2e7..8e65598 100644
--- a/include/media/v4l2-mediabus.h
+++ b/include/media/v4l2-mediabus.h
@@ -28,10 +28,18 @@
 	V4L2_MBUS_FMT_YVYU8_2X8,
 	V4L2_MBUS_FMT_UYVY8_2X8,
 	V4L2_MBUS_FMT_VYUY8_2X8,
+	V4L2_MBUS_FMT_YVYU10_2X10,
+	V4L2_MBUS_FMT_YUYV10_2X10,
+	V4L2_MBUS_FMT_YVYU10_1X20,
+	V4L2_MBUS_FMT_YUYV10_1X20,
+	V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
+	V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE,
 	V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
 	V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
 	V4L2_MBUS_FMT_RGB565_2X8_LE,
 	V4L2_MBUS_FMT_RGB565_2X8_BE,
+	V4L2_MBUS_FMT_BGR565_2X8_LE,
+	V4L2_MBUS_FMT_BGR565_2X8_BE,
 	V4L2_MBUS_FMT_SBGGR8_1X8,
 	V4L2_MBUS_FMT_SBGGR10_1X10,
 	V4L2_MBUS_FMT_GREY8_1X8,
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index 4a97d73..b0316a7 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -256,10 +256,6 @@
 	int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std);
 	int (*g_input_status)(struct v4l2_subdev *sd, u32 *status);
 	int (*s_stream)(struct v4l2_subdev *sd, int enable);
-	int (*enum_fmt)(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmtdesc);
-	int (*g_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);
-	int (*try_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);
-	int (*s_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);
 	int (*cropcap)(struct v4l2_subdev *sd, struct v4l2_cropcap *cc);
 	int (*g_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop);
 	int (*s_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop);
@@ -442,17 +438,28 @@
 	/* can be used to group similar subdevs, value is driver-specific */
 	u32 grp_id;
 	/* pointer to private data */
-	void *priv;
+	void *dev_priv;
+	void *host_priv;
 };
 
 static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p)
 {
-	sd->priv = p;
+	sd->dev_priv = p;
 }
 
 static inline void *v4l2_get_subdevdata(const struct v4l2_subdev *sd)
 {
-	return sd->priv;
+	return sd->dev_priv;
+}
+
+static inline void v4l2_set_subdev_hostdata(struct v4l2_subdev *sd, void *p)
+{
+	sd->host_priv = p;
+}
+
+static inline void *v4l2_get_subdev_hostdata(const struct v4l2_subdev *sd)
+{
+	return sd->host_priv;
 }
 
 static inline void v4l2_subdev_init(struct v4l2_subdev *sd,
@@ -466,7 +473,8 @@
 	sd->flags = 0;
 	sd->name[0] = '\0';
 	sd->grp_id = 0;
-	sd->priv = NULL;
+	sd->dev_priv = NULL;
+	sd->host_priv = NULL;
 }
 
 /* Call an ops of a v4l2_subdev, doing the right checks against
diff --git a/include/media/videobuf-core.h b/include/media/videobuf-core.h
index f2c41ce..1d3835f 100644
--- a/include/media/videobuf-core.h
+++ b/include/media/videobuf-core.h
@@ -139,6 +139,7 @@
 
 struct videobuf_queue {
 	struct mutex               vb_lock;
+	struct mutex               *ext_lock;
 	spinlock_t                 *irqlock;
 	struct device		   *dev;
 
@@ -167,7 +168,20 @@
 	void                       *priv_data;
 };
 
-int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr);
+static inline void videobuf_queue_lock(struct videobuf_queue *q)
+{
+	if (!q->ext_lock)
+		mutex_lock(&q->vb_lock);
+}
+
+static inline void videobuf_queue_unlock(struct videobuf_queue *q)
+{
+	if (!q->ext_lock)
+		mutex_unlock(&q->vb_lock);
+}
+
+int videobuf_waiton(struct videobuf_queue *q, struct videobuf_buffer *vb,
+		int non_blocking, int intr);
 int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb,
 		struct v4l2_framebuffer *fbuf);
 
@@ -185,7 +199,8 @@
 			 enum v4l2_field field,
 			 unsigned int msize,
 			 void *priv,
-			 struct videobuf_qtype_ops *int_ops);
+			 struct videobuf_qtype_ops *int_ops,
+			 struct mutex *ext_lock);
 int  videobuf_queue_is_busy(struct videobuf_queue *q);
 void videobuf_queue_cancel(struct videobuf_queue *q);
 
diff --git a/include/media/videobuf-dma-contig.h b/include/media/videobuf-dma-contig.h
index ebaa9bc..f0ed825 100644
--- a/include/media/videobuf-dma-contig.h
+++ b/include/media/videobuf-dma-contig.h
@@ -23,7 +23,8 @@
 				    enum v4l2_buf_type type,
 				    enum v4l2_field field,
 				    unsigned int msize,
-				    void *priv);
+				    void *priv,
+				    struct mutex *ext_lock);
 
 dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf);
 void videobuf_dma_contig_free(struct videobuf_queue *q,
diff --git a/include/media/videobuf-dma-sg.h b/include/media/videobuf-dma-sg.h
index aa4ebb4..1c647e8 100644
--- a/include/media/videobuf-dma-sg.h
+++ b/include/media/videobuf-dma-sg.h
@@ -103,7 +103,8 @@
 			 enum v4l2_buf_type type,
 			 enum v4l2_field field,
 			 unsigned int msize,
-			 void *priv);
+			 void *priv,
+			 struct mutex *ext_lock);
 
 #endif /* _VIDEOBUF_DMA_SG_H */
 
diff --git a/include/media/videobuf-vmalloc.h b/include/media/videobuf-vmalloc.h
index e19403c..486a97e 100644
--- a/include/media/videobuf-vmalloc.h
+++ b/include/media/videobuf-vmalloc.h
@@ -36,7 +36,8 @@
 			 enum v4l2_buf_type type,
 			 enum v4l2_field field,
 			 unsigned int msize,
-			 void *priv);
+			 void *priv,
+			 struct mutex *ext_lock);
 
 void *videobuf_to_vmalloc(struct videobuf_buffer *buf);
 
diff --git a/include/media/wm8775.h b/include/media/wm8775.h
index 60739c5..a1c4d41 100644
--- a/include/media/wm8775.h
+++ b/include/media/wm8775.h
@@ -32,4 +32,7 @@
 #define WM8775_AIN3 4
 #define WM8775_AIN4 8
 
+/* subdev group ID */
+#define WM8775_GID (1 << 0)
+
 #endif