]> git.openfabrics.org - ~shefty/rdma-dev.git/commitdiff
Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 1 Aug 2012 01:47:44 +0000 (18:47 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 1 Aug 2012 01:47:44 +0000 (18:47 -0700)
Pull second set of media updates from Mauro Carvalho Chehab:

 - radio API: add support to work with radio frequency bands

 - new AM/FM radio drivers: radio-shark, radio-shark2

 - new Remote Controller USB driver: iguanair

 - conversion of several drivers to the v4l2 core control framework

 - new board additions at existing drivers

 - the remaining (and vast majority of the patches) are due to
   drivers/DocBook fixes/cleanups.

* 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (154 commits)
  [media] radio-tea5777: use library for 64bits div
  [media] tlg2300: Declare MODULE_FIRMWARE usage
  [media] lgs8gxx: Declare MODULE_FIRMWARE usage
  [media] xc5000: Add MODULE_FIRMWARE statements
  [media] s2255drv: Add MODULE_FIRMWARE statement
  [media] dib8000: move dereference after check for NULL
  [media] Documentation: Update cardlists
  [media] bttv: add support for Aposonic W-DVR
  [media] cx25821: Remove bad strcpy to read-only char*
  [media] pms.c: remove duplicated include
  [media] smiapp-core.c: remove duplicated include
  [media] via-camera: pass correct format settings to sensor
  [media] rtl2832.c: minor cleanup
  [media] Add support for the IguanaWorks USB IR Transceiver
  [media] Minor cleanups for MCE USB
  [media] drivers/media/dvb/siano/smscoreapi.c: use list_for_each_entry
  [media] Use a named union in struct v4l2_ioctl_info
  [media] mceusb: Add Twisted Melon USB IDs
  [media] staging/media/solo6x10: use module_pci_driver macro
  [media] staging/media/dt3155v4l: use module_pci_driver macro
  ...

Conflicts:
Documentation/feature-removal-schedule.txt

146 files changed:
Documentation/DocBook/media/v4l/compat.xml
Documentation/DocBook/media/v4l/controls.xml
Documentation/DocBook/media/v4l/v4l2.xml
Documentation/DocBook/media/v4l/vidioc-create-bufs.xml
Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml
Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml [new file with mode: 0644]
Documentation/DocBook/media/v4l/vidioc-g-frequency.xml
Documentation/DocBook/media/v4l/vidioc-g-tuner.xml
Documentation/DocBook/media/v4l/vidioc-querycap.xml
Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml
Documentation/feature-removal-schedule.txt
Documentation/video4linux/CARDLIST.au0828
Documentation/video4linux/CARDLIST.bttv
Documentation/video4linux/CARDLIST.cx23885
Documentation/video4linux/CARDLIST.saa7134
drivers/hid/hid-core.c
drivers/hid/hid-ids.h
drivers/media/common/tuners/tuner-xc2028.c
drivers/media/common/tuners/xc5000.c
drivers/media/dvb/dvb-usb/az6007.c
drivers/media/dvb/frontends/dib8000.c
drivers/media/dvb/frontends/lgs8gxx.c
drivers/media/dvb/frontends/rtl2832.c
drivers/media/dvb/siano/smscoreapi.c
drivers/media/radio/Kconfig
drivers/media/radio/Makefile
drivers/media/radio/radio-cadet.c
drivers/media/radio/radio-shark.c [new file with mode: 0644]
drivers/media/radio/radio-shark2.c [new file with mode: 0644]
drivers/media/radio/radio-tea5777.c [new file with mode: 0644]
drivers/media/radio/radio-tea5777.h [new file with mode: 0644]
drivers/media/radio/si470x/radio-si470x-common.c
drivers/media/radio/si470x/radio-si470x-i2c.c
drivers/media/radio/si470x/radio-si470x-usb.c
drivers/media/radio/si470x/radio-si470x.h
drivers/media/rc/Kconfig
drivers/media/rc/Makefile
drivers/media/rc/ati_remote.c
drivers/media/rc/iguanair.c [new file with mode: 0644]
drivers/media/rc/mceusb.c
drivers/media/rc/rc-main.c
drivers/media/video/adv7180.c
drivers/media/video/bt8xx/bttv-cards.c
drivers/media/video/bt8xx/bttv.h
drivers/media/video/cx231xx/cx231xx-i2c.c
drivers/media/video/cx231xx/cx231xx.h
drivers/media/video/cx23885/cx23885-i2c.c
drivers/media/video/cx23885/cx23885.h
drivers/media/video/cx25821/cx25821-i2c.c
drivers/media/video/cx25821/cx25821-medusa-video.c
drivers/media/video/cx25821/cx25821.h
drivers/media/video/davinci/Kconfig
drivers/media/video/davinci/Makefile
drivers/media/video/davinci/vpbe_display.c
drivers/media/video/davinci/vpif.c
drivers/media/video/davinci/vpif.h
drivers/media/video/davinci/vpif_capture.c
drivers/media/video/davinci/vpif_capture.h
drivers/media/video/davinci/vpif_display.c
drivers/media/video/davinci/vpif_display.h
drivers/media/video/gspca/benq.c
drivers/media/video/gspca/conex.c
drivers/media/video/gspca/cpia1.c
drivers/media/video/gspca/etoms.c
drivers/media/video/gspca/finepix.c
drivers/media/video/gspca/gl860/gl860.c
drivers/media/video/gspca/gspca.c
drivers/media/video/gspca/jeilinj.c
drivers/media/video/gspca/jl2005bcd.c
drivers/media/video/gspca/kinect.c
drivers/media/video/gspca/konica.c
drivers/media/video/gspca/m5602/m5602_core.c
drivers/media/video/gspca/mars.c
drivers/media/video/gspca/mr97310a.c
drivers/media/video/gspca/nw80x.c
drivers/media/video/gspca/ov519.c
drivers/media/video/gspca/ov534.c
drivers/media/video/gspca/ov534_9.c
drivers/media/video/gspca/pac207.c
drivers/media/video/gspca/pac7302.c
drivers/media/video/gspca/pac7311.c
drivers/media/video/gspca/se401.c
drivers/media/video/gspca/sn9c2028.c
drivers/media/video/gspca/sonixb.c
drivers/media/video/gspca/sonixj.c
drivers/media/video/gspca/spca1528.c
drivers/media/video/gspca/spca500.c
drivers/media/video/gspca/spca501.c
drivers/media/video/gspca/spca505.c
drivers/media/video/gspca/spca506.c
drivers/media/video/gspca/spca508.c
drivers/media/video/gspca/spca561.c
drivers/media/video/gspca/sq905.c
drivers/media/video/gspca/sq905c.c
drivers/media/video/gspca/sq930x.c
drivers/media/video/gspca/stk014.c
drivers/media/video/gspca/stv0680.c
drivers/media/video/gspca/sunplus.c
drivers/media/video/gspca/t613.c
drivers/media/video/gspca/topro.c
drivers/media/video/gspca/tv8532.c
drivers/media/video/gspca/vc032x.c
drivers/media/video/gspca/vicam.c
drivers/media/video/gspca/w996Xcf.c
drivers/media/video/gspca/xirlink_cit.c
drivers/media/video/m5mols/m5mols_controls.c
drivers/media/video/mem2mem_testdev.c
drivers/media/video/mx2_emmaprp.c
drivers/media/video/ov2640.c
drivers/media/video/ov772x.c
drivers/media/video/ov9640.c
drivers/media/video/pms.c
drivers/media/video/s2255drv.c
drivers/media/video/s5p-fimc/fimc-capture.c
drivers/media/video/s5p-fimc/fimc-core.h
drivers/media/video/s5p-fimc/fimc-m2m.c
drivers/media/video/s5p-fimc/fimc-reg.c
drivers/media/video/s5p-g2d/g2d.c
drivers/media/video/s5p-jpeg/jpeg-core.c
drivers/media/video/s5p-mfc/s5p_mfc_dec.c
drivers/media/video/s5p-mfc/s5p_mfc_enc.c
drivers/media/video/saa7164/saa7164-i2c.c
drivers/media/video/saa7164/saa7164.h
drivers/media/video/smiapp/smiapp-core.c
drivers/media/video/soc_camera.c
drivers/media/video/tlg2300/pd-main.c
drivers/media/video/tvp5150.c
drivers/media/video/tw9910.c
drivers/media/video/v4l2-compat-ioctl32.c
drivers/media/video/v4l2-ctrls.c
drivers/media/video/v4l2-dev.c
drivers/media/video/v4l2-ioctl.c
drivers/media/video/v4l2-mem2mem.c
drivers/media/video/via-camera.c
drivers/media/video/videobuf-dma-contig.c
drivers/media/video/videobuf2-core.c
drivers/media/video/vivi.c
drivers/staging/media/dt3155v4l/dt3155v4l.c
drivers/staging/media/easycap/easycap_main.c
drivers/staging/media/lirc/lirc_bt829.c
drivers/staging/media/solo6x10/core.c
include/linux/videodev2.h
include/media/davinci/vpif_types.h
include/media/v4l2-ioctl.h
include/sound/tea575x-tuner.h
sound/i2c/other/tea575x-tuner.c

index 97b895151bb02d76fabdb63e97e9d807947ea872..faa0fd14666a54bc2688ca176c9dee531cc5eccf 100644 (file)
@@ -2460,7 +2460,7 @@ that used it. It was originally scheduled for removal in 2.6.35.
     </section>
 
     <section>
-      <title>V4L2 in Linux 3.5</title>
+      <title>V4L2 in Linux 3.6</title>
       <orderedlist>
        <listitem>
          <para>Replaced <structfield>input</structfield> in
@@ -2471,6 +2471,24 @@ that used it. It was originally scheduled for removal in 2.6.35.
       </orderedlist>
     </section>
 
+    <section>
+      <title>V4L2 in Linux 3.6</title>
+      <orderedlist>
+        <listitem>
+         <para>Added V4L2_CAP_VIDEO_M2M and V4L2_CAP_VIDEO_M2M_MPLANE capabilities.</para>
+        </listitem>
+      </orderedlist>
+    </section>
+
+    <section>
+      <title>V4L2 in Linux 3.6</title>
+      <orderedlist>
+        <listitem>
+         <para>Added support for frequency band enumerations: &VIDIOC-ENUM-FREQ-BANDS;.</para>
+        </listitem>
+      </orderedlist>
+    </section>
+
     <section id="other">
       <title>Relation of V4L2 to other Linux multimedia APIs</title>
 
@@ -2600,6 +2618,9 @@ ioctls.</para>
          <para><link linkend="v4l2-auto-focus-area"><constant>
          V4L2_CID_AUTO_FOCUS_AREA</constant></link> control.</para>
         </listitem>
+        <listitem>
+         <para>Support for frequency band enumeration: &VIDIOC-ENUM-FREQ-BANDS; ioctl.</para>
+        </listitem>
       </itemizedlist>
     </section>
 
index cda0dfb6769aee5d0fcadb8a30b4d19658259bc7..b0964fb4e8348853619efd3296b7821d4e6b6803 100644 (file)
@@ -372,6 +372,11 @@ minimum value disables backlight compensation.</entry>
            Cr component, bits [15:8] as Cb component and bits [31:16] must be zero.
          </entry>
          </row>
+         <row>
+           <entry><constant>V4L2_CID_AUTOBRIGHTNESS</constant></entry>
+           <entry>boolean</entry>
+           <entry>Enable Automatic Brightness.</entry>
+         </row>
          <row>
            <entry><constant>V4L2_CID_ROTATE</constant></entry>
            <entry>integer</entry>
index 36bafc48e03baa86890d1cf2c062c825be55a35c..eee6908c749fd3b6fb66e0e9ab84c9775c09509b 100644 (file)
@@ -140,6 +140,11 @@ structs, ioctls) must be noted in more detail in the history chapter
 applications. -->
 
       <revision>
+       <revnumber>3.6</revnumber>
+       <date>2012-07-02</date>
+       <authorinitials>hv</authorinitials>
+       <revremark>Added VIDIOC_ENUM_FREQ_BANDS.
+       </revremark>
        <revnumber>3.5</revnumber>
        <date>2012-05-07</date>
        <authorinitials>sa, sn</authorinitials>
@@ -534,6 +539,7 @@ and discussions on the V4L mailing list.</revremark>
     &sub-enum-fmt;
     &sub-enum-framesizes;
     &sub-enum-frameintervals;
+    &sub-enum-freq-bands;
     &sub-enuminput;
     &sub-enumoutput;
     &sub-enumstd;
index 5e73b1c8d09543183d3c388916a29f8d91dfdd0f..a8cda1acacd9a129b47d15451bfe054d6fcfd9c8 100644 (file)
@@ -64,7 +64,7 @@ different sizes.</para>
     <para>To allocate device buffers applications initialize relevant fields of
 the <structname>v4l2_create_buffers</structname> structure. They set the
 <structfield>type</structfield> field in the
-<structname>v4l2_format</structname> structure, embedded in this
+&v4l2-format; structure, embedded in this
 structure, to the respective stream or buffer type.
 <structfield>count</structfield> must be set to the number of required buffers.
 <structfield>memory</structfield> specifies the required I/O method. The
@@ -114,7 +114,7 @@ information.</para>
 /></entry>
          </row>
          <row>
-           <entry>struct&nbsp;v4l2_format</entry>
+           <entry>&v4l2-format;</entry>
            <entry><structfield>format</structfield></entry>
            <entry>Filled in by the application, preserved by the driver.</entry>
          </row>
index 6673ce582050d244e4a6fb28f88a65a49c0eb018..cd7720d404eaf337acb6b6fef9cff431903c05cb 100644 (file)
       interface and may change in the future.</para>
     </note>
 
-    <para>To query the available timings, applications initialize the
-<structfield>index</structfield> field and zero the reserved array of &v4l2-dv-timings-cap;
-and call the <constant>VIDIOC_DV_TIMINGS_CAP</constant> ioctl with a pointer to this
-structure. Drivers fill the rest of the structure or return an
-&EINVAL; when the index is out of bounds. To enumerate all supported DV timings,
-applications shall begin at index zero, incrementing by one until the
-driver returns <errorcode>EINVAL</errorcode>. Note that drivers may enumerate a
-different set of DV timings after switching the video input or
-output.</para>
+    <para>To query the capabilities of the DV receiver/transmitter applications can call
+this ioctl and the driver will fill in the structure. Note that drivers may return
+different values after switching the video input or output.</para>
 
     <table pgwide="1" frame="none" id="v4l2-bt-timings-cap">
       <title>struct <structname>v4l2_bt_timings_cap</structname></title>
@@ -115,7 +109,7 @@ output.</para>
          <row>
            <entry>__u32</entry>
            <entry><structfield>reserved</structfield>[16]</entry>
-           <entry></entry>
+           <entry>Reserved for future extensions. Drivers must set the array to zero.</entry>
          </row>
        </tbody>
       </tgroup>
diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml b/Documentation/DocBook/media/v4l/vidioc-enum-freq-bands.xml
new file mode 100644 (file)
index 0000000..6541ba0
--- /dev/null
@@ -0,0 +1,179 @@
+<refentry id="vidioc-enum-freq-bands">
+  <refmeta>
+    <refentrytitle>ioctl VIDIOC_ENUM_FREQ_BANDS</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>VIDIOC_ENUM_FREQ_BANDS</refname>
+    <refpurpose>Enumerate supported frequency bands</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+       <funcdef>int <function>ioctl</function></funcdef>
+       <paramdef>int <parameter>fd</parameter></paramdef>
+       <paramdef>int <parameter>request</parameter></paramdef>
+       <paramdef>struct v4l2_frequency_band
+*<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+       <term><parameter>fd</parameter></term>
+       <listitem>
+         <para>&fd;</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>request</parameter></term>
+       <listitem>
+         <para>VIDIOC_ENUM_FREQ_BANDS</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>argp</parameter></term>
+       <listitem>
+         <para></para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <note>
+      <title>Experimental</title>
+      <para>This is an <link linkend="experimental"> experimental </link>
+      interface and may change in the future.</para>
+    </note>
+
+    <para>Enumerates the frequency bands that a tuner or modulator supports.
+To do this applications initialize the <structfield>tuner</structfield>,
+<structfield>type</structfield> and <structfield>index</structfield> fields,
+and zero out the <structfield>reserved</structfield> array of a &v4l2-frequency-band; and
+call the <constant>VIDIOC_ENUM_FREQ_BANDS</constant> ioctl with a pointer
+to this structure.</para>
+
+    <para>This ioctl is supported if the <constant>V4L2_TUNER_CAP_FREQ_BANDS</constant> capability
+    of the corresponding tuner/modulator is set.</para>
+
+    <table pgwide="1" frame="none" id="v4l2-frequency-band">
+      <title>struct <structname>v4l2_frequency_band</structname></title>
+      <tgroup cols="3">
+       &cs-str;
+       <tbody valign="top">
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>tuner</structfield></entry>
+           <entry>The tuner or modulator index number. This is the
+same value as in the &v4l2-input; <structfield>tuner</structfield>
+field and the &v4l2-tuner; <structfield>index</structfield> field, or
+the &v4l2-output; <structfield>modulator</structfield> field and the
+&v4l2-modulator; <structfield>index</structfield> field.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>type</structfield></entry>
+           <entry>The tuner type. This is the same value as in the
+&v4l2-tuner; <structfield>type</structfield> field. The type must be set
+to <constant>V4L2_TUNER_RADIO</constant> for <filename>/dev/radioX</filename>
+device nodes, and to <constant>V4L2_TUNER_ANALOG_TV</constant>
+for all others. Set this field to <constant>V4L2_TUNER_RADIO</constant> for
+modulators (currently only radio modulators are supported).
+See <xref linkend="v4l2-tuner-type" /></entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>index</structfield></entry>
+           <entry>Identifies the frequency band, set by the application.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>capability</structfield></entry>
+           <entry spanname="hspan">The tuner/modulator capability flags for
+this frequency band, see <xref linkend="tuner-capability" />. The <constant>V4L2_TUNER_CAP_LOW</constant>
+capability must be the same for all frequency bands of the selected tuner/modulator.
+So either all bands have that capability set, or none of them have that capability.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>rangelow</structfield></entry>
+           <entry spanname="hspan">The lowest tunable frequency in
+units of 62.5 kHz, or if the <structfield>capability</structfield>
+flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
+Hz, for this frequency band.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>rangehigh</structfield></entry>
+           <entry spanname="hspan">The highest tunable frequency in
+units of 62.5 kHz, or if the <structfield>capability</structfield>
+flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
+Hz, for this frequency band.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>modulation</structfield></entry>
+           <entry spanname="hspan">The supported modulation systems of this frequency band.
+           See <xref linkend="band-modulation" />. Note that currently only one
+           modulation system per frequency band is supported. More work will need to
+           be done if multiple modulation systems are possible. Contact the
+           linux-media mailing list (&v4l-ml;) if you need that functionality.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>reserved</structfield>[9]</entry>
+           <entry>Reserved for future extensions. Applications and drivers
+           must set the array to zero.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="band-modulation">
+      <title>Band Modulation Systems</title>
+      <tgroup cols="3">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>V4L2_BAND_MODULATION_VSB</constant></entry>
+           <entry>0x02</entry>
+           <entry>Vestigial Sideband modulation, used for analog TV.</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_BAND_MODULATION_FM</constant></entry>
+           <entry>0x04</entry>
+           <entry>Frequency Modulation, commonly used for analog radio.</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_BAND_MODULATION_AM</constant></entry>
+           <entry>0x08</entry>
+           <entry>Amplitude Modulation, commonly used for analog radio.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+
+    <variablelist>
+      <varlistentry>
+       <term><errorcode>EINVAL</errorcode></term>
+       <listitem>
+         <para>The <structfield>tuner</structfield> or <structfield>index</structfield>
+is out of bounds or the <structfield>type</structfield> field is wrong.</para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+</refentry>
index 40e58a42eb26d55123ccf1153d702eb1b9a70953..c7a1c462e7243fe665dab4b5e4fbcb762765f874 100644 (file)
@@ -98,11 +98,12 @@ the &v4l2-output; <structfield>modulator</structfield> field and the
            <entry>__u32</entry>
            <entry><structfield>type</structfield></entry>
            <entry>The tuner type. This is the same value as in the
-&v4l2-tuner; <structfield>type</structfield> field. See The type must be set
+&v4l2-tuner; <structfield>type</structfield> field. The type must be set
 to <constant>V4L2_TUNER_RADIO</constant> for <filename>/dev/radioX</filename>
 device nodes, and to <constant>V4L2_TUNER_ANALOG_TV</constant>
-for all others. The field is not applicable to modulators, &ie; ignored
-by drivers. See <xref linkend="v4l2-tuner-type" /></entry>
+for all others. Set this field to <constant>V4L2_TUNER_RADIO</constant> for
+modulators (currently only radio modulators are supported).
+See <xref linkend="v4l2-tuner-type" /></entry>
          </row>
          <row>
            <entry>__u32</entry>
index 95d5371c17090af4ff0c9ba1e51aa3a0dbdc0d98..720395127904574b69db6dd2b042f46b3d14901b 100644 (file)
@@ -119,10 +119,14 @@ field is not quite clear.--></para></entry>
 <xref linkend="tuner-capability" />. Audio flags indicate the ability
 to decode audio subprograms. They will <emphasis>not</emphasis>
 change, for example with the current video standard.</para><para>When
-the structure refers to a radio tuner only the
-<constant>V4L2_TUNER_CAP_LOW</constant>,
-<constant>V4L2_TUNER_CAP_STEREO</constant> and
-<constant>V4L2_TUNER_CAP_RDS</constant> flags can be set.</para></entry>
+the structure refers to a radio tuner the
+<constant>V4L2_TUNER_CAP_LANG1</constant>,
+<constant>V4L2_TUNER_CAP_LANG2</constant> and
+<constant>V4L2_TUNER_CAP_NORM</constant> flags can't be used.</para>
+<para>If multiple frequency bands are supported, then
+<structfield>capability</structfield> is the union of all
+<structfield>capability></structfield> fields of each &v4l2-frequency-band;.
+</para></entry>
          </row>
          <row>
            <entry>__u32</entry>
@@ -130,7 +134,9 @@ the structure refers to a radio tuner only the
            <entry spanname="hspan">The lowest tunable frequency in
 units of 62.5 kHz, or if the <structfield>capability</structfield>
 flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
-Hz.</entry>
+Hz. If multiple frequency bands are supported, then
+<structfield>rangelow</structfield> is the lowest frequency
+of all the frequency bands.</entry>
          </row>
          <row>
            <entry>__u32</entry>
@@ -138,7 +144,9 @@ Hz.</entry>
            <entry spanname="hspan">The highest tunable frequency in
 units of 62.5 kHz, or if the <structfield>capability</structfield>
 flag <constant>V4L2_TUNER_CAP_LOW</constant> is set, in units of 62.5
-Hz.</entry>
+Hz. If multiple frequency bands are supported, then
+<structfield>rangehigh</structfield> is the highest frequency
+of all the frequency bands.</entry>
          </row>
          <row>
            <entry>__u32</entry>
@@ -340,6 +348,12 @@ radio tuners.</entry>
        <entry>0x0200</entry>
        <entry>The RDS data is parsed by the hardware and set via controls.</entry>
          </row>
+         <row>
+       <entry><constant>V4L2_TUNER_CAP_FREQ_BANDS</constant></entry>
+       <entry>0x0400</entry>
+       <entry>The &VIDIOC-ENUM-FREQ-BANDS; ioctl can be used to enumerate
+       the available frequency bands.</entry>
+         </row>
        </tbody>
       </tgroup>
     </table>
index 4643505cd4ca575df1d6d584c03159fda60349f2..f33dd746b66b8177b60f5a431aefc937973e5eea 100644 (file)
@@ -191,6 +191,19 @@ linkend="output">Video Output</link> interface.</entry>
            <link linkend="planar-apis">multi-planar API</link> through the
            <link linkend="output">Video Output</link> interface.</entry>
          </row>
+         <row>
+           <entry><constant>V4L2_CAP_VIDEO_M2M</constant></entry>
+           <entry>0x00004000</entry>
+           <entry>The device supports the single-planar API through the
+           Video Memory-To-Memory interface.</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_CAP_VIDEO_M2M_MPLANE</constant></entry>
+           <entry>0x00008000</entry>
+           <entry>The device supports the
+           <link linkend="planar-apis">multi-planar API</link> through the
+           Video Memory-To-Memory  interface.</entry>
+         </row>
          <row>
            <entry><constant>V4L2_CAP_VIDEO_OVERLAY</constant></entry>
            <entry>0x00000004</entry>
index f4db44d0d95a8c0b065bf99dccae34b63c70a6af..3dd1bec6d3c74c1a6619364aaae41d85d615d17f 100644 (file)
     <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>,
-<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
-to this structure.</para>
+<structfield>wrap_around</structfield>, <structfield>spacing</structfield>,
+<structfield>rangelow</structfield> and <structfield>rangehigh</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 to this structure.</para>
+
+    <para>The <structfield>rangelow</structfield> and
+<structfield>rangehigh</structfield> fields can be set to a non-zero value to
+tell the driver to search a specific band. If the &v4l2-tuner;
+<structfield>capability</structfield> field has the
+<constant>V4L2_TUNER_CAP_HWSEEK_PROG_LIM</constant> flag set, these values
+must fall within one of the bands returned by &VIDIOC-ENUM-FREQ-BANDS;. If
+the <constant>V4L2_TUNER_CAP_HWSEEK_PROG_LIM</constant> flag is not set,
+then these values must exactly match those of one of the bands returned by
+&VIDIOC-ENUM-FREQ-BANDS;. If the current frequency of the tuner does not fall
+within the selected band it will be clamped to fit in the band before the
+seek is started.</para>
 
     <para>If an error is returned, then the original frequency will
     be restored.</para>
@@ -102,7 +114,27 @@ field and the &v4l2-tuner; <structfield>index</structfield> field.</entry>
          </row>
          <row>
            <entry>__u32</entry>
-           <entry><structfield>reserved</structfield>[7]</entry>
+           <entry><structfield>rangelow</structfield></entry>
+           <entry>If non-zero, the lowest tunable frequency of the band to
+search in units of 62.5 kHz, or if the &v4l2-tuner;
+<structfield>capability</structfield> field has the
+<constant>V4L2_TUNER_CAP_LOW</constant> flag set, in units of 62.5 Hz.
+If <structfield>rangelow</structfield> is zero a reasonable default value
+is used.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>rangehigh</structfield></entry>
+           <entry>If non-zero, the highest tunable frequency of the band to
+search in units of 62.5 kHz, or if the &v4l2-tuner;
+<structfield>capability</structfield> field has the
+<constant>V4L2_TUNER_CAP_LOW</constant> flag set, in units of 62.5 Hz.
+If <structfield>rangehigh</structfield> is zero a reasonable default value
+is used.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>reserved</structfield>[5]</entry>
            <entry>Reserved for future extensions. Applications
            must set the array to zero.</entry>
          </row>
@@ -119,8 +151,10 @@ field and the &v4l2-tuner; <structfield>index</structfield> field.</entry>
        <term><errorcode>EINVAL</errorcode></term>
        <listitem>
          <para>The <structfield>tuner</structfield> index is out of
-bounds, the wrap_around value is not supported or the value in the <structfield>type</structfield> field is
-wrong.</para>
+bounds, the <structfield>wrap_around</structfield> value is not supported or
+one of the values in the <structfield>type</structfield>,
+<structfield>rangelow</structfield> or <structfield>rangehigh</structfield>
+fields is wrong.</para>
        </listitem>
       </varlistentry>
       <varlistentry>
index e9237fb719507abe070064c7fc62848e7307748d..09b132dd062d29fec269f5e18406e0f7f3a196b7 100644 (file)
@@ -618,3 +618,17 @@ Why:       The regular V4L2 selections and the subdev selection API originally
 Who:   Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
 
 ----------------------------
+
+What:  Using V4L2_CAP_VIDEO_CAPTURE and V4L2_CAP_VIDEO_OUTPUT flags
+       to indicate a V4L2 memory-to-memory device capability
+When:  3.8
+Why:   New drivers should use new V4L2_CAP_VIDEO_M2M capability flag
+       to indicate a V4L2 video memory-to-memory (M2M) device and
+       applications can now identify a M2M video device by checking
+       for V4L2_CAP_VIDEO_M2M, with VIDIOC_QUERYCAP ioctl. Using ORed
+       V4L2_CAP_VIDEO_CAPTURE and V4L2_CAP_VIDEO_OUTPUT flags for M2M
+       devices is ambiguous and may lead, for example, to identifying
+       a M2M device as a video capture or output device.
+Who:   Sylwester Nawrocki <s.nawrocki@samsung.com>
+
+----------------------------
index 7b59e953c4bfe8673b7d65314fe9e65feb3676ef..a8a65753e544c7df7b4098cba8394f96f605a4ab 100644 (file)
@@ -3,4 +3,4 @@
   2 -> Hauppauge HVR850                         (au0828)        [2040:7240]
   3 -> DViCO FusionHDTV USB                     (au0828)        [0fe9:d620]
   4 -> Hauppauge HVR950Q rev xxF8               (au0828)        [2040:7201,2040:7211,2040:7281]
-  5 -> Hauppauge Woodbury                       (au0828)        [2040:8200]
+  5 -> Hauppauge Woodbury                       (au0828)        [05e1:0480,2040:8200]
index b753906c71830b892d95b623c857a767ba885571..581f666a76cfc9b4200053f347b257756ee1a6c6 100644 (file)
 158 -> Geovision GV-800(S) (slave)                         [800b:763d,800c:763d,800d:763d]
 159 -> ProVideo PV183                                      [1830:1540,1831:1540,1832:1540,1833:1540,1834:1540,1835:1540,1836:1540,1837:1540]
 160 -> Tongwei Video Technology TD-3116                    [f200:3116]
+161 -> Aposonic W-DVR                                      [0279:0228]
index f316d1816fcd2265acb9a0b9efdb3f1e56160ac3..652aecd131998a7d2958a6a7cc2978e0eeeaa9ae 100644 (file)
@@ -18,7 +18,7 @@
  17 -> NetUP Dual DVB-S2 CI                                [1b55:2a2c]
  18 -> Hauppauge WinTV-HVR1270                             [0070:2211]
  19 -> Hauppauge WinTV-HVR1275                             [0070:2215,0070:221d,0070:22f2]
- 20 -> Hauppauge WinTV-HVR1255                             [0070:2251,0070:2259,0070:22f1]
+ 20 -> Hauppauge WinTV-HVR1255                             [0070:2251,0070:22f1]
  21 -> Hauppauge WinTV-HVR1210                             [0070:2291,0070:2295,0070:2299,0070:229d,0070:22f0,0070:22f3,0070:22f4,0070:22f5]
  22 -> Mygica X8506 DMB-TH                                 [14f1:8651]
  23 -> Magic-Pro ProHDTV Extreme 2                         [14f1:8657]
@@ -33,3 +33,5 @@
  32 -> MPX-885
  33 -> Mygica X8507                                        [14f1:8502]
  34 -> TerraTec Cinergy T PCIe Dual                        [153b:117e]
+ 35 -> TeVii S471                                          [d471:9022]
+ 36 -> Hauppauge WinTV-HVR1255                             [0070:2259]
index 34f3b330e5f40033c128364c068b6895f10e307a..94d9025aa82d97306bd44f79368d48cb8cfa4acc 100644 (file)
 187 -> Beholder BeholdTV 503 FM                 [5ace:5030]
 188 -> Sensoray 811/911                         [6000:0811,6000:0911]
 189 -> Kworld PC150-U                           [17de:a134]
+190 -> Asus My Cinema PS3-100                   [1043:48cd]
index 500844f04f93abb668f7ce57b0d841f776c074c7..60ea284407cea4d1f62db43a72b3e6cdbea47e31 100644 (file)
@@ -1928,6 +1928,7 @@ static const struct hid_device_id hid_ignore_list[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_RADIOSHARK) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101) },
index 41c34f21bd001ee00864c596bb217db4647bde7c..1dcb76ff51e340a2215d735bdde23e5c370bade0 100644 (file)
 #define USB_VENDOR_ID_GRIFFIN          0x077d
 #define USB_DEVICE_ID_POWERMATE                0x0410
 #define USB_DEVICE_ID_SOUNDKNOB                0x04AA
+#define USB_DEVICE_ID_RADIOSHARK       0x627a
 
 #define USB_VENDOR_ID_GTCO             0x078c
 #define USB_DEVICE_ID_GTCO_90          0x0090
index f88f948efee2e51fd39f2e98d74d696f08924f5b..ea0550eafe7d185f9d547a80af33cead4d007050 100644 (file)
@@ -756,7 +756,7 @@ retry:
         * No need to reload base firmware if it matches and if the tuner
         * is not at sleep mode
         */
-       if ((priv->state = XC2028_ACTIVE) &&
+       if ((priv->state == XC2028_ACTIVE) &&
            (((BASE | new_fw.type) & BASE_TYPES) ==
            (priv->cur_fw.type & BASE_TYPES))) {
                tuner_dbg("BASE firmware not changed.\n");
@@ -978,7 +978,7 @@ static int xc2028_get_afc(struct dvb_frontend *fe, s32 *afc)
        /* Get AFC */
        rc = xc2028_get_reg(priv, XREG_FREQ_ERROR, &afc_reg);
        if (rc < 0)
-               return rc;
+               goto ret;
 
        *afc = afc_reg * 15625; /* Hz */
 
index bac8009e1d4979680211fddcd8a06e7c71a4e23c..362a8d7c9738e018df797ed7504c215b4595d0de 100644 (file)
@@ -210,13 +210,15 @@ struct xc5000_fw_cfg {
        u16 size;
 };
 
+#define XC5000A_FIRMWARE "dvb-fe-xc5000-1.6.114.fw"
 static const struct xc5000_fw_cfg xc5000a_1_6_114 = {
-       .name = "dvb-fe-xc5000-1.6.114.fw",
+       .name = XC5000A_FIRMWARE,
        .size = 12401,
 };
 
+#define XC5000C_FIRMWARE "dvb-fe-xc5000c-41.024.5.fw"
 static const struct xc5000_fw_cfg xc5000c_41_024_5 = {
-       .name = "dvb-fe-xc5000c-41.024.5.fw",
+       .name = XC5000C_FIRMWARE,
        .size = 16497,
 };
 
@@ -1259,3 +1261,5 @@ EXPORT_SYMBOL(xc5000_attach);
 MODULE_AUTHOR("Steven Toth");
 MODULE_DESCRIPTION("Xceive xc5000 silicon tuner driver");
 MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(XC5000A_FIRMWARE);
+MODULE_FIRMWARE(XC5000C_FIRMWARE);
index 8ffcad000ad3fb146eb7a1e5292dc902a4745a28..86861e6f86d23053ebe990aa58b24e7d377d7d45 100644 (file)
@@ -590,7 +590,7 @@ static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6])
        int ret;
 
        ret = az6007_read(d, AZ6007_READ_DATA, 6, 0, st->data, 6);
-       memcpy(mac, st->data, sizeof(mac));
+       memcpy(mac, st->data, 6);
 
        if (ret > 0)
                deb_info("%s: mac is %pM\n", __func__, mac);
index 9ca34f495009dc29f15509f9075986af3247fe48..1f3bcb5a1de8a592e25aff1bcd100069fcded1ec 100644 (file)
@@ -2680,12 +2680,14 @@ static int dib8000_tune(struct dvb_frontend *fe)
 {
        struct dib8000_state *state = fe->demodulator_priv;
        int ret = 0;
-       u16 lock, value, mode = fft_to_mode(state);
+       u16 lock, value, mode;
 
        // we are already tuned - just resuming from suspend
        if (state == NULL)
                return -EINVAL;
 
+       mode = fft_to_mode(state);
+
        dib8000_set_bandwidth(fe, state->fe[0]->dtv_property_cache.bandwidth_hz / 1000);
        dib8000_set_channel(state, 0, 0);
 
index 568363a10a31ca2241be7a3772273ba6d45b046f..c2ea2749ebedd40c0466a96ec33f90bfebcbee13 100644 (file)
@@ -40,6 +40,8 @@
 static int debug;
 static int fake_signal_str = 1;
 
+#define LGS8GXX_FIRMWARE "lgs8g75.fw"
+
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
@@ -592,7 +594,7 @@ static int lgs8g75_init_data(struct lgs8gxx_state *priv)
        int rc;
        int i;
 
-       rc = request_firmware(&fw, "lgs8g75.fw", &priv->i2c->dev);
+       rc = request_firmware(&fw, LGS8GXX_FIRMWARE, &priv->i2c->dev);
        if (rc)
                return rc;
 
@@ -1070,3 +1072,4 @@ EXPORT_SYMBOL(lgs8gxx_attach);
 MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver");
 MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
 MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(LGS8GXX_FIRMWARE);
index 2da592fb38adc0c03244fb75d4dd51d9ca40a6ff..28269ccaeab709a95ce5a02ab345f1a86f6ec305 100644 (file)
@@ -589,7 +589,7 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe)
                return -EINVAL;
        }
 
-       for (j = 0; j < sizeof(bw_params[j]); j++) {
+       for (j = 0; j < sizeof(bw_params[0]); j++) {
                ret = rtl2832_wr_regs(priv, 0x1c+j, 1, &bw_params[i][j], 1);
                if (ret)
                        goto err;
index 7331e8450d1a7d96caa64b2ba88e3d13be622b6f..9cc55546cc30b138660ddc5cdcb4f857003caf5d 100644 (file)
@@ -276,16 +276,13 @@ static void smscore_notify_clients(struct smscore_device_t *coredev)
 static int smscore_notify_callbacks(struct smscore_device_t *coredev,
                                    struct device *device, int arrival)
 {
-       struct list_head *next, *first;
+       struct smscore_device_notifyee_t *elem;
        int rc = 0;
 
        /* note: must be called under g_deviceslock */
 
-       first = &g_smscore_notifyees;
-
-       for (next = first->next; next != first; next = next->next) {
-               rc = ((struct smscore_device_notifyee_t *) next)->
-                               hotplug(coredev, device, arrival);
+       list_for_each_entry(elem, &g_smscore_notifyees, entry) {
+               rc = elem->hotplug(coredev, device, arrival);
                if (rc < 0)
                        break;
        }
@@ -940,29 +937,25 @@ static struct
 smscore_client_t *smscore_find_client(struct smscore_device_t *coredev,
                                      int data_type, int id)
 {
-       struct smscore_client_t *client = NULL;
-       struct list_head *next, *first;
+       struct list_head *first;
+       struct smscore_client_t *client;
        unsigned long flags;
-       struct list_head *firstid, *nextid;
-
+       struct list_head *firstid;
+       struct smscore_idlist_t *client_id;
 
        spin_lock_irqsave(&coredev->clientslock, flags);
        first = &coredev->clients;
-       for (next = first->next;
-            (next != first) && !client;
-            next = next->next) {
-               firstid = &((struct smscore_client_t *)next)->idlist;
-               for (nextid = firstid->next;
-                    nextid != firstid;
-                    nextid = nextid->next) {
-                       if ((((struct smscore_idlist_t *)nextid)->id == id) &&
-                           (((struct smscore_idlist_t *)nextid)->data_type == data_type ||
-                           (((struct smscore_idlist_t *)nextid)->data_type == 0))) {
-                               client = (struct smscore_client_t *) next;
-                               break;
-                       }
+       list_for_each_entry(client, first, entry) {
+               firstid = &client->idlist;
+               list_for_each_entry(client_id, firstid, entry) {
+                       if ((client_id->id == id) &&
+                           (client_id->data_type == data_type ||
+                           (client_id->data_type == 0)))
+                               goto found;
                }
        }
+       client = NULL;
+found:
        spin_unlock_irqrestore(&coredev->clientslock, flags);
        return client;
 }
index 24ce5a47f9550d1383dd27c0e751c588775dfbbb..8090b87b306664b6401f05d6ba71cea0f0047b2c 100644 (file)
@@ -57,6 +57,39 @@ config RADIO_MAXIRADIO
          To compile this driver as a module, choose M here: the
          module will be called radio-maxiradio.
 
+config RADIO_SHARK
+       tristate "Griffin radioSHARK USB radio receiver"
+       depends on USB && SND
+       ---help---
+         Choose Y here if you have this radio receiver.
+
+         There are 2 versions of this device, this driver is for version 1,
+         which is white.
+
+         In order to control your radio card, you will need to use programs
+         that are compatible with the Video For Linux API.  Information on
+         this API and pointers to "v4l" programs may be found at
+         <file:Documentation/video4linux/API.html>.
+
+         To compile this driver as a module, choose M here: the
+         module will be called radio-shark.
+
+config RADIO_SHARK2
+       tristate "Griffin radioSHARK2 USB radio receiver"
+       depends on USB
+       ---help---
+         Choose Y here if you have this radio receiver.
+
+         There are 2 versions of this device, this driver is for version 2,
+         which is black.
+
+         In order to control your radio card, you will need to use programs
+         that are compatible with the Video For Linux API.  Information on
+         this API and pointers to "v4l" programs may be found at
+         <file:Documentation/video4linux/API.html>.
+
+         To compile this driver as a module, choose M here: the
+         module will be called radio-shark2.
 
 config I2C_SI4713
        tristate "I2C driver for Silicon Labs Si4713 device"
index ca8c7d134b95137f6bacf8e0870d95dff828aeaf..c03ce4fe74e97fb5e0bb92f8da5bbb8065599e82 100644 (file)
@@ -11,6 +11,8 @@ obj-$(CONFIG_RADIO_CADET) += radio-cadet.o
 obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o
 obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o
 obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o
+obj-$(CONFIG_RADIO_SHARK) += radio-shark.o
+obj-$(CONFIG_RADIO_SHARK2) += shark2.o
 obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o
 obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o
 obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o
@@ -29,4 +31,6 @@ obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
 obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o
 obj-$(CONFIG_RADIO_WL128X) += wl128x/
 
+shark2-objs := radio-shark2.o radio-tea5777.o
+
 ccflags-y += -Isound
index 16a089fad909f241f76d6f5a8a1eb8b22f9358bb..697a421c99406faa84df2906d41e3d4492c8bd48 100644 (file)
@@ -41,6 +41,9 @@
 #include <linux/io.h>          /* outb, outb_p                 */
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
 
 MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
 MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
@@ -61,14 +64,15 @@ module_param(radio_nr, int, 0);
 struct cadet {
        struct v4l2_device v4l2_dev;
        struct video_device vdev;
+       struct v4l2_ctrl_handler ctrl_handler;
        int io;
-       int users;
-       int curtuner;
+       bool is_fm_band;
+       u32 curfreq;
        int tunestat;
        int sigstrength;
        wait_queue_head_t read_queue;
        struct timer_list readtimer;
-       __u8 rdsin, rdsout, rdsstat;
+       u8 rdsin, rdsout, rdsstat;
        unsigned char rdsbuf[RDS_BUFFER];
        struct mutex lock;
        int reading;
@@ -81,9 +85,9 @@ static struct cadet cadet_card;
  * The V4L API spec does not define any particular unit for the signal
  * strength value.  These values are in microvolts of RF at the tuner's input.
  */
-static __u16 sigtable[2][4] = {
-       {  5, 10, 30,  150 },
-       { 28, 40, 63, 1000 }
+static u16 sigtable[2][4] = {
+       { 1835, 2621,  4128, 65535 },
+       { 2185, 4369, 13107, 65535 },
 };
 
 
@@ -91,14 +95,12 @@ static int cadet_getstereo(struct cadet *dev)
 {
        int ret = V4L2_TUNER_SUB_MONO;
 
-       if (dev->curtuner != 0) /* Only FM has stereo capability! */
+       if (!dev->is_fm_band)   /* Only FM has stereo capability! */
                return V4L2_TUNER_SUB_MONO;
 
-       mutex_lock(&dev->lock);
        outb(7, dev->io);          /* Select tuner control */
        if ((inb(dev->io + 1) & 0x40) == 0)
                ret = V4L2_TUNER_SUB_STEREO;
-       mutex_unlock(&dev->lock);
        return ret;
 }
 
@@ -111,8 +113,6 @@ static unsigned cadet_gettune(struct cadet *dev)
         * Prepare for read
         */
 
-       mutex_lock(&dev->lock);
-
        outb(7, dev->io);       /* Select tuner control */
        curvol = inb(dev->io + 1); /* Save current volume/mute setting */
        outb(0x00, dev->io + 1);  /* Ensure WRITE-ENABLE is LOW */
@@ -134,8 +134,6 @@ static unsigned cadet_gettune(struct cadet *dev)
         * Restore volume/mute setting
         */
        outb(curvol, dev->io + 1);
-       mutex_unlock(&dev->lock);
-
        return fifo;
 }
 
@@ -152,20 +150,18 @@ static unsigned cadet_getfreq(struct cadet *dev)
        /*
         * Convert to actual frequency
         */
-       if (dev->curtuner == 0) {    /* FM */
-               test = 12500;
-               for (i = 0; i < 14; i++) {
-                       if ((fifo & 0x01) != 0)
-                               freq += test;
-                       test = test << 1;
-                       fifo = fifo >> 1;
-               }
-               freq -= 10700000;           /* IF frequency is 10.7 MHz */
-               freq = (freq * 16) / 1000000;   /* Make it 1/16 MHz */
+       if (!dev->is_fm_band)    /* AM */
+               return ((fifo & 0x7fff) - 450) * 16;
+
+       test = 12500;
+       for (i = 0; i < 14; i++) {
+               if ((fifo & 0x01) != 0)
+                       freq += test;
+               test = test << 1;
+               fifo = fifo >> 1;
        }
-       if (dev->curtuner == 1)    /* AM */
-               freq = ((fifo & 0x7fff) - 2010) * 16;
-
+       freq -= 10700000;           /* IF frequency is 10.7 MHz */
+       freq = (freq * 16) / 1000;   /* Make it 1/16 kHz */
        return freq;
 }
 
@@ -174,8 +170,6 @@ static void cadet_settune(struct cadet *dev, unsigned fifo)
        int i;
        unsigned test;
 
-       mutex_lock(&dev->lock);
-
        outb(7, dev->io);                /* Select tuner control */
        /*
         * Write the shift register
@@ -194,7 +188,6 @@ static void cadet_settune(struct cadet *dev, unsigned fifo)
                test = 0x1c | ((fifo >> 23) & 0x02);
                outb(test, dev->io + 1);
        }
-       mutex_unlock(&dev->lock);
 }
 
 static void cadet_setfreq(struct cadet *dev, unsigned freq)
@@ -203,13 +196,14 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
        int i, j, test;
        int curvol;
 
+       dev->curfreq = freq;
        /*
         * Formulate a fifo command
         */
        fifo = 0;
-       if (dev->curtuner == 0) {    /* FM */
+       if (dev->is_fm_band) {    /* FM */
                test = 102400;
-               freq = (freq * 1000) / 16;       /* Make it kHz */
+               freq = freq / 16;       /* Make it kHz */
                freq += 10700;               /* IF is 10700 kHz */
                for (i = 0; i < 14; i++) {
                        fifo = fifo << 1;
@@ -219,20 +213,17 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
                        }
                        test = test >> 1;
                }
-       }
-       if (dev->curtuner == 1) {    /* AM */
-               fifo = (freq / 16) + 2010;            /* Make it kHz */
-               fifo |= 0x100000;            /* Select AM Band */
+       } else {        /* AM */
+               fifo = (freq / 16) + 450;       /* Make it kHz */
+               fifo |= 0x100000;               /* Select AM Band */
        }
 
        /*
         * Save current volume/mute setting
         */
 
-       mutex_lock(&dev->lock);
        outb(7, dev->io);                /* Select tuner control */
        curvol = inb(dev->io + 1);
-       mutex_unlock(&dev->lock);
 
        /*
         * Tune the card
@@ -240,49 +231,24 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
        for (j = 3; j > -1; j--) {
                cadet_settune(dev, fifo | (j << 16));
 
-               mutex_lock(&dev->lock);
                outb(7, dev->io);         /* Select tuner control */
                outb(curvol, dev->io + 1);
-               mutex_unlock(&dev->lock);
 
                msleep(100);
 
                cadet_gettune(dev);
                if ((dev->tunestat & 0x40) == 0) {   /* Tuned */
-                       dev->sigstrength = sigtable[dev->curtuner][j];
-                       return;
+                       dev->sigstrength = sigtable[dev->is_fm_band][j];
+                       goto reset_rds;
                }
        }
        dev->sigstrength = 0;
+reset_rds:
+       outb(3, dev->io);
+       outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
 }
 
 
-static int cadet_getvol(struct cadet *dev)
-{
-       int ret = 0;
-
-       mutex_lock(&dev->lock);
-
-       outb(7, dev->io);                /* Select tuner control */
-       if ((inb(dev->io + 1) & 0x20) != 0)
-               ret = 0xffff;
-
-       mutex_unlock(&dev->lock);
-       return ret;
-}
-
-
-static void cadet_setvol(struct cadet *dev, int vol)
-{
-       mutex_lock(&dev->lock);
-       outb(7, dev->io);                /* Select tuner control */
-       if (vol > 0)
-               outb(0x20, dev->io + 1);
-       else
-               outb(0x00, dev->io + 1);
-       mutex_unlock(&dev->lock);
-}
-
 static void cadet_handler(unsigned long data)
 {
        struct cadet *dev = (void *)data;
@@ -295,7 +261,7 @@ static void cadet_handler(unsigned long data)
                outb(0x80, dev->io);      /* Select RDS fifo */
                while ((inb(dev->io) & 0x80) != 0) {
                        dev->rdsbuf[dev->rdsin] = inb(dev->io + 1);
-                       if (dev->rdsin == dev->rdsout)
+                       if (dev->rdsin + 1 == dev->rdsout)
                                printk(KERN_WARNING "cadet: RDS buffer overflow\n");
                        else
                                dev->rdsin++;
@@ -314,11 +280,21 @@ static void cadet_handler(unsigned long data)
         */
        init_timer(&dev->readtimer);
        dev->readtimer.function = cadet_handler;
-       dev->readtimer.data = (unsigned long)0;
+       dev->readtimer.data = data;
        dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
        add_timer(&dev->readtimer);
 }
 
+static void cadet_start_rds(struct cadet *dev)
+{
+       dev->rdsstat = 1;
+       outb(0x80, dev->io);        /* Select RDS fifo */
+       init_timer(&dev->readtimer);
+       dev->readtimer.function = cadet_handler;
+       dev->readtimer.data = (unsigned long)dev;
+       dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
+       add_timer(&dev->readtimer);
+}
 
 static ssize_t cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
 {
@@ -327,28 +303,24 @@ static ssize_t cadet_read(struct file *file, char __user *data, size_t count, lo
        int i = 0;
 
        mutex_lock(&dev->lock);
-       if (dev->rdsstat == 0) {
-               dev->rdsstat = 1;
-               outb(0x80, dev->io);        /* Select RDS fifo */
-               init_timer(&dev->readtimer);
-               dev->readtimer.function = cadet_handler;
-               dev->readtimer.data = (unsigned long)dev;
-               dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
-               add_timer(&dev->readtimer);
-       }
+       if (dev->rdsstat == 0)
+               cadet_start_rds(dev);
        if (dev->rdsin == dev->rdsout) {
+               if (file->f_flags & O_NONBLOCK) {
+                       i = -EWOULDBLOCK;
+                       goto unlock;
+               }
                mutex_unlock(&dev->lock);
-               if (file->f_flags & O_NONBLOCK)
-                       return -EWOULDBLOCK;
                interruptible_sleep_on(&dev->read_queue);
                mutex_lock(&dev->lock);
        }
        while (i < count && dev->rdsin != dev->rdsout)
                readbuf[i++] = dev->rdsbuf[dev->rdsout++];
-       mutex_unlock(&dev->lock);
 
-       if (copy_to_user(data, readbuf, i))
-               return -EFAULT;
+       if (i && copy_to_user(data, readbuf, i))
+               i = -EFAULT;
+unlock:
+       mutex_unlock(&dev->lock);
        return i;
 }
 
@@ -359,48 +331,58 @@ static int vidioc_querycap(struct file *file, void *priv,
        strlcpy(v->driver, "ADS Cadet", sizeof(v->driver));
        strlcpy(v->card, "ADS Cadet", sizeof(v->card));
        strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
-       v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
+       v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
                          V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE;
+       v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
        return 0;
 }
 
+static const struct v4l2_frequency_band bands[] = {
+       {
+               .index = 0,
+               .type = V4L2_TUNER_RADIO,
+               .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
+               .rangelow = 8320,      /* 520 kHz */
+               .rangehigh = 26400,    /* 1650 kHz */
+               .modulation = V4L2_BAND_MODULATION_AM,
+       }, {
+               .index = 1,
+               .type = V4L2_TUNER_RADIO,
+               .capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
+                       V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_LOW |
+                       V4L2_TUNER_CAP_FREQ_BANDS,
+               .rangelow = 1400000,   /* 87.5 MHz */
+               .rangehigh = 1728000,  /* 108.0 MHz */
+               .modulation = V4L2_BAND_MODULATION_FM,
+       },
+};
+
 static int vidioc_g_tuner(struct file *file, void *priv,
                                struct v4l2_tuner *v)
 {
        struct cadet *dev = video_drvdata(file);
 
+       if (v->index)
+               return -EINVAL;
        v->type = V4L2_TUNER_RADIO;
-       switch (v->index) {
-       case 0:
-               strlcpy(v->name, "FM", sizeof(v->name));
-               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 */
+       strlcpy(v->name, "Radio", sizeof(v->name));
+       v->capability = bands[0].capability | bands[1].capability;
+       v->rangelow = bands[0].rangelow;           /* 520 kHz (start of AM band) */
+       v->rangehigh = bands[1].rangehigh;    /* 108.0 MHz (end of FM band) */
+       if (dev->is_fm_band) {
                v->rxsubchans = cadet_getstereo(dev);
-               switch (v->rxsubchans) {
-               case V4L2_TUNER_SUB_MONO:
-                       v->audmode = V4L2_TUNER_MODE_MONO;
-                       break;
-               case V4L2_TUNER_SUB_STEREO:
-                       v->audmode = V4L2_TUNER_MODE_STEREO;
-                       break;
-               default:
-                       break;
-               }
-               v->rxsubchans |= V4L2_TUNER_SUB_RDS;
-               break;
-       case 1:
-               strlcpy(v->name, "AM", sizeof(v->name));
-               v->capability = V4L2_TUNER_CAP_LOW;
+               outb(3, dev->io);
+               outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
+               mdelay(100);
+               outb(3, dev->io);
+               if (inb(dev->io + 1) & 0x80)
+                       v->rxsubchans |= V4L2_TUNER_SUB_RDS;
+       } else {
                v->rangelow = 8320;      /* 520 kHz */
                v->rangehigh = 26400;    /* 1650 kHz */
                v->rxsubchans = V4L2_TUNER_SUB_MONO;
-               v->audmode = V4L2_TUNER_MODE_MONO;
-               break;
-       default:
-               return -EINVAL;
        }
+       v->audmode = V4L2_TUNER_MODE_STEREO;
        v->signal = dev->sigstrength; /* We might need to modify scaling of this */
        return 0;
 }
@@ -408,11 +390,17 @@ static int vidioc_g_tuner(struct file *file, void *priv,
 static int vidioc_s_tuner(struct file *file, void *priv,
                                struct v4l2_tuner *v)
 {
-       struct cadet *dev = video_drvdata(file);
+       return v->index ? -EINVAL : 0;
+}
 
-       if (v->index != 0 && v->index != 1)
+static int vidioc_enum_freq_bands(struct file *file, void *priv,
+                               struct v4l2_frequency_band *band)
+{
+       if (band->tuner)
+               return -EINVAL;
+       if (band->index >= ARRAY_SIZE(bands))
                return -EINVAL;
-       dev->curtuner = v->index;
+       *band = bands[band->index];
        return 0;
 }
 
@@ -421,9 +409,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
 {
        struct cadet *dev = video_drvdata(file);
 
-       f->tuner = dev->curtuner;
+       if (f->tuner)
+               return -EINVAL;
        f->type = V4L2_TUNER_RADIO;
-       f->frequency = cadet_getfreq(dev);
+       f->frequency = dev->curfreq;
        return 0;
 }
 
@@ -433,103 +422,46 @@ static int vidioc_s_frequency(struct file *file, void *priv,
 {
        struct cadet *dev = video_drvdata(file);
 
-       if (f->type != V4L2_TUNER_RADIO)
-               return -EINVAL;
-       if (dev->curtuner == 0 && (f->frequency < 1400 || f->frequency > 1728))
-               return -EINVAL;
-       if (dev->curtuner == 1 && (f->frequency < 8320 || f->frequency > 26400))
+       if (f->tuner)
                return -EINVAL;
+       dev->is_fm_band =
+               f->frequency >= (bands[0].rangehigh + bands[1].rangelow) / 2;
+       clamp(f->frequency, bands[dev->is_fm_band].rangelow,
+                           bands[dev->is_fm_band].rangehigh);
        cadet_setfreq(dev, f->frequency);
        return 0;
 }
 
-static int vidioc_queryctrl(struct file *file, void *priv,
-                               struct v4l2_queryctrl *qc)
+static int cadet_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       switch (qc->id) {
-       case V4L2_CID_AUDIO_MUTE:
-               return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
-       case V4L2_CID_AUDIO_VOLUME:
-               return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
-       }
-       return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
-                               struct v4l2_control *ctrl)
-{
-       struct cadet *dev = video_drvdata(file);
+       struct cadet *dev = container_of(ctrl->handler, struct cadet, ctrl_handler);
 
        switch (ctrl->id) {
-       case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
-               ctrl->value = (cadet_getvol(dev) == 0);
-               break;
-       case V4L2_CID_AUDIO_VOLUME:
-               ctrl->value = cadet_getvol(dev);
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
-                               struct v4l2_control *ctrl)
-{
-       struct cadet *dev = video_drvdata(file);
-
-       switch (ctrl->id){
-       case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
-               if (ctrl->value)
-                       cadet_setvol(dev, 0);
+       case V4L2_CID_AUDIO_MUTE:
+               outb(7, dev->io);                /* Select tuner control */
+               if (ctrl->val)
+                       outb(0x00, dev->io + 1);
                else
-                       cadet_setvol(dev, 0xffff);
-               break;
-       case V4L2_CID_AUDIO_VOLUME:
-               cadet_setvol(dev, ctrl->value);
-               break;
-       default:
-               return -EINVAL;
+                       outb(0x20, dev->io + 1);
+               return 0;
        }
-       return 0;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
-       *i = 0;
-       return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
-       return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
-                               struct v4l2_audio *a)
-{
-       a->index = 0;
-       strlcpy(a->name, "Radio", sizeof(a->name));
-       a->capability = V4L2_AUDCAP_STEREO;
-       return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
-                               struct v4l2_audio *a)
-{
-       return a->index ? -EINVAL : 0;
+       return -EINVAL;
 }
 
 static int cadet_open(struct file *file)
 {
        struct cadet *dev = video_drvdata(file);
+       int err;
 
        mutex_lock(&dev->lock);
-       dev->users++;
-       if (1 == dev->users)
+       err = v4l2_fh_open(file);
+       if (err)
+               goto fail;
+       if (v4l2_fh_is_singular_file(file))
                init_waitqueue_head(&dev->read_queue);
+fail:
        mutex_unlock(&dev->lock);
-       return 0;
+       return err;
 }
 
 static int cadet_release(struct file *file)
@@ -537,11 +469,11 @@ static int cadet_release(struct file *file)
        struct cadet *dev = video_drvdata(file);
 
        mutex_lock(&dev->lock);
-       dev->users--;
-       if (0 == dev->users) {
+       if (v4l2_fh_is_singular_file(file) && dev->rdsstat) {
                del_timer_sync(&dev->readtimer);
                dev->rdsstat = 0;
        }
+       v4l2_fh_release(file);
        mutex_unlock(&dev->lock);
        return 0;
 }
@@ -549,11 +481,19 @@ static int cadet_release(struct file *file)
 static unsigned int cadet_poll(struct file *file, struct poll_table_struct *wait)
 {
        struct cadet *dev = video_drvdata(file);
+       unsigned long req_events = poll_requested_events(wait);
+       unsigned int res = v4l2_ctrl_poll(file, wait);
 
        poll_wait(file, &dev->read_queue, wait);
+       if (dev->rdsstat == 0 && (req_events & (POLLIN | POLLRDNORM))) {
+               mutex_lock(&dev->lock);
+               if (dev->rdsstat == 0)
+                       cadet_start_rds(dev);
+               mutex_unlock(&dev->lock);
+       }
        if (dev->rdsin != dev->rdsout)
-               return POLLIN | POLLRDNORM;
-       return 0;
+               res |= POLLIN | POLLRDNORM;
+       return res;
 }
 
 
@@ -572,13 +512,14 @@ static const struct v4l2_ioctl_ops cadet_ioctl_ops = {
        .vidioc_s_tuner     = vidioc_s_tuner,
        .vidioc_g_frequency = vidioc_g_frequency,
        .vidioc_s_frequency = vidioc_s_frequency,
-       .vidioc_queryctrl   = vidioc_queryctrl,
-       .vidioc_g_ctrl      = vidioc_g_ctrl,
-       .vidioc_s_ctrl      = vidioc_s_ctrl,
-       .vidioc_g_audio     = vidioc_g_audio,
-       .vidioc_s_audio     = vidioc_s_audio,
-       .vidioc_g_input     = vidioc_g_input,
-       .vidioc_s_input     = vidioc_s_input,
+       .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
+       .vidioc_log_status  = v4l2_ctrl_log_status,
+       .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_ctrl_ops cadet_ctrl_ops = {
+       .s_ctrl = cadet_s_ctrl,
 };
 
 #ifdef CONFIG_PNP
@@ -628,8 +569,8 @@ static void cadet_probe(struct cadet *dev)
        for (i = 0; i < 8; i++) {
                dev->io = iovals[i];
                if (request_region(dev->io, 2, "cadet-probe")) {
-                       cadet_setfreq(dev, 1410);
-                       if (cadet_getfreq(dev) == 1410) {
+                       cadet_setfreq(dev, bands[1].rangelow);
+                       if (cadet_getfreq(dev) == bands[1].rangelow) {
                                release_region(dev->io, 2);
                                return;
                        }
@@ -648,7 +589,8 @@ static int __init cadet_init(void)
 {
        struct cadet *dev = &cadet_card;
        struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
-       int res;
+       struct v4l2_ctrl_handler *hdl;
+       int res = -ENODEV;
 
        strlcpy(v4l2_dev->name, "cadet", sizeof(v4l2_dev->name));
        mutex_init(&dev->lock);
@@ -680,23 +622,40 @@ static int __init cadet_init(void)
                goto fail;
        }
 
+       hdl = &dev->ctrl_handler;
+       v4l2_ctrl_handler_init(hdl, 2);
+       v4l2_ctrl_new_std(hdl, &cadet_ctrl_ops,
+                       V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+       v4l2_dev->ctrl_handler = hdl;
+       if (hdl->error) {
+               res = hdl->error;
+               v4l2_err(v4l2_dev, "Could not register controls\n");
+               goto err_hdl;
+       }
+
+       dev->is_fm_band = true;
+       dev->curfreq = bands[dev->is_fm_band].rangelow;
+       cadet_setfreq(dev, dev->curfreq);
        strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
        dev->vdev.v4l2_dev = v4l2_dev;
        dev->vdev.fops = &cadet_fops;
        dev->vdev.ioctl_ops = &cadet_ioctl_ops;
        dev->vdev.release = video_device_release_empty;
+       dev->vdev.lock = &dev->lock;
+       set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);
        video_set_drvdata(&dev->vdev, dev);
 
-       if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
-               v4l2_device_unregister(v4l2_dev);
-               release_region(dev->io, 2);
-               goto fail;
-       }
+       if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0)
+               goto err_hdl;
        v4l2_info(v4l2_dev, "ADS Cadet Radio Card at 0x%x\n", dev->io);
        return 0;
+err_hdl:
+       v4l2_ctrl_handler_free(hdl);
+       v4l2_device_unregister(v4l2_dev);
+       release_region(dev->io, 2);
 fail:
        pnp_unregister_driver(&cadet_pnp_driver);
-       return -ENODEV;
+       return res;
 }
 
 static void __exit cadet_exit(void)
@@ -704,7 +663,10 @@ static void __exit cadet_exit(void)
        struct cadet *dev = &cadet_card;
 
        video_unregister_device(&dev->vdev);
+       v4l2_ctrl_handler_free(&dev->ctrl_handler);
        v4l2_device_unregister(&dev->v4l2_dev);
+       outb(7, dev->io);       /* Mute */
+       outb(0x00, dev->io + 1);
        release_region(dev->io, 2);
        pnp_unregister_driver(&cadet_pnp_driver);
 }
diff --git a/drivers/media/radio/radio-shark.c b/drivers/media/radio/radio-shark.c
new file mode 100644 (file)
index 0000000..d0b6bb5
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * Linux V4L2 radio driver for the Griffin radioSHARK USB radio receiver
+ *
+ * Note the radioSHARK offers the audio through a regular USB audio device,
+ * this driver only handles the tuning.
+ *
+ * The info necessary to drive the shark was taken from the small userspace
+ * shark.c program by Michael Rolig, which he kindly placed in the Public
+ * Domain.
+ *
+ * Copyright (c) 2012 Hans de Goede <hdegoede@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.
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-device.h>
+#include <sound/tea575x-tuner.h>
+
+/*
+ * Version Information
+ */
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Griffin radioSHARK, USB radio receiver driver");
+MODULE_LICENSE("GPL");
+
+#define SHARK_IN_EP            0x83
+#define SHARK_OUT_EP           0x05
+
+#define TEA575X_BIT_MONO       (1<<22)         /* 0 = stereo, 1 = mono */
+#define TEA575X_BIT_BAND_MASK  (3<<20)
+#define TEA575X_BIT_BAND_FM    (0<<20)
+
+#define TB_LEN 6
+#define DRV_NAME "radioshark"
+
+#define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev)
+
+enum { BLUE_LED, BLUE_PULSE_LED, RED_LED, NO_LEDS };
+
+static void shark_led_set_blue(struct led_classdev *led_cdev,
+                              enum led_brightness value);
+static void shark_led_set_blue_pulse(struct led_classdev *led_cdev,
+                                    enum led_brightness value);
+static void shark_led_set_red(struct led_classdev *led_cdev,
+                             enum led_brightness value);
+
+static const struct led_classdev shark_led_templates[NO_LEDS] = {
+       [BLUE_LED] = {
+               .name           = "%s:blue:",
+               .brightness     = LED_OFF,
+               .max_brightness = 127,
+               .brightness_set = shark_led_set_blue,
+       },
+       [BLUE_PULSE_LED] = {
+               .name           = "%s:blue-pulse:",
+               .brightness     = LED_OFF,
+               .max_brightness = 255,
+               .brightness_set = shark_led_set_blue_pulse,
+       },
+       [RED_LED] = {
+               .name           = "%s:red:",
+               .brightness     = LED_OFF,
+               .max_brightness = 1,
+               .brightness_set = shark_led_set_red,
+       },
+};
+
+struct shark_device {
+       struct usb_device *usbdev;
+       struct v4l2_device v4l2_dev;
+       struct snd_tea575x tea;
+
+       struct work_struct led_work;
+       struct led_classdev leds[NO_LEDS];
+       char led_names[NO_LEDS][32];
+       atomic_t brightness[NO_LEDS];
+       unsigned long brightness_new;
+
+       u8 *transfer_buffer;
+       u32 last_val;
+};
+
+static atomic_t shark_instance = ATOMIC_INIT(0);
+
+static void shark_write_val(struct snd_tea575x *tea, u32 val)
+{
+       struct shark_device *shark = tea->private_data;
+       int i, res, actual_len;
+
+       /* Avoid unnecessary (slow) USB transfers */
+       if (shark->last_val == val)
+               return;
+
+       memset(shark->transfer_buffer, 0, TB_LEN);
+       shark->transfer_buffer[0] = 0xc0; /* Write shift register command */
+       for (i = 0; i < 4; i++)
+               shark->transfer_buffer[i] |= (val >> (24 - i * 8)) & 0xff;
+
+       res = usb_interrupt_msg(shark->usbdev,
+                               usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
+                               shark->transfer_buffer, TB_LEN,
+                               &actual_len, 1000);
+       if (res >= 0)
+               shark->last_val = val;
+       else
+               v4l2_err(&shark->v4l2_dev, "set-freq error: %d\n", res);
+}
+
+static u32 shark_read_val(struct snd_tea575x *tea)
+{
+       struct shark_device *shark = tea->private_data;
+       int i, res, actual_len;
+       u32 val = 0;
+
+       memset(shark->transfer_buffer, 0, TB_LEN);
+       shark->transfer_buffer[0] = 0x80;
+       res = usb_interrupt_msg(shark->usbdev,
+                               usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
+                               shark->transfer_buffer, TB_LEN,
+                               &actual_len, 1000);
+       if (res < 0) {
+               v4l2_err(&shark->v4l2_dev, "request-status error: %d\n", res);
+               return shark->last_val;
+       }
+
+       res = usb_interrupt_msg(shark->usbdev,
+                               usb_rcvintpipe(shark->usbdev, SHARK_IN_EP),
+                               shark->transfer_buffer, TB_LEN,
+                               &actual_len, 1000);
+       if (res < 0) {
+               v4l2_err(&shark->v4l2_dev, "get-status error: %d\n", res);
+               return shark->last_val;
+       }
+
+       for (i = 0; i < 4; i++)
+               val |= shark->transfer_buffer[i] << (24 - i * 8);
+
+       shark->last_val = val;
+
+       /*
+        * The shark does not allow actually reading the stereo / mono pin :(
+        * So assume that when we're tuned to an FM station and mono has not
+        * been requested, that we're receiving stereo.
+        */
+       if (((val & TEA575X_BIT_BAND_MASK) == TEA575X_BIT_BAND_FM) &&
+           !(val & TEA575X_BIT_MONO))
+               shark->tea.stereo = true;
+       else
+               shark->tea.stereo = false;
+
+       return val;
+}
+
+static struct snd_tea575x_ops shark_tea_ops = {
+       .write_val = shark_write_val,
+       .read_val  = shark_read_val,
+};
+
+static void shark_led_work(struct work_struct *work)
+{
+       struct shark_device *shark =
+               container_of(work, struct shark_device, led_work);
+       int i, res, brightness, actual_len;
+
+       /*
+        * We use the v4l2_dev lock and registered bit to ensure the device
+        * does not get unplugged and unreffed while we're running.
+        */
+       mutex_lock(&shark->tea.mutex);
+       if (!video_is_registered(&shark->tea.vd))
+               goto leave;
+
+       for (i = 0; i < 3; i++) {
+               if (!test_and_clear_bit(i, &shark->brightness_new))
+                       continue;
+
+               brightness = atomic_read(&shark->brightness[i]);
+               memset(shark->transfer_buffer, 0, TB_LEN);
+               if (i != RED_LED) {
+                       shark->transfer_buffer[0] = 0xA0 + i;
+                       shark->transfer_buffer[1] = brightness;
+               } else
+                       shark->transfer_buffer[0] = brightness ? 0xA9 : 0xA8;
+               res = usb_interrupt_msg(shark->usbdev,
+                                       usb_sndintpipe(shark->usbdev, 0x05),
+                                       shark->transfer_buffer, TB_LEN,
+                                       &actual_len, 1000);
+               if (res < 0)
+                       v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n",
+                                shark->led_names[i], res);
+       }
+leave:
+       mutex_unlock(&shark->tea.mutex);
+}
+
+static void shark_led_set_blue(struct led_classdev *led_cdev,
+                              enum led_brightness value)
+{
+       struct shark_device *shark =
+               container_of(led_cdev, struct shark_device, leds[BLUE_LED]);
+
+       atomic_set(&shark->brightness[BLUE_LED], value);
+       set_bit(BLUE_LED, &shark->brightness_new);
+       schedule_work(&shark->led_work);
+}
+
+static void shark_led_set_blue_pulse(struct led_classdev *led_cdev,
+                                    enum led_brightness value)
+{
+       struct shark_device *shark = container_of(led_cdev,
+                               struct shark_device, leds[BLUE_PULSE_LED]);
+
+       atomic_set(&shark->brightness[BLUE_PULSE_LED], 256 - value);
+       set_bit(BLUE_PULSE_LED, &shark->brightness_new);
+       schedule_work(&shark->led_work);
+}
+
+static void shark_led_set_red(struct led_classdev *led_cdev,
+                             enum led_brightness value)
+{
+       struct shark_device *shark =
+               container_of(led_cdev, struct shark_device, leds[RED_LED]);
+
+       atomic_set(&shark->brightness[RED_LED], value);
+       set_bit(RED_LED, &shark->brightness_new);
+       schedule_work(&shark->led_work);
+}
+
+static void usb_shark_disconnect(struct usb_interface *intf)
+{
+       struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
+       struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
+       int i;
+
+       mutex_lock(&shark->tea.mutex);
+       v4l2_device_disconnect(&shark->v4l2_dev);
+       snd_tea575x_exit(&shark->tea);
+       mutex_unlock(&shark->tea.mutex);
+
+       for (i = 0; i < NO_LEDS; i++)
+               led_classdev_unregister(&shark->leds[i]);
+
+       v4l2_device_put(&shark->v4l2_dev);
+}
+
+static void usb_shark_release(struct v4l2_device *v4l2_dev)
+{
+       struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
+
+       cancel_work_sync(&shark->led_work);
+       v4l2_device_unregister(&shark->v4l2_dev);
+       kfree(shark->transfer_buffer);
+       kfree(shark);
+}
+
+static int usb_shark_probe(struct usb_interface *intf,
+                          const struct usb_device_id *id)
+{
+       struct shark_device *shark;
+       int i, retval = -ENOMEM;
+
+       shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL);
+       if (!shark)
+               return retval;
+
+       shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);
+       if (!shark->transfer_buffer)
+               goto err_alloc_buffer;
+
+       /*
+        * Work around a bug in usbhid/hid-core.c, where it leaves a dangling
+        * pointer in intfdata causing v4l2-device.c to not set it. Which
+        * results in usb_shark_disconnect() referencing the dangling pointer
+        *
+        * REMOVE (as soon as the above bug is fixed, patch submitted)
+        */
+       usb_set_intfdata(intf, NULL);
+
+       shark->v4l2_dev.release = usb_shark_release;
+       v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
+       retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev);
+       if (retval) {
+               v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n");
+               goto err_reg_dev;
+       }
+
+       shark->usbdev = interface_to_usbdev(intf);
+       shark->tea.v4l2_dev = &shark->v4l2_dev;
+       shark->tea.private_data = shark;
+       shark->tea.radio_nr = -1;
+       shark->tea.ops = &shark_tea_ops;
+       shark->tea.cannot_mute = true;
+       strlcpy(shark->tea.card, "Griffin radioSHARK",
+               sizeof(shark->tea.card));
+       usb_make_path(shark->usbdev, shark->tea.bus_info,
+               sizeof(shark->tea.bus_info));
+
+       retval = snd_tea575x_init(&shark->tea, THIS_MODULE);
+       if (retval) {
+               v4l2_err(&shark->v4l2_dev, "couldn't init tea5757\n");
+               goto err_init_tea;
+       }
+
+       INIT_WORK(&shark->led_work, shark_led_work);
+       for (i = 0; i < NO_LEDS; i++) {
+               shark->leds[i] = shark_led_templates[i];
+               snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
+                        shark->leds[i].name, shark->v4l2_dev.name);
+               shark->leds[i].name = shark->led_names[i];
+               /*
+                * We don't fail the probe if we fail to register the leds,
+                * because once we've called snd_tea575x_init, the /dev/radio0
+                * node may be opened from userspace holding a reference to us!
+                *
+                * Note we cannot register the leds first instead as
+                * shark_led_work depends on the v4l2 mutex and registered bit.
+                */
+               retval = led_classdev_register(&intf->dev, &shark->leds[i]);
+               if (retval)
+                       v4l2_err(&shark->v4l2_dev,
+                                "couldn't register led: %s\n",
+                                shark->led_names[i]);
+       }
+
+       return 0;
+
+err_init_tea:
+       v4l2_device_unregister(&shark->v4l2_dev);
+err_reg_dev:
+       kfree(shark->transfer_buffer);
+err_alloc_buffer:
+       kfree(shark);
+
+       return retval;
+}
+
+/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */
+static struct usb_device_id usb_shark_device_table[] = {
+       { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
+                        USB_DEVICE_ID_MATCH_INT_CLASS,
+         .idVendor     = 0x077d,
+         .idProduct    = 0x627a,
+         .bcdDevice_lo = 0x0001,
+         .bcdDevice_hi = 0x0001,
+         .bInterfaceClass = 3,
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(usb, usb_shark_device_table);
+
+static struct usb_driver usb_shark_driver = {
+       .name                   = DRV_NAME,
+       .probe                  = usb_shark_probe,
+       .disconnect             = usb_shark_disconnect,
+       .id_table               = usb_shark_device_table,
+};
+module_usb_driver(usb_shark_driver);
diff --git a/drivers/media/radio/radio-shark2.c b/drivers/media/radio/radio-shark2.c
new file mode 100644 (file)
index 0000000..b9575de
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * Linux V4L2 radio driver for the Griffin radioSHARK2 USB radio receiver
+ *
+ * Note the radioSHARK2 offers the audio through a regular USB audio device,
+ * this driver only handles the tuning.
+ *
+ * The info necessary to drive the shark2 was taken from the small userspace
+ * shark2.c program by Hisaaki Shibata, which he kindly placed in the Public
+ * Domain.
+ *
+ * Copyright (c) 2012 Hans de Goede <hdegoede@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.
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-device.h>
+#include "radio-tea5777.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Griffin radioSHARK2, USB radio receiver driver");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+#define SHARK_IN_EP            0x83
+#define SHARK_OUT_EP           0x05
+
+#define TB_LEN 7
+#define DRV_NAME "radioshark2"
+
+#define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev)
+
+enum { BLUE_LED, RED_LED, NO_LEDS };
+
+static void shark_led_set_blue(struct led_classdev *led_cdev,
+                              enum led_brightness value);
+static void shark_led_set_red(struct led_classdev *led_cdev,
+                             enum led_brightness value);
+
+static const struct led_classdev shark_led_templates[NO_LEDS] = {
+       [BLUE_LED] = {
+               .name           = "%s:blue:",
+               .brightness     = LED_OFF,
+               .max_brightness = 127,
+               .brightness_set = shark_led_set_blue,
+       },
+       [RED_LED] = {
+               .name           = "%s:red:",
+               .brightness     = LED_OFF,
+               .max_brightness = 1,
+               .brightness_set = shark_led_set_red,
+       },
+};
+
+struct shark_device {
+       struct usb_device *usbdev;
+       struct v4l2_device v4l2_dev;
+       struct radio_tea5777 tea;
+
+       struct work_struct led_work;
+       struct led_classdev leds[NO_LEDS];
+       char led_names[NO_LEDS][32];
+       atomic_t brightness[NO_LEDS];
+       unsigned long brightness_new;
+
+       u8 *transfer_buffer;
+};
+
+static atomic_t shark_instance = ATOMIC_INIT(0);
+
+static int shark_write_reg(struct radio_tea5777 *tea, u64 reg)
+{
+       struct shark_device *shark = tea->private_data;
+       int i, res, actual_len;
+
+       memset(shark->transfer_buffer, 0, TB_LEN);
+       shark->transfer_buffer[0] = 0x81; /* Write register command */
+       for (i = 0; i < 6; i++)
+               shark->transfer_buffer[i + 1] = (reg >> (40 - i * 8)) & 0xff;
+
+       v4l2_dbg(1, debug, tea->v4l2_dev,
+                "shark2-write: %02x %02x %02x %02x %02x %02x %02x\n",
+                shark->transfer_buffer[0], shark->transfer_buffer[1],
+                shark->transfer_buffer[2], shark->transfer_buffer[3],
+                shark->transfer_buffer[4], shark->transfer_buffer[5],
+                shark->transfer_buffer[6]);
+
+       res = usb_interrupt_msg(shark->usbdev,
+                               usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
+                               shark->transfer_buffer, TB_LEN,
+                               &actual_len, 1000);
+       if (res < 0) {
+               v4l2_err(tea->v4l2_dev, "write error: %d\n", res);
+               return res;
+       }
+
+       return 0;
+}
+
+static int shark_read_reg(struct radio_tea5777 *tea, u32 *reg_ret)
+{
+       struct shark_device *shark = tea->private_data;
+       int i, res, actual_len;
+       u32 reg = 0;
+
+       memset(shark->transfer_buffer, 0, TB_LEN);
+       shark->transfer_buffer[0] = 0x82;
+       res = usb_interrupt_msg(shark->usbdev,
+                               usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
+                               shark->transfer_buffer, TB_LEN,
+                               &actual_len, 1000);
+       if (res < 0) {
+               v4l2_err(tea->v4l2_dev, "request-read error: %d\n", res);
+               return res;
+       }
+
+       res = usb_interrupt_msg(shark->usbdev,
+                               usb_rcvintpipe(shark->usbdev, SHARK_IN_EP),
+                               shark->transfer_buffer, TB_LEN,
+                               &actual_len, 1000);
+       if (res < 0) {
+               v4l2_err(tea->v4l2_dev, "read error: %d\n", res);
+               return res;
+       }
+
+       for (i = 0; i < 3; i++)
+               reg |= shark->transfer_buffer[i] << (16 - i * 8);
+
+       v4l2_dbg(1, debug, tea->v4l2_dev, "shark2-read: %02x %02x %02x\n",
+                shark->transfer_buffer[0], shark->transfer_buffer[1],
+                shark->transfer_buffer[2]);
+
+       *reg_ret = reg;
+       return 0;
+}
+
+static struct radio_tea5777_ops shark_tea_ops = {
+       .write_reg = shark_write_reg,
+       .read_reg  = shark_read_reg,
+};
+
+static void shark_led_work(struct work_struct *work)
+{
+       struct shark_device *shark =
+               container_of(work, struct shark_device, led_work);
+       int i, res, brightness, actual_len;
+       /*
+        * We use the v4l2_dev lock and registered bit to ensure the device
+        * does not get unplugged and unreffed while we're running.
+        */
+       mutex_lock(&shark->tea.mutex);
+       if (!video_is_registered(&shark->tea.vd))
+               goto leave;
+
+       for (i = 0; i < 2; i++) {
+               if (!test_and_clear_bit(i, &shark->brightness_new))
+                       continue;
+
+               brightness = atomic_read(&shark->brightness[i]);
+               memset(shark->transfer_buffer, 0, TB_LEN);
+               shark->transfer_buffer[0] = 0x83 + i;
+               shark->transfer_buffer[1] = brightness;
+               res = usb_interrupt_msg(shark->usbdev,
+                                       usb_sndintpipe(shark->usbdev,
+                                                      SHARK_OUT_EP),
+                                       shark->transfer_buffer, TB_LEN,
+                                       &actual_len, 1000);
+               if (res < 0)
+                       v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n",
+                                shark->led_names[i], res);
+       }
+leave:
+       mutex_unlock(&shark->tea.mutex);
+}
+
+static void shark_led_set_blue(struct led_classdev *led_cdev,
+                              enum led_brightness value)
+{
+       struct shark_device *shark =
+               container_of(led_cdev, struct shark_device, leds[BLUE_LED]);
+
+       atomic_set(&shark->brightness[BLUE_LED], value);
+       set_bit(BLUE_LED, &shark->brightness_new);
+       schedule_work(&shark->led_work);
+}
+
+static void shark_led_set_red(struct led_classdev *led_cdev,
+                             enum led_brightness value)
+{
+       struct shark_device *shark =
+               container_of(led_cdev, struct shark_device, leds[RED_LED]);
+
+       atomic_set(&shark->brightness[RED_LED], value);
+       set_bit(RED_LED, &shark->brightness_new);
+       schedule_work(&shark->led_work);
+}
+
+static void usb_shark_disconnect(struct usb_interface *intf)
+{
+       struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
+       struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
+       int i;
+
+       mutex_lock(&shark->tea.mutex);
+       v4l2_device_disconnect(&shark->v4l2_dev);
+       radio_tea5777_exit(&shark->tea);
+       mutex_unlock(&shark->tea.mutex);
+
+       for (i = 0; i < NO_LEDS; i++)
+               led_classdev_unregister(&shark->leds[i]);
+
+       v4l2_device_put(&shark->v4l2_dev);
+}
+
+static void usb_shark_release(struct v4l2_device *v4l2_dev)
+{
+       struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
+
+       cancel_work_sync(&shark->led_work);
+       v4l2_device_unregister(&shark->v4l2_dev);
+       kfree(shark->transfer_buffer);
+       kfree(shark);
+}
+
+static int usb_shark_probe(struct usb_interface *intf,
+                          const struct usb_device_id *id)
+{
+       struct shark_device *shark;
+       int i, retval = -ENOMEM;
+
+       shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL);
+       if (!shark)
+               return retval;
+
+       shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);
+       if (!shark->transfer_buffer)
+               goto err_alloc_buffer;
+
+       /*
+        * Work around a bug in usbhid/hid-core.c, where it leaves a dangling
+        * pointer in intfdata causing v4l2-device.c to not set it. Which
+        * results in usb_shark_disconnect() referencing the dangling pointer
+        *
+        * REMOVE (as soon as the above bug is fixed, patch submitted)
+        */
+       usb_set_intfdata(intf, NULL);
+
+       shark->v4l2_dev.release = usb_shark_release;
+       v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
+       retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev);
+       if (retval) {
+               v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n");
+               goto err_reg_dev;
+       }
+
+       shark->usbdev = interface_to_usbdev(intf);
+       shark->tea.v4l2_dev = &shark->v4l2_dev;
+       shark->tea.private_data = shark;
+       shark->tea.ops = &shark_tea_ops;
+       shark->tea.has_am = true;
+       shark->tea.write_before_read = true;
+       strlcpy(shark->tea.card, "Griffin radioSHARK2",
+               sizeof(shark->tea.card));
+       usb_make_path(shark->usbdev, shark->tea.bus_info,
+               sizeof(shark->tea.bus_info));
+
+       retval = radio_tea5777_init(&shark->tea, THIS_MODULE);
+       if (retval) {
+               v4l2_err(&shark->v4l2_dev, "couldn't init tea5777\n");
+               goto err_init_tea;
+       }
+
+       INIT_WORK(&shark->led_work, shark_led_work);
+       for (i = 0; i < NO_LEDS; i++) {
+               shark->leds[i] = shark_led_templates[i];
+               snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
+                        shark->leds[i].name, shark->v4l2_dev.name);
+               shark->leds[i].name = shark->led_names[i];
+               /*
+                * We don't fail the probe if we fail to register the leds,
+                * because once we've called radio_tea5777_init, the /dev/radio0
+                * node may be opened from userspace holding a reference to us!
+                *
+                * Note we cannot register the leds first instead as
+                * shark_led_work depends on the v4l2 mutex and registered bit.
+                */
+               retval = led_classdev_register(&intf->dev, &shark->leds[i]);
+               if (retval)
+                       v4l2_err(&shark->v4l2_dev,
+                                "couldn't register led: %s\n",
+                                shark->led_names[i]);
+       }
+
+       return 0;
+
+err_init_tea:
+       v4l2_device_unregister(&shark->v4l2_dev);
+err_reg_dev:
+       kfree(shark->transfer_buffer);
+err_alloc_buffer:
+       kfree(shark);
+
+       return retval;
+}
+
+/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */
+static struct usb_device_id usb_shark_device_table[] = {
+       { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
+                        USB_DEVICE_ID_MATCH_INT_CLASS,
+         .idVendor     = 0x077d,
+         .idProduct    = 0x627a,
+         .bcdDevice_lo = 0x0010,
+         .bcdDevice_hi = 0x0010,
+         .bInterfaceClass = 3,
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(usb, usb_shark_device_table);
+
+static struct usb_driver usb_shark_driver = {
+       .name                   = DRV_NAME,
+       .probe                  = usb_shark_probe,
+       .disconnect             = usb_shark_disconnect,
+       .id_table               = usb_shark_device_table,
+};
+module_usb_driver(usb_shark_driver);
diff --git a/drivers/media/radio/radio-tea5777.c b/drivers/media/radio/radio-tea5777.c
new file mode 100644 (file)
index 0000000..5bc9fa6
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ *   v4l2 driver for TEA5777 Philips AM/FM radio tuner chips
+ *
+ *     Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
+ *
+ *   Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips:
+ *
+ *     Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
+ *
+ *   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/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <asm/div64.h>
+#include "radio-tea5777.h"
+
+MODULE_AUTHOR("Hans de Goede <perex@perex.cz>");
+MODULE_DESCRIPTION("Routines for control of TEA5777 Philips AM/FM radio tuner chips");
+MODULE_LICENSE("GPL");
+
+/* Fixed FM only band for now, will implement multi-band support when the
+   VIDIOC_ENUM_FREQ_BANDS API is upstream */
+#define TEA5777_FM_RANGELOW            (76000 * 16)
+#define TEA5777_FM_RANGEHIGH           (108000 * 16)
+
+#define TEA5777_FM_IF                  150 /* kHz */
+#define TEA5777_FM_FREQ_STEP           50 /* kHz */
+
+/* Write reg, common bits */
+#define TEA5777_W_MUTE_MASK            (1LL << 47)
+#define TEA5777_W_MUTE_SHIFT           47
+#define TEA5777_W_AM_FM_MASK           (1LL << 46)
+#define TEA5777_W_AM_FM_SHIFT          46
+#define TEA5777_W_STB_MASK             (1LL << 45)
+#define TEA5777_W_STB_SHIFT            45
+
+#define TEA5777_W_IFCE_MASK            (1LL << 29)
+#define TEA5777_W_IFCE_SHIFT           29
+#define TEA5777_W_IFW_MASK             (1LL << 28)
+#define TEA5777_W_IFW_SHIFT            28
+#define TEA5777_W_HILO_MASK            (1LL << 27)
+#define TEA5777_W_HILO_SHIFT           27
+#define TEA5777_W_DBUS_MASK            (1LL << 26)
+#define TEA5777_W_DBUS_SHIFT           26
+
+#define TEA5777_W_INTEXT_MASK          (1LL << 24)
+#define TEA5777_W_INTEXT_SHIFT         24
+#define TEA5777_W_P1_MASK              (1LL << 23)
+#define TEA5777_W_P1_SHIFT             23
+#define TEA5777_W_P0_MASK              (1LL << 22)
+#define TEA5777_W_P0_SHIFT             22
+#define TEA5777_W_PEN1_MASK            (1LL << 21)
+#define TEA5777_W_PEN1_SHIFT           21
+#define TEA5777_W_PEN0_MASK            (1LL << 20)
+#define TEA5777_W_PEN0_SHIFT           20
+
+#define TEA5777_W_CHP0_MASK            (1LL << 18)
+#define TEA5777_W_CHP0_SHIFT           18
+#define TEA5777_W_DEEM_MASK            (1LL << 17)
+#define TEA5777_W_DEEM_SHIFT           17
+
+#define TEA5777_W_SEARCH_MASK          (1LL << 7)
+#define TEA5777_W_SEARCH_SHIFT         7
+#define TEA5777_W_PROGBLIM_MASK                (1LL << 6)
+#define TEA5777_W_PROGBLIM_SHIFT       6
+#define TEA5777_W_UPDWN_MASK           (1LL << 5)
+#define TEA5777_W_UPDWN_SHIFT          5
+#define TEA5777_W_SLEV_MASK            (3LL << 3)
+#define TEA5777_W_SLEV_SHIFT           3
+
+/* Write reg, FM specific bits */
+#define TEA5777_W_FM_PLL_MASK          (0x1fffLL << 32)
+#define TEA5777_W_FM_PLL_SHIFT         32
+#define TEA5777_W_FM_FREF_MASK         (0x03LL << 30)
+#define TEA5777_W_FM_FREF_SHIFT                30
+#define TEA5777_W_FM_FREF_VALUE                0 /* 50 kHz tune steps, 150 kHz IF */
+
+#define TEA5777_W_FM_FORCEMONO_MASK    (1LL << 15)
+#define TEA5777_W_FM_FORCEMONO_SHIFT   15
+#define TEA5777_W_FM_SDSOFF_MASK       (1LL << 14)
+#define TEA5777_W_FM_SDSOFF_SHIFT      14
+#define TEA5777_W_FM_DOFF_MASK         (1LL << 13)
+#define TEA5777_W_FM_DOFF_SHIFT                13
+
+#define TEA5777_W_FM_STEP_MASK         (3LL << 1)
+#define TEA5777_W_FM_STEP_SHIFT                1
+
+/* Write reg, AM specific bits */
+#define TEA5777_W_AM_PLL_MASK          (0x7ffLL << 34)
+#define TEA5777_W_AM_PLL_SHIFT         34
+#define TEA5777_W_AM_AGCRF_MASK                (1LL << 33)
+#define TEA5777_W_AM_AGCRF_SHIFT       33
+#define TEA5777_W_AM_AGCIF_MASK                (1LL << 32)
+#define TEA5777_W_AM_AGCIF_SHIFT       32
+#define TEA5777_W_AM_MWLW_MASK         (1LL << 31)
+#define TEA5777_W_AM_MWLW_SHIFT                31
+#define TEA5777_W_AM_LNA_MASK          (1LL << 30)
+#define TEA5777_W_AM_LNA_SHIFT         30
+
+#define TEA5777_W_AM_PEAK_MASK         (1LL << 25)
+#define TEA5777_W_AM_PEAK_SHIFT                25
+
+#define TEA5777_W_AM_RFB_MASK          (1LL << 16)
+#define TEA5777_W_AM_RFB_SHIFT         16
+#define TEA5777_W_AM_CALLIGN_MASK      (1LL << 15)
+#define TEA5777_W_AM_CALLIGN_SHIFT     15
+#define TEA5777_W_AM_CBANK_MASK                (0x7fLL << 8)
+#define TEA5777_W_AM_CBANK_SHIFT       8
+
+#define TEA5777_W_AM_DELAY_MASK                (1LL << 2)
+#define TEA5777_W_AM_DELAY_SHIFT       2
+#define TEA5777_W_AM_STEP_MASK         (1LL << 1)
+#define TEA5777_W_AM_STEP_SHIFT                1
+
+/* Read reg, common bits */
+#define TEA5777_R_LEVEL_MASK           (0x0f << 17)
+#define TEA5777_R_LEVEL_SHIFT          17
+#define TEA5777_R_SFOUND_MASK          (0x01 << 16)
+#define TEA5777_R_SFOUND_SHIFT         16
+#define TEA5777_R_BLIM_MASK            (0x01 << 15)
+#define TEA5777_R_BLIM_SHIFT           15
+
+/* Read reg, FM specific bits */
+#define TEA5777_R_FM_STEREO_MASK       (0x01 << 21)
+#define TEA5777_R_FM_STEREO_SHIFT      21
+#define TEA5777_R_FM_PLL_MASK          0x1fff
+#define TEA5777_R_FM_PLL_SHIFT         0
+
+static u32 tea5777_freq_to_v4l2_freq(struct radio_tea5777 *tea, u32 freq)
+{
+       return (freq * TEA5777_FM_FREQ_STEP + TEA5777_FM_IF) * 16;
+}
+
+static int radio_tea5777_set_freq(struct radio_tea5777 *tea)
+{
+       u64 freq;
+       int res;
+
+       freq = clamp_t(u32, tea->freq,
+                      TEA5777_FM_RANGELOW, TEA5777_FM_RANGEHIGH) + 8;
+       do_div(freq, 16); /* to kHz */
+
+       freq -= TEA5777_FM_IF;
+       do_div(freq, TEA5777_FM_FREQ_STEP);
+
+       tea->write_reg &= ~(TEA5777_W_FM_PLL_MASK | TEA5777_W_FM_FREF_MASK);
+       tea->write_reg |= freq << TEA5777_W_FM_PLL_SHIFT;
+       tea->write_reg |= TEA5777_W_FM_FREF_VALUE << TEA5777_W_FM_FREF_SHIFT;
+
+       res = tea->ops->write_reg(tea, tea->write_reg);
+       if (res)
+               return res;
+
+       tea->needs_write = false;
+       tea->read_reg = -1;
+       tea->freq = tea5777_freq_to_v4l2_freq(tea, freq);
+
+       return 0;
+}
+
+static int radio_tea5777_update_read_reg(struct radio_tea5777 *tea, int wait)
+{
+       int res;
+
+       if (tea->read_reg != -1)
+               return 0;
+
+       if (tea->write_before_read && tea->needs_write) {
+               res = radio_tea5777_set_freq(tea);
+               if (res)
+                       return res;
+       }
+
+       if (wait) {
+               if (schedule_timeout_interruptible(msecs_to_jiffies(wait)))
+                       return -ERESTARTSYS;
+       }
+
+       res = tea->ops->read_reg(tea, &tea->read_reg);
+       if (res)
+               return res;
+
+       tea->needs_write = true;
+       return 0;
+}
+
+/*
+ * Linux Video interface
+ */
+
+static int vidioc_querycap(struct file *file, void  *priv,
+                                       struct v4l2_capability *v)
+{
+       struct radio_tea5777 *tea = video_drvdata(file);
+
+       strlcpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
+       strlcpy(v->card, tea->card, sizeof(v->card));
+       strlcat(v->card, " TEA5777", sizeof(v->card));
+       strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
+       v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+       v->device_caps |= V4L2_CAP_HW_FREQ_SEEK;
+       v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
+       return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+                                       struct v4l2_tuner *v)
+{
+       struct radio_tea5777 *tea = video_drvdata(file);
+       int res;
+
+       if (v->index > 0)
+               return -EINVAL;
+
+       res = radio_tea5777_update_read_reg(tea, 0);
+       if (res)
+               return res;
+
+       memset(v, 0, sizeof(*v));
+       if (tea->has_am)
+               strlcpy(v->name, "AM/FM", sizeof(v->name));
+       else
+               strlcpy(v->name, "FM", sizeof(v->name));
+       v->type = V4L2_TUNER_RADIO;
+       v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+                       V4L2_TUNER_CAP_HWSEEK_BOUNDED;
+       v->rangelow   = TEA5777_FM_RANGELOW;
+       v->rangehigh  = TEA5777_FM_RANGEHIGH;
+       v->rxsubchans = (tea->read_reg & TEA5777_R_FM_STEREO_MASK) ?
+                       V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
+       v->audmode = (tea->write_reg & TEA5777_W_FM_FORCEMONO_MASK) ?
+               V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
+       /* shift - 12 to convert 4-bits (0-15) scale to 16-bits (0-65535) */
+       v->signal = (tea->read_reg & TEA5777_R_LEVEL_MASK) >>
+                   (TEA5777_R_LEVEL_SHIFT - 12);
+
+       /* Invalidate read_reg, so that next call we return up2date signal */
+       tea->read_reg = -1;
+
+       return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+                                       struct v4l2_tuner *v)
+{
+       struct radio_tea5777 *tea = video_drvdata(file);
+
+       if (v->index)
+               return -EINVAL;
+
+       if (v->audmode == V4L2_TUNER_MODE_MONO)
+               tea->write_reg |= TEA5777_W_FM_FORCEMONO_MASK;
+       else
+               tea->write_reg &= ~TEA5777_W_FM_FORCEMONO_MASK;
+
+       return radio_tea5777_set_freq(tea);
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+                                       struct v4l2_frequency *f)
+{
+       struct radio_tea5777 *tea = video_drvdata(file);
+
+       if (f->tuner != 0)
+               return -EINVAL;
+       f->type = V4L2_TUNER_RADIO;
+       f->frequency = tea->freq;
+       return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+                                       struct v4l2_frequency *f)
+{
+       struct radio_tea5777 *tea = video_drvdata(file);
+
+       if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+               return -EINVAL;
+
+       tea->freq = f->frequency;
+       return radio_tea5777_set_freq(tea);
+}
+
+static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
+                                       struct v4l2_hw_freq_seek *a)
+{
+       struct radio_tea5777 *tea = video_drvdata(file);
+       u32 orig_freq = tea->freq;
+       unsigned long timeout;
+       int res, spacing = 200 * 16; /* 200 kHz */
+       /* These are fixed *for now* */
+       const u32 seek_rangelow  = TEA5777_FM_RANGELOW;
+       const u32 seek_rangehigh = TEA5777_FM_RANGEHIGH;
+
+       if (a->tuner || a->wrap_around)
+               return -EINVAL;
+
+       tea->write_reg |= TEA5777_W_PROGBLIM_MASK;
+       if (seek_rangelow != tea->seek_rangelow) {
+               tea->write_reg &= ~TEA5777_W_UPDWN_MASK;
+               tea->freq = seek_rangelow;
+               res = radio_tea5777_set_freq(tea);
+               if (res)
+                       goto leave;
+               tea->seek_rangelow = tea->freq;
+       }
+       if (seek_rangehigh != tea->seek_rangehigh) {
+               tea->write_reg |= TEA5777_W_UPDWN_MASK;
+               tea->freq = seek_rangehigh;
+               res = radio_tea5777_set_freq(tea);
+               if (res)
+                       goto leave;
+               tea->seek_rangehigh = tea->freq;
+       }
+       tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK;
+
+       tea->write_reg |= TEA5777_W_SEARCH_MASK;
+       if (a->seek_upward) {
+               tea->write_reg |= TEA5777_W_UPDWN_MASK;
+               tea->freq = orig_freq + spacing;
+       } else {
+               tea->write_reg &= ~TEA5777_W_UPDWN_MASK;
+               tea->freq = orig_freq - spacing;
+       }
+       res = radio_tea5777_set_freq(tea);
+       if (res)
+               goto leave;
+
+       timeout = jiffies + msecs_to_jiffies(5000);
+       for (;;) {
+               if (time_after(jiffies, timeout)) {
+                       res = -ENODATA;
+                       break;
+               }
+
+               res = radio_tea5777_update_read_reg(tea, 100);
+               if (res)
+                       break;
+
+               /*
+                * Note we use tea->freq to track how far we've searched sofar
+                * this is necessary to ensure we continue seeking at the right
+                * point, in the write_before_read case.
+                */
+               tea->freq = (tea->read_reg & TEA5777_R_FM_PLL_MASK);
+               tea->freq = tea5777_freq_to_v4l2_freq(tea, tea->freq);
+
+               if ((tea->read_reg & TEA5777_R_SFOUND_MASK)) {
+                       tea->write_reg &= ~TEA5777_W_SEARCH_MASK;
+                       return 0;
+               }
+
+               if (tea->read_reg & TEA5777_R_BLIM_MASK) {
+                       res = -ENODATA;
+                       break;
+               }
+
+               /* Force read_reg update */
+               tea->read_reg = -1;
+       }
+leave:
+       tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK;
+       tea->write_reg &= ~TEA5777_W_SEARCH_MASK;
+       tea->freq = orig_freq;
+       radio_tea5777_set_freq(tea);
+       return res;
+}
+
+static int tea575x_s_ctrl(struct v4l2_ctrl *c)
+{
+       struct radio_tea5777 *tea =
+               container_of(c->handler, struct radio_tea5777, ctrl_handler);
+
+       switch (c->id) {
+       case V4L2_CID_AUDIO_MUTE:
+               if (c->val)
+                       tea->write_reg |= TEA5777_W_MUTE_MASK;
+               else
+                       tea->write_reg &= ~TEA5777_W_MUTE_MASK;
+
+               return radio_tea5777_set_freq(tea);
+       }
+
+       return -EINVAL;
+}
+
+static const struct v4l2_file_operations tea575x_fops = {
+       .unlocked_ioctl = video_ioctl2,
+       .open           = v4l2_fh_open,
+       .release        = v4l2_fh_release,
+       .poll           = v4l2_ctrl_poll,
+};
+
+static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
+       .vidioc_querycap    = vidioc_querycap,
+       .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_hw_freq_seek = vidioc_s_hw_freq_seek,
+       .vidioc_log_status  = v4l2_ctrl_log_status,
+       .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct video_device tea575x_radio = {
+       .ioctl_ops      = &tea575x_ioctl_ops,
+       .release        = video_device_release_empty,
+};
+
+static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
+       .s_ctrl = tea575x_s_ctrl,
+};
+
+int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner)
+{
+       int res;
+
+       tea->write_reg = (1LL << TEA5777_W_IFCE_SHIFT) |
+                        (1LL << TEA5777_W_IFW_SHIFT) |
+                        (1LL << TEA5777_W_INTEXT_SHIFT) |
+                        (1LL << TEA5777_W_CHP0_SHIFT) |
+                        (2LL << TEA5777_W_SLEV_SHIFT);
+       tea->freq = 90500 * 16; /* 90.5Mhz default */
+       res = radio_tea5777_set_freq(tea);
+       if (res) {
+               v4l2_err(tea->v4l2_dev, "can't set initial freq (%d)\n", res);
+               return res;
+       }
+
+       tea->vd = tea575x_radio;
+       video_set_drvdata(&tea->vd, tea);
+       mutex_init(&tea->mutex);
+       strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name));
+       tea->vd.lock = &tea->mutex;
+       tea->vd.v4l2_dev = tea->v4l2_dev;
+       tea->fops = tea575x_fops;
+       tea->fops.owner = owner;
+       tea->vd.fops = &tea->fops;
+       set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags);
+
+       tea->vd.ctrl_handler = &tea->ctrl_handler;
+       v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+       v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops,
+                         V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+       res = tea->ctrl_handler.error;
+       if (res) {
+               v4l2_err(tea->v4l2_dev, "can't initialize controls\n");
+               v4l2_ctrl_handler_free(&tea->ctrl_handler);
+               return res;
+       }
+       v4l2_ctrl_handler_setup(&tea->ctrl_handler);
+
+       res = video_register_device(&tea->vd, VFL_TYPE_RADIO, -1);
+       if (res) {
+               v4l2_err(tea->v4l2_dev, "can't register video device!\n");
+               v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
+               return res;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(radio_tea5777_init);
+
+void radio_tea5777_exit(struct radio_tea5777 *tea)
+{
+       video_unregister_device(&tea->vd);
+       v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
+}
+EXPORT_SYMBOL_GPL(radio_tea5777_exit);
diff --git a/drivers/media/radio/radio-tea5777.h b/drivers/media/radio/radio-tea5777.h
new file mode 100644 (file)
index 0000000..55cbd78
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef __RADIO_TEA5777_H
+#define __RADIO_TEA5777_H
+
+/*
+ *   v4l2 driver for TEA5777 Philips AM/FM radio tuner chips
+ *
+ *     Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
+ *
+ *   Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips:
+ *
+ *     Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
+ *     Copyright (c) 2012 Hans de Goede <hdegoede@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.
+ *
+ *   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/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+
+#define TEA575X_FMIF   10700
+#define TEA575X_AMIF     450
+
+struct radio_tea5777;
+
+struct radio_tea5777_ops {
+       /*
+        * Write the 6 bytes large write register of the tea5777
+        *
+        * val represents the 6 write registers, with byte 1 from the
+        * datasheet being the most significant byte (so byte 5 of the u64),
+        * and byte 6 from the datasheet being the least significant byte.
+        *
+        * returns 0 on success.
+        */
+       int (*write_reg)(struct radio_tea5777 *tea, u64 val);
+       /*
+        * Read the 3 bytes large read register of the tea5777
+        *
+        * The read value gets returned in val, akin to write_reg, byte 1 from
+        * the datasheet is stored as the most significant byte (so byte 2 of
+        * the u32), and byte 3 from the datasheet gets stored as the least
+        * significant byte (iow byte 0 of the u32).
+        *
+        * returns 0 on success.
+        */
+       int (*read_reg)(struct radio_tea5777 *tea, u32 *val);
+};
+
+struct radio_tea5777 {
+       struct v4l2_device *v4l2_dev;
+       struct v4l2_file_operations fops;
+       struct video_device vd;         /* video device */
+       bool has_am;                    /* Device can tune to AM freqs */
+       bool write_before_read;         /* must write before read quirk */
+       bool needs_write;               /* for write before read quirk */
+       u32 freq;                       /* current frequency */
+       u32 seek_rangelow;              /* current hwseek limits */
+       u32 seek_rangehigh;
+       u32 read_reg;
+       u64 write_reg;
+       struct mutex mutex;
+       struct radio_tea5777_ops *ops;
+       void *private_data;
+       u8 card[32];
+       u8 bus_info[32];
+       struct v4l2_ctrl_handler ctrl_handler;
+};
+
+int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner);
+void radio_tea5777_exit(struct radio_tea5777 *tea);
+
+#endif /* __RADIO_TEA5777_H */
index d485b79222fd4f554fb8834a9ed7f3ace84e7efd..9e38132afec66488299ebd4227a94e9a2e5d3a11 100644 (file)
@@ -4,6 +4,7 @@
  *  Driver for radios with Silicon Labs Si470x FM Radio Receivers
  *
  *  Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
+ *  Copyright (c) 2012 Hans de Goede <hdegoede@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
@@ -127,14 +128,6 @@ static unsigned short space = 2;
 module_param(space, ushort, 0444);
 MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
 
-/* Bottom of Band (MHz) */
-/* 0: 87.5 - 108 MHz (USA, Europe)*/
-/* 1: 76   - 108 MHz (Japan wide band) */
-/* 2: 76   -  90 MHz (Japan) */
-static unsigned short band = 1;
-module_param(band, ushort, 0444);
-MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
-
 /* De-emphasis */
 /* 0: 75 us (USA) */
 /* 1: 50 us (Europe, Australia, Japan) */
@@ -152,19 +145,66 @@ static unsigned int seek_timeout = 5000;
 module_param(seek_timeout, uint, 0644);
 MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
 
-
+static const struct v4l2_frequency_band bands[] = {
+       {
+               .type = V4L2_TUNER_RADIO,
+               .index = 0,
+               .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+                           V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+                           V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+                           V4L2_TUNER_CAP_HWSEEK_WRAP,
+               .rangelow   =  87500 * 16,
+               .rangehigh  = 108000 * 16,
+               .modulation = V4L2_BAND_MODULATION_FM,
+       },
+       {
+               .type = V4L2_TUNER_RADIO,
+               .index = 1,
+               .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+                           V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+                           V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+                           V4L2_TUNER_CAP_HWSEEK_WRAP,
+               .rangelow   =  76000 * 16,
+               .rangehigh  = 108000 * 16,
+               .modulation = V4L2_BAND_MODULATION_FM,
+       },
+       {
+               .type = V4L2_TUNER_RADIO,
+               .index = 2,
+               .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+                           V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+                           V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+                           V4L2_TUNER_CAP_HWSEEK_WRAP,
+               .rangelow   =  76000 * 16,
+               .rangehigh  =  90000 * 16,
+               .modulation = V4L2_BAND_MODULATION_FM,
+       },
+};
 
 /**************************************************************************
  * Generic Functions
  **************************************************************************/
 
+/*
+ * si470x_set_band - set the band
+ */
+static int si470x_set_band(struct si470x_device *radio, int band)
+{
+       if (radio->band == band)
+               return 0;
+
+       radio->band = band;
+       radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_BAND;
+       radio->registers[SYSCONFIG2] |= radio->band << 6;
+       return si470x_set_register(radio, SYSCONFIG2);
+}
+
 /*
  * si470x_set_chan - set the channel
  */
 static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
 {
        int retval;
-       unsigned long timeout;
        bool timed_out = 0;
 
        /* start tuning */
@@ -174,26 +214,12 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
        if (retval < 0)
                goto done;
 
-       /* currently I2C driver only uses interrupt way to tune */
-       if (radio->stci_enabled) {
-               INIT_COMPLETION(radio->completion);
-
-               /* wait till tune operation has completed */
-               retval = wait_for_completion_timeout(&radio->completion,
-                               msecs_to_jiffies(tune_timeout));
-               if (!retval)
-                       timed_out = true;
-       } else {
-               /* wait till tune operation has completed */
-               timeout = jiffies + msecs_to_jiffies(tune_timeout);
-               do {
-                       retval = si470x_get_register(radio, STATUSRSSI);
-                       if (retval < 0)
-                               goto stop;
-                       timed_out = time_after(jiffies, timeout);
-               } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
-                               && (!timed_out));
-       }
+       /* wait till tune operation has completed */
+       INIT_COMPLETION(radio->completion);
+       retval = wait_for_completion_timeout(&radio->completion,
+                       msecs_to_jiffies(tune_timeout));
+       if (!retval)
+               timed_out = true;
 
        if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
                dev_warn(&radio->videodev.dev, "tune does not complete\n");
@@ -201,7 +227,6 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
                dev_warn(&radio->videodev.dev,
                        "tune timed out after %u ms\n", tune_timeout);
 
-stop:
        /* stop tuning */
        radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
        retval = si470x_set_register(radio, CHANNEL);
@@ -210,48 +235,39 @@ done:
        return retval;
 }
 
-
 /*
- * si470x_get_freq - get the frequency
+ * si470x_get_step - get channel spacing
  */
-static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
+static unsigned int si470x_get_step(struct si470x_device *radio)
 {
-       unsigned int spacing, band_bottom;
-       unsigned short chan;
-       int retval;
-
        /* Spacing (kHz) */
        switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
        /* 0: 200 kHz (USA, Australia) */
        case 0:
-               spacing = 0.200 * FREQ_MUL; break;
+               return 200 * 16;
        /* 1: 100 kHz (Europe, Japan) */
        case 1:
-               spacing = 0.100 * FREQ_MUL; break;
+               return 100 * 16;
        /* 2:  50 kHz */
        default:
-               spacing = 0.050 * FREQ_MUL; break;
+               return 50 * 16;
        };
+}
 
-       /* Bottom of Band (MHz) */
-       switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
-       /* 0: 87.5 - 108 MHz (USA, Europe) */
-       case 0:
-               band_bottom = 87.5 * FREQ_MUL; break;
-       /* 1: 76   - 108 MHz (Japan wide band) */
-       default:
-               band_bottom = 76   * FREQ_MUL; break;
-       /* 2: 76   -  90 MHz (Japan) */
-       case 2:
-               band_bottom = 76   * FREQ_MUL; break;
-       };
+
+/*
+ * si470x_get_freq - get the frequency
+ */
+static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
+{
+       int chan, retval;
 
        /* read channel */
        retval = si470x_get_register(radio, READCHAN);
        chan = radio->registers[READCHAN] & READCHAN_READCHAN;
 
        /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
-       *freq = chan * spacing + band_bottom;
+       *freq = chan * si470x_get_step(radio) + bands[radio->band].rangelow;
 
        return retval;
 }
@@ -262,44 +278,12 @@ static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
  */
 int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
 {
-       unsigned int spacing, band_bottom, band_top;
        unsigned short chan;
 
-       /* Spacing (kHz) */
-       switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
-       /* 0: 200 kHz (USA, Australia) */
-       case 0:
-               spacing = 0.200 * FREQ_MUL; break;
-       /* 1: 100 kHz (Europe, Japan) */
-       case 1:
-               spacing = 0.100 * FREQ_MUL; break;
-       /* 2:  50 kHz */
-       default:
-               spacing = 0.050 * FREQ_MUL; break;
-       };
-
-       /* Bottom/Top of Band (MHz) */
-       switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
-       /* 0: 87.5 - 108 MHz (USA, Europe) */
-       case 0:
-               band_bottom = 87.5 * FREQ_MUL;
-               band_top = 108 * FREQ_MUL;
-               break;
-       /* 1: 76   - 108 MHz (Japan wide band) */
-       default:
-               band_bottom = 76 * FREQ_MUL;
-               band_top = 108 * FREQ_MUL;
-               break;
-       /* 2: 76   -  90 MHz (Japan) */
-       case 2:
-               band_bottom = 76 * FREQ_MUL;
-               band_top = 90 * FREQ_MUL;
-               break;
-       };
-
-       freq = clamp(freq, band_bottom, band_top);
+       freq = clamp(freq, bands[radio->band].rangelow,
+                          bands[radio->band].rangehigh);
        /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
-       chan = (freq - band_bottom) / spacing;
+       chan = (freq - bands[radio->band].rangelow) / si470x_get_step(radio);
 
        return si470x_set_chan(radio, chan);
 }
@@ -309,19 +293,43 @@ int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
  * si470x_set_seek - set seek
  */
 static int si470x_set_seek(struct si470x_device *radio,
-               unsigned int wrap_around, unsigned int seek_upward)
+                          struct v4l2_hw_freq_seek *seek)
 {
-       int retval = 0;
-       unsigned long timeout;
+       int band, retval;
+       unsigned int freq;
        bool timed_out = 0;
 
+       /* set band */
+       if (seek->rangelow || seek->rangehigh) {
+               for (band = 0; band < ARRAY_SIZE(bands); band++) {
+                       if (bands[band].rangelow  == seek->rangelow &&
+                           bands[band].rangehigh == seek->rangehigh)
+                               break;
+               }
+               if (band == ARRAY_SIZE(bands))
+                       return -EINVAL; /* No matching band found */
+       } else
+               band = 1; /* If nothing is specified seek 76 - 108 Mhz */
+
+       if (radio->band != band) {
+               retval = si470x_get_freq(radio, &freq);
+               if (retval)
+                       return retval;
+               retval = si470x_set_band(radio, band);
+               if (retval)
+                       return retval;
+               retval = si470x_set_freq(radio, freq);
+               if (retval)
+                       return retval;
+       }
+
        /* start seeking */
        radio->registers[POWERCFG] |= POWERCFG_SEEK;
-       if (wrap_around == 1)
+       if (seek->wrap_around)
                radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
        else
                radio->registers[POWERCFG] |= POWERCFG_SKMODE;
-       if (seek_upward == 1)
+       if (seek->seek_upward)
                radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
        else
                radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
@@ -329,26 +337,12 @@ static int si470x_set_seek(struct si470x_device *radio,
        if (retval < 0)
                return retval;
 
-       /* currently I2C driver only uses interrupt way to seek */
-       if (radio->stci_enabled) {
-               INIT_COMPLETION(radio->completion);
-
-               /* wait till seek operation has completed */
-               retval = wait_for_completion_timeout(&radio->completion,
-                               msecs_to_jiffies(seek_timeout));
-               if (!retval)
-                       timed_out = true;
-       } else {
-               /* wait till seek operation has completed */
-               timeout = jiffies + msecs_to_jiffies(seek_timeout);
-               do {
-                       retval = si470x_get_register(radio, STATUSRSSI);
-                       if (retval < 0)
-                               goto stop;
-                       timed_out = time_after(jiffies, timeout);
-               } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
-                               && (!timed_out));
-       }
+       /* wait till tune operation has completed */
+       INIT_COMPLETION(radio->completion);
+       retval = wait_for_completion_timeout(&radio->completion,
+                       msecs_to_jiffies(seek_timeout));
+       if (!retval)
+               timed_out = true;
 
        if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
                dev_warn(&radio->videodev.dev, "seek does not complete\n");
@@ -356,7 +350,6 @@ static int si470x_set_seek(struct si470x_device *radio,
                dev_warn(&radio->videodev.dev,
                        "seek failed / band limit reached\n");
 
-stop:
        /* stop seeking */
        radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
        retval = si470x_set_register(radio, POWERCFG);
@@ -391,8 +384,8 @@ int si470x_start(struct si470x_device *radio)
 
        /* sysconfig 2 */
        radio->registers[SYSCONFIG2] =
-               (0x3f  << 8) |                          /* SEEKTH */
-               ((band  << 6) & SYSCONFIG2_BAND)  |     /* BAND */
+               (0x1f  << 8) |                          /* SEEKTH */
+               ((radio->band << 6) & SYSCONFIG2_BAND) |/* BAND */
                ((space << 4) & SYSCONFIG2_SPACE) |     /* SPACE */
                15;                                     /* VOLUME (max) */
        retval = si470x_set_register(radio, SYSCONFIG2);
@@ -583,14 +576,16 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
                struct v4l2_tuner *tuner)
 {
        struct si470x_device *radio = video_drvdata(file);
-       int retval;
+       int retval = 0;
 
        if (tuner->index != 0)
                return -EINVAL;
 
-       retval = si470x_get_register(radio, STATUSRSSI);
-       if (retval < 0)
-               return retval;
+       if (!radio->status_rssi_auto_update) {
+               retval = si470x_get_register(radio, STATUSRSSI);
+               if (retval < 0)
+                       return retval;
+       }
 
        /* driver constants */
        strcpy(tuner->name, "FM");
@@ -599,25 +594,8 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
                            V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
                            V4L2_TUNER_CAP_HWSEEK_BOUNDED |
                            V4L2_TUNER_CAP_HWSEEK_WRAP;
-
-       /* range limits */
-       switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
-       /* 0: 87.5 - 108 MHz (USA, Europe, default) */
-       default:
-               tuner->rangelow  =  87.5 * FREQ_MUL;
-               tuner->rangehigh = 108   * FREQ_MUL;
-               break;
-       /* 1: 76   - 108 MHz (Japan wide band) */
-       case 1:
-               tuner->rangelow  =  76   * FREQ_MUL;
-               tuner->rangehigh = 108   * FREQ_MUL;
-               break;
-       /* 2: 76   -  90 MHz (Japan) */
-       case 2:
-               tuner->rangelow  =  76   * FREQ_MUL;
-               tuner->rangehigh =  90   * FREQ_MUL;
-               break;
-       };
+       tuner->rangelow  =  76 * FREQ_MUL;
+       tuner->rangehigh = 108 * FREQ_MUL;
 
        /* stereo indicator == stereo (instead of mono) */
        if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
@@ -700,10 +678,18 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv,
                struct v4l2_frequency *freq)
 {
        struct si470x_device *radio = video_drvdata(file);
+       int retval;
 
        if (freq->tuner != 0)
                return -EINVAL;
 
+       if (freq->frequency < bands[radio->band].rangelow ||
+           freq->frequency > bands[radio->band].rangehigh) {
+               /* Switch to band 1 which covers everything we support */
+               retval = si470x_set_band(radio, 1);
+               if (retval)
+                       return retval;
+       }
        return si470x_set_freq(radio, freq->frequency);
 }
 
@@ -719,7 +705,21 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
        if (seek->tuner != 0)
                return -EINVAL;
 
-       return si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
+       return si470x_set_seek(radio, seek);
+}
+
+/*
+ * si470x_vidioc_enum_freq_bands - enumerate supported bands
+ */
+static int si470x_vidioc_enum_freq_bands(struct file *file, void *priv,
+                                        struct v4l2_frequency_band *band)
+{
+       if (band->tuner != 0)
+               return -EINVAL;
+       if (band->index >= ARRAY_SIZE(bands))
+               return -EINVAL;
+       *band = bands[band->index];
+       return 0;
 }
 
 const struct v4l2_ctrl_ops si470x_ctrl_ops = {
@@ -736,6 +736,7 @@ static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
        .vidioc_g_frequency     = si470x_vidioc_g_frequency,
        .vidioc_s_frequency     = si470x_vidioc_s_frequency,
        .vidioc_s_hw_freq_seek  = si470x_vidioc_s_hw_freq_seek,
+       .vidioc_enum_freq_bands = si470x_vidioc_enum_freq_bands,
        .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
        .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
 };
index a80044c5874e4c3fafef10fafd7a6ae41d484eb9..643a6ff7c5d0e629e70112c0c4e67fe0459c580b 100644 (file)
@@ -350,7 +350,9 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
        }
 
        radio->client = client;
+       radio->band = 1; /* Default to 76 - 108 MHz */
        mutex_init(&radio->lock);
+       init_completion(&radio->completion);
 
        /* video device initialization */
        radio->videodev = si470x_viddev_template;
@@ -406,10 +408,6 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
        radio->rd_index = 0;
        init_waitqueue_head(&radio->read_queue);
 
-       /* mark Seek/Tune Complete Interrupt enabled */
-       radio->stci_enabled = true;
-       init_completion(&radio->completion);
-
        retval = request_threaded_irq(client->irq, NULL, si470x_i2c_interrupt,
                        IRQF_TRIGGER_FALLING, DRIVER_NAME, radio);
        if (retval) {
index f412f7ab270b63e0f39bc47aefdf58b3d3e5f82c..146be4263ea17d92ab072d13ae4d12fa610b9276 100644 (file)
@@ -143,7 +143,7 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
  * Software/Hardware Versions from Scratch Page
  **************************************************************************/
 #define RADIO_SW_VERSION_NOT_BOOTLOADABLE      6
-#define RADIO_SW_VERSION                       7
+#define RADIO_SW_VERSION                       1
 #define RADIO_HW_VERSION                       1
 
 
@@ -399,12 +399,19 @@ static void si470x_int_in_callback(struct urb *urb)
                }
        }
 
-       if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+       /* Sometimes the device returns len 0 packets */
+       if (urb->actual_length != RDS_REPORT_SIZE)
                goto resubmit;
 
-       if (urb->actual_length > 0) {
+       radio->registers[STATUSRSSI] =
+               get_unaligned_be16(&radio->int_in_buffer[1]);
+
+       if (radio->registers[STATUSRSSI] & STATUSRSSI_STC)
+               complete(&radio->completion);
+
+       if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS)) {
                /* Update RDS registers with URB data */
-               for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
+               for (regnr = 1; regnr < RDS_REGISTER_NUM; regnr++)
                        radio->registers[STATUSRSSI + regnr] =
                            get_unaligned_be16(&radio->int_in_buffer[
                                regnr * RADIO_REGISTER_SIZE + 1]);
@@ -480,6 +487,7 @@ resubmit:
                        radio->int_in_running = 0;
                }
        }
+       radio->status_rssi_auto_update = radio->int_in_running;
 }
 
 
@@ -534,13 +542,6 @@ static int si470x_start_usb(struct si470x_device *radio)
 {
        int retval;
 
-       /* start radio */
-       retval = si470x_start(radio);
-       if (retval < 0)
-               return retval;
-
-       v4l2_ctrl_handler_setup(&radio->hdl);
-
        /* initialize interrupt urb */
        usb_fill_int_urb(radio->int_in_urb, radio->usbdev,
                        usb_rcvintpipe(radio->usbdev,
@@ -560,6 +561,15 @@ static int si470x_start_usb(struct si470x_device *radio)
                                "submitting int urb failed (%d)\n", retval);
                radio->int_in_running = 0;
        }
+       radio->status_rssi_auto_update = radio->int_in_running;
+
+       /* start radio */
+       retval = si470x_start(radio);
+       if (retval < 0)
+               return retval;
+
+       v4l2_ctrl_handler_setup(&radio->hdl);
+
        return retval;
 }
 
@@ -587,7 +597,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
        }
        radio->usbdev = interface_to_usbdev(intf);
        radio->intf = intf;
+       radio->band = 1; /* Default to 76 - 108 MHz */
        mutex_init(&radio->lock);
+       init_completion(&radio->completion);
 
        iface_desc = intf->cur_altsetting;
 
@@ -698,9 +710,6 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
                        "linux-media@vger.kernel.org\n");
        }
 
-       /* set initial frequency */
-       si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
-
        /* set led to connect state */
        si470x_set_led_state(radio, BLINK_GREEN_LED);
 
@@ -723,6 +732,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
        if (retval < 0)
                goto err_all;
 
+       /* set initial frequency */
+       si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
+
        /* register video device */
        retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO,
                        radio_nr);
@@ -781,11 +793,16 @@ static int si470x_usb_driver_suspend(struct usb_interface *intf,
 static int si470x_usb_driver_resume(struct usb_interface *intf)
 {
        struct si470x_device *radio = usb_get_intfdata(intf);
+       int ret;
 
        dev_info(&intf->dev, "resuming now...\n");
 
        /* start radio */
-       return si470x_start_usb(radio);
+       ret = si470x_start_usb(radio);
+       if (ret == 0)
+               v4l2_ctrl_handler_setup(&radio->hdl);
+
+       return ret;
 }
 
 
index 4921cab8e0fa084b4add96a348515749cde18de8..2f089b4252dfe2f9f10956acb267392a38373caa 100644 (file)
@@ -87,7 +87,7 @@
 
 #define SYSCONFIG2             5       /* System Configuration 2 */
 #define SYSCONFIG2_SEEKTH      0xff00  /* bits 15..08: RSSI Seek Threshold */
-#define SYSCONFIG2_BAND                0x0080  /* bits 07..06: Band Select */
+#define SYSCONFIG2_BAND                0x00c0  /* bits 07..06: Band Select */
 #define SYSCONFIG2_SPACE       0x0030  /* bits 05..04: Channel Spacing */
 #define SYSCONFIG2_VOLUME      0x000f  /* bits 03..00: Volume */
 
@@ -147,6 +147,7 @@ struct si470x_device {
        struct v4l2_device v4l2_dev;
        struct video_device videodev;
        struct v4l2_ctrl_handler hdl;
+       int band;
 
        /* Silabs internal registers (0..15) */
        unsigned short registers[RADIO_REGISTER_NUM];
@@ -160,7 +161,7 @@ struct si470x_device {
        unsigned int wr_index;
 
        struct completion completion;
-       bool stci_enabled;              /* Seek/Tune Complete Interrupt */
+       bool status_rssi_auto_update;   /* Does RSSI get updated automatic? */
 
 #if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
        /* reference to USB and video device */
@@ -189,7 +190,7 @@ struct si470x_device {
  * Firmware Versions
  **************************************************************************/
 
-#define RADIO_FW_VERSION       15
+#define RADIO_FW_VERSION       12
 
 
 
index 908ef70430e94caebe56a8d361cfbbb48dae30ff..5180390be7ab0915ffa24189269ce4707e1e3baf 100644 (file)
@@ -259,6 +259,17 @@ config IR_WINBOND_CIR
           To compile this driver as a module, choose M here: the module will
           be called winbond_cir.
 
+config IR_IGUANA
+       tristate "IguanaWorks USB IR Transceiver"
+       depends on RC_CORE
+       select USB
+       ---help---
+          Say Y here if you want to use the IgaunaWorks USB IR Transceiver.
+          Both infrared receive and send are supported.
+
+          To compile this driver as a module, choose M here: the module will
+          be called iguanair.
+
 config RC_LOOPBACK
        tristate "Remote Control Loopback Driver"
        depends on RC_CORE
index 29f364f88a94d0e8c492829c2a1fd280d1765704..f871d1986c21901011477f61f60374d032c28c14 100644 (file)
@@ -27,3 +27,4 @@ obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
 obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
 obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
 obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
+obj-$(CONFIG_IR_IGUANA) += iguanair.o
index 7be377fc1be8a02fd9ac07140de7252ad299fbe2..8fa72e2dacb1cf709abded7d041fd35d0e2f456e 100644 (file)
@@ -147,7 +147,8 @@ static bool mouse = true;
 module_param(mouse, bool, 0444);
 MODULE_PARM_DESC(mouse, "Enable mouse device, default = yes");
 
-#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+#define dbginfo(dev, format, arg...) \
+       do { if (debug) dev_info(dev , format , ## arg); } while (0)
 #undef err
 #define err(format, arg...) printk(KERN_ERR format , ## arg)
 
@@ -191,17 +192,41 @@ static const char *get_medion_keymap(struct usb_interface *interface)
        return RC_MAP_MEDION_X10;
 }
 
-static const struct ati_receiver_type type_ati         = { .default_keymap = RC_MAP_ATI_X10 };
-static const struct ati_receiver_type type_medion      = { .get_default_keymap = get_medion_keymap };
-static const struct ati_receiver_type type_firefly     = { .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY };
+static const struct ati_receiver_type type_ati         = {
+       .default_keymap = RC_MAP_ATI_X10
+};
+static const struct ati_receiver_type type_medion      = {
+       .get_default_keymap = get_medion_keymap
+};
+static const struct ati_receiver_type type_firefly     = {
+       .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY
+};
 
 static struct usb_device_id ati_remote_table[] = {
-       { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID),     .driver_info = (unsigned long)&type_ati },
-       { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID),    .driver_info = (unsigned long)&type_ati },
-       { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID),      .driver_info = (unsigned long)&type_ati },
-       { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID),   .driver_info = (unsigned long)&type_ati },
-       { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID),   .driver_info = (unsigned long)&type_medion },
-       { USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID),  .driver_info = (unsigned long)&type_firefly },
+       {
+               USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID),
+               .driver_info = (unsigned long)&type_ati
+       },
+       {
+               USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID),
+               .driver_info = (unsigned long)&type_ati
+       },
+       {
+               USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID),
+               .driver_info = (unsigned long)&type_ati
+       },
+       {
+               USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID),
+               .driver_info = (unsigned long)&type_ati
+       },
+       {
+               USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID),
+               .driver_info = (unsigned long)&type_medion
+       },
+       {
+               USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID),
+               .driver_info = (unsigned long)&type_firefly
+       },
        {}      /* Terminating entry */
 };
 
@@ -296,25 +321,8 @@ static const struct {
        {KIND_END, 0x00, EV_MAX + 1, 0, 0}
 };
 
-/* Local function prototypes */
-static int ati_remote_sendpacket       (struct ati_remote *ati_remote, u16 cmd, unsigned char *data);
-static void ati_remote_irq_out         (struct urb *urb);
-static void ati_remote_irq_in          (struct urb *urb);
-static void ati_remote_input_report    (struct urb *urb);
-static int ati_remote_initialize       (struct ati_remote *ati_remote);
-static int ati_remote_probe            (struct usb_interface *interface, const struct usb_device_id *id);
-static void ati_remote_disconnect      (struct usb_interface *interface);
-
-/* usb specific object to register with the usb subsystem */
-static struct usb_driver ati_remote_driver = {
-       .name         = "ati_remote",
-       .probe        = ati_remote_probe,
-       .disconnect   = ati_remote_disconnect,
-       .id_table     = ati_remote_table,
-};
-
 /*
- *     ati_remote_dump_input
+ * ati_remote_dump_input
  */
 static void ati_remote_dump(struct device *dev, unsigned char *data,
                            unsigned int len)
@@ -326,12 +334,14 @@ static void ati_remote_dump(struct device *dev, unsigned char *data,
                dev_warn(dev, "Weird key %02x %02x %02x %02x\n",
                     data[0], data[1], data[2], data[3]);
        else
-               dev_warn(dev, "Weird data, len=%d %02x %02x %02x %02x %02x %02x ...\n",
-                    len, data[0], data[1], data[2], data[3], data[4], data[5]);
+               dev_warn(dev,
+                       "Weird data, len=%d %02x %02x %02x %02x %02x %02x ...\n",
+                       len, data[0], data[1], data[2], data[3], data[4],
+                       data[5]);
 }
 
 /*
- *     ati_remote_open
+ * ati_remote_open
  */
 static int ati_remote_open(struct ati_remote *ati_remote)
 {
@@ -355,7 +365,7 @@ out:        mutex_unlock(&ati_remote->open_mutex);
 }
 
 /*
- *     ati_remote_close
+ * ati_remote_close
  */
 static void ati_remote_close(struct ati_remote *ati_remote)
 {
@@ -390,7 +400,7 @@ static void ati_remote_rc_close(struct rc_dev *rdev)
 }
 
 /*
- *             ati_remote_irq_out
+ * ati_remote_irq_out
  */
 static void ati_remote_irq_out(struct urb *urb)
 {
@@ -408,11 +418,12 @@ static void ati_remote_irq_out(struct urb *urb)
 }
 
 /*
- *     ati_remote_sendpacket
+ * ati_remote_sendpacket
  *
- *     Used to send device initialization strings
+ * Used to send device initialization strings
  */
-static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data)
+static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd,
+       unsigned char *data)
 {
        int retval = 0;
 
@@ -441,7 +452,7 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigne
 }
 
 /*
- *     ati_remote_compute_accel
+ * ati_remote_compute_accel
  *
  * Implements acceleration curve for directional control pad
  * If elapsed time since last event is > 1/4 second, user "stopped",
@@ -478,7 +489,7 @@ static int ati_remote_compute_accel(struct ati_remote *ati_remote)
 }
 
 /*
- *     ati_remote_report_input
+ * ati_remote_report_input
  */
 static void ati_remote_input_report(struct urb *urb)
 {
@@ -518,7 +529,8 @@ static void ati_remote_input_report(struct urb *urb)
        remote_num = (data[3] >> 4) & 0x0f;
        if (channel_mask & (1 << (remote_num + 1))) {
                dbginfo(&ati_remote->interface->dev,
-                       "Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n",
+                       "Masked input from channel 0x%02x: data %02x,%02x, "
+                       "mask= 0x%02lx\n",
                        remote_num, data[1], data[2], channel_mask);
                return;
        }
@@ -546,7 +558,9 @@ static void ati_remote_input_report(struct urb *urb)
                if (wheel_keycode == KEY_RESERVED) {
                        /* scrollwheel was not mapped, assume mouse */
 
-                       /* Look up event code index in the mouse translation table. */
+                       /* Look up event code index in the mouse translation
+                        * table.
+                        */
                        for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
                                if (scancode == ati_remote_tbl[i].data) {
                                        index = i;
@@ -630,9 +644,9 @@ static void ati_remote_input_report(struct urb *urb)
        } else {
 
                /*
-                * Other event kinds are from the directional control pad, and have an
-                * acceleration factor applied to them.  Without this acceleration, the
-                * control pad is mostly unusable.
+                * Other event kinds are from the directional control pad, and
+                * have an acceleration factor applied to them.  Without this
+                * acceleration, the control pad is mostly unusable.
                 */
                acc = ati_remote_compute_accel(ati_remote);
 
@@ -659,7 +673,8 @@ static void ati_remote_input_report(struct urb *urb)
                        input_report_rel(dev, REL_Y, acc);
                        break;
                default:
-                       dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n",
+                       dev_dbg(&ati_remote->interface->dev,
+                               "ati_remote kind=%d\n",
                                ati_remote_tbl[index].kind);
                }
                input_sync(dev);
@@ -670,7 +685,7 @@ static void ati_remote_input_report(struct urb *urb)
 }
 
 /*
- *     ati_remote_irq_in
+ * ati_remote_irq_in
  */
 static void ati_remote_irq_in(struct urb *urb)
 {
@@ -684,22 +699,25 @@ static void ati_remote_irq_in(struct urb *urb)
        case -ECONNRESET:       /* unlink */
        case -ENOENT:
        case -ESHUTDOWN:
-               dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n",
+               dev_dbg(&ati_remote->interface->dev,
+                       "%s: urb error status, unlink?\n",
                        __func__);
                return;
        default:                /* error */
-               dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n",
+               dev_dbg(&ati_remote->interface->dev,
+                       "%s: Nonzero urb status %d\n",
                        __func__, urb->status);
        }
 
        retval = usb_submit_urb(urb, GFP_ATOMIC);
        if (retval)
-               dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n",
+               dev_err(&ati_remote->interface->dev,
+                       "%s: usb_submit_urb()=%d\n",
                        __func__, retval);
 }
 
 /*
- *     ati_remote_alloc_buffers
+ * ati_remote_alloc_buffers
  */
 static int ati_remote_alloc_buffers(struct usb_device *udev,
                                    struct ati_remote *ati_remote)
@@ -726,7 +744,7 @@ static int ati_remote_alloc_buffers(struct usb_device *udev,
 }
 
 /*
- *     ati_remote_free_buffers
+ * ati_remote_free_buffers
  */
 static void ati_remote_free_buffers(struct ati_remote *ati_remote)
 {
@@ -825,9 +843,10 @@ static int ati_remote_initialize(struct ati_remote *ati_remote)
 }
 
 /*
- *     ati_remote_probe
+ * ati_remote_probe
  */
-static int ati_remote_probe(struct usb_interface *interface, const struct usb_device_id *id)
+static int ati_remote_probe(struct usb_interface *interface,
+       const struct usb_device_id *id)
 {
        struct usb_device *udev = interface_to_usbdev(interface);
        struct usb_host_interface *iface_host = interface->cur_altsetting;
@@ -949,7 +968,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
 }
 
 /*
- *     ati_remote_disconnect
+ * ati_remote_disconnect
  */
 static void ati_remote_disconnect(struct usb_interface *interface)
 {
@@ -971,6 +990,14 @@ static void ati_remote_disconnect(struct usb_interface *interface)
        kfree(ati_remote);
 }
 
+/* usb specific object to register with the usb subsystem */
+static struct usb_driver ati_remote_driver = {
+       .name         = "ati_remote",
+       .probe        = ati_remote_probe,
+       .disconnect   = ati_remote_disconnect,
+       .id_table     = ati_remote_table,
+};
+
 module_usb_driver(ati_remote_driver);
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
new file mode 100644 (file)
index 0000000..5e2eaf8
--- /dev/null
@@ -0,0 +1,639 @@
+/*
+ * IguanaWorks USB IR Transceiver support
+ *
+ * Copyright (C) 2012 Sean Young <sean@mess.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; 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/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <media/rc-core.h>
+
+#define DRIVER_NAME "iguanair"
+
+struct iguanair {
+       struct rc_dev *rc;
+
+       struct device *dev;
+       struct usb_device *udev;
+
+       int pipe_in, pipe_out;
+       uint8_t bufsize;
+       uint8_t version[2];
+
+       struct mutex lock;
+
+       /* receiver support */
+       bool receiver_on;
+       dma_addr_t dma_in;
+       uint8_t *buf_in;
+       struct urb *urb_in;
+       struct completion completion;
+
+       /* transmit support */
+       bool tx_overflow;
+       uint32_t carrier;
+       uint8_t cycle_overhead;
+       uint8_t channels;
+       uint8_t busy4;
+       uint8_t busy7;
+
+       char name[64];
+       char phys[64];
+};
+
+#define CMD_GET_VERSION                0x01
+#define CMD_GET_BUFSIZE                0x11
+#define CMD_GET_FEATURES       0x10
+#define CMD_SEND               0x15
+#define CMD_EXECUTE            0x1f
+#define CMD_RX_OVERFLOW                0x31
+#define CMD_TX_OVERFLOW                0x32
+#define CMD_RECEIVER_ON                0x12
+#define CMD_RECEIVER_OFF       0x14
+
+#define DIR_IN                 0xdc
+#define DIR_OUT                        0xcd
+
+#define MAX_PACKET_SIZE                8u
+#define TIMEOUT                        1000
+
+struct packet {
+       uint16_t start;
+       uint8_t direction;
+       uint8_t cmd;
+};
+
+struct response_packet {
+       struct packet header;
+       uint8_t data[4];
+};
+
+struct send_packet {
+       struct packet header;
+       uint8_t length;
+       uint8_t channels;
+       uint8_t busy7;
+       uint8_t busy4;
+       uint8_t payload[0];
+};
+
+static void process_ir_data(struct iguanair *ir, unsigned len)
+{
+       if (len >= 4 && ir->buf_in[0] == 0 && ir->buf_in[1] == 0) {
+               switch (ir->buf_in[3]) {
+               case CMD_TX_OVERFLOW:
+                       ir->tx_overflow = true;
+               case CMD_RECEIVER_OFF:
+               case CMD_RECEIVER_ON:
+               case CMD_SEND:
+                       complete(&ir->completion);
+                       break;
+               case CMD_RX_OVERFLOW:
+                       dev_warn(ir->dev, "receive overflow\n");
+                       break;
+               default:
+                       dev_warn(ir->dev, "control code %02x received\n",
+                                                       ir->buf_in[3]);
+                       break;
+               }
+       } else if (len >= 7) {
+               DEFINE_IR_RAW_EVENT(rawir);
+               unsigned i;
+
+               init_ir_raw_event(&rawir);
+
+               for (i = 0; i < 7; i++) {
+                       if (ir->buf_in[i] == 0x80) {
+                               rawir.pulse = false;
+                               rawir.duration = US_TO_NS(21845);
+                       } else {
+                               rawir.pulse = (ir->buf_in[i] & 0x80) == 0;
+                               rawir.duration = ((ir->buf_in[i] & 0x7f) + 1) *
+                                                                        21330;
+                       }
+
+                       ir_raw_event_store_with_filter(ir->rc, &rawir);
+               }
+
+               ir_raw_event_handle(ir->rc);
+       }
+}
+
+static void iguanair_rx(struct urb *urb)
+{
+       struct iguanair *ir;
+
+       if (!urb)
+               return;
+
+       ir = urb->context;
+       if (!ir) {
+               usb_unlink_urb(urb);
+               return;
+       }
+
+       switch (urb->status) {
+       case 0:
+               process_ir_data(ir, urb->actual_length);
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               usb_unlink_urb(urb);
+               return;
+       case -EPIPE:
+       default:
+               dev_dbg(ir->dev, "Error: urb status = %d\n", urb->status);
+               break;
+       }
+
+       usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int iguanair_send(struct iguanair *ir, void *data, unsigned size,
+                       struct response_packet *response, unsigned *res_len)
+{
+       unsigned offset, len;
+       int rc, transferred;
+
+       for (offset = 0; offset < size; offset += MAX_PACKET_SIZE) {
+               len = min(size - offset, MAX_PACKET_SIZE);
+
+               if (ir->tx_overflow)
+                       return -EOVERFLOW;
+
+               rc = usb_interrupt_msg(ir->udev, ir->pipe_out, data + offset,
+                                               len, &transferred, TIMEOUT);
+               if (rc)
+                       return rc;
+
+               if (transferred != len)
+                       return -EIO;
+       }
+
+       if (response) {
+               rc = usb_interrupt_msg(ir->udev, ir->pipe_in, response,
+                                       sizeof(*response), res_len, TIMEOUT);
+       }
+
+       return rc;
+}
+
+static int iguanair_get_features(struct iguanair *ir)
+{
+       struct packet packet;
+       struct response_packet response;
+       int rc, len;
+
+       packet.start = 0;
+       packet.direction = DIR_OUT;
+       packet.cmd = CMD_GET_VERSION;
+
+       rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+       if (rc) {
+               dev_info(ir->dev, "failed to get version\n");
+               goto out;
+       }
+
+       if (len != 6) {
+               dev_info(ir->dev, "failed to get version\n");
+               rc = -EIO;
+               goto out;
+       }
+
+       ir->version[0] = response.data[0];
+       ir->version[1] = response.data[1];
+       ir->bufsize = 150;
+       ir->cycle_overhead = 65;
+
+       packet.cmd = CMD_GET_BUFSIZE;
+
+       rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+       if (rc) {
+               dev_info(ir->dev, "failed to get buffer size\n");
+               goto out;
+       }
+
+       if (len != 5) {
+               dev_info(ir->dev, "failed to get buffer size\n");
+               rc = -EIO;
+               goto out;
+       }
+
+       ir->bufsize = response.data[0];
+
+       if (ir->version[0] == 0 || ir->version[1] == 0)
+               goto out;
+
+       packet.cmd = CMD_GET_FEATURES;
+
+       rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+       if (rc) {
+               dev_info(ir->dev, "failed to get features\n");
+               goto out;
+       }
+
+       if (len < 5) {
+               dev_info(ir->dev, "failed to get features\n");
+               rc = -EIO;
+               goto out;
+       }
+
+       if (len > 5 && ir->version[0] >= 4)
+               ir->cycle_overhead = response.data[1];
+
+out:
+       return rc;
+}
+
+static int iguanair_receiver(struct iguanair *ir, bool enable)
+{
+       struct packet packet = { 0, DIR_OUT, enable ?
+                               CMD_RECEIVER_ON : CMD_RECEIVER_OFF };
+       int rc;
+
+       INIT_COMPLETION(ir->completion);
+
+       rc = iguanair_send(ir, &packet, sizeof(packet), NULL, NULL);
+       if (rc)
+               return rc;
+
+       wait_for_completion_timeout(&ir->completion, TIMEOUT);
+
+       return 0;
+}
+
+/*
+ * The iguana ir creates the carrier by busy spinning after each pulse or
+ * space. This is counted in CPU cycles, with the CPU running at 24MHz. It is
+ * broken down into 7-cycles and 4-cyles delays, with a preference for
+ * 4-cycle delays.
+ */
+static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
+{
+       struct iguanair *ir = dev->priv;
+
+       if (carrier < 25000 || carrier > 150000)
+               return -EINVAL;
+
+       mutex_lock(&ir->lock);
+
+       if (carrier != ir->carrier) {
+               uint32_t cycles, fours, sevens;
+
+               ir->carrier = carrier;
+
+               cycles = DIV_ROUND_CLOSEST(24000000, carrier * 2) -
+                                                       ir->cycle_overhead;
+
+               /*  make up the the remainer of 4-cycle blocks */
+               switch (cycles & 3) {
+               case 0:
+                       sevens = 0;
+                       break;
+               case 1:
+                       sevens = 3;
+                       break;
+               case 2:
+                       sevens = 2;
+                       break;
+               case 3:
+                       sevens = 1;
+                       break;
+               }
+
+               fours = (cycles - sevens * 7) / 4;
+
+               /* magic happens here */
+               ir->busy7 = (4 - sevens) * 2;
+               ir->busy4 = 110 - fours;
+       }
+
+       mutex_unlock(&ir->lock);
+
+       return carrier;
+}
+
+static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
+{
+       struct iguanair *ir = dev->priv;
+
+       if (mask > 15)
+               return 4;
+
+       mutex_lock(&ir->lock);
+       ir->channels = mask;
+       mutex_unlock(&ir->lock);
+
+       return 0;
+}
+
+static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
+{
+       struct iguanair *ir = dev->priv;
+       uint8_t space, *payload;
+       unsigned i, size, rc;
+       struct send_packet *packet;
+
+       mutex_lock(&ir->lock);
+
+       /* convert from us to carrier periods */
+       for (i = size = 0; i < count; i++) {
+               txbuf[i] = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
+               size += (txbuf[i] + 126) / 127;
+       }
+
+       packet = kmalloc(sizeof(*packet) + size, GFP_KERNEL);
+       if (!packet) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       if (size > ir->bufsize) {
+               rc = -E2BIG;
+               goto out;
+       }
+
+       packet->header.start = 0;
+       packet->header.direction = DIR_OUT;
+       packet->header.cmd = CMD_SEND;
+       packet->length = size;
+       packet->channels = ir->channels << 4;
+       packet->busy7 = ir->busy7;
+       packet->busy4 = ir->busy4;
+
+       space = 0;
+       payload = packet->payload;
+
+       for (i = 0; i < count; i++) {
+               unsigned periods = txbuf[i];
+
+               while (periods > 127) {
+                       *payload++ = 127 | space;
+                       periods -= 127;
+               }
+
+               *payload++ = periods | space;
+               space ^= 0x80;
+       }
+
+       if (ir->receiver_on) {
+               rc = iguanair_receiver(ir, false);
+               if (rc) {
+                       dev_warn(ir->dev, "disable receiver before transmit failed\n");
+                       goto out;
+               }
+       }
+
+       ir->tx_overflow = false;
+
+       INIT_COMPLETION(ir->completion);
+
+       rc = iguanair_send(ir, packet, size + 8, NULL, NULL);
+
+       if (rc == 0) {
+               wait_for_completion_timeout(&ir->completion, TIMEOUT);
+               if (ir->tx_overflow)
+                       rc = -EOVERFLOW;
+       }
+
+       ir->tx_overflow = false;
+
+       if (ir->receiver_on) {
+               if (iguanair_receiver(ir, true))
+                       dev_warn(ir->dev, "re-enable receiver after transmit failed\n");
+       }
+
+out:
+       mutex_unlock(&ir->lock);
+       kfree(packet);
+
+       return rc;
+}
+
+static int iguanair_open(struct rc_dev *rdev)
+{
+       struct iguanair *ir = rdev->priv;
+       int rc;
+
+       mutex_lock(&ir->lock);
+
+       usb_submit_urb(ir->urb_in, GFP_KERNEL);
+
+       BUG_ON(ir->receiver_on);
+
+       rc = iguanair_receiver(ir, true);
+       if (rc == 0)
+               ir->receiver_on = true;
+
+       mutex_unlock(&ir->lock);
+
+       return rc;
+}
+
+static void iguanair_close(struct rc_dev *rdev)
+{
+       struct iguanair *ir = rdev->priv;
+       int rc;
+
+       mutex_lock(&ir->lock);
+
+       rc = iguanair_receiver(ir, false);
+       ir->receiver_on = false;
+       if (rc)
+               dev_warn(ir->dev, "failed to disable receiver: %d\n", rc);
+
+       usb_kill_urb(ir->urb_in);
+
+       mutex_unlock(&ir->lock);
+}
+
+static int __devinit iguanair_probe(struct usb_interface *intf,
+                                               const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct iguanair *ir;
+       struct rc_dev *rc;
+       int ret;
+       struct usb_host_interface *idesc;
+
+       ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+       rc = rc_allocate_device();
+       if (!ir || !rc) {
+               ret = ENOMEM;
+               goto out;
+       }
+
+       ir->buf_in = usb_alloc_coherent(udev, MAX_PACKET_SIZE, GFP_ATOMIC,
+                                                               &ir->dma_in);
+       ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+
+       if (!ir->buf_in || !ir->urb_in) {
+               ret = ENOMEM;
+               goto out;
+       }
+
+       idesc = intf->altsetting;
+
+       if (idesc->desc.bNumEndpoints < 2) {
+               ret = -ENODEV;
+               goto out;
+       }
+
+       ir->rc = rc;
+       ir->dev = &intf->dev;
+       ir->udev = udev;
+       ir->pipe_in = usb_rcvintpipe(udev,
+                               idesc->endpoint[0].desc.bEndpointAddress);
+       ir->pipe_out = usb_sndintpipe(udev,
+                               idesc->endpoint[1].desc.bEndpointAddress);
+       mutex_init(&ir->lock);
+       init_completion(&ir->completion);
+
+       ret = iguanair_get_features(ir);
+       if (ret) {
+               dev_warn(&intf->dev, "failed to get device features");
+               goto out;
+       }
+
+       usb_fill_int_urb(ir->urb_in, ir->udev, ir->pipe_in, ir->buf_in,
+               MAX_PACKET_SIZE, iguanair_rx, ir,
+               idesc->endpoint[0].desc.bInterval);
+       ir->urb_in->transfer_dma = ir->dma_in;
+       ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+       snprintf(ir->name, sizeof(ir->name),
+               "IguanaWorks USB IR Transceiver version %d.%d",
+               ir->version[0], ir->version[1]);
+
+       usb_make_path(ir->udev, ir->phys, sizeof(ir->phys));
+
+       rc->input_name = ir->name;
+       rc->input_phys = ir->phys;
+       usb_to_input_id(ir->udev, &rc->input_id);
+       rc->dev.parent = &intf->dev;
+       rc->driver_type = RC_DRIVER_IR_RAW;
+       rc->allowed_protos = RC_TYPE_ALL;
+       rc->priv = ir;
+       rc->open = iguanair_open;
+       rc->close = iguanair_close;
+       rc->s_tx_mask = iguanair_set_tx_mask;
+       rc->s_tx_carrier = iguanair_set_tx_carrier;
+       rc->tx_ir = iguanair_tx;
+       rc->driver_name = DRIVER_NAME;
+       rc->map_name = RC_MAP_EMPTY;
+
+       iguanair_set_tx_carrier(rc, 38000);
+
+       ret = rc_register_device(rc);
+       if (ret < 0) {
+               dev_err(&intf->dev, "failed to register rc device %d", ret);
+               goto out;
+       }
+
+       usb_set_intfdata(intf, ir);
+
+       dev_info(&intf->dev, "Registered %s", ir->name);
+
+       return 0;
+out:
+       if (ir) {
+               usb_free_urb(ir->urb_in);
+               usb_free_coherent(udev, MAX_PACKET_SIZE, ir->buf_in,
+                                                               ir->dma_in);
+       }
+       rc_free_device(rc);
+       kfree(ir);
+       return ret;
+}
+
+static void __devexit iguanair_disconnect(struct usb_interface *intf)
+{
+       struct iguanair *ir = usb_get_intfdata(intf);
+
+       usb_set_intfdata(intf, NULL);
+
+       usb_kill_urb(ir->urb_in);
+       usb_free_urb(ir->urb_in);
+       usb_free_coherent(ir->udev, MAX_PACKET_SIZE, ir->buf_in, ir->dma_in);
+       rc_unregister_device(ir->rc);
+       kfree(ir);
+}
+
+static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct iguanair *ir = usb_get_intfdata(intf);
+       int rc = 0;
+
+       mutex_lock(&ir->lock);
+
+       if (ir->receiver_on) {
+               rc = iguanair_receiver(ir, false);
+               if (rc)
+                       dev_warn(ir->dev, "failed to disable receiver for suspend\n");
+       }
+
+       mutex_unlock(&ir->lock);
+
+       return rc;
+}
+
+static int iguanair_resume(struct usb_interface *intf)
+{
+       struct iguanair *ir = usb_get_intfdata(intf);
+       int rc = 0;
+
+       mutex_lock(&ir->lock);
+
+       if (ir->receiver_on) {
+               rc = iguanair_receiver(ir, true);
+               if (rc)
+                       dev_warn(ir->dev, "failed to enable receiver after resume\n");
+       }
+
+       mutex_unlock(&ir->lock);
+
+       return rc;
+}
+
+static const struct usb_device_id iguanair_table[] = {
+       { USB_DEVICE(0x1781, 0x0938) },
+       { }
+};
+
+static struct usb_driver iguanair_driver = {
+       .name = DRIVER_NAME,
+       .probe = iguanair_probe,
+       .disconnect = __devexit_p(iguanair_disconnect),
+       .suspend = iguanair_suspend,
+       .resume = iguanair_resume,
+       .reset_resume = iguanair_resume,
+       .id_table = iguanair_table
+};
+
+module_usb_driver(iguanair_driver);
+
+MODULE_DESCRIPTION("IguanaWorks USB IR Transceiver");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, iguanair_table);
+
index 84e06d3aa696bafe0f3360e337293a27332a6d27..f38d9a8c6880168a4c6256bdb227218c70123ed9 100644 (file)
@@ -199,6 +199,7 @@ static bool debug;
 #define VENDOR_REALTEK         0x0bda
 #define VENDOR_TIVO            0x105a
 #define VENDOR_CONEXANT                0x0572
+#define VENDOR_TWISTEDMELON    0x2596
 
 enum mceusb_model_type {
        MCE_GEN2 = 0,           /* Most boards */
@@ -391,6 +392,12 @@ static struct usb_device_id mceusb_dev_table[] = {
        /* Conexant Hybrid TV RDU253S Polaris */
        { USB_DEVICE(VENDOR_CONEXANT, 0x58a5),
          .driver_info = CX_HYBRID_TV },
+       /* Twisted Melon Inc. - Manta Mini Receiver */
+       { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8008) },
+       /* Twisted Melon Inc. - Manta Pico Receiver */
+       { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8016) },
+       /* Twisted Melon Inc. - Manta Transceiver */
+       { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8042) },
        /* Terminating entry */
        { }
 };
@@ -410,14 +417,12 @@ struct mceusb_dev {
        /* usb */
        struct usb_device *usbdev;
        struct urb *urb_in;
-       struct usb_endpoint_descriptor *usb_ep_in;
        struct usb_endpoint_descriptor *usb_ep_out;
 
        /* buffers and dma */
        unsigned char *buf_in;
        unsigned int len_in;
        dma_addr_t dma_in;
-       dma_addr_t dma_out;
 
        enum {
                CMD_HEADER = 0,
@@ -686,7 +691,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
                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)
+static void mce_async_callback(struct urb *urb)
 {
        struct mceusb_dev *ir;
        int len;
@@ -733,7 +738,7 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data,
                pipe = usb_sndintpipe(ir->usbdev,
                                      ir->usb_ep_out->bEndpointAddress);
                usb_fill_int_urb(async_urb, ir->usbdev, pipe,
-                       async_buf, size, (usb_complete_t)mce_async_callback,
+                       async_buf, size, mce_async_callback,
                        ir, ir->usb_ep_out->bInterval);
                memcpy(async_buf, data, size);
 
@@ -1031,7 +1036,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
        ir_raw_event_handle(ir->rc);
 }
 
-static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
+static void mceusb_dev_recv(struct urb *urb)
 {
        struct mceusb_dev *ir;
        int buf_len;
@@ -1331,7 +1336,6 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
        ir->model = model;
 
        /* Saving usb interface data for use by the transmitter routine */
-       ir->usb_ep_in = ep_in;
        ir->usb_ep_out = ep_out;
 
        if (dev->descriptor.iManufacturer
@@ -1349,8 +1353,8 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
                goto rc_dev_fail;
 
        /* wire up inbound data handler */
-       usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
-               maxp, (usb_complete_t) mceusb_dev_recv, ir, ep_in->bInterval);
+       usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp,
+                               mceusb_dev_recv, ir, ep_in->bInterval);
        ir->urb_in->transfer_dma = ir->dma_in;
        ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 
index 6e16b09c24a99e1883bf0f4ad987e552bf98aa4e..cabc19c105152937dab25fd0b04052f7d053a63f 100644 (file)
@@ -775,10 +775,11 @@ static ssize_t show_protocols(struct device *device,
        if (dev->driver_type == RC_DRIVER_SCANCODE) {
                enabled = dev->rc_map.rc_type;
                allowed = dev->allowed_protos;
-       } else {
+       } else if (dev->raw) {
                enabled = dev->raw->enabled_protocols;
                allowed = ir_raw_get_allowed_protocols();
-       }
+       } else
+               return -ENODEV;
 
        IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n",
                   (long long)allowed,
index 174bffacf1173fd41136adc99147de807a2cb8b3..45ecf8db1eaecadbc6debf310d0d5313dd2bc185 100644 (file)
 #include <media/v4l2-ioctl.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-chip-ident.h>
 #include <linux/mutex.h>
 
-#define DRIVER_NAME "adv7180"
-
 #define ADV7180_INPUT_CONTROL_REG                      0x00
 #define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM   0x00
 #define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10
 
 #define ADV7180_AUTODETECT_ENABLE_REG                  0x07
 #define ADV7180_AUTODETECT_DEFAULT                     0x7f
-
+/* Contrast */
 #define ADV7180_CON_REG                0x08    /*Unsigned */
-#define CON_REG_MIN            0
-#define CON_REG_DEF            128
-#define CON_REG_MAX            255
-
+#define ADV7180_CON_MIN                0
+#define ADV7180_CON_DEF                128
+#define ADV7180_CON_MAX                255
+/* Brightness*/
 #define ADV7180_BRI_REG                0x0a    /*Signed */
-#define BRI_REG_MIN            -128
-#define BRI_REG_DEF            0
-#define BRI_REG_MAX            127
-
+#define ADV7180_BRI_MIN                -128
+#define ADV7180_BRI_DEF                0
+#define ADV7180_BRI_MAX                127
+/* Hue */
 #define ADV7180_HUE_REG                0x0b    /*Signed, inverted */
-#define HUE_REG_MIN            -127
-#define HUE_REG_DEF            0
-#define HUE_REG_MAX            128
+#define ADV7180_HUE_MIN                -127
+#define ADV7180_HUE_DEF                0
+#define ADV7180_HUE_MAX                128
 
 #define ADV7180_ADI_CTRL_REG                           0x0e
 #define ADV7180_ADI_CTRL_IRQ_SPACE                     0x20
 #define ADV7180_ICONF1_ACTIVE_LOW      0x01
 #define ADV7180_ICONF1_PSYNC_ONLY      0x10
 #define ADV7180_ICONF1_ACTIVE_TO_CLR   0xC0
-
+/* Saturation */
 #define ADV7180_SD_SAT_CB_REG  0xe3    /*Unsigned */
 #define ADV7180_SD_SAT_CR_REG  0xe4    /*Unsigned */
-#define SAT_REG_MIN            0
-#define SAT_REG_DEF            128
-#define SAT_REG_MAX            255
+#define ADV7180_SAT_MIN                0
+#define ADV7180_SAT_DEF                128
+#define ADV7180_SAT_MAX                255
 
 #define ADV7180_IRQ1_LOCK      0x01
 #define ADV7180_IRQ1_UNLOCK    0x02
 #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND    0x4F
 
 struct adv7180_state {
+       struct v4l2_ctrl_handler ctrl_hdl;
        struct v4l2_subdev      sd;
        struct work_struct      work;
        struct mutex            mutex; /* mutual excl. when accessing chip */
        int                     irq;
        v4l2_std_id             curr_norm;
        bool                    autodetect;
-       s8                      brightness;
-       s16                     hue;
-       u8                      contrast;
-       u8                      saturation;
        u8                      input;
 };
+#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler,            \
+                                           struct adv7180_state,       \
+                                           ctrl_hdl)->sd)
 
 static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
 {
@@ -237,7 +236,7 @@ static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
        if (ret)
                return ret;
 
-       /*We cannot discriminate between LQFP and 40-pin LFCSP, so accept
+       /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
         * all inputs and let the card driver take care of validation
         */
        if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input)
@@ -316,117 +315,39 @@ out:
        return ret;
 }
 
-static int adv7180_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
-       switch (qc->id) {
-       case V4L2_CID_BRIGHTNESS:
-               return v4l2_ctrl_query_fill(qc, BRI_REG_MIN, BRI_REG_MAX,
-                                           1, BRI_REG_DEF);
-       case V4L2_CID_HUE:
-               return v4l2_ctrl_query_fill(qc, HUE_REG_MIN, HUE_REG_MAX,
-                                           1, HUE_REG_DEF);
-       case V4L2_CID_CONTRAST:
-               return v4l2_ctrl_query_fill(qc, CON_REG_MIN, CON_REG_MAX,
-                                           1, CON_REG_DEF);
-       case V4L2_CID_SATURATION:
-               return v4l2_ctrl_query_fill(qc, SAT_REG_MIN, SAT_REG_MAX,
-                                           1, SAT_REG_DEF);
-       default:
-               break;
-       }
-
-       return -EINVAL;
-}
-
-static int adv7180_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       struct adv7180_state *state = to_state(sd);
-       int ret = mutex_lock_interruptible(&state->mutex);
-       if (ret)
-               return ret;
-
-       switch (ctrl->id) {
-       case V4L2_CID_BRIGHTNESS:
-               ctrl->value = state->brightness;
-               break;
-       case V4L2_CID_HUE:
-               ctrl->value = state->hue;
-               break;
-       case V4L2_CID_CONTRAST:
-               ctrl->value = state->contrast;
-               break;
-       case V4L2_CID_SATURATION:
-               ctrl->value = state->saturation;
-               break;
-       default:
-               ret = -EINVAL;
-       }
-
-       mutex_unlock(&state->mutex);
-       return ret;
-}
-
-static int adv7180_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+       struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
        struct adv7180_state *state = to_state(sd);
        struct i2c_client *client = v4l2_get_subdevdata(sd);
        int ret = mutex_lock_interruptible(&state->mutex);
+       int val;
+
        if (ret)
                return ret;
-
+       val = ctrl->val;
        switch (ctrl->id) {
        case V4L2_CID_BRIGHTNESS:
-               if ((ctrl->value > BRI_REG_MAX)
-                   || (ctrl->value < BRI_REG_MIN)) {
-                       ret = -ERANGE;
-                       break;
-               }
-               state->brightness = ctrl->value;
-               ret = i2c_smbus_write_byte_data(client,
-                                               ADV7180_BRI_REG,
-                                               state->brightness);
+               ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val);
                break;
        case V4L2_CID_HUE:
-               if ((ctrl->value > HUE_REG_MAX)
-                   || (ctrl->value < HUE_REG_MIN)) {
-                       ret = -ERANGE;
-                       break;
-               }
-               state->hue = ctrl->value;
                /*Hue is inverted according to HSL chart */
-               ret = i2c_smbus_write_byte_data(client,
-                                               ADV7180_HUE_REG, -state->hue);
+               ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val);
                break;
        case V4L2_CID_CONTRAST:
-               if ((ctrl->value > CON_REG_MAX)
-                   || (ctrl->value < CON_REG_MIN)) {
-                       ret = -ERANGE;
-                       break;
-               }
-               state->contrast = ctrl->value;
-               ret = i2c_smbus_write_byte_data(client,
-                                               ADV7180_CON_REG,
-                                               state->contrast);
+               ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val);
                break;
        case V4L2_CID_SATURATION:
-               if ((ctrl->value > SAT_REG_MAX)
-                   || (ctrl->value < SAT_REG_MIN)) {
-                       ret = -ERANGE;
-                       break;
-               }
                /*
                 *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
                 *Let's not confuse the user, everybody understands saturation
                 */
-               state->saturation = ctrl->value;
-               ret = i2c_smbus_write_byte_data(client,
-                                               ADV7180_SD_SAT_CB_REG,
-                                               state->saturation);
+               ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG,
+                                               val);
                if (ret < 0)
                        break;
-               ret = i2c_smbus_write_byte_data(client,
-                                               ADV7180_SD_SAT_CR_REG,
-                                               state->saturation);
+               ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG,
+                                               val);
                break;
        default:
                ret = -EINVAL;
@@ -436,6 +357,42 @@ static int adv7180_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
        return ret;
 }
 
+static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
+       .s_ctrl = adv7180_s_ctrl,
+};
+
+static int adv7180_init_controls(struct adv7180_state *state)
+{
+       v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
+
+       v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
+                         V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN,
+                         ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF);
+       v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
+                         V4L2_CID_CONTRAST, ADV7180_CON_MIN,
+                         ADV7180_CON_MAX, 1, ADV7180_CON_DEF);
+       v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
+                         V4L2_CID_SATURATION, ADV7180_SAT_MIN,
+                         ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF);
+       v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
+                         V4L2_CID_HUE, ADV7180_HUE_MIN,
+                         ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
+       state->sd.ctrl_handler = &state->ctrl_hdl;
+       if (state->ctrl_hdl.error) {
+               int err = state->ctrl_hdl.error;
+
+               v4l2_ctrl_handler_free(&state->ctrl_hdl);
+               return err;
+       }
+       v4l2_ctrl_handler_setup(&state->ctrl_hdl);
+
+       return 0;
+}
+static void adv7180_exit_controls(struct adv7180_state *state)
+{
+       v4l2_ctrl_handler_free(&state->ctrl_hdl);
+}
+
 static const struct v4l2_subdev_video_ops adv7180_video_ops = {
        .querystd = adv7180_querystd,
        .g_input_status = adv7180_g_input_status,
@@ -445,9 +402,9 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = {
 static const struct v4l2_subdev_core_ops adv7180_core_ops = {
        .g_chip_ident = adv7180_g_chip_ident,
        .s_std = adv7180_s_std,
-       .queryctrl = adv7180_queryctrl,
-       .g_ctrl = adv7180_g_ctrl,
-       .s_ctrl = adv7180_s_ctrl,
+       .queryctrl = v4l2_subdev_queryctrl,
+       .g_ctrl = v4l2_subdev_g_ctrl,
+       .s_ctrl = v4l2_subdev_s_ctrl,
 };
 
 static const struct v4l2_subdev_ops adv7180_ops = {
@@ -539,7 +496,7 @@ static int init_device(struct i2c_client *client, struct adv7180_state *state)
 
        /* register for interrupts */
        if (state->irq > 0) {
-               ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME,
+               ret = request_irq(state->irq, adv7180_irq, 0, KBUILD_MODNAME,
                                  state);
                if (ret)
                        return ret;
@@ -580,31 +537,6 @@ static int init_device(struct i2c_client *client, struct adv7180_state *state)
                        return ret;
        }
 
-       /*Set default value for controls */
-       ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG,
-                                       state->brightness);
-       if (ret < 0)
-               return ret;
-
-       ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, state->hue);
-       if (ret < 0)
-               return ret;
-
-       ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG,
-                                       state->contrast);
-       if (ret < 0)
-               return ret;
-
-       ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG,
-                                       state->saturation);
-       if (ret < 0)
-               return ret;
-
-       ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG,
-                                       state->saturation);
-       if (ret < 0)
-               return ret;
-
        return 0;
 }
 
@@ -632,25 +564,26 @@ static __devinit int adv7180_probe(struct i2c_client *client,
        INIT_WORK(&state->work, adv7180_work);
        mutex_init(&state->mutex);
        state->autodetect = true;
-       state->brightness = BRI_REG_DEF;
-       state->hue = HUE_REG_DEF;
-       state->contrast = CON_REG_DEF;
-       state->saturation = SAT_REG_DEF;
        state->input = 0;
        sd = &state->sd;
        v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
 
-       ret = init_device(client, state);
-       if (0 != ret)
+       ret = adv7180_init_controls(state);
+       if (ret)
                goto err_unreg_subdev;
+       ret = init_device(client, state);
+       if (ret)
+               goto err_free_ctrl;
        return 0;
 
+err_free_ctrl:
+       adv7180_exit_controls(state);
 err_unreg_subdev:
        mutex_destroy(&state->mutex);
        v4l2_device_unregister_subdev(sd);
        kfree(state);
 err:
-       printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", ret);
+       printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret);
        return ret;
 }
 
@@ -678,7 +611,7 @@ static __devexit int adv7180_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id adv7180_id[] = {
-       {DRIVER_NAME, 0},
+       {KBUILD_MODNAME, 0},
        {},
 };
 
@@ -716,7 +649,7 @@ MODULE_DEVICE_TABLE(i2c, adv7180_id);
 static struct i2c_driver adv7180_driver = {
        .driver = {
                   .owner = THIS_MODULE,
-                  .name = DRIVER_NAME,
+                  .name = KBUILD_MODNAME,
                   },
        .probe = adv7180_probe,
        .remove = __devexit_p(adv7180_remove),
index 5f3a00c2c4f6685a47b644136fe7beab01b15533..38952faaffda3a44ed81d7d268bc7c98306d3b64 100644 (file)
@@ -345,7 +345,7 @@ static struct CARD {
        { 0x15401836, BTTV_BOARD_PV183,         "Provideo PV183-7" },
        { 0x15401837, BTTV_BOARD_PV183,         "Provideo PV183-8" },
        { 0x3116f200, BTTV_BOARD_TVT_TD3116,    "Tongwei Video Technology TD-3116" },
-
+       { 0x02280279, BTTV_BOARD_APOSONIC_WDVR, "Aposonic W-DVR" },
        { 0, -1, NULL }
 };
 
@@ -2818,6 +2818,14 @@ struct tvcard bttv_tvcards[] = {
                .pll            = PLL_28,
                .tuner_type     = TUNER_ABSENT,
        },
+       [BTTV_BOARD_APOSONIC_WDVR] = {
+               .name           = "Aposonic W-DVR",
+               .video_inputs   = 4,
+               .svhs           = NO_SVHS,
+               .muxsel         = MUXSEL(2, 3, 1, 0),
+               .tuner_type     = TUNER_ABSENT,
+       },
+
 };
 
 static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
index acfe2f3b92d9abdbb050a6586a7d9f4f310409e6..79a11240a5901b41e785b36f43913b19314ab955 100644 (file)
 #define BTTV_BOARD_GEOVISION_GV800S_SL    0x9e
 #define BTTV_BOARD_PV183                   0x9f
 #define BTTV_BOARD_TVT_TD3116             0xa0
-
+#define BTTV_BOARD_APOSONIC_WDVR           0xa1
 
 /* more card-specific defines */
 #define PT2254_L_CHANNEL 0x10
index 925f3a04e53c40daf32e28970a9e7df22397f81c..781feed406f72747bb510cb8e4f2eed70afadd77 100644 (file)
@@ -499,16 +499,12 @@ int cx231xx_i2c_register(struct cx231xx_i2c *bus)
 
        BUG_ON(!dev->cx231xx_send_usb_command);
 
-       memcpy(&bus->i2c_adap, &cx231xx_adap_template, sizeof(bus->i2c_adap));
-       memcpy(&bus->i2c_algo, &cx231xx_algo, sizeof(bus->i2c_algo));
-       memcpy(&bus->i2c_client, &cx231xx_client_template,
-              sizeof(bus->i2c_client));
-
+       bus->i2c_adap = cx231xx_adap_template;
+       bus->i2c_client = cx231xx_client_template;
        bus->i2c_adap.dev.parent = &dev->udev->dev;
 
        strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
 
-       bus->i2c_algo.data = bus;
        bus->i2c_adap.algo_data = bus;
        i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
        i2c_add_adapter(&bus->i2c_adap);
index e17447554a0d6eed2de8deb94360019e20114cea..a89d020de94836d9cdbac3795760ae2d355e67a6 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/types.h>
 #include <linux/ioctl.h>
 #include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
 #include <linux/workqueue.h>
 #include <linux/mutex.h>
 
@@ -481,7 +480,6 @@ struct cx231xx_i2c {
 
        /* i2c i/o */
        struct i2c_adapter i2c_adap;
-       struct i2c_algo_bit_data i2c_algo;
        struct i2c_client i2c_client;
        u32 i2c_rc;
 
index be1e21d8295c8b382c0289d3265d7038abb2f73a..4887314339cbfa9071db9a898e985fae2495a9c8 100644 (file)
@@ -316,19 +316,13 @@ int cx23885_i2c_register(struct cx23885_i2c *bus)
 
        dprintk(1, "%s(bus = %d)\n", __func__, bus->nr);
 
-       memcpy(&bus->i2c_adap, &cx23885_i2c_adap_template,
-              sizeof(bus->i2c_adap));
-       memcpy(&bus->i2c_algo, &cx23885_i2c_algo_template,
-              sizeof(bus->i2c_algo));
-       memcpy(&bus->i2c_client, &cx23885_i2c_client_template,
-              sizeof(bus->i2c_client));
-
+       bus->i2c_adap = cx23885_i2c_adap_template;
+       bus->i2c_client = cx23885_i2c_client_template;
        bus->i2c_adap.dev.parent = &dev->pci->dev;
 
        strlcpy(bus->i2c_adap.name, bus->dev->name,
                sizeof(bus->i2c_adap.name));
 
-       bus->i2c_algo.data = bus;
        bus->i2c_adap.algo_data = bus;
        i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
        i2c_add_adapter(&bus->i2c_adap);
index 13c37ec07ae7e250b54694aad7c821c670fd584f..5d560c747e09346d7f90b70b6eccb24a6c949ea1 100644 (file)
@@ -21,7 +21,6 @@
 
 #include <linux/pci.h>
 #include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
 #include <linux/kdev_t.h>
 #include <linux/slab.h>
 
@@ -247,7 +246,6 @@ struct cx23885_i2c {
 
        /* i2c i/o */
        struct i2c_adapter         i2c_adap;
-       struct i2c_algo_bit_data   i2c_algo;
        struct i2c_client          i2c_client;
        u32                        i2c_rc;
 
index 6311180f430c43e93c126b8e219c9198fa354e7b..9844549764c96a8c10e288513149baf60908c2c7 100644 (file)
@@ -305,18 +305,12 @@ int cx25821_i2c_register(struct cx25821_i2c *bus)
 
        dprintk(1, "%s(bus = %d)\n", __func__, bus->nr);
 
-       memcpy(&bus->i2c_adap, &cx25821_i2c_adap_template,
-              sizeof(bus->i2c_adap));
-       memcpy(&bus->i2c_algo, &cx25821_i2c_algo_template,
-              sizeof(bus->i2c_algo));
-       memcpy(&bus->i2c_client, &cx25821_i2c_client_template,
-              sizeof(bus->i2c_client));
-
+       bus->i2c_adap = cx25821_i2c_adap_template;
+       bus->i2c_client = cx25821_i2c_client_template;
        bus->i2c_adap.dev.parent = &dev->pci->dev;
 
        strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
 
-       bus->i2c_algo.data = bus;
        bus->i2c_adap.algo_data = bus;
        i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
        i2c_add_adapter(&bus->i2c_adap);
index 313fb20a0b47a01b1094e45d227d382a9f2ad296..6a92e5c70c2a95b10d16e57cfea87e2651f77069 100644 (file)
@@ -499,7 +499,7 @@ static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder,
        mutex_lock(&dev->lock);
 
        /* no support */
-       if (decoder < VDEC_A && decoder > VDEC_H) {
+       if (decoder < VDEC_A || decoder > VDEC_H) {
                mutex_unlock(&dev->lock);
                return;
        }
index 029f2934a6d88bccdb409becdfd17849b99dff3b..8a9c0c869412aa36d6fb0ce558755aa0e62711f1 100644 (file)
@@ -26,7 +26,6 @@
 
 #include <linux/pci.h>
 #include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/sched.h>
@@ -213,7 +212,6 @@ struct cx25821_i2c {
 
        /* i2c i/o */
        struct i2c_adapter i2c_adap;
-       struct i2c_algo_bit_data i2c_algo;
        struct i2c_client i2c_client;
        u32 i2c_rc;
 
index 9337b5605c906272a12c6c1acf6a1e7001366412..52c5ca68cb3d38941acd97c1e00c7ae5f3048ba2 100644 (file)
@@ -1,30 +1,34 @@
-config DISPLAY_DAVINCI_DM646X_EVM
-       tristate "DM646x EVM Video Display"
-       depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM
-       select VIDEOBUF_DMA_CONTIG
+config VIDEO_DAVINCI_VPIF_DISPLAY
+       tristate "DM646x/DA850/OMAPL138 EVM Video Display"
+       depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM)
+       select VIDEOBUF2_DMA_CONTIG
        select VIDEO_DAVINCI_VPIF
-       select VIDEO_ADV7343
-       select VIDEO_THS7303
+       select VIDEO_ADV7343 if VIDEO_HELPER_CHIPS_AUTO
+       select VIDEO_THS7303 if VIDEO_HELPER_CHIPS_AUTO
        help
-         Support for DM6467 based display device.
+         Enables Davinci VPIF module used for display devices.
+         This module is common for following DM6467/DA850/OMAPL138
+         based display devices.
 
          To compile this driver as a module, choose M here: the
          module will be called vpif_display.
 
-config CAPTURE_DAVINCI_DM646X_EVM
-       tristate "DM646x EVM Video Capture"
-       depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM
-       select VIDEOBUF_DMA_CONTIG
+config VIDEO_DAVINCI_VPIF_CAPTURE
+       tristate "DM646x/DA850/OMAPL138 EVM Video Capture"
+       depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM)
+       select VIDEOBUF2_DMA_CONTIG
        select VIDEO_DAVINCI_VPIF
        help
-         Support for DM6467 based capture device.
+         Enables Davinci VPIF module used for captur devices.
+         This module is common for following DM6467/DA850/OMAPL138
+         based capture devices.
 
          To compile this driver as a module, choose M here: the
          module will be called vpif_capture.
 
 config VIDEO_DAVINCI_VPIF
        tristate "DaVinci VPIF Driver"
-       depends on DISPLAY_DAVINCI_DM646X_EVM
+       depends on VIDEO_DAVINCI_VPIF_DISPLAY || VIDEO_DAVINCI_VPIF_CAPTURE
        help
          Support for DaVinci VPIF Driver.
 
index ae7dafb689ab9917678f16a81fb00072f337bff4..74ed92d09257c6ca828467202c817af627c64e74 100644 (file)
@@ -5,10 +5,10 @@
 # VPIF
 obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o
 
-#DM646x EVM Display driver
-obj-$(CONFIG_DISPLAY_DAVINCI_DM646X_EVM) += vpif_display.o
-#DM646x EVM Capture driver
-obj-$(CONFIG_CAPTURE_DAVINCI_DM646X_EVM) += vpif_capture.o
+#VPIF Display driver
+obj-$(CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY) += vpif_display.o
+#VPIF Capture driver
+obj-$(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE) += vpif_capture.o
 
 # Capture: DM6446 and DM355
 obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o
index e106b72810a947f7d78b0f01681fe02c1d6d2934..6fe7034bea7c427e9da4503ea42b4b83991bb1ec 100644 (file)
@@ -1083,7 +1083,7 @@ vpbe_display_s_dv_preset(struct file *file, void *priv,
        }
 
        /* Set the given standard in the encoder */
-       if (NULL != vpbe_dev->ops.s_dv_preset)
+       if (!vpbe_dev->ops.s_dv_preset)
                return -EINVAL;
 
        ret = vpbe_dev->ops.s_dv_preset(vpbe_dev, preset);
@@ -1517,6 +1517,8 @@ static int vpbe_display_g_register(struct file *file, void *priv,
                        struct v4l2_dbg_register *reg)
 {
        struct v4l2_dbg_match *match = &reg->match;
+       struct vpbe_fh *fh = file->private_data;
+       struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
 
        if (match->type >= 2) {
                v4l2_subdev_call(vpbe_dev->venc,
index af9680273ff9723b56f2285532a084dd2bbc49c2..b3637aff8fee85be4eff1d5e23eeb5c6881efcdf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * vpif - DM646x Video Port Interface driver
+ * vpif - Video Port Interface driver
  * VPIF is a receiver and transmitter for video data. It has two channels(0, 1)
  * that receiveing video byte stream and two channels(2, 3) for video output.
  * The hardware supports SDTV, HDTV formats, raw data capture.
@@ -23,6 +23,8 @@
 #include <linux/spinlock.h>
 #include <linux/kernel.h>
 #include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
 #include <mach/hardware.h>
 
 #include "vpif.h"
@@ -40,6 +42,7 @@ static struct resource        *res;
 spinlock_t vpif_lock;
 
 void __iomem *vpif_base;
+struct clk *vpif_clk;
 
 /**
  * ch_params: video standard configuration parameters for vpif
@@ -346,7 +349,7 @@ static void config_vpif_params(struct vpif_params *vpifparams,
 
                        value = regr(reg);
                        /* Set data width */
-                       value &= ((~(unsigned int)(0x3)) <<
+                       value &= ~(0x3u <<
                                        VPIF_CH_DATA_WIDTH_BIT);
                        value |= ((vpifparams->params.data_sz) <<
                                                     VPIF_CH_DATA_WIDTH_BIT);
@@ -434,10 +437,19 @@ static int __init vpif_probe(struct platform_device *pdev)
                goto fail;
        }
 
+       vpif_clk = clk_get(&pdev->dev, "vpif");
+       if (IS_ERR(vpif_clk)) {
+               status = PTR_ERR(vpif_clk);
+               goto clk_fail;
+       }
+       clk_enable(vpif_clk);
+
        spin_lock_init(&vpif_lock);
        dev_info(&pdev->dev, "vpif probe success\n");
        return 0;
 
+clk_fail:
+       iounmap(vpif_base);
 fail:
        release_mem_region(res->start, res_len);
        return status;
@@ -445,15 +457,44 @@ fail:
 
 static int __devexit vpif_remove(struct platform_device *pdev)
 {
+       if (vpif_clk) {
+               clk_disable(vpif_clk);
+               clk_put(vpif_clk);
+       }
+
        iounmap(vpif_base);
        release_mem_region(res->start, res_len);
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int vpif_suspend(struct device *dev)
+{
+       clk_disable(vpif_clk);
+       return 0;
+}
+
+static int vpif_resume(struct device *dev)
+{
+       clk_enable(vpif_clk);
+       return 0;
+}
+
+static const struct dev_pm_ops vpif_pm = {
+       .suspend        = vpif_suspend,
+       .resume         = vpif_resume,
+};
+
+#define vpif_pm_ops (&vpif_pm)
+#else
+#define vpif_pm_ops NULL
+#endif
+
 static struct platform_driver vpif_driver = {
        .driver = {
                .name   = "vpif",
                .owner = THIS_MODULE,
+               .pm     = vpif_pm_ops,
        },
        .remove = __devexit_p(vpif_remove),
        .probe = vpif_probe,
index 8bcac65f9294e4abc4c886441e887fe0f3b35453..c2ce4d97c279cd8aaf24983da4ea29124a6f48ce 100644 (file)
@@ -211,6 +211,12 @@ static inline void vpif_clr_bit(u32 reg, u32 bit)
 #define VPIF_CH3_INT_CTRL_SHIFT        (6)
 #define VPIF_CH_INT_CTRL_SHIFT (6)
 
+#define VPIF_CH2_CLIP_ANC_EN   14
+#define VPIF_CH2_CLIP_ACTIVE_EN        13
+
+#define VPIF_CH3_CLIP_ANC_EN   14
+#define VPIF_CH3_CLIP_ACTIVE_EN        13
+
 /* enabled interrupt on both the fields on vpid_ch0_ctrl register */
 #define channel0_intr_assert() (regw((regr(VPIF_CH0_CTRL)|\
        (VPIF_INT_BOTH << VPIF_CH0_INT_CTRL_SHIFT)), VPIF_CH0_CTRL))
@@ -515,6 +521,30 @@ static inline void channel3_raw_enable(int enable, u8 index)
                vpif_clr_bit(VPIF_CH3_CTRL, mask);
 }
 
+/* function to enable clipping (for both active and blanking regions) on ch 2 */
+static inline void channel2_clipping_enable(int enable)
+{
+       if (enable) {
+               vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN);
+               vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN);
+       } else {
+               vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN);
+               vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN);
+       }
+}
+
+/* function to enable clipping (for both active and blanking regions) on ch 2 */
+static inline void channel3_clipping_enable(int enable)
+{
+       if (enable) {
+               vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN);
+               vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN);
+       } else {
+               vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN);
+               vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN);
+       }
+}
+
 /* inline function to set buffer addresses in case of Y/C non mux mode */
 static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma,
                                                 unsigned long btm_strt_luma,
@@ -569,6 +599,21 @@ static inline void ch3_set_vbi_addr(unsigned long top_strt_luma,
        regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_VANC);
 }
 
+static inline int vpif_intr_status(int channel)
+{
+       int status = 0;
+       int mask;
+
+       if (channel < 0 || channel > 3)
+               return 0;
+
+       mask = 1 << channel;
+       status = regr(VPIF_STATUS) & mask;
+       regw(status, VPIF_STATUS_CLR);
+
+       return status;
+}
+
 #define VPIF_MAX_NAME  (30)
 
 /* This structure will store size parameters as per the mode selected by user */
index 96046957bf21cb3832375215bd8dc13a4cb70ca9..266025e5d81d424b90ceedd4a1de6fe9c1d016e8 100644 (file)
@@ -80,108 +80,45 @@ static struct vpif_config_params config_params = {
 /* global variables */
 static struct vpif_device vpif_obj = { {NULL} };
 static struct device *vpif_dev;
-
-/**
- * vpif_uservirt_to_phys : translate user/virtual address to phy address
- * @virtp: user/virtual address
- *
- * This inline function is used to convert user space virtual address to
- * physical address.
- */
-static inline u32 vpif_uservirt_to_phys(u32 virtp)
-{
-       unsigned long physp = 0;
-       struct mm_struct *mm = current->mm;
-       struct vm_area_struct *vma;
-
-       vma = find_vma(mm, virtp);
-
-       /* For kernel direct-mapped memory, take the easy way */
-       if (virtp >= PAGE_OFFSET)
-               physp = virt_to_phys((void *)virtp);
-       else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff))
-               /**
-                * this will catch, kernel-allocated, mmaped-to-usermode
-                * addresses
-                */
-               physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
-       else {
-               /* otherwise, use get_user_pages() for general userland pages */
-               int res, nr_pages = 1;
-                       struct page *pages;
-
-               down_read(&current->mm->mmap_sem);
-
-               res = get_user_pages(current, current->mm,
-                                    virtp, nr_pages, 1, 0, &pages, NULL);
-               up_read(&current->mm->mmap_sem);
-
-               if (res == nr_pages)
-                       physp = __pa(page_address(&pages[0]) +
-                                    (virtp & ~PAGE_MASK));
-               else {
-                       vpif_err("get_user_pages failed\n");
-                       return 0;
-               }
-       }
-       return physp;
-}
+static void vpif_calculate_offsets(struct channel_obj *ch);
+static void vpif_config_addr(struct channel_obj *ch, int muxmode);
 
 /**
  * buffer_prepare :  callback function for buffer prepare
- * @q : buffer queue ptr
- * @vb: ptr to video buffer
- * @field: field info
+ * @vb: ptr to vb2_buffer
  *
- * This is the callback function for buffer prepare when videobuf_qbuf()
+ * This is the callback function for buffer prepare when vb2_qbuf()
  * function is called. The buffer is prepared and user space virtual address
  * or user address is converted into  physical address
  */
-static int vpif_buffer_prepare(struct videobuf_queue *q,
-                              struct videobuf_buffer *vb,
-                              enum v4l2_field field)
+static int vpif_buffer_prepare(struct vb2_buffer *vb)
 {
        /* Get the file handle object and channel object */
-       struct vpif_fh *fh = q->priv_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+       struct vb2_queue *q = vb->vb2_queue;
        struct channel_obj *ch = fh->channel;
        struct common_obj *common;
        unsigned long addr;
 
-
        vpif_dbg(2, debug, "vpif_buffer_prepare\n");
 
        common = &ch->common[VPIF_VIDEO_INDEX];
 
-       /* If buffer is not initialized, initialize it */
-       if (VIDEOBUF_NEEDS_INIT == vb->state) {
-               vb->width = common->width;
-               vb->height = common->height;
-               vb->size = vb->width * vb->height;
-               vb->field = field;
-       }
-       vb->state = VIDEOBUF_PREPARED;
-       /**
-        * if user pointer memory mechanism is used, get the physical
-        * address of the buffer
-        */
-       if (V4L2_MEMORY_USERPTR == common->memory) {
-               if (0 == vb->baddr) {
-                       vpif_dbg(1, debug, "buffer address is 0\n");
-                       return -EINVAL;
-
-               }
-               vb->boff = vpif_uservirt_to_phys(vb->baddr);
-               if (!IS_ALIGNED(vb->boff, 8))
+       if (vb->state != VB2_BUF_STATE_ACTIVE &&
+               vb->state != VB2_BUF_STATE_PREPARED) {
+               vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
+               if (vb2_plane_vaddr(vb, 0) &&
+               vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
                        goto exit;
-       }
+               addr = vb2_dma_contig_plane_dma_addr(vb, 0);
 
-       addr = vb->boff;
-       if (q->streaming) {
-               if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
-                   !IS_ALIGNED((addr + common->ybtm_off), 8) ||
-                   !IS_ALIGNED((addr + common->ctop_off), 8) ||
-                   !IS_ALIGNED((addr + common->cbtm_off), 8))
-                       goto exit;
+               if (q->streaming) {
+                       if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
+                               !IS_ALIGNED((addr + common->ybtm_off), 8) ||
+                               !IS_ALIGNED((addr + common->ctop_off), 8) ||
+                               !IS_ALIGNED((addr + common->cbtm_off), 8))
+                               goto exit;
+               }
        }
        return 0;
 exit:
@@ -190,49 +127,79 @@ exit:
 }
 
 /**
- * vpif_buffer_setup : Callback function for buffer setup.
- * @q: buffer queue ptr
- * @count: number of buffers
- * @size: size of the buffer
+ * vpif_buffer_queue_setup : Callback function for buffer setup.
+ * @vq: vb2_queue ptr
+ * @fmt: v4l2 format
+ * @nbuffers: ptr to number of buffers requested by application
+ * @nplanes:: contains number of distinct video planes needed to hold a frame
+ * @sizes[]: contains the size (in bytes) of each plane.
+ * @alloc_ctxs: ptr to allocation context
  *
  * This callback function is called when reqbuf() is called to adjust
  * the buffer count and buffer size
  */
-static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count,
-                            unsigned int *size)
+static int vpif_buffer_queue_setup(struct vb2_queue *vq,
+                               const struct v4l2_format *fmt,
+                               unsigned int *nbuffers, unsigned int *nplanes,
+                               unsigned int sizes[], void *alloc_ctxs[])
 {
        /* Get the file handle object and channel object */
-       struct vpif_fh *fh = q->priv_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
        struct channel_obj *ch = fh->channel;
        struct common_obj *common;
+       unsigned long size;
 
        common = &ch->common[VPIF_VIDEO_INDEX];
 
        vpif_dbg(2, debug, "vpif_buffer_setup\n");
 
        /* If memory type is not mmap, return */
-       if (V4L2_MEMORY_MMAP != common->memory)
-               return 0;
+       if (V4L2_MEMORY_MMAP == common->memory) {
+               /* Calculate the size of the buffer */
+               size = config_params.channel_bufsize[ch->channel_id];
+               /*
+                * Checking if the buffer size exceeds the available buffer
+                * ycmux_mode = 0 means 1 channel mode HD and
+                * ycmux_mode = 1 means 2 channels mode SD
+                */
+               if (ch->vpifparams.std_info.ycmux_mode == 0) {
+                       if (config_params.video_limit[ch->channel_id])
+                               while (size * *nbuffers >
+                                       (config_params.video_limit[0]
+                                               + config_params.video_limit[1]))
+                                       (*nbuffers)--;
+               } else {
+                       if (config_params.video_limit[ch->channel_id])
+                               while (size * *nbuffers >
+                               config_params.video_limit[ch->channel_id])
+                                       (*nbuffers)--;
+               }
+
+       } else {
+               size = common->fmt.fmt.pix.sizeimage;
+       }
+
+       if (*nbuffers < config_params.min_numbuffers)
+               *nbuffers = config_params.min_numbuffers;
 
-       /* Calculate the size of the buffer */
-       *size = config_params.channel_bufsize[ch->channel_id];
+       *nplanes = 1;
+       sizes[0] = size;
+       alloc_ctxs[0] = common->alloc_ctx;
 
-       if (*count < config_params.min_numbuffers)
-               *count = config_params.min_numbuffers;
        return 0;
 }
 
 /**
  * vpif_buffer_queue : Callback function to add buffer to DMA queue
- * @q: ptr to videobuf_queue
- * @vb: ptr to videobuf_buffer
+ * @vb: ptr to vb2_buffer
  */
-static void vpif_buffer_queue(struct videobuf_queue *q,
-                             struct videobuf_buffer *vb)
+static void vpif_buffer_queue(struct vb2_buffer *vb)
 {
        /* Get the file handle object and channel object */
-       struct vpif_fh *fh = q->priv_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
        struct channel_obj *ch = fh->channel;
+       struct vpif_cap_buffer *buf = container_of(vb,
+                               struct vpif_cap_buffer, vb);
        struct common_obj *common;
 
        common = &ch->common[VPIF_VIDEO_INDEX];
@@ -240,43 +207,189 @@ static void vpif_buffer_queue(struct videobuf_queue *q,
        vpif_dbg(2, debug, "vpif_buffer_queue\n");
 
        /* add the buffer to the DMA queue */
-       list_add_tail(&vb->queue, &common->dma_queue);
-       /* Change state of the buffer */
-       vb->state = VIDEOBUF_QUEUED;
+       list_add_tail(&buf->list, &common->dma_queue);
 }
 
 /**
- * vpif_buffer_release : Callback function to free buffer
- * @q: buffer queue ptr
- * @vb: ptr to video buffer
+ * vpif_buf_cleanup : Callback function to free buffer
+ * @vb: ptr to vb2_buffer
  *
- * This function is called from the videobuf layer to free memory
+ * This function is called from the videobuf2 layer to free memory
  * allocated to  the buffers
  */
-static void vpif_buffer_release(struct videobuf_queue *q,
-                               struct videobuf_buffer *vb)
+static void vpif_buf_cleanup(struct vb2_buffer *vb)
 {
        /* Get the file handle object and channel object */
-       struct vpif_fh *fh = q->priv_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+       struct vpif_cap_buffer *buf = container_of(vb,
+                                       struct vpif_cap_buffer, vb);
        struct channel_obj *ch = fh->channel;
        struct common_obj *common;
+       unsigned long flags;
 
        common = &ch->common[VPIF_VIDEO_INDEX];
 
-       videobuf_dma_contig_free(q, vb);
-       vb->state = VIDEOBUF_NEEDS_INIT;
+       spin_lock_irqsave(&common->irqlock, flags);
+       if (vb->state == VB2_BUF_STATE_ACTIVE)
+               list_del_init(&buf->list);
+       spin_unlock_irqrestore(&common->irqlock, flags);
+
 }
 
-static struct videobuf_queue_ops video_qops = {
-       .buf_setup = vpif_buffer_setup,
-       .buf_prepare = vpif_buffer_prepare,
-       .buf_queue = vpif_buffer_queue,
-       .buf_release = vpif_buffer_release,
-};
+static void vpif_wait_prepare(struct vb2_queue *vq)
+{
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
+       struct channel_obj *ch = fh->channel;
+       struct common_obj *common;
+
+       common = &ch->common[VPIF_VIDEO_INDEX];
+       mutex_unlock(&common->lock);
+}
+
+static void vpif_wait_finish(struct vb2_queue *vq)
+{
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
+       struct channel_obj *ch = fh->channel;
+       struct common_obj *common;
+
+       common = &ch->common[VPIF_VIDEO_INDEX];
+       mutex_lock(&common->lock);
+}
+
+static int vpif_buffer_init(struct vb2_buffer *vb)
+{
+       struct vpif_cap_buffer *buf = container_of(vb,
+                                       struct vpif_cap_buffer, vb);
+
+       INIT_LIST_HEAD(&buf->list);
+
+       return 0;
+}
 
 static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] =
        { {1, 1} };
 
+static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+       struct vpif_capture_config *vpif_config_data =
+                                       vpif_dev->platform_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
+       struct channel_obj *ch = fh->channel;
+       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+       struct vpif_params *vpif = &ch->vpifparams;
+       unsigned long addr = 0;
+       int ret;
+
+               /* If buffer queue is empty, return error */
+       if (list_empty(&common->dma_queue)) {
+               vpif_dbg(1, debug, "buffer queue is empty\n");
+               return -EIO;
+       }
+
+       /* Get the next frame from the buffer queue */
+       common->cur_frm = common->next_frm = list_entry(common->dma_queue.next,
+                                   struct vpif_cap_buffer, list);
+       /* Remove buffer from the buffer queue */
+       list_del(&common->cur_frm->list);
+       /* Mark state of the current frame to active */
+       common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+       /* Initialize field_id and started member */
+       ch->field_id = 0;
+       common->started = 1;
+       addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
+
+       /* Calculate the offset for Y and C data in the buffer */
+       vpif_calculate_offsets(ch);
+
+       if ((vpif->std_info.frm_fmt &&
+           ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) &&
+            (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) ||
+           (!vpif->std_info.frm_fmt &&
+            (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
+               vpif_dbg(1, debug, "conflict in field format and std format\n");
+               return -EINVAL;
+       }
+
+       /* configure 1 or 2 channel mode */
+       ret = vpif_config_data->setup_input_channel_mode
+                                       (vpif->std_info.ycmux_mode);
+
+       if (ret < 0) {
+               vpif_dbg(1, debug, "can't set vpif channel mode\n");
+               return ret;
+       }
+
+       /* Call vpif_set_params function to set the parameters and addresses */
+       ret = vpif_set_video_params(vpif, ch->channel_id);
+
+       if (ret < 0) {
+               vpif_dbg(1, debug, "can't set video params\n");
+               return ret;
+       }
+
+       common->started = ret;
+       vpif_config_addr(ch, ret);
+
+       common->set_addr(addr + common->ytop_off,
+                        addr + common->ybtm_off,
+                        addr + common->ctop_off,
+                        addr + common->cbtm_off);
+
+       /**
+        * Set interrupt for both the fields in VPIF Register enable channel in
+        * VPIF register
+        */
+       if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) {
+               channel0_intr_assert();
+               channel0_intr_enable(1);
+               enable_channel0(1);
+       }
+       if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
+           (common->started == 2)) {
+               channel1_intr_assert();
+               channel1_intr_enable(1);
+               enable_channel1(1);
+       }
+       channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
+
+       return 0;
+}
+
+/* abort streaming and wait for last buffer */
+static int vpif_stop_streaming(struct vb2_queue *vq)
+{
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
+       struct channel_obj *ch = fh->channel;
+       struct common_obj *common;
+
+       if (!vb2_is_streaming(vq))
+               return 0;
+
+       common = &ch->common[VPIF_VIDEO_INDEX];
+
+       /* release all active buffers */
+       while (!list_empty(&common->dma_queue)) {
+               common->next_frm = list_entry(common->dma_queue.next,
+                                               struct vpif_cap_buffer, list);
+               list_del(&common->next_frm->list);
+               vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
+       }
+
+       return 0;
+}
+
+static struct vb2_ops video_qops = {
+       .queue_setup            = vpif_buffer_queue_setup,
+       .wait_prepare           = vpif_wait_prepare,
+       .wait_finish            = vpif_wait_finish,
+       .buf_init               = vpif_buffer_init,
+       .buf_prepare            = vpif_buffer_prepare,
+       .start_streaming        = vpif_start_streaming,
+       .stop_streaming         = vpif_stop_streaming,
+       .buf_cleanup            = vpif_buf_cleanup,
+       .buf_queue              = vpif_buffer_queue,
+};
+
 /**
  * vpif_process_buffer_complete: process a completed buffer
  * @common: ptr to common channel object
@@ -287,9 +400,9 @@ static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] =
  */
 static void vpif_process_buffer_complete(struct common_obj *common)
 {
-       do_gettimeofday(&common->cur_frm->ts);
-       common->cur_frm->state = VIDEOBUF_DONE;
-       wake_up_interruptible(&common->cur_frm->done);
+       do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp);
+       vb2_buffer_done(&common->cur_frm->vb,
+                                           VB2_BUF_STATE_DONE);
        /* Make curFrm pointing to nextFrm */
        common->cur_frm = common->next_frm;
 }
@@ -307,14 +420,11 @@ static void vpif_schedule_next_buffer(struct common_obj *common)
        unsigned long addr = 0;
 
        common->next_frm = list_entry(common->dma_queue.next,
-                                    struct videobuf_buffer, queue);
+                                    struct vpif_cap_buffer, list);
        /* Remove that buffer from the buffer queue */
-       list_del(&common->next_frm->queue);
-       common->next_frm->state = VIDEOBUF_ACTIVE;
-       if (V4L2_MEMORY_USERPTR == common->memory)
-               addr = common->next_frm->boff;
-       else
-               addr = videobuf_to_dma_contig(common->next_frm);
+       list_del(&common->next_frm->list);
+       common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+       addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
 
        /* Set top and bottom field addresses in VPIF registers */
        common->set_addr(addr + common->ytop_off,
@@ -341,6 +451,9 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
        int fid = -1, i;
 
        channel_id = *(int *)(dev_id);
+       if (!vpif_intr_status(channel_id))
+               return IRQ_NONE;
+
        ch = dev->dev[channel_id];
 
        field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
@@ -485,10 +598,7 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
        } else
                vid_ch->buf_field = common->fmt.fmt.pix.field;
 
-       if (V4L2_MEMORY_USERPTR == common->memory)
-               sizeimage = common->fmt.fmt.pix.sizeimage;
-       else
-               sizeimage = config_params.channel_bufsize[ch->channel_id];
+       sizeimage = common->fmt.fmt.pix.sizeimage;
 
        hpitch = common->fmt.fmt.pix.bytesperline;
        vpitch = sizeimage / (hpitch * 2);
@@ -640,10 +750,7 @@ static int vpif_check_format(struct channel_obj *ch,
                hpitch = vpif_params->std_info.width;
        }
 
-       if (V4L2_MEMORY_USERPTR == common->memory)
-               sizeimage = pixfmt->sizeimage;
-       else
-               sizeimage = config_params.channel_bufsize[ch->channel_id];
+       sizeimage = pixfmt->sizeimage;
 
        vpitch = sizeimage / (hpitch * 2);
 
@@ -703,7 +810,7 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode)
 }
 
 /**
- * vpfe_mmap : It is used to map kernel space buffers into user spaces
+ * vpif_mmap : It is used to map kernel space buffers into user spaces
  * @filep: file pointer
  * @vma: ptr to vm_area_struct
  */
@@ -716,7 +823,7 @@ static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
 
        vpif_dbg(2, debug, "vpif_mmap\n");
 
-       return videobuf_mmap_mapper(&common->buffer_queue, vma);
+       return vb2_mmap(&common->buffer_queue, vma);
 }
 
 /**
@@ -733,7 +840,7 @@ static unsigned int vpif_poll(struct file *filep, poll_table * wait)
        vpif_dbg(2, debug, "vpif_poll\n");
 
        if (common->started)
-               return videobuf_poll_stream(filep, &common->buffer_queue, wait);
+               return vb2_poll(&common->buffer_queue, filep, wait);
        return 0;
 }
 
@@ -812,7 +919,7 @@ static int vpif_open(struct file *filep)
  * vpif_release : function to clean up file close
  * @filep: file pointer
  *
- * This function deletes buffer queue, frees the buffers and the vpfe file
+ * This function deletes buffer queue, frees the buffers and the vpif file
  * handle
  */
 static int vpif_release(struct file *filep)
@@ -841,8 +948,8 @@ static int vpif_release(struct file *filep)
                }
                common->started = 0;
                /* Free buffers allocated */
-               videobuf_queue_cancel(&common->buffer_queue);
-               videobuf_mmap_free(&common->buffer_queue);
+               vb2_queue_release(&common->buffer_queue);
+               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
        }
 
        /* Decrement channel usrs counter */
@@ -872,6 +979,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
        struct channel_obj *ch = fh->channel;
        struct common_obj *common;
        u8 index = 0;
+       struct vb2_queue *q;
 
        vpif_dbg(2, debug, "vpif_reqbufs\n");
 
@@ -887,7 +995,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
                }
        }
 
-       if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type)
+       if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type || !vpif_dev)
                return -EINVAL;
 
        index = VPIF_VIDEO_INDEX;
@@ -897,14 +1005,21 @@ static int vpif_reqbufs(struct file *file, void *priv,
        if (0 != common->io_usrs)
                return -EBUSY;
 
-       /* Initialize videobuf queue as per the buffer type */
-       videobuf_queue_dma_contig_init(&common->buffer_queue,
-                                           &video_qops, NULL,
-                                           &common->irqlock,
-                                           reqbuf->type,
-                                           common->fmt.fmt.pix.field,
-                                           sizeof(struct videobuf_buffer), fh,
-                                           &common->lock);
+       /* Initialize videobuf2 queue as per the buffer type */
+       common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
+       if (!common->alloc_ctx) {
+               vpif_err("Failed to get the context\n");
+               return -EINVAL;
+       }
+       q = &common->buffer_queue;
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       q->io_modes = VB2_MMAP | VB2_USERPTR;
+       q->drv_priv = fh;
+       q->ops = &video_qops;
+       q->mem_ops = &vb2_dma_contig_memops;
+       q->buf_struct_size = sizeof(struct vpif_cap_buffer);
+
+       vb2_queue_init(q);
 
        /* Set io allowed member of file handle to TRUE */
        fh->io_allowed[index] = 1;
@@ -915,7 +1030,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
        INIT_LIST_HEAD(&common->dma_queue);
 
        /* Allocate buffers */
-       return videobuf_reqbufs(&common->buffer_queue, reqbuf);
+       return vb2_reqbufs(&common->buffer_queue, reqbuf);
 }
 
 /**
@@ -941,7 +1056,7 @@ static int vpif_querybuf(struct file *file, void *priv,
                return -EINVAL;
        }
 
-       return videobuf_querybuf(&common->buffer_queue, buf);
+       return vb2_querybuf(&common->buffer_queue, buf);
 }
 
 /**
@@ -957,10 +1072,6 @@ static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
        struct channel_obj *ch = fh->channel;
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
        struct v4l2_buffer tbuf = *buf;
-       struct videobuf_buffer *buf1;
-       unsigned long addr = 0;
-       unsigned long flags;
-       int ret = 0;
 
        vpif_dbg(2, debug, "vpif_qbuf\n");
 
@@ -970,76 +1081,11 @@ static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
        }
 
        if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
-               vpif_err("fh io not allowed \n");
+               vpif_err("fh io not allowed\n");
                return -EACCES;
        }
 
-       if (!(list_empty(&common->dma_queue)) ||
-           (common->cur_frm != common->next_frm) ||
-           !common->started ||
-           (common->started && (0 == ch->field_id)))
-               return videobuf_qbuf(&common->buffer_queue, buf);
-
-       /* bufferqueue is empty store buffer address in VPIF registers */
-       mutex_lock(&common->buffer_queue.vb_lock);
-       buf1 = common->buffer_queue.bufs[tbuf.index];
-
-       if ((buf1->state == VIDEOBUF_QUEUED) ||
-           (buf1->state == VIDEOBUF_ACTIVE)) {
-               vpif_err("invalid state\n");
-               goto qbuf_exit;
-       }
-
-       switch (buf1->memory) {
-       case V4L2_MEMORY_MMAP:
-               if (buf1->baddr == 0)
-                       goto qbuf_exit;
-               break;
-
-       case V4L2_MEMORY_USERPTR:
-               if (tbuf.length < buf1->bsize)
-                       goto qbuf_exit;
-
-               if ((VIDEOBUF_NEEDS_INIT != buf1->state)
-                           && (buf1->baddr != tbuf.m.userptr)) {
-                       vpif_buffer_release(&common->buffer_queue, buf1);
-                       buf1->baddr = tbuf.m.userptr;
-               }
-               break;
-
-       default:
-               goto qbuf_exit;
-       }
-
-       local_irq_save(flags);
-       ret = vpif_buffer_prepare(&common->buffer_queue, buf1,
-                                       common->buffer_queue.field);
-       if (ret < 0) {
-               local_irq_restore(flags);
-               goto qbuf_exit;
-       }
-
-       buf1->state = VIDEOBUF_ACTIVE;
-
-       if (V4L2_MEMORY_USERPTR == common->memory)
-               addr = buf1->boff;
-       else
-               addr = videobuf_to_dma_contig(buf1);
-
-       common->next_frm = buf1;
-       common->set_addr(addr + common->ytop_off,
-                        addr + common->ybtm_off,
-                        addr + common->ctop_off,
-                        addr + common->cbtm_off);
-
-       local_irq_restore(flags);
-       list_add_tail(&buf1->stream, &common->buffer_queue.stream);
-       mutex_unlock(&common->buffer_queue.vb_lock);
-       return 0;
-
-qbuf_exit:
-       mutex_unlock(&common->buffer_queue.vb_lock);
-       return -EINVAL;
+       return vb2_qbuf(&common->buffer_queue, buf);
 }
 
 /**
@@ -1056,8 +1102,8 @@ static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
 
        vpif_dbg(2, debug, "vpif_dqbuf\n");
 
-       return videobuf_dqbuf(&common->buffer_queue, buf,
-                                       file->f_flags & O_NONBLOCK);
+       return vb2_dqbuf(&common->buffer_queue, buf,
+                        (file->f_flags & O_NONBLOCK));
 }
 
 /**
@@ -1070,13 +1116,11 @@ static int vpif_streamon(struct file *file, void *priv,
                                enum v4l2_buf_type buftype)
 {
 
-       struct vpif_capture_config *config = vpif_dev->platform_data;
        struct vpif_fh *fh = priv;
        struct channel_obj *ch = fh->channel;
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
        struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
        struct vpif_params *vpif;
-       unsigned long addr = 0;
        int ret = 0;
 
        vpif_dbg(2, debug, "vpif_streamon\n");
@@ -1122,95 +1166,13 @@ static int vpif_streamon(struct file *file, void *priv,
                return ret;
        }
 
-       /* Call videobuf_streamon to start streaming in videobuf */
-       ret = videobuf_streamon(&common->buffer_queue);
+       /* Call vb2_streamon to start streaming in videobuf2 */
+       ret = vb2_streamon(&common->buffer_queue, buftype);
        if (ret) {
-               vpif_dbg(1, debug, "videobuf_streamon\n");
+               vpif_dbg(1, debug, "vb2_streamon\n");
                return ret;
        }
 
-       /* If buffer queue is empty, return error */
-       if (list_empty(&common->dma_queue)) {
-               vpif_dbg(1, debug, "buffer queue is empty\n");
-               ret = -EIO;
-               goto exit;
-       }
-
-       /* Get the next frame from the buffer queue */
-       common->cur_frm = list_entry(common->dma_queue.next,
-                                   struct videobuf_buffer, queue);
-       common->next_frm = common->cur_frm;
-
-       /* Remove buffer from the buffer queue */
-       list_del(&common->cur_frm->queue);
-       /* Mark state of the current frame to active */
-       common->cur_frm->state = VIDEOBUF_ACTIVE;
-       /* Initialize field_id and started member */
-       ch->field_id = 0;
-       common->started = 1;
-
-       if (V4L2_MEMORY_USERPTR == common->memory)
-               addr = common->cur_frm->boff;
-       else
-               addr = videobuf_to_dma_contig(common->cur_frm);
-
-       /* Calculate the offset for Y and C data in the buffer */
-       vpif_calculate_offsets(ch);
-
-       if ((vpif->std_info.frm_fmt &&
-           ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) &&
-            (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) ||
-           (!vpif->std_info.frm_fmt &&
-            (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
-               vpif_dbg(1, debug, "conflict in field format and std format\n");
-               ret = -EINVAL;
-               goto exit;
-       }
-
-       /* configure 1 or 2 channel mode */
-       ret = config->setup_input_channel_mode(vpif->std_info.ycmux_mode);
-
-       if (ret < 0) {
-               vpif_dbg(1, debug, "can't set vpif channel mode\n");
-               goto exit;
-       }
-
-       /* Call vpif_set_params function to set the parameters and addresses */
-       ret = vpif_set_video_params(vpif, ch->channel_id);
-
-       if (ret < 0) {
-               vpif_dbg(1, debug, "can't set video params\n");
-               goto exit;
-       }
-
-       common->started = ret;
-       vpif_config_addr(ch, ret);
-
-       common->set_addr(addr + common->ytop_off,
-                        addr + common->ybtm_off,
-                        addr + common->ctop_off,
-                        addr + common->cbtm_off);
-
-       /**
-        * Set interrupt for both the fields in VPIF Register enable channel in
-        * VPIF register
-        */
-       if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) {
-               channel0_intr_assert();
-               channel0_intr_enable(1);
-               enable_channel0(1);
-       }
-       if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
-           (common->started == 2)) {
-               channel1_intr_assert();
-               channel1_intr_enable(1);
-               enable_channel1(1);
-       }
-       channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
-       return ret;
-
-exit:
-       videobuf_streamoff(&common->buffer_queue);
        return ret;
 }
 
@@ -1265,7 +1227,7 @@ static int vpif_streamoff(struct file *file, void *priv,
        if (ret && (ret != -ENOIOCTLCMD))
                vpif_dbg(1, debug, "stream off failed in subdev\n");
 
-       return videobuf_streamoff(&common->buffer_queue);
+       return vb2_streamoff(&common->buffer_queue, buftype);
 }
 
 /**
@@ -1679,7 +1641,7 @@ static int vpif_querycap(struct file *file, void  *priv,
 
        cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
        strlcpy(cap->driver, "vpif capture", sizeof(cap->driver));
-       strlcpy(cap->bus_info, "DM646x Platform", sizeof(cap->bus_info));
+       strlcpy(cap->bus_info, "VPIF Platform", sizeof(cap->bus_info));
        strlcpy(cap->card, config->card_name, sizeof(cap->card));
 
        return 0;
@@ -2168,6 +2130,7 @@ static __init int vpif_probe(struct platform_device *pdev)
        struct video_device *vfd;
        struct resource *res;
        int subdev_count;
+       size_t size;
 
        vpif_dev = &pdev->dev;
 
@@ -2186,8 +2149,8 @@ static __init int vpif_probe(struct platform_device *pdev)
        k = 0;
        while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) {
                for (i = res->start; i <= res->end; i++) {
-                       if (request_irq(i, vpif_channel_isr, IRQF_DISABLED,
-                                       "DM646x_Capture",
+                       if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
+                                       "VPIF_Capture",
                                (void *)(&vpif_obj.dev[k]->channel_id))) {
                                err = -EBUSY;
                                i--;
@@ -2216,12 +2179,29 @@ static __init int vpif_probe(struct platform_device *pdev)
                vfd->v4l2_dev = &vpif_obj.v4l2_dev;
                vfd->release = video_device_release;
                snprintf(vfd->name, sizeof(vfd->name),
-                        "DM646x_VPIFCapture_DRIVER_V%s",
+                        "VPIF_Capture_DRIVER_V%s",
                         VPIF_CAPTURE_VERSION);
                /* Set video_dev to the video device */
                ch->video_dev = vfd;
        }
 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res) {
+               size = resource_size(res);
+               /* The resources are divided into two equal memory and when we
+                * have HD output we can add them together
+                */
+               for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
+                       ch = vpif_obj.dev[j];
+                       ch->channel_id = j;
+                       /* only enabled if second resource exists */
+                       config_params.video_limit[ch->channel_id] = 0;
+                       if (size)
+                               config_params.video_limit[ch->channel_id] =
+                                                                       size/2;
+               }
+       }
+
        for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
                ch = vpif_obj.dev[j];
                ch->channel_id = j;
@@ -2275,8 +2255,7 @@ static __init int vpif_probe(struct platform_device *pdev)
                        vpif_obj.sd[i]->grp_id = 1 << i;
        }
 
-       v4l2_info(&vpif_obj.v4l2_dev,
-                       "DM646x VPIF capture driver initialized\n");
+       v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n");
        return 0;
 
 probe_subdev_out:
@@ -2333,26 +2312,70 @@ static int vpif_remove(struct platform_device *device)
        return 0;
 }
 
+#ifdef CONFIG_PM
 /**
  * vpif_suspend: vpif device suspend
- *
- * TODO: Add suspend code here
  */
-static int
-vpif_suspend(struct device *dev)
+static int vpif_suspend(struct device *dev)
 {
-       return -1;
+
+       struct common_obj *common;
+       struct channel_obj *ch;
+       int i;
+
+       for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+               /* Get the pointer to the channel object */
+               ch = vpif_obj.dev[i];
+               common = &ch->common[VPIF_VIDEO_INDEX];
+               mutex_lock(&common->lock);
+               if (ch->usrs && common->io_usrs) {
+                       /* Disable channel */
+                       if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
+                               enable_channel0(0);
+                               channel0_intr_enable(0);
+                       }
+                       if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
+                           common->started == 2) {
+                               enable_channel1(0);
+                               channel1_intr_enable(0);
+                       }
+               }
+               mutex_unlock(&common->lock);
+       }
+
+       return 0;
 }
 
-/**
+/*
  * vpif_resume: vpif device suspend
- *
- * TODO: Add resume code here
  */
-static int
-vpif_resume(struct device *dev)
+static int vpif_resume(struct device *dev)
 {
-       return -1;
+       struct common_obj *common;
+       struct channel_obj *ch;
+       int i;
+
+       for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+               /* Get the pointer to the channel object */
+               ch = vpif_obj.dev[i];
+               common = &ch->common[VPIF_VIDEO_INDEX];
+               mutex_lock(&common->lock);
+               if (ch->usrs && common->io_usrs) {
+                       /* Disable channel */
+                       if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
+                               enable_channel0(1);
+                               channel0_intr_enable(1);
+                       }
+                       if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
+                           common->started == 2) {
+                               enable_channel1(1);
+                               channel1_intr_enable(1);
+                       }
+               }
+               mutex_unlock(&common->lock);
+       }
+
+       return 0;
 }
 
 static const struct dev_pm_ops vpif_dev_pm_ops = {
@@ -2360,11 +2383,16 @@ static const struct dev_pm_ops vpif_dev_pm_ops = {
        .resume = vpif_resume,
 };
 
+#define vpif_pm_ops (&vpif_dev_pm_ops)
+#else
+#define vpif_pm_ops NULL
+#endif
+
 static __refdata struct platform_driver vpif_driver = {
        .driver = {
                .name   = "vpif_capture",
                .owner  = THIS_MODULE,
-               .pm = &vpif_dev_pm_ops,
+               .pm     = vpif_pm_ops,
        },
        .probe = vpif_probe,
        .remove = vpif_remove,
index a693d4ebda557ea0ee972813f8a5877d40e79efb..3511510f43ee32098732e0f480f1067579404a69 100644 (file)
@@ -26,7 +26,7 @@
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
 #include <media/videobuf-core.h>
-#include <media/videobuf-dma-contig.h>
+#include <media/videobuf2-dma-contig.h>
 #include <media/davinci/vpif_types.h>
 
 #include "vpif.h"
@@ -60,11 +60,16 @@ struct video_obj {
        u32 input_idx;
 };
 
+struct vpif_cap_buffer {
+       struct vb2_buffer vb;
+       struct list_head list;
+};
+
 struct common_obj {
        /* Pointer pointing to current v4l2_buffer */
-       struct videobuf_buffer *cur_frm;
+       struct vpif_cap_buffer *cur_frm;
        /* Pointer pointing to current v4l2_buffer */
-       struct videobuf_buffer *next_frm;
+       struct vpif_cap_buffer *next_frm;
        /*
         * This field keeps track of type of buffer exchange mechanism
         * user has selected
@@ -73,7 +78,9 @@ struct common_obj {
        /* Used to store pixel format */
        struct v4l2_format fmt;
        /* Buffer queue used in video-buf */
-       struct videobuf_queue buffer_queue;
+       struct vb2_queue buffer_queue;
+       /* allocator-specific contexts for each plane */
+       struct vb2_alloc_ctx *alloc_ctx;
        /* Queue of filled frames */
        struct list_head dma_queue;
        /* Used in video-buf */
@@ -151,6 +158,7 @@ struct vpif_config_params {
        u32 min_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
        u32 channel_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
        u8 default_device[VPIF_CAPTURE_NUM_CHANNELS];
+       u32 video_limit[VPIF_CAPTURE_NUM_CHANNELS];
        u8 max_device_type;
 };
 /* Struct which keeps track of the line numbers for the sliced vbi service */
index e6488ee7db1877510a490724c7311f5f98f16e78..e129c98921ad493b0b8ea90562d2878325218fc5 100644 (file)
@@ -46,7 +46,7 @@ MODULE_DESCRIPTION("TI DaVinci VPIF Display driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(VPIF_DISPLAY_VERSION);
 
-#define DM646X_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
+#define VPIF_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
 
 #define vpif_err(fmt, arg...)  v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
 #define vpif_dbg(level, debug, fmt, arg...)    \
@@ -82,89 +82,38 @@ static struct vpif_config_params config_params = {
 
 static struct vpif_device vpif_obj = { {NULL} };
 static struct device *vpif_dev;
+static void vpif_calculate_offsets(struct channel_obj *ch);
+static void vpif_config_addr(struct channel_obj *ch, int muxmode);
 
 /*
- * vpif_uservirt_to_phys: This function is used to convert user
- * space virtual address to physical address.
- */
-static u32 vpif_uservirt_to_phys(u32 virtp)
-{
-       struct mm_struct *mm = current->mm;
-       unsigned long physp = 0;
-       struct vm_area_struct *vma;
-
-       vma = find_vma(mm, virtp);
-
-       /* For kernel direct-mapped memory, take the easy way */
-       if (virtp >= PAGE_OFFSET) {
-               physp = virt_to_phys((void *)virtp);
-       } else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) {
-               /* this will catch, kernel-allocated, mmaped-to-usermode addr */
-               physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
-       } else {
-               /* otherwise, use get_user_pages() for general userland pages */
-               int res, nr_pages = 1;
-               struct page *pages;
-               down_read(&current->mm->mmap_sem);
-
-               res = get_user_pages(current, current->mm,
-                                    virtp, nr_pages, 1, 0, &pages, NULL);
-               up_read(&current->mm->mmap_sem);
-
-               if (res == nr_pages) {
-                       physp = __pa(page_address(&pages[0]) +
-                                                       (virtp & ~PAGE_MASK));
-               } else {
-                       vpif_err("get_user_pages failed\n");
-                       return 0;
-               }
-       }
-
-       return physp;
-}
-
-/*
- * buffer_prepare: This is the callback function called from videobuf_qbuf()
+ * buffer_prepare: This is the callback function called from vb2_qbuf()
  * function the buffer is prepared and user space virtual address is converted
  * into physical address
  */
-static int vpif_buffer_prepare(struct videobuf_queue *q,
-                              struct videobuf_buffer *vb,
-                              enum v4l2_field field)
+static int vpif_buffer_prepare(struct vb2_buffer *vb)
 {
-       struct vpif_fh *fh = q->priv_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+       struct vb2_queue *q = vb->vb2_queue;
        struct common_obj *common;
        unsigned long addr;
 
        common = &fh->channel->common[VPIF_VIDEO_INDEX];
-       if (VIDEOBUF_NEEDS_INIT == vb->state) {
-               vb->width       = common->width;
-               vb->height      = common->height;
-               vb->size        = vb->width * vb->height;
-               vb->field       = field;
-       }
-       vb->state = VIDEOBUF_PREPARED;
-
-       /* if user pointer memory mechanism is used, get the physical
-        * address of the buffer */
-       if (V4L2_MEMORY_USERPTR == common->memory) {
-               if (!vb->baddr) {
-                       vpif_err("buffer_address is 0\n");
-                       return -EINVAL;
-               }
-
-               vb->boff = vpif_uservirt_to_phys(vb->baddr);
-               if (!ISALIGNED(vb->boff))
+       if (vb->state != VB2_BUF_STATE_ACTIVE &&
+               vb->state != VB2_BUF_STATE_PREPARED) {
+               vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
+               if (vb2_plane_vaddr(vb, 0) &&
+               vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
                        goto buf_align_exit;
-       }
 
-       addr = vb->boff;
-       if (q->streaming && (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) {
-               if (!ISALIGNED(addr + common->ytop_off) ||
-                   !ISALIGNED(addr + common->ybtm_off) ||
-                   !ISALIGNED(addr + common->ctop_off) ||
-                   !ISALIGNED(addr + common->cbtm_off))
-                       goto buf_align_exit;
+               addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+               if (q->streaming &&
+                       (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) {
+                       if (!ISALIGNED(addr + common->ytop_off) ||
+                       !ISALIGNED(addr + common->ybtm_off) ||
+                       !ISALIGNED(addr + common->ctop_off) ||
+                       !ISALIGNED(addr + common->cbtm_off))
+                               goto buf_align_exit;
+               }
        }
        return 0;
 
@@ -174,86 +123,255 @@ buf_align_exit:
 }
 
 /*
- * vpif_buffer_setup: This function allocates memory for the buffers
+ * vpif_buffer_queue_setup: This function allocates memory for the buffers
  */
-static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count,
-                               unsigned int *size)
+static int vpif_buffer_queue_setup(struct vb2_queue *vq,
+                               const struct v4l2_format *fmt,
+                               unsigned int *nbuffers, unsigned int *nplanes,
+                               unsigned int sizes[], void *alloc_ctxs[])
 {
-       struct vpif_fh *fh = q->priv_data;
+       struct vpif_fh *fh = vb2_get_drv_priv(vq);
        struct channel_obj *ch = fh->channel;
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+       unsigned long size;
+
+       if (V4L2_MEMORY_MMAP == common->memory) {
+               size = config_params.channel_bufsize[ch->channel_id];
+               /*
+               * Checking if the buffer size exceeds the available buffer
+               * ycmux_mode = 0 means 1 channel mode HD and
+               * ycmux_mode = 1 means 2 channels mode SD
+               */
+               if (ch->vpifparams.std_info.ycmux_mode == 0) {
+                       if (config_params.video_limit[ch->channel_id])
+                               while (size * *nbuffers >
+