Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetoot...
authorDavid S. Miller <davem@davemloft.net>
Fri, 19 Dec 2014 20:47:32 +0000 (15:47 -0500)
committerDavid S. Miller <davem@davemloft.net>
Fri, 19 Dec 2014 20:47:32 +0000 (15:47 -0500)
Johan Hedberg says:

====================
pull request: bluetooth 2014-12-19

Here's one more pull request for 3.19. It contains the socket type
verification fixes from Al Viro as well as an skb double-free fix for
6lowpan from Jukka Rissanen.

Please let me know if there are any issues pulling. Thanks.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
254 files changed:
.mailmap
Documentation/ABI/testing/sysfs-platform-dell-laptop [new file with mode: 0644]
Documentation/DocBook/media/v4l/compat.xml
Documentation/DocBook/media/v4l/pixfmt.xml
Documentation/DocBook/media/v4l/subdev-formats.xml
Documentation/DocBook/media/v4l/v4l2.xml
Documentation/devicetree/bindings/media/rcar_vin.txt
Documentation/kernel-parameters.txt
Documentation/video4linux/vivid.txt
MAINTAINERS
arch/arm/kernel/perf_event.c
arch/arm/mach-davinci/pm_domain.c
arch/arm/mach-exynos/Kconfig
arch/arm/mach-keystone/pm_domain.c
arch/arm/mach-omap1/pm_bus.c
arch/arm/mach-omap2/io.c
arch/arm/mach-omap2/omap_device.c
arch/ia64/Kconfig
arch/x86/kernel/apic/io_apic.c
drivers/acpi/blacklist.c
drivers/acpi/device_pm.c
drivers/acpi/ec.c
drivers/acpi/fan.c
drivers/acpi/scan.c
drivers/acpi/utils.c
drivers/acpi/video.c
drivers/ata/Kconfig
drivers/base/power/opp.c
drivers/cpufreq/intel_pstate.c
drivers/infiniband/Kconfig
drivers/infiniband/core/Makefile
drivers/infiniband/core/addr.c
drivers/infiniband/core/multicast.c
drivers/infiniband/core/umem.c
drivers/infiniband/core/umem_odp.c [new file with mode: 0644]
drivers/infiniband/core/umem_rbtree.c [new file with mode: 0644]
drivers/infiniband/core/uverbs.h
drivers/infiniband/core/uverbs_cmd.c
drivers/infiniband/core/uverbs_main.c
drivers/infiniband/core/verbs.c
drivers/infiniband/hw/amso1100/c2_provider.c
drivers/infiniband/hw/cxgb4/cm.c
drivers/infiniband/hw/cxgb4/device.c
drivers/infiniband/hw/cxgb4/mem.c
drivers/infiniband/hw/cxgb4/qp.c
drivers/infiniband/hw/ehca/ehca_mrmw.c
drivers/infiniband/hw/ipath/ipath_mr.c
drivers/infiniband/hw/mlx4/mr.c
drivers/infiniband/hw/mlx5/Makefile
drivers/infiniband/hw/mlx5/main.c
drivers/infiniband/hw/mlx5/mem.c
drivers/infiniband/hw/mlx5/mlx5_ib.h
drivers/infiniband/hw/mlx5/mr.c
drivers/infiniband/hw/mlx5/odp.c [new file with mode: 0644]
drivers/infiniband/hw/mlx5/qp.c
drivers/infiniband/hw/nes/nes_verbs.c
drivers/infiniband/hw/ocrdma/ocrdma_ah.c
drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
drivers/infiniband/hw/qib/qib_mr.c
drivers/infiniband/ulp/ipoib/ipoib.h
drivers/infiniband/ulp/ipoib/ipoib_cm.c
drivers/infiniband/ulp/ipoib/ipoib_ib.c
drivers/infiniband/ulp/ipoib/ipoib_main.c
drivers/infiniband/ulp/ipoib/ipoib_multicast.c
drivers/infiniband/ulp/ipoib/ipoib_verbs.c
drivers/infiniband/ulp/iser/iscsi_iser.c
drivers/infiniband/ulp/iser/iscsi_iser.h
drivers/infiniband/ulp/iser/iser_initiator.c
drivers/infiniband/ulp/iser/iser_memory.c
drivers/infiniband/ulp/iser/iser_verbs.c
drivers/infiniband/ulp/srp/ib_srp.c
drivers/leds/leds-gpio.c
drivers/media/Kconfig
drivers/media/Makefile
drivers/media/i2c/Kconfig
drivers/media/i2c/Makefile
drivers/media/i2c/saa7191.c [deleted file]
drivers/media/i2c/saa7191.h [deleted file]
drivers/media/parport/Kconfig [deleted file]
drivers/media/parport/Makefile [deleted file]
drivers/media/parport/bw-qcam.c [deleted file]
drivers/media/parport/c-qcam.c [deleted file]
drivers/media/parport/pms.c [deleted file]
drivers/media/parport/w9966.c [deleted file]
drivers/media/pci/cx88/cx88-blackbird.c
drivers/media/pci/cx88/cx88-dvb.c
drivers/media/pci/cx88/cx88-mpeg.c
drivers/media/pci/cx88/cx88-vbi.c
drivers/media/pci/cx88/cx88-video.c
drivers/media/pci/cx88/cx88.h
drivers/media/platform/Kconfig
drivers/media/platform/Makefile
drivers/media/platform/indycam.c [deleted file]
drivers/media/platform/indycam.h [deleted file]
drivers/media/platform/s5p-tv/Kconfig
drivers/media/platform/soc_camera/rcar_vin.c
drivers/media/platform/vino.c [deleted file]
drivers/media/platform/vino.h [deleted file]
drivers/media/platform/vivid/vivid-vid-out.c
drivers/media/usb/Kconfig
drivers/media/usb/Makefile
drivers/media/usb/tlg2300/Kconfig [deleted file]
drivers/media/usb/tlg2300/Makefile [deleted file]
drivers/media/usb/tlg2300/pd-alsa.c [deleted file]
drivers/media/usb/tlg2300/pd-common.h [deleted file]
drivers/media/usb/tlg2300/pd-dvb.c [deleted file]
drivers/media/usb/tlg2300/pd-main.c [deleted file]
drivers/media/usb/tlg2300/pd-radio.c [deleted file]
drivers/media/usb/tlg2300/pd-video.c [deleted file]
drivers/media/usb/tlg2300/vendorcmds.h [deleted file]
drivers/media/v4l2-core/v4l2-ioctl.c
drivers/misc/mic/host/mic_debugfs.c
drivers/mmc/host/atmel-mci.c
drivers/net/ethernet/cisco/enic/enic_main.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx5/core/eq.c
drivers/net/ethernet/mellanox/mlx5/core/fw.c
drivers/net/ethernet/mellanox/mlx5/core/qp.c
drivers/net/ethernet/sun/sunvnet.c
drivers/nfc/trf7970a.c
drivers/phy/phy-omap-usb2.c
drivers/phy/phy-ti-pipe3.c
drivers/platform/x86/Kconfig
drivers/platform/x86/acerhdf.c
drivers/platform/x86/asus-laptop.c
drivers/platform/x86/asus-nb-wmi.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/dell-laptop.c
drivers/platform/x86/dell-smo8800.c
drivers/platform/x86/dell-wmi.c
drivers/platform/x86/eeepc-laptop.c
drivers/platform/x86/fujitsu-laptop.c
drivers/platform/x86/hp-wireless.c
drivers/platform/x86/hp_accel.c
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/intel_ips.c
drivers/platform/x86/intel_oaktrail.c
drivers/platform/x86/msi-laptop.c
drivers/platform/x86/msi-wmi.c
drivers/platform/x86/sony-laptop.c
drivers/platform/x86/thinkpad_acpi.c
drivers/platform/x86/toshiba_acpi.c
drivers/power/pm2301_charger.c
drivers/scsi/scsi_pm.c
drivers/scsi/scsi_priv.h
drivers/scsi/ufs/ufshcd-pci.c
drivers/scsi/ufs/ufshcd-pltfrm.c
drivers/spi/spi-coldfire-qspi.c
drivers/spi/spi-orion.c
drivers/spi/spi-pxa2xx.c
drivers/spi/spi-qup.c
drivers/spi/spi-rockchip.c
drivers/spi/spi-s3c64xx.c
drivers/staging/gdm72xx/Kconfig
drivers/staging/media/Kconfig
drivers/staging/media/Makefile
drivers/staging/media/parport/Kconfig [new file with mode: 0644]
drivers/staging/media/parport/Makefile [new file with mode: 0644]
drivers/staging/media/parport/bw-qcam.c [new file with mode: 0644]
drivers/staging/media/parport/c-qcam.c [new file with mode: 0644]
drivers/staging/media/parport/pms.c [new file with mode: 0644]
drivers/staging/media/parport/w9966.c [new file with mode: 0644]
drivers/staging/media/tlg2300/Kconfig [new file with mode: 0644]
drivers/staging/media/tlg2300/Makefile [new file with mode: 0644]
drivers/staging/media/tlg2300/pd-alsa.c [new file with mode: 0644]
drivers/staging/media/tlg2300/pd-common.h [new file with mode: 0644]
drivers/staging/media/tlg2300/pd-dvb.c [new file with mode: 0644]
drivers/staging/media/tlg2300/pd-main.c [new file with mode: 0644]
drivers/staging/media/tlg2300/pd-radio.c [new file with mode: 0644]
drivers/staging/media/tlg2300/pd-video.c [new file with mode: 0644]
drivers/staging/media/tlg2300/vendorcmds.h [new file with mode: 0644]
drivers/staging/media/vino/Kconfig [new file with mode: 0644]
drivers/staging/media/vino/Makefile [new file with mode: 0644]
drivers/staging/media/vino/indycam.c [new file with mode: 0644]
drivers/staging/media/vino/indycam.h [new file with mode: 0644]
drivers/staging/media/vino/saa7191.c [new file with mode: 0644]
drivers/staging/media/vino/saa7191.h [new file with mode: 0644]
drivers/staging/media/vino/vino.c [new file with mode: 0644]
drivers/staging/media/vino/vino.h [new file with mode: 0644]
drivers/tty/serial/8250/8250_dw.c
drivers/tty/serial/8250/8250_mtk.c
drivers/tty/serial/mfd.c
drivers/tty/serial/msm_serial_hs.c
drivers/tty/serial/omap-serial.c
drivers/usb/core/Kconfig
drivers/usb/phy/Kconfig
drivers/usb/storage/Kconfig
drivers/vhost/vringh.c
drivers/video/fbdev/s3c-fb.c
drivers/video/fbdev/sh_mobile_meram.c
drivers/virtio/virtio.c
drivers/virtio/virtio_pci_common.c
drivers/virtio/virtio_pci_common.h
drivers/virtio/virtio_pci_legacy.c
drivers/watchdog/imx2_wdt.c
fs/hfsplus/catalog.c
fs/hfsplus/dir.c
fs/hfsplus/hfsplus_fs.h
fs/hfsplus/super.c
fs/ocfs2/alloc.c
fs/ocfs2/alloc.h
fs/ocfs2/aops.c
fs/ocfs2/dir.c
fs/ocfs2/dlm/dlmmaster.c
fs/ocfs2/file.c
fs/proc/meminfo.c
include/acpi/acpi_bus.h
include/linux/cma.h
include/linux/mlx5/device.h
include/linux/mlx5/driver.h
include/linux/mlx5/qp.h
include/linux/module.h
include/linux/pm.h
include/linux/virtio_config.h
include/linux/vringh.h
include/rdma/ib_umem.h
include/rdma/ib_umem_odp.h [new file with mode: 0644]
include/rdma/ib_verbs.h
include/scsi/scsi_device.h
include/trace/events/module.h
include/uapi/linux/v4l2-mediabus.h
include/uapi/linux/virtio_pci.h
include/uapi/rdma/ib_user_verbs.h
kernel/module.c
kernel/params.c
kernel/trace/Makefile
lib/bug.c
lib/show_mem.c
mm/cma.c
mm/memory.c
mm/mempolicy.c
mm/page_alloc.c
mm/zsmalloc.c
sound/pci/hda/hda_controller.c
sound/pci/hda/hda_intel.c
sound/soc/codecs/cs35l32.c
sound/soc/codecs/cs42xx8.c
sound/soc/codecs/max98090.c
sound/soc/codecs/pcm512x.c
sound/soc/codecs/tas2552.c
sound/soc/codecs/wm2200.c
sound/soc/codecs/wm5100.c
sound/soc/codecs/wm8962.c
sound/soc/fsl/fsl_asrc.c
sound/soc/samsung/i2s.c
tools/power/cpupower/utils/cpuidle-info.c
tools/testing/selftests/Makefile
tools/virtio/Makefile
tools/virtio/linux/virtio.h
tools/virtio/linux/virtio_byteorder.h [new file with mode: 0644]
tools/virtio/linux/virtio_config.h
tools/virtio/uapi/linux/virtio_types.h [new file with mode: 0644]
tools/virtio/virtio_test.c
tools/virtio/vringh_test.c

index 1ad6873..ada8ad6 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -17,7 +17,7 @@ Aleksey Gorelov <aleksey_gorelov@phoenix.com>
 Al Viro <viro@ftp.linux.org.uk>
 Al Viro <viro@zenIV.linux.org.uk>
 Andreas Herrmann <aherrman@de.ibm.com>
-Andrew Morton <akpm@osdl.org>
+Andrew Morton <akpm@linux-foundation.org>
 Andrew Vasquez <andrew.vasquez@qlogic.com>
 Andy Adamson <andros@citi.umich.edu>
 Archit Taneja <archit@ti.com>
@@ -102,6 +102,8 @@ Rudolf Marek <R.Marek@sh.cvut.cz>
 Rui Saraiva <rmps@joel.ist.utl.pt>
 Sachin P Sant <ssant@in.ibm.com>
 Sam Ravnborg <sam@mars.ravnborg.org>
+Santosh Shilimkar <ssantosh@kernel.org>
+Santosh Shilimkar <santosh.shilimkar@oracle.org>
 Sascha Hauer <s.hauer@pengutronix.de>
 S.ÇaÄŸlar Onur <caglar@pardus.org.tr>
 Shiraz Hashim <shiraz.linux.kernel@gmail.com> <shiraz.hashim@st.com>
diff --git a/Documentation/ABI/testing/sysfs-platform-dell-laptop b/Documentation/ABI/testing/sysfs-platform-dell-laptop
new file mode 100644 (file)
index 0000000..7969443
--- /dev/null
@@ -0,0 +1,60 @@
+What:          /sys/class/leds/dell::kbd_backlight/als_setting
+Date:          December 2014
+KernelVersion: 3.19
+Contact:       Gabriele Mazzotta <gabriele.mzt@gmail.com>,
+               Pali Rohár <pali.rohar@gmail.com>
+Description:
+               This file allows to control the automatic keyboard
+               illumination mode on some systems that have an ambient
+               light sensor. Write 1 to this file to enable the auto
+               mode, 0 to disable it.
+
+What:          /sys/class/leds/dell::kbd_backlight/start_triggers
+Date:          December 2014
+KernelVersion: 3.19
+Contact:       Gabriele Mazzotta <gabriele.mzt@gmail.com>,
+               Pali Rohár <pali.rohar@gmail.com>
+Description:
+               This file allows to control the input triggers that
+               turn on the keyboard backlight illumination that is
+               disabled because of inactivity.
+               Read the file to see the triggers available. The ones
+               enabled are preceded by '+', those disabled by '-'.
+
+               To enable a trigger, write its name preceded by '+' to
+               this file. To disable a trigger, write its name preceded
+               by '-' instead.
+
+               For example, to enable the keyboard as trigger run:
+                   echo +keyboard > /sys/class/leds/dell::kbd_backlight/start_triggers
+               To disable it:
+                   echo -keyboard > /sys/class/leds/dell::kbd_backlight/start_triggers
+
+               Note that not all the available triggers can be configured.
+
+What:          /sys/class/leds/dell::kbd_backlight/stop_timeout
+Date:          December 2014
+KernelVersion: 3.19
+Contact:       Gabriele Mazzotta <gabriele.mzt@gmail.com>,
+               Pali Rohár <pali.rohar@gmail.com>
+Description:
+               This file allows to specify the interval after which the
+               keyboard illumination is disabled because of inactivity.
+               The timeouts are expressed in seconds, minutes, hours and
+               days, for which the symbols are 's', 'm', 'h' and 'd'
+               respectively.
+
+               To configure the timeout, write to this file a value along
+               with any the above units. If no unit is specified, the value
+               is assumed to be expressed in seconds.
+
+               For example, to set the timeout to 10 minutes run:
+                   echo 10m > /sys/class/leds/dell::kbd_backlight/stop_timeout
+
+               Note that when this file is read, the returned value might be
+               expressed in a different unit than the one used when the timeout
+               was set.
+
+               Also note that only some timeouts are supported and that
+               some systems might fall back to a specific timeout in case
+               an invalid timeout is written to this file.
index 0a2debf..350dfb3 100644 (file)
@@ -2579,6 +2579,18 @@ fields changed from _s32 to _u32.
       </orderedlist>
     </section>
 
+    <section>
+      <title>V4L2 in Linux 3.19</title>
+      <orderedlist>
+       <listitem>
+         <para>Rewrote Colorspace chapter, added new &v4l2-ycbcr-encoding;
+and &v4l2-quantization; fields to &v4l2-pix-format;, &v4l2-pix-format-mplane;
+and &v4l2-mbus-framefmt;.
+         </para>
+       </listitem>
+      </orderedlist>
+    </section>
+
     <section id="other">
       <title>Relation of V4L2 to other Linux multimedia APIs</title>
 
index ccf6053..d5eca4b 100644 (file)
@@ -138,9 +138,25 @@ applicable values.</para></entry>
        <row>
          <entry>__u32</entry>
          <entry><structfield>flags</structfield></entry>
-           <entry>Flags set by the application or driver, see <xref
+         <entry>Flags set by the application or driver, see <xref
 linkend="format-flags" />.</entry>
        </row>
+       <row>
+         <entry>&v4l2-ycbcr-encoding;</entry>
+         <entry><structfield>ycbcr_enc</structfield></entry>
+         <entry>This information supplements the
+<structfield>colorspace</structfield> and must be set by the driver for
+capture streams and by the application for output streams,
+see <xref linkend="colorspaces" />.</entry>
+       </row>
+       <row>
+         <entry>&v4l2-quantization;</entry>
+         <entry><structfield>quantization</structfield></entry>
+         <entry>This information supplements the
+<structfield>colorspace</structfield> and must be set by the driver for
+capture streams and by the application for output streams,
+see <xref linkend="colorspaces" />.</entry>
+       </row>
       </tbody>
     </tgroup>
   </table>
@@ -231,10 +247,26 @@ codes can be used.</entry>
          <entry><structfield>flags</structfield></entry>
          <entry>Flags set by the application or driver, see <xref
 linkend="format-flags" />.</entry>
+       </row>
+       <row>
+         <entry>&v4l2-ycbcr-encoding;</entry>
+         <entry><structfield>ycbcr_enc</structfield></entry>
+         <entry>This information supplements the
+<structfield>colorspace</structfield> and must be set by the driver for
+capture streams and by the application for output streams,
+see <xref linkend="colorspaces" />.</entry>
+       </row>
+       <row>
+         <entry>&v4l2-quantization;</entry>
+         <entry><structfield>quantization</structfield></entry>
+         <entry>This information supplements the
+<structfield>colorspace</structfield> and must be set by the driver for
+capture streams and by the application for output streams,
+see <xref linkend="colorspaces" />.</entry>
        </row>
         <row>
           <entry>__u8</entry>
-          <entry><structfield>reserved[10]</structfield></entry>
+          <entry><structfield>reserved[8]</structfield></entry>
           <entry>Reserved for future extensions. Should be zeroed by the
            application.</entry>
         </row>
index 18730b9..c5ea868 100644 (file)
          <entry>Image colorspace, from &v4l2-colorspace;. See
          <xref linkend="colorspaces" /> for details.</entry>
        </row>
+       <row>
+         <entry>&v4l2-ycbcr-encoding;</entry>
+         <entry><structfield>ycbcr_enc</structfield></entry>
+         <entry>This information supplements the
+<structfield>colorspace</structfield> and must be set by the driver for
+capture streams and by the application for output streams,
+see <xref linkend="colorspaces" />.</entry>
+       </row>
+       <row>
+         <entry>&v4l2-quantization;</entry>
+         <entry><structfield>quantization</structfield></entry>
+         <entry>This information supplements the
+<structfield>colorspace</structfield> and must be set by the driver for
+capture streams and by the application for output streams,
+see <xref linkend="colorspaces" />.</entry>
+       </row>
        <row>
          <entry>__u32</entry>
-         <entry><structfield>reserved</structfield>[7]</entry>
+         <entry><structfield>reserved</structfield>[6]</entry>
          <entry>Reserved for future extensions. Applications and drivers must
          set the array to zero.</entry>
        </row>
index 7cfe618..ac0f8d9 100644 (file)
@@ -151,6 +151,15 @@ structs, ioctls) must be noted in more detail in the history chapter
 (compat.xml), along with the possible impact on existing drivers and
 applications. -->
 
+      <revision>
+       <revnumber>3.19</revnumber>
+       <date>2014-12-05</date>
+       <authorinitials>hv</authorinitials>
+       <revremark>Rewrote Colorspace chapter, added new &v4l2-ycbcr-encoding; and &v4l2-quantization; fields
+to &v4l2-pix-format;, &v4l2-pix-format-mplane; and &v4l2-mbus-framefmt;.
+       </revremark>
+      </revision>
+
       <revision>
        <revnumber>3.17</revnumber>
        <date>2014-08-04</date>
@@ -539,7 +548,7 @@ and discussions on the V4L mailing list.</revremark>
 </partinfo>
 
 <title>Video for Linux Two API Specification</title>
- <subtitle>Revision 3.17</subtitle>
+ <subtitle>Revision 3.19</subtitle>
 
   <chapter id="common">
     &sub-common;
index ba61782..9dafe6b 100644 (file)
@@ -6,6 +6,8 @@ family of devices. The current blocks are always slaves and suppot one input
 channel which can be either RGB, YUYV or BT656.
 
  - compatible: Must be one of the following
+   - "renesas,vin-r8a7794" for the R8A7794 device
+   - "renesas,vin-r8a7793" for the R8A7793 device
    - "renesas,vin-r8a7791" for the R8A7791 device
    - "renesas,vin-r8a7790" for the R8A7790 device
    - "renesas,vin-r8a7779" for the R8A7779 device
index bda85f1..4df73da 100644 (file)
@@ -1457,6 +1457,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                       disable
                         Do not enable intel_pstate as the default
                         scaling driver for the supported processors
+                      force
+                        Enable intel_pstate on systems that prohibit it by default
+                        in favor of acpi-cpufreq. Forcing the intel_pstate driver
+                        instead of acpi-cpufreq may disable platform features, such
+                        as thermal controls and power capping, that rely on ACPI
+                        P-States information being indicated to OSPM and therefore
+                        should be used with caution. This option does not work with
+                        processors that aren't supported by the intel_pstate driver
+                        or on platforms that use pcc-cpufreq instead of acpi-cpufreq.
                       no_hwp
                         Do not enable hardware P state control (HWP)
                         if available.
index e5a940e..6cfc854 100644 (file)
@@ -640,6 +640,21 @@ Colorspace: selects which colorspace should be used when generating the image.
        Changing the colorspace will result in the V4L2_EVENT_SOURCE_CHANGE
        to be sent since it emulates a detected colorspace change.
 
+Y'CbCr Encoding: selects which Y'CbCr encoding should be used when generating
+       a Y'CbCr image. This only applies if the CSC Colorbar test pattern is
+       selected, and if the format is set to a Y'CbCr format as opposed to an
+       RGB format.
+
+       Changing the Y'CbCr encoding will result in the V4L2_EVENT_SOURCE_CHANGE
+       to be sent since it emulates a detected colorspace change.
+
+Quantization: selects which quantization should be used for the RGB or Y'CbCr
+       encoding when generating the test pattern. This only applies if the CSC
+       Colorbar test pattern is selected.
+
+       Changing the quantization will result in the V4L2_EVENT_SOURCE_CHANGE
+       to be sent since it emulates a detected colorspace change.
+
 Limited RGB Range (16-235): selects if the RGB range of the HDMI source should
        be limited or full range. This combines with the Digital Video 'Rx RGB
        Quantization Range' control and can be used to test what happens if
index bae54c7..08f671d 100644 (file)
@@ -4963,6 +4963,12 @@ T:       git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux.git
 S:     Supported
 F:     drivers/idle/intel_idle.c
 
+INTEL PSTATE DRIVER
+M:     Kristen Carlson Accardi <kristen@linux.intel.com>
+L:     linux-pm@vger.kernel.org
+S:     Supported
+F:     drivers/cpufreq/intel_pstate.c
+
 INTEL FRAMEBUFFER DRIVER (excluding 810 and 815)
 M:     Maik Broemme <mbroemme@plusserver.de>
 L:     linux-fbdev@vger.kernel.org
@@ -10237,13 +10243,13 @@ L:    netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/ethernet/via/via-velocity.*
 
-VIVI VIRTUAL VIDEO DRIVER
+VIVID VIRTUAL VIDEO DRIVER
 M:     Hans Verkuil <hverkuil@xs4all.nl>
 L:     linux-media@vger.kernel.org
 T:     git git://linuxtv.org/media_tree.git
 W:     http://linuxtv.org
 S:     Maintained
-F:     drivers/media/platform/vivi*
+F:     drivers/media/platform/vivid/*
 
 VLAN (802.1Q)
 M:     Patrick McHardy <kaber@trash.net>
index e34934f..f7c65ad 100644 (file)
@@ -484,7 +484,7 @@ static void armpmu_disable(struct pmu *pmu)
        armpmu->stop(armpmu);
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int armpmu_runtime_resume(struct device *dev)
 {
        struct arm_pmu_platdata *plat = dev_get_platdata(dev);
index 6b98413..641edc3 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/pm_clock.h>
 #include <linux/platform_device.h>
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int davinci_pm_runtime_suspend(struct device *dev)
 {
        int ret;
index e4a00ba..603820e 100644 (file)
@@ -21,7 +21,7 @@ menuconfig ARCH_EXYNOS
        select HAVE_S3C_RTC if RTC_CLASS
        select PINCTRL
        select PINCTRL_EXYNOS
-       select PM_GENERIC_DOMAINS if PM_RUNTIME
+       select PM_GENERIC_DOMAINS if PM
        select S5P_DEV_MFC
        select SRAM
        select MFD_SYSCON
index ca79dda..ef6041e 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/clk-provider.h>
 #include <linux/of.h>
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int keystone_pm_runtime_suspend(struct device *dev)
 {
        int ret;
index 3f2d396..c40e209 100644 (file)
@@ -21,7 +21,7 @@
 
 #include "soc.h"
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int omap1_pm_runtime_suspend(struct device *dev)
 {
        int ret;
@@ -59,7 +59,7 @@ static struct dev_pm_domain default_pm_domain = {
 #define OMAP1_PM_DOMAIN (&default_pm_domain)
 #else
 #define OMAP1_PM_DOMAIN NULL
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM */
 
 static struct pm_clk_notifier_block platform_bus_notifier = {
        .pm_domain = OMAP1_PM_DOMAIN,
index 4fc8383..a1bd6af 100644 (file)
@@ -361,7 +361,7 @@ static void __init omap_hwmod_init_postsetup(void)
        u8 postsetup_state;
 
        /* Set the default postsetup state for all hwmods */
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
        postsetup_state = _HWMOD_STATE_IDLE;
 #else
        postsetup_state = _HWMOD_STATE_ENABLED;
index 8c58b71..be9541e 100644 (file)
@@ -588,7 +588,7 @@ odbs_exit:
        return ERR_PTR(ret);
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int _od_runtime_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
index 371b55b..074e52b 100644 (file)
@@ -231,7 +231,7 @@ config IA64_SGI_UV
 config IA64_HP_SIM
        bool "Ski-simulator"
        select SWIOTLB
-       depends on !PM_RUNTIME
+       depends on !PM
 
 endchoice
 
index 7ffe0a2..a6745e7 100644 (file)
@@ -3968,7 +3968,7 @@ bool mp_should_keep_irq(struct device *dev)
 {
        if (dev->power.is_prepared)
                return true;
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
        if (dev->power.runtime_status == RPM_SUSPENDING)
                return true;
 #endif
index 7556e7c..9b693d5 100644 (file)
@@ -304,60 +304,6 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
         * Linux ignores it, except for the machines enumerated below.
         */
 
-       /*
-        * Lenovo has a mix of systems OSI(Linux) situations
-        * and thus we can not wildcard the vendor.
-        *
-        * _OSI(Linux) helps sound
-        * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"),
-        * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"),
-        * T400, T500
-        * _OSI(Linux) has Linux specific hooks
-        * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X61"),
-        * _OSI(Linux) is a NOP:
-        * DMI_MATCH(DMI_PRODUCT_VERSION, "3000 N100"),
-        * DMI_MATCH(DMI_PRODUCT_VERSION, "LENOVO3000 V100"),
-        */
-       {
-       .callback = dmi_enable_osi_linux,
-       .ident = "Lenovo ThinkPad R61",
-       .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                    DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"),
-               },
-       },
-       {
-       .callback = dmi_enable_osi_linux,
-       .ident = "Lenovo ThinkPad T61",
-       .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                    DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"),
-               },
-       },
-       {
-       .callback = dmi_enable_osi_linux,
-       .ident = "Lenovo ThinkPad X61",
-       .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                    DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X61"),
-               },
-       },
-       {
-       .callback = dmi_enable_osi_linux,
-       .ident = "Lenovo ThinkPad T400",
-       .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                    DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T400"),
-               },
-       },
-       {
-       .callback = dmi_enable_osi_linux,
-       .ident = "Lenovo ThinkPad T500",
-       .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                    DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T500"),
-               },
-       },
        /*
         * Without this this EEEpc exports a non working WMI interface, with
         * this it exports a working "good old" eeepc_laptop interface, fixing
index 8976401..c2daa85 100644 (file)
@@ -680,13 +680,21 @@ static int acpi_device_wakeup(struct acpi_device *adev, u32 target_state,
                if (error)
                        return error;
 
+               if (adev->wakeup.flags.enabled)
+                       return 0;
+
                res = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number);
-               if (ACPI_FAILURE(res)) {
+               if (ACPI_SUCCESS(res)) {
+                       adev->wakeup.flags.enabled = 1;
+               } else {
                        acpi_disable_wakeup_device_power(adev);
                        return -EIO;
                }
        } else {
-               acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number);
+               if (adev->wakeup.flags.enabled) {
+                       acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number);
+                       adev->wakeup.flags.enabled = 0;
+               }
                acpi_disable_wakeup_device_power(adev);
        }
        return 0;
index 5f9b74b..1b5853f 100644 (file)
@@ -844,6 +844,8 @@ static int ec_install_handlers(struct acpi_ec *ec)
 
 static void ec_remove_handlers(struct acpi_ec *ec)
 {
+       if (!test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags))
+               return;
        acpi_disable_gpe(NULL, ec->gpe);
        if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle,
                                ACPI_ADR_SPACE_EC, &acpi_ec_space_handler)))
index caf9b76..7a36f02 100644 (file)
@@ -325,6 +325,7 @@ static int acpi_fan_probe(struct platform_device *pdev)
        struct thermal_cooling_device *cdev;
        struct acpi_fan *fan;
        struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
+       char *name;
 
        fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
        if (!fan) {
@@ -346,7 +347,12 @@ static int acpi_fan_probe(struct platform_device *pdev)
                }
        }
 
-       cdev = thermal_cooling_device_register("Fan", device,
+       if (!strncmp(pdev->name, "PNP0C0B", strlen("PNP0C0B")))
+               name = "Fan";
+       else
+               name = acpi_device_bid(device);
+
+       cdev = thermal_cooling_device_register(name, device,
                                                &fan_cooling_ops);
        if (IS_ERR(cdev)) {
                result = PTR_ERR(cdev);
index 1b1cf55..16914cc 100644 (file)
@@ -2214,7 +2214,7 @@ static void acpi_device_dep_initialize(struct acpi_device *adev)
        status = acpi_evaluate_reference(adev->handle, "_DEP", NULL,
                                        &dep_devices);
        if (ACPI_FAILURE(status)) {
-               dev_err(&adev->dev, "Failed to evaluate _DEP.\n");
+               dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n");
                return;
        }
 
@@ -2224,7 +2224,7 @@ static void acpi_device_dep_initialize(struct acpi_device *adev)
 
                status = acpi_get_object_info(dep_devices.handles[i], &info);
                if (ACPI_FAILURE(status)) {
-                       dev_err(&adev->dev, "Error reading device info\n");
+                       dev_dbg(&adev->dev, "Error reading _DEP device info\n");
                        continue;
                }
 
index dd8ff63..cd49a39 100644 (file)
@@ -346,22 +346,16 @@ acpi_evaluate_reference(acpi_handle handle,
        package = buffer.pointer;
 
        if ((buffer.length == 0) || !package) {
-               printk(KERN_ERR PREFIX "No return object (len %X ptr %p)\n",
-                           (unsigned)buffer.length, package);
                status = AE_BAD_DATA;
                acpi_util_eval_error(handle, pathname, status);
                goto end;
        }
        if (package->type != ACPI_TYPE_PACKAGE) {
-               printk(KERN_ERR PREFIX "Expecting a [Package], found type %X\n",
-                           package->type);
                status = AE_BAD_DATA;
                acpi_util_eval_error(handle, pathname, status);
                goto end;
        }
        if (!package->package.count) {
-               printk(KERN_ERR PREFIX "[Package] has zero elements (%p)\n",
-                           package);
                status = AE_BAD_DATA;
                acpi_util_eval_error(handle, pathname, status);
                goto end;
@@ -380,17 +374,13 @@ acpi_evaluate_reference(acpi_handle handle,
 
                if (element->type != ACPI_TYPE_LOCAL_REFERENCE) {
                        status = AE_BAD_DATA;
-                       printk(KERN_ERR PREFIX
-                                   "Expecting a [Reference] package element, found type %X\n",
-                                   element->type);
                        acpi_util_eval_error(handle, pathname, status);
                        break;
                }
 
                if (!element->reference.handle) {
-                       printk(KERN_WARNING PREFIX "Invalid reference in"
-                              " package %s\n", pathname);
                        status = AE_NULL_ENTRY;
+                       acpi_util_eval_error(handle, pathname, status);
                        break;
                }
                /* Get the  acpi_handle. */
index 185a57d..1eaadff 100644 (file)
@@ -155,6 +155,7 @@ struct acpi_video_bus {
        u8 dos_setting;
        struct acpi_video_enumerated_device *attached_array;
        u8 attached_count;
+       u8 child_count;
        struct acpi_video_bus_cap cap;
        struct acpi_video_bus_flags flags;
        struct list_head video_device_list;
@@ -1159,8 +1160,12 @@ static bool acpi_video_device_in_dod(struct acpi_video_device *device)
        struct acpi_video_bus *video = device->video;
        int i;
 
-       /* If we have a broken _DOD, no need to test */
-       if (!video->attached_count)
+       /*
+        * If we have a broken _DOD or we have more than 8 output devices
+        * under the graphics controller node that we can't proper deal with
+        * in the operation region code currently, no need to test.
+        */
+       if (!video->attached_count || video->child_count > 8)
                return true;
 
        for (i = 0; i < video->attached_count; i++) {
@@ -1413,6 +1418,7 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video,
                        dev_err(&dev->dev, "Can't attach device\n");
                        break;
                }
+               video->child_count++;
        }
        return status;
 }
index cd4cccb..a3a1360 100644 (file)
@@ -61,7 +61,7 @@ config ATA_ACPI
 
 config SATA_ZPODD
        bool "SATA Zero Power Optical Disc Drive (ZPODD) support"
-       depends on ATA_ACPI && PM_RUNTIME
+       depends on ATA_ACPI && PM
        default n
        help
          This option adds support for SATA Zero Power Optical Disc
index 2d195f3..d24dd61 100644 (file)
@@ -84,7 +84,11 @@ struct dev_pm_opp {
  *
  * This is an internal data structure maintaining the link to opps attached to
  * a device. This structure is not meant to be shared to users as it is
- * meant for book keeping and private to OPP library
+ * meant for book keeping and private to OPP library.
+ *
+ * Because the opp structures can be used from both rcu and srcu readers, we
+ * need to wait for the grace period of both of them before freeing any
+ * resources. And so we have used kfree_rcu() from within call_srcu() handlers.
  */
 struct device_opp {
        struct list_head node;
@@ -382,12 +386,34 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor);
 
+static struct device_opp *add_device_opp(struct device *dev)
+{
+       struct device_opp *dev_opp;
+
+       /*
+        * Allocate a new device OPP table. In the infrequent case where a new
+        * device is needed to be added, we pay this penalty.
+        */
+       dev_opp = kzalloc(sizeof(*dev_opp), GFP_KERNEL);
+       if (!dev_opp)
+               return NULL;
+
+       dev_opp->dev = dev;
+       srcu_init_notifier_head(&dev_opp->srcu_head);
+       INIT_LIST_HEAD(&dev_opp->opp_list);
+
+       /* Secure the device list modification */
+       list_add_rcu(&dev_opp->node, &dev_opp_list);
+       return dev_opp;
+}
+
 static int dev_pm_opp_add_dynamic(struct device *dev, unsigned long freq,
                                  unsigned long u_volt, bool dynamic)
 {
        struct device_opp *dev_opp = NULL;
        struct dev_pm_opp *opp, *new_opp;
        struct list_head *head;
+       int ret;
 
        /* allocate new OPP node */
        new_opp = kzalloc(sizeof(*new_opp), GFP_KERNEL);
@@ -400,7 +426,6 @@ static int dev_pm_opp_add_dynamic(struct device *dev, unsigned long freq,
        mutex_lock(&dev_opp_list_lock);
 
        /* populate the opp table */
-       new_opp->dev_opp = dev_opp;
        new_opp->rate = freq;
        new_opp->u_volt = u_volt;
        new_opp->available = true;
@@ -409,27 +434,12 @@ static int dev_pm_opp_add_dynamic(struct device *dev, unsigned long freq,
        /* Check for existing list for 'dev' */
        dev_opp = find_device_opp(dev);
        if (IS_ERR(dev_opp)) {
-               /*
-                * Allocate a new device OPP table. In the infrequent case
-                * where a new device is needed to be added, we pay this
-                * penalty.
-                */
-               dev_opp = kzalloc(sizeof(struct device_opp), GFP_KERNEL);
+               dev_opp = add_device_opp(dev);
                if (!dev_opp) {
-                       mutex_unlock(&dev_opp_list_lock);
-                       kfree(new_opp);
-                       dev_warn(dev,
-                               "%s: Unable to create device OPP structure\n",
-                               __func__);
-                       return -ENOMEM;
+                       ret = -ENOMEM;
+                       goto free_opp;
                }
 
-               dev_opp->dev = dev;
-               srcu_init_notifier_head(&dev_opp->srcu_head);
-               INIT_LIST_HEAD(&dev_opp->opp_list);
-
-               /* Secure the device list modification */
-               list_add_rcu(&dev_opp->node, &dev_opp_list);
                head = &dev_opp->opp_list;
                goto list_add;
        }
@@ -448,18 +458,17 @@ static int dev_pm_opp_add_dynamic(struct device *dev, unsigned long freq,
 
        /* Duplicate OPPs ? */
        if (new_opp->rate == opp->rate) {
-               int ret = opp->available && new_opp->u_volt == opp->u_volt ?
+               ret = opp->available && new_opp->u_volt == opp->u_volt ?
                        0 : -EEXIST;
 
                dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n",
                         __func__, opp->rate, opp->u_volt, opp->available,
                         new_opp->rate, new_opp->u_volt, new_opp->available);
-               mutex_unlock(&dev_opp_list_lock);
-               kfree(new_opp);
-               return ret;
+               goto free_opp;
        }
 
 list_add:
+       new_opp->dev_opp = dev_opp;
        list_add_rcu(&new_opp->node, head);
        mutex_unlock(&dev_opp_list_lock);
 
@@ -469,6 +478,11 @@ list_add:
         */
        srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADD, new_opp);
        return 0;
+
+free_opp:
+       mutex_unlock(&dev_opp_list_lock);
+       kfree(new_opp);
+       return ret;
 }
 
 /**
@@ -511,10 +525,11 @@ static void kfree_device_rcu(struct rcu_head *head)
 {
        struct device_opp *device_opp = container_of(head, struct device_opp, rcu_head);
 
-       kfree(device_opp);
+       kfree_rcu(device_opp, rcu_head);
 }
 
-void __dev_pm_opp_remove(struct device_opp *dev_opp, struct dev_pm_opp *opp)
+static void __dev_pm_opp_remove(struct device_opp *dev_opp,
+                               struct dev_pm_opp *opp)
 {
        /*
         * Notify the changes in the availability of the operable
@@ -592,7 +607,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_remove);
 static int opp_set_availability(struct device *dev, unsigned long freq,
                bool availability_req)
 {
-       struct device_opp *tmp_dev_opp, *dev_opp = ERR_PTR(-ENODEV);
+       struct device_opp *dev_opp;
        struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV);
        int r = 0;
 
@@ -606,12 +621,7 @@ static int opp_set_availability(struct device *dev, unsigned long freq,
        mutex_lock(&dev_opp_list_lock);
 
        /* Find the device_opp */
-       list_for_each_entry(tmp_dev_opp, &dev_opp_list, node) {
-               if (dev == tmp_dev_opp->dev) {
-                       dev_opp = tmp_dev_opp;
-                       break;
-               }
-       }
+       dev_opp = find_device_opp(dev);
        if (IS_ERR(dev_opp)) {
                r = PTR_ERR(dev_opp);
                dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r);
@@ -768,7 +778,7 @@ EXPORT_SYMBOL_GPL(of_init_opp_table);
  */
 void of_free_opp_table(struct device *dev)
 {
-       struct device_opp *dev_opp = find_device_opp(dev);
+       struct device_opp *dev_opp;
        struct dev_pm_opp *opp, *tmp;
 
        /* Check for existing list for 'dev' */
index 1405b39..742eefb 100644 (file)
@@ -199,7 +199,14 @@ static signed int pid_calc(struct _pid *pid, int32_t busy)
 
        pid->integral += fp_error;
 
-       /* limit the integral term */
+       /*
+        * We limit the integral here so that it will never
+        * get higher than 30.  This prevents it from becoming
+        * too large an input over long periods of time and allows
+        * it to get factored out sooner.
+        *
+        * The value of 30 was chosen through experimentation.
+        */
        integral_limit = int_tofp(30);
        if (pid->integral > integral_limit)
                pid->integral = integral_limit;
@@ -616,6 +623,11 @@ static void intel_pstate_get_min_max(struct cpudata *cpu, int *min, int *max)
        if (limits.no_turbo || limits.turbo_disabled)
                max_perf = cpu->pstate.max_pstate;
 
+       /*
+        * performance can be limited by user through sysfs, by cpufreq
+        * policy, or by cpu specific default values determined through
+        * experimentation.
+        */
        max_perf_adj = fp_toint(mul_fp(int_tofp(max_perf), limits.max_perf));
        *max = clamp_t(int, max_perf_adj,
                        cpu->pstate.min_pstate, cpu->pstate.turbo_pstate);
@@ -717,11 +729,29 @@ static inline int32_t intel_pstate_get_scaled_busy(struct cpudata *cpu)
        u32 duration_us;
        u32 sample_time;
 
+       /*
+        * core_busy is the ratio of actual performance to max
+        * max_pstate is the max non turbo pstate available
+        * current_pstate was the pstate that was requested during
+        *      the last sample period.
+        *
+        * We normalize core_busy, which was our actual percent
+        * performance to what we requested during the last sample
+        * period. The result will be a percentage of busy at a
+        * specified pstate.
+        */
        core_busy = cpu->sample.core_pct_busy;
        max_pstate = int_tofp(cpu->pstate.max_pstate);
        current_pstate = int_tofp(cpu->pstate.current_pstate);
        core_busy = mul_fp(core_busy, div_fp(max_pstate, current_pstate));
 
+       /*
+        * Since we have a deferred timer, it will not fire unless
+        * we are in C0.  So, determine if the actual elapsed time
+        * is significantly greater (3x) than our sample interval.  If it
+        * is, then we were idle for a long enough period of time
+        * to adjust our busyness.
+        */
        sample_time = pid_params.sample_rate_ms  * USEC_PER_MSEC;
        duration_us = (u32) ktime_us_delta(cpu->sample.time,
                                           cpu->last_sample_time);
@@ -948,6 +978,7 @@ static struct cpufreq_driver intel_pstate_driver = {
 
 static int __initdata no_load;
 static int __initdata no_hwp;
+static unsigned int force_load;
 
 static int intel_pstate_msrs_not_valid(void)
 {
@@ -1094,7 +1125,8 @@ static bool intel_pstate_platform_pwr_mgmt_exists(void)
                        case PSS:
                                return intel_pstate_no_acpi_pss();
                        case PPC:
-                               return intel_pstate_has_acpi_ppc();
+                               return intel_pstate_has_acpi_ppc() &&
+                                       (!force_load);
                        }
        }
 
@@ -1175,6 +1207,8 @@ static int __init intel_pstate_setup(char *str)
                no_load = 1;
        if (!strcmp(str, "no_hwp"))
                no_hwp = 1;
+       if (!strcmp(str, "force"))
+               force_load = 1;
        return 0;
 }
 early_param("intel_pstate", intel_pstate_setup);
index 7708939..b899531 100644 (file)
@@ -38,6 +38,17 @@ config INFINIBAND_USER_MEM
        depends on INFINIBAND_USER_ACCESS != n
        default y
 
+config INFINIBAND_ON_DEMAND_PAGING
+       bool "InfiniBand on-demand paging support"
+       depends on INFINIBAND_USER_MEM
+       select MMU_NOTIFIER
+       default y
+       ---help---
+         On demand paging support for the InfiniBand subsystem.
+         Together with driver support this allows registration of
+         memory regions without pinning their pages, fetching the
+         pages on demand instead.
+
 config INFINIBAND_ADDR_TRANS
        bool
        depends on INFINIBAND
index ffd0af6..acf7367 100644 (file)
@@ -11,6 +11,7 @@ obj-$(CONFIG_INFINIBAND_USER_ACCESS) +=       ib_uverbs.o ib_ucm.o \
 ib_core-y :=                   packer.o ud_header.o verbs.o sysfs.o \
                                device.o fmr_pool.o cache.o netlink.o
 ib_core-$(CONFIG_INFINIBAND_USER_MEM) += umem.o
+ib_core-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o umem_rbtree.o
 
 ib_mad-y :=                    mad.o smi.o agent.o mad_rmpp.o
 
index 8172d37..f80da50 100644 (file)
@@ -176,8 +176,8 @@ static void set_timeout(unsigned long time)
        unsigned long delay;
 
        delay = time - jiffies;
-       if ((long)delay <= 0)
-               delay = 1;
+       if ((long)delay < 0)
+               delay = 0;
 
        mod_delayed_work(addr_wq, &work, delay);
 }
index d2360a8..fa17b55 100644 (file)
@@ -525,17 +525,22 @@ static void join_handler(int status, struct ib_sa_mcmember_rec *rec,
        if (status)
                process_join_error(group, status);
        else {
+               int mgids_changed, is_mgid0;
                ib_find_pkey(group->port->dev->device, group->port->port_num,
                             be16_to_cpu(rec->pkey), &pkey_index);
 
                spin_lock_irq(&group->port->lock);
-               group->rec = *rec;
                if (group->state == MCAST_BUSY &&
                    group->pkey_index == MCAST_INVALID_PKEY_INDEX)
                        group->pkey_index = pkey_index;
-               if (!memcmp(&mgid0, &group->rec.mgid, sizeof mgid0)) {
+               mgids_changed = memcmp(&rec->mgid, &group->rec.mgid,
+                                      sizeof(group->rec.mgid));
+               group->rec = *rec;
+               if (mgids_changed) {
                        rb_erase(&group->node, &group->port->table);
-                       mcast_insert(group->port, group, 1);
+                       is_mgid0 = !memcmp(&mgid0, &group->rec.mgid,
+                                          sizeof(mgid0));
+                       mcast_insert(group->port, group, is_mgid0);
                }
                spin_unlock_irq(&group->port->lock);
        }
index df0c4f6..aec7a6a 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/hugetlb.h>
 #include <linux/dma-attrs.h>
 #include <linux/slab.h>
+#include <rdma/ib_umem_odp.h>
 
 #include "uverbs.h"
 
@@ -69,6 +70,10 @@ static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int d
 
 /**
  * ib_umem_get - Pin and DMA map userspace memory.
+ *
+ * If access flags indicate ODP memory, avoid pinning. Instead, stores
+ * the mm for future page fault handling in conjunction with MMU notifiers.
+ *
  * @context: userspace context to pin memory for
  * @addr: userspace virtual address to start at
  * @size: length of region to pin
@@ -103,17 +108,30 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
 
        umem->context   = context;
        umem->length    = size;
-       umem->offset    = addr & ~PAGE_MASK;
+       umem->address   = addr;
        umem->page_size = PAGE_SIZE;
        umem->pid       = get_task_pid(current, PIDTYPE_PID);
        /*
-        * We ask for writable memory if any access flags other than
-        * "remote read" are set.  "Local write" and "remote write"
+        * We ask for writable memory if any of the following
+        * access flags are set.  "Local write" and "remote write"
         * obviously require write access.  "Remote atomic" can do
         * things like fetch and add, which will modify memory, and
         * "MW bind" can change permissions by binding a window.
         */
-       umem->writable  = !!(access & ~IB_ACCESS_REMOTE_READ);
+       umem->writable  = !!(access &
+               (IB_ACCESS_LOCAL_WRITE   | IB_ACCESS_REMOTE_WRITE |
+                IB_ACCESS_REMOTE_ATOMIC | IB_ACCESS_MW_BIND));
+
+       if (access & IB_ACCESS_ON_DEMAND) {
+               ret = ib_umem_odp_get(context, umem);
+               if (ret) {
+                       kfree(umem);
+                       return ERR_PTR(ret);
+               }
+               return umem;
+       }
+
+       umem->odp_data = NULL;
 
        /* We assume the memory is from hugetlb until proved otherwise */
        umem->hugetlb   = 1;
@@ -132,7 +150,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
        if (!vma_list)
                umem->hugetlb = 0;
 
-       npages = PAGE_ALIGN(size + umem->offset) >> PAGE_SHIFT;
+       npages = ib_umem_num_pages(umem);
 
        down_write(&current->mm->mmap_sem);
 
@@ -235,6 +253,11 @@ void ib_umem_release(struct ib_umem *umem)
        struct task_struct *task;
        unsigned long diff;
 
+       if (umem->odp_data) {
+               ib_umem_odp_release(umem);
+               return;
+       }
+
        __ib_umem_release(umem->context->device, umem, 1);
 
        task = get_pid_task(umem->pid, PIDTYPE_PID);
@@ -246,7 +269,7 @@ void ib_umem_release(struct ib_umem *umem)
        if (!mm)
                goto out;
 
-       diff = PAGE_ALIGN(umem->length + umem->offset) >> PAGE_SHIFT;
+       diff = ib_umem_num_pages(umem);
 
        /*
         * We may be called with the mm's mmap_sem already held.  This
@@ -283,6 +306,9 @@ int ib_umem_page_count(struct ib_umem *umem)
        int n;
        struct scatterlist *sg;
 
+       if (umem->odp_data)
+               return ib_umem_num_pages(umem);
+
        shift = ilog2(umem->page_size);
 
        n = 0;
@@ -292,3 +318,37 @@ int ib_umem_page_count(struct ib_umem *umem)
        return n;
 }
 EXPORT_SYMBOL(ib_umem_page_count);
+
+/*
+ * Copy from the given ib_umem's pages to the given buffer.
+ *
+ * umem - the umem to copy from
+ * offset - offset to start copying from
+ * dst - destination buffer
+ * length - buffer length
+ *
+ * Returns 0 on success, or an error code.
+ */
+int ib_umem_copy_from(void *dst, struct ib_umem *umem, size_t offset,
+                     size_t length)
+{
+       size_t end = offset + length;
+       int ret;
+
+       if (offset > umem->length || length > umem->length - offset) {
+               pr_err("ib_umem_copy_from not in range. offset: %zd umem length: %zd end: %zd\n",
+                      offset, umem->length, end);
+               return -EINVAL;
+       }
+
+       ret = sg_pcopy_to_buffer(umem->sg_head.sgl, umem->nmap, dst, length,
+                                offset + ib_umem_offset(umem));
+
+       if (ret < 0)
+               return ret;
+       else if (ret != length)
+               return -EINVAL;
+       else
+               return 0;
+}
+EXPORT_SYMBOL(ib_umem_copy_from);
diff --git a/drivers/infiniband/core/umem_odp.c b/drivers/infiniband/core/umem_odp.c
new file mode 100644 (file)
index 0000000..6095872
--- /dev/null
@@ -0,0 +1,668 @@
+/*
+ * Copyright (c) 2014 Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pid.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/vmalloc.h>
+
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_umem.h>
+#include <rdma/ib_umem_odp.h>
+
+static void ib_umem_notifier_start_account(struct ib_umem *item)
+{
+       mutex_lock(&item->odp_data->umem_mutex);
+
+       /* Only update private counters for this umem if it has them.
+        * Otherwise skip it. All page faults will be delayed for this umem. */
+       if (item->odp_data->mn_counters_active) {
+               int notifiers_count = item->odp_data->notifiers_count++;
+
+               if (notifiers_count == 0)
+                       /* Initialize the completion object for waiting on
+                        * notifiers. Since notifier_count is zero, no one
+                        * should be waiting right now. */
+                       reinit_completion(&item->odp_data->notifier_completion);
+       }
+       mutex_unlock(&item->odp_data->umem_mutex);
+}
+
+static void ib_umem_notifier_end_account(struct ib_umem *item)
+{
+       mutex_lock(&item->odp_data->umem_mutex);
+
+       /* Only update private counters for this umem if it has them.
+        * Otherwise skip it. All page faults will be delayed for this umem. */
+       if (item->odp_data->mn_counters_active) {
+               /*
+                * This sequence increase will notify the QP page fault that
+                * the page that is going to be mapped in the spte could have
+                * been freed.
+                */
+               ++item->odp_data->notifiers_seq;
+               if (--item->odp_data->notifiers_count == 0)
+                       complete_all(&item->odp_data->notifier_completion);
+       }
+       mutex_unlock(&item->odp_data->umem_mutex);
+}
+
+/* Account for a new mmu notifier in an ib_ucontext. */
+static void ib_ucontext_notifier_start_account(struct ib_ucontext *context)
+{
+       atomic_inc(&context->notifier_count);
+}
+
+/* Account for a terminating mmu notifier in an ib_ucontext.
+ *
+ * Must be called with the ib_ucontext->umem_rwsem semaphore unlocked, since
+ * the function takes the semaphore itself. */
+static void ib_ucontext_notifier_end_account(struct ib_ucontext *context)
+{
+       int zero_notifiers = atomic_dec_and_test(&context->notifier_count);
+
+       if (zero_notifiers &&
+           !list_empty(&context->no_private_counters)) {
+               /* No currently running mmu notifiers. Now is the chance to
+                * add private accounting to all previously added umems. */
+               struct ib_umem_odp *odp_data, *next;
+
+               /* Prevent concurrent mmu notifiers from working on the
+                * no_private_counters list. */
+               down_write(&context->umem_rwsem);
+
+               /* Read the notifier_count again, with the umem_rwsem
+                * semaphore taken for write. */
+               if (!atomic_read(&context->notifier_count)) {
+                       list_for_each_entry_safe(odp_data, next,
+                                                &context->no_private_counters,
+                                                no_private_counters) {
+                               mutex_lock(&odp_data->umem_mutex);
+                               odp_data->mn_counters_active = true;
+                               list_del(&odp_data->no_private_counters);
+                               complete_all(&odp_data->notifier_completion);
+                               mutex_unlock(&odp_data->umem_mutex);
+                       }
+               }
+
+               up_write(&context->umem_rwsem);
+       }
+}
+
+static int ib_umem_notifier_release_trampoline(struct ib_umem *item, u64 start,
+                                              u64 end, void *cookie) {
+       /*
+        * Increase the number of notifiers running, to
+        * prevent any further fault handling on this MR.
+        */
+       ib_umem_notifier_start_account(item);
+       item->odp_data->dying = 1;
+       /* Make sure that the fact the umem is dying is out before we release
+        * all pending page faults. */
+       smp_wmb();
+       complete_all(&item->odp_data->notifier_completion);
+       item->context->invalidate_range(item, ib_umem_start(item),
+                                       ib_umem_end(item));
+       return 0;
+}
+
+static void ib_umem_notifier_release(struct mmu_notifier *mn,
+                                    struct mm_struct *mm)
+{
+       struct ib_ucontext *context = container_of(mn, struct ib_ucontext, mn);
+
+       if (!context->invalidate_range)
+               return;
+
+       ib_ucontext_notifier_start_account(context);
+       down_read(&context->umem_rwsem);
+       rbt_ib_umem_for_each_in_range(&context->umem_tree, 0,
+                                     ULLONG_MAX,
+                                     ib_umem_notifier_release_trampoline,
+                                     NULL);
+       up_read(&context->umem_rwsem);
+}
+
+static int invalidate_page_trampoline(struct ib_umem *item, u64 start,
+                                     u64 end, void *cookie)
+{
+       ib_umem_notifier_start_account(item);
+       item->context->invalidate_range(item, start, start + PAGE_SIZE);
+       ib_umem_notifier_end_account(item);
+       return 0;
+}
+
+static void ib_umem_notifier_invalidate_page(struct mmu_notifier *mn,
+                                            struct mm_struct *mm,
+                                            unsigned long address)
+{
+       struct ib_ucontext *context = container_of(mn, struct ib_ucontext, mn);
+
+       if (!context->invalidate_range)
+               return;
+
+       ib_ucontext_notifier_start_account(context);
+       down_read(&context->umem_rwsem);
+       rbt_ib_umem_for_each_in_range(&context->umem_tree, address,
+                                     address + PAGE_SIZE,
+                                     invalidate_page_trampoline, NULL);
+       up_read(&context->umem_rwsem);
+       ib_ucontext_notifier_end_account(context);
+}
+
+static int invalidate_range_start_trampoline(struct ib_umem *item, u64 start,
+                                            u64 end, void *cookie)
+{
+       ib_umem_notifier_start_account(item);
+       item->context->invalidate_range(item, start, end);
+       return 0;
+}
+
+static void ib_umem_notifier_invalidate_range_start(struct mmu_notifier *mn,
+                                                   struct mm_struct *mm,
+                                                   unsigned long start,
+                                                   unsigned long end)
+{
+       struct ib_ucontext *context = container_of(mn, struct ib_ucontext, mn);
+
+       if (!context->invalidate_range)
+               return;
+
+       ib_ucontext_notifier_start_account(context);
+       down_read(&context->umem_rwsem);
+       rbt_ib_umem_for_each_in_range(&context->umem_tree, start,
+                                     end,
+                                     invalidate_range_start_trampoline, NULL);
+       up_read(&context->umem_rwsem);
+}
+
+static int invalidate_range_end_trampoline(struct ib_umem *item, u64 start,
+                                          u64 end, void *cookie)
+{
+       ib_umem_notifier_end_account(item);
+       return 0;
+}
+
+static void ib_umem_notifier_invalidate_range_end(struct mmu_notifier *mn,
+                                                 struct mm_struct *mm,
+                                                 unsigned long start,
+                                                 unsigned long end)
+{
+       struct ib_ucontext *context = container_of(mn, struct ib_ucontext, mn);
+
+       if (!context->invalidate_range)
+               return;
+
+       down_read(&context->umem_rwsem);
+       rbt_ib_umem_for_each_in_range(&context->umem_tree, start,
+                                     end,
+                                     invalidate_range_end_trampoline, NULL);
+       up_read(&context->umem_rwsem);
+       ib_ucontext_notifier_end_account(context);
+}
+
+static struct mmu_notifier_ops ib_umem_notifiers = {
+       .release                    = ib_umem_notifier_release,
+       .invalidate_page            = ib_umem_notifier_invalidate_page,
+       .invalidate_range_start     = ib_umem_notifier_invalidate_range_start,
+       .invalidate_range_end       = ib_umem_notifier_invalidate_range_end,
+};
+
+int ib_umem_odp_get(struct ib_ucontext *context, struct ib_umem *umem)
+{
+       int ret_val;
+       struct pid *our_pid;
+       struct mm_struct *mm = get_task_mm(current);
+
+       if (!mm)
+               return -EINVAL;
+
+       /* Prevent creating ODP MRs in child processes */
+       rcu_read_lock();
+       our_pid = get_task_pid(current->group_leader, PIDTYPE_PID);
+       rcu_read_unlock();
+       put_pid(our_pid);
+       if (context->tgid != our_pid) {
+               ret_val = -EINVAL;
+               goto out_mm;
+       }
+
+       umem->hugetlb = 0;
+       umem->odp_data = kzalloc(sizeof(*umem->odp_data), GFP_KERNEL);
+       if (!umem->odp_data) {
+               ret_val = -ENOMEM;
+               goto out_mm;
+       }
+       umem->odp_data->umem = umem;
+
+       mutex_init(&umem->odp_data->umem_mutex);
+
+       init_completion(&umem->odp_data->notifier_completion);
+
+       umem->odp_data->page_list = vzalloc(ib_umem_num_pages(umem) *
+                                           sizeof(*umem->odp_data->page_list));
+       if (!umem->odp_data->page_list) {
+               ret_val = -ENOMEM;
+               goto out_odp_data;
+       }
+
+       umem->odp_data->dma_list = vzalloc(ib_umem_num_pages(umem) *
+                                         sizeof(*umem->odp_data->dma_list));
+       if (!umem->odp_data->dma_list) {
+               ret_val = -ENOMEM;
+               goto out_page_list;
+       }
+
+       /*
+        * When using MMU notifiers, we will get a
+        * notification before the "current" task (and MM) is
+        * destroyed. We use the umem_rwsem semaphore to synchronize.
+        */
+       down_write(&context->umem_rwsem);
+       context->odp_mrs_count++;
+       if (likely(ib_umem_start(umem) != ib_umem_end(umem)))
+               rbt_ib_umem_insert(&umem->odp_data->interval_tree,
+                                  &context->umem_tree);
+       if (likely(!atomic_read(&context->notifier_count)))
+               umem->odp_data->mn_counters_active = true;
+       else
+               list_add(&umem->odp_data->no_private_counters,
+                        &context->no_private_counters);
+       downgrade_write(&context->umem_rwsem);
+
+       if (context->odp_mrs_count == 1) {
+               /*
+                * Note that at this point, no MMU notifier is running
+                * for this context!
+                */
+               atomic_set(&context->notifier_count, 0);
+               INIT_HLIST_NODE(&context->mn.hlist);
+               context->mn.ops = &ib_umem_notifiers;
+               /*
+                * Lock-dep detects a false positive for mmap_sem vs.
+                * umem_rwsem, due to not grasping downgrade_write correctly.
+                */
+               lockdep_off();
+               ret_val = mmu_notifier_register(&context->mn, mm);
+               lockdep_on();
+               if (ret_val) {
+                       pr_err("Failed to register mmu_notifier %d\n", ret_val);
+                       ret_val = -EBUSY;
+                       goto out_mutex;
+               }
+       }
+
+       up_read(&context->umem_rwsem);
+
+       /*
+        * Note that doing an mmput can cause a notifier for the relevant mm.
+        * If the notifier is called while we hold the umem_rwsem, this will
+        * cause a deadlock. Therefore, we release the reference only after we
+        * released the semaphore.
+        */
+       mmput(mm);
+       return 0;
+
+out_mutex:
+       up_read(&context->umem_rwsem);
+       vfree(umem->odp_data->dma_list);
+out_page_list:
+       vfree(umem->odp_data->page_list);
+out_odp_data:
+       kfree(umem->odp_data);
+out_mm:
+       mmput(mm);
+       return ret_val;
+}
+
+void ib_umem_odp_release(struct ib_umem *umem)
+{
+       struct ib_ucontext *context = umem->context;
+
+       /*
+        * Ensure that no more pages are mapped in the umem.
+        *
+        * It is the driver's responsibility to ensure, before calling us,
+        * that the hardware will not attempt to access the MR any more.
+        */
+       ib_umem_odp_unmap_dma_pages(umem, ib_umem_start(umem),
+                                   ib_umem_end(umem));
+
+       down_write(&context->umem_rwsem);
+       if (likely(ib_umem_start(umem) != ib_umem_end(umem)))
+               rbt_ib_umem_remove(&umem->odp_data->interval_tree,
+                                  &context->umem_tree);
+       context->odp_mrs_count--;
+       if (!umem->odp_data->mn_counters_active) {
+               list_del(&umem->odp_data->no_private_counters);
+               complete_all(&umem->odp_data->notifier_completion);
+       }
+
+       /*
+        * Downgrade the lock to a read lock. This ensures that the notifiers
+        * (who lock the mutex for reading) will be able to finish, and we
+        * will be able to enventually obtain the mmu notifiers SRCU. Note
+        * that since we are doing it atomically, no other user could register
+        * and unregister while we do the check.
+        */
+       downgrade_write(&context->umem_rwsem);
+       if (!context->odp_mrs_count) {
+               struct task_struct *owning_process = NULL;
+               struct mm_struct *owning_mm        = NULL;
+
+               owning_process = get_pid_task(context->tgid,
+                                             PIDTYPE_PID);
+               if (owning_process == NULL)
+                       /*
+                        * The process is already dead, notifier were removed
+                        * already.
+                        */
+                       goto out;
+
+               owning_mm = get_task_mm(owning_process);
+               if (owning_mm == NULL)
+                       /*
+                        * The process' mm is already dead, notifier were
+                        * removed already.
+                        */
+                       goto out_put_task;
+               mmu_notifier_unregister(&context->mn, owning_mm);
+
+               mmput(owning_mm);
+
+out_put_task:
+               put_task_struct(owning_process);
+       }
+out:
+       up_read(&context->umem_rwsem);
+
+       vfree(umem->odp_data->dma_list);
+       vfree(umem->odp_data->page_list);
+       kfree(umem->odp_data);
+       kfree(umem);
+}
+
+/*
+ * Map for DMA and insert a single page into the on-demand paging page tables.
+ *
+ * @umem: the umem to insert the page to.
+ * @page_index: index in the umem to add the page to.
+ * @page: the page struct to map and add.
+ * @access_mask: access permissions needed for this page.
+ * @current_seq: sequence number for synchronization with invalidations.
+ *               the sequence number is taken from
+ *               umem->odp_data->notifiers_seq.
+ *
+ * The function returns -EFAULT if the DMA mapping operation fails. It returns
+ * -EAGAIN if a concurrent invalidation prevents us from updating the page.
+ *
+ * The page is released via put_page even if the operation failed. For
+ * on-demand pinning, the page is released whenever it isn't stored in the
+ * umem.
+ */
+static int ib_umem_odp_map_dma_single_page(
+               struct ib_umem *umem,
+               int page_index,
+               u64 base_virt_addr,
+               struct page *page,
+               u64 access_mask,
+               unsigned long current_seq)
+{
+       struct ib_device *dev = umem->context->device;
+       dma_addr_t dma_addr;
+       int stored_page = 0;
+       int remove_existing_mapping = 0;
+       int ret = 0;
+
+       mutex_lock(&umem->odp_data->umem_mutex);
+       /*
+        * Note: we avoid writing if seq is different from the initial seq, to
+        * handle case of a racing notifier. This check also allows us to bail
+        * early if we have a notifier running in parallel with us.
+        */
+       if (ib_umem_mmu_notifier_retry(umem, current_seq)) {
+               ret = -EAGAIN;
+               goto out;
+       }
+       if (!(umem->odp_data->dma_list[page_index])) {
+               dma_addr = ib_dma_map_page(dev,
+                                          page,
+                                          0, PAGE_SIZE,
+                                          DMA_BIDIRECTIONAL);
+               if (ib_dma_mapping_error(dev, dma_addr)) {
+                       ret = -EFAULT;
+                       goto out;
+               }
+               umem->odp_data->dma_list[page_index] = dma_addr | access_mask;
+               umem->odp_data->page_list[page_index] = page;
+               stored_page = 1;
+       } else if (umem->odp_data->page_list[page_index] == page) {
+               umem->odp_data->dma_list[page_index] |= access_mask;
+       } else {
+               pr_err("error: got different pages in IB device and from get_user_pages. IB device page: %p, gup page: %p\n",
+                      umem->odp_data->page_list[page_index], page);
+               /* Better remove the mapping now, to prevent any further
+                * damage. */
+               remove_existing_mapping = 1;
+       }
+
+out:
+       mutex_unlock(&umem->odp_data->umem_mutex);
+
+       /* On Demand Paging - avoid pinning the page */
+       if (umem->context->invalidate_range || !stored_page)
+               put_page(page);
+
+       if (remove_existing_mapping && umem->context->invalidate_range) {
+               invalidate_page_trampoline(
+                       umem,
+                       base_virt_addr + (page_index * PAGE_SIZE),
+                       base_virt_addr + ((page_index+1)*PAGE_SIZE),
+                       NULL);
+               ret = -EAGAIN;
+       }
+
+       return ret;
+}
+
+/**
+ * ib_umem_odp_map_dma_pages - Pin and DMA map userspace memory in an ODP MR.
+ *
+ * Pins the range of pages passed in the argument, and maps them to
+ * DMA addresses. The DMA addresses of the mapped pages is updated in
+ * umem->odp_data->dma_list.
+ *
+ * Returns the number of pages mapped in success, negative error code
+ * for failure.
+ * An -EAGAIN error code is returned when a concurrent mmu notifier prevents
+ * the function from completing its task.
+ *
+ * @umem: the umem to map and pin
+ * @user_virt: the address from which we need to map.
+ * @bcnt: the minimal number of bytes to pin and map. The mapping might be
+ *        bigger due to alignment, and may also be smaller in case of an error
+ *        pinning or mapping a page. The actual pages mapped is returned in
+ *        the return value.
+ * @access_mask: bit mask of the requested access permissions for the given
+ *               range.
+ * @current_seq: the MMU notifiers sequance value for synchronization with
+ *               invalidations. the sequance number is read from
+ *               umem->odp_data->notifiers_seq before calling this function
+ */
+int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 user_virt, u64 bcnt,
+                             u64 access_mask, unsigned long current_seq)
+{
+       struct task_struct *owning_process  = NULL;
+       struct mm_struct   *owning_mm       = NULL;
+       struct page       **local_page_list = NULL;
+       u64 off;
+       int j, k, ret = 0, start_idx, npages = 0;
+       u64 base_virt_addr;
+
+       if (access_mask == 0)
+               return -EINVAL;
+
+       if (user_virt < ib_umem_start(umem) ||
+           user_virt + bcnt > ib_umem_end(umem))
+               return -EFAULT;
+
+       local_page_list = (struct page **)__get_free_page(GFP_KERNEL);
+       if (!local_page_list)
+               return -ENOMEM;
+
+       off = user_virt & (~PAGE_MASK);
+       user_virt = user_virt & PAGE_MASK;
+       base_virt_addr = user_virt;
+       bcnt += off; /* Charge for the first page offset as well. */
+
+       owning_process = get_pid_task(umem->context->tgid, PIDTYPE_PID);
+       if (owning_process == NULL) {
+               ret = -EINVAL;
+               goto out_no_task;
+       }
+
+       owning_mm = get_task_mm(owning_process);
+       if (owning_mm == NULL) {
+               ret = -EINVAL;
+               goto out_put_task;
+       }
+
+       start_idx = (user_virt - ib_umem_start(umem)) >> PAGE_SHIFT;
+       k = start_idx;
+
+       while (bcnt > 0) {
+               const size_t gup_num_pages =
+                       min_t(size_t, ALIGN(bcnt, PAGE_SIZE) / PAGE_SIZE,
+                             PAGE_SIZE / sizeof(struct page *));
+
+               down_read(&owning_mm->mmap_sem);
+               /*
+                * Note: this might result in redundent page getting. We can
+                * avoid this by checking dma_list to be 0 before calling
+                * get_user_pages. However, this make the code much more
+                * complex (and doesn't gain us much performance in most use
+                * cases).
+                */
+               npages = get_user_pages(owning_process, owning_mm, user_virt,
+                                       gup_num_pages,
+                                       access_mask & ODP_WRITE_ALLOWED_BIT, 0,
+                                       local_page_list, NULL);
+               up_read(&owning_mm->mmap_sem);
+
+               if (npages < 0)
+                       break;
+
+               bcnt -= min_t(size_t, npages << PAGE_SHIFT, bcnt);
+               user_virt += npages << PAGE_SHIFT;
+               for (j = 0; j < npages; ++j) {
+                       ret = ib_umem_odp_map_dma_single_page(
+                               umem, k, base_virt_addr, local_page_list[j],
+                               access_mask, current_seq);
+                       if (ret < 0)
+                               break;
+                       k++;
+               }
+
+               if (ret < 0) {
+                       /* Release left over pages when handling errors. */
+                       for (++j; j < npages; ++j)
+                               put_page(local_page_list[j]);
+                       break;
+               }
+       }
+
+       if (ret >= 0) {
+               if (npages < 0 && k == start_idx)
+                       ret = npages;
+               else
+                       ret = k - start_idx;
+       }
+
+       mmput(owning_mm);
+out_put_task:
+       put_task_struct(owning_process);
+out_no_task:
+       free_page((unsigned long)local_page_list);
+       return ret;
+}
+EXPORT_SYMBOL(ib_umem_odp_map_dma_pages);
+
+void ib_umem_odp_unmap_dma_pages(struct ib_umem *umem, u64 virt,
+                                u64 bound)
+{
+       int idx;
+       u64 addr;
+       struct ib_device *dev = umem->context->device;
+
+       virt  = max_t(u64, virt,  ib_umem_start(umem));
+       bound = min_t(u64, bound, ib_umem_end(umem));
+       /* Note that during the run of this function, the
+        * notifiers_count of the MR is > 0, preventing any racing
+        * faults from completion. We might be racing with other
+        * invalidations, so we must make sure we free each page only
+        * once. */
+       for (addr = virt; addr < bound; addr += (u64)umem->page_size) {
+               idx = (addr - ib_umem_start(umem)) / PAGE_SIZE;
+               mutex_lock(&umem->odp_data->umem_mutex);
+               if (umem->odp_data->page_list[idx]) {
+                       struct page *page = umem->odp_data->page_list[idx];
+                       struct page *head_page = compound_head(page);
+                       dma_addr_t dma = umem->odp_data->dma_list[idx];
+                       dma_addr_t dma_addr = dma & ODP_DMA_ADDR_MASK;
+
+                       WARN_ON(!dma_addr);
+
+                       ib_dma_unmap_page(dev, dma_addr, PAGE_SIZE,
+                                         DMA_BIDIRECTIONAL);
+                       if (dma & ODP_WRITE_ALLOWED_BIT)
+                               /*
+                                * set_page_dirty prefers being called with
+                                * the page lock. However, MMU notifiers are
+                                * called sometimes with and sometimes without
+                                * the lock. We rely on the umem_mutex instead
+                                * to prevent other mmu notifiers from
+                                * continuing and allowing the page mapping to
+                                * be removed.
+                                */
+                               set_page_dirty(head_page);
+                       /* on demand pinning support */
+                       if (!umem->context->invalidate_range)
+                               put_page(page);
+                       umem->odp_data->page_list[idx] = NULL;
+                       umem->odp_data->dma_list[idx] = 0;
+               }
+               mutex_unlock(&umem->odp_data->umem_mutex);
+       }
+}
+EXPORT_SYMBOL(ib_umem_odp_unmap_dma_pages);
diff --git a/drivers/infiniband/core/umem_rbtree.c b/drivers/infiniband/core/umem_rbtree.c
new file mode 100644 (file)
index 0000000..727d788
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2014 Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interval_tree_generic.h>
+#include <linux/sched.h>
+#include <linux/gfp.h>
+#include <rdma/ib_umem_odp.h>
+
+/*
+ * The ib_umem list keeps track of memory regions for which the HW
+ * device request to receive notification when the related memory
+ * mapping is changed.
+ *
+ * ib_umem_lock protects the list.
+ */
+
+static inline u64 node_start(struct umem_odp_node *n)
+{
+       struct ib_umem_odp *umem_odp =
+                       container_of(n, struct ib_umem_odp, interval_tree);
+
+       return ib_umem_start(umem_odp->umem);
+}
+
+/* Note that the representation of the intervals in the interval tree
+ * considers the ending point as contained in the interval, while the
+ * function ib_umem_end returns the first address which is not contained
+ * in the umem.
+ */
+static inline u64 node_last(struct umem_odp_node *n)
+{
+       struct ib_umem_odp *umem_odp =
+                       container_of(n, struct ib_umem_odp, interval_tree);
+
+       return ib_umem_end(umem_odp->umem) - 1;
+}
+
+INTERVAL_TREE_DEFINE(struct umem_odp_node, rb, u64, __subtree_last,
+                    node_start, node_last, , rbt_ib_umem)
+
+/* @last is not a part of the interval. See comment for function
+ * node_last.
+ */
+int rbt_ib_umem_for_each_in_range(struct rb_root *root,
+                                 u64 start, u64 last,
+                                 umem_call_back cb,
+                                 void *cookie)
+{
+       int ret_val = 0;
+       struct umem_odp_node *node;
+       struct ib_umem_odp *umem;
+
+       if (unlikely(start == last))
+               return ret_val;
+
+       for (node = rbt_ib_umem_iter_first(root, start, last - 1); node;
+                       node = rbt_ib_umem_iter_next(node, start, last - 1)) {
+               umem = container_of(node, struct ib_umem_odp, interval_tree);
+               ret_val = cb(umem->umem, start, last, cookie) || ret_val;
+       }
+
+       return ret_val;
+}
index 643c08a..b716b08 100644 (file)
@@ -258,5 +258,6 @@ IB_UVERBS_DECLARE_CMD(close_xrcd);
 
 IB_UVERBS_DECLARE_EX_CMD(create_flow);
 IB_UVERBS_DECLARE_EX_CMD(destroy_flow);
+IB_UVERBS_DECLARE_EX_CMD(query_device);
 
 #endif /* UVERBS_H */
index 5ba2a86..532d8eb 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/file.h>
 #include <linux/fs.h>
 #include <linux/slab.h>
+#include <linux/sched.h>
 
 #include <asm/uaccess.h>
 
@@ -288,6 +289,9 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
        struct ib_uverbs_get_context_resp resp;
        struct ib_udata                   udata;
        struct ib_device                 *ibdev = file->device->ib_dev;
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+       struct ib_device_attr             dev_attr;
+#endif
        struct ib_ucontext               *ucontext;
        struct file                      *filp;
        int ret;
@@ -325,8 +329,25 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
        INIT_LIST_HEAD(&ucontext->ah_list);
        INIT_LIST_HEAD(&ucontext->xrcd_list);
        INIT_LIST_HEAD(&ucontext->rule_list);
+       rcu_read_lock();
+       ucontext->tgid = get_task_pid(current->group_leader, PIDTYPE_PID);
+       rcu_read_unlock();
        ucontext->closing = 0;
 
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+       ucontext->umem_tree = RB_ROOT;
+       init_rwsem(&ucontext->umem_rwsem);
+       ucontext->odp_mrs_count = 0;
+       INIT_LIST_HEAD(&ucontext->no_private_counters);
+
+       ret = ib_query_device(ibdev, &dev_attr);
+       if (ret)
+               goto err_free;
+       if (!(dev_attr.device_cap_flags & IB_DEVICE_ON_DEMAND_PAGING))
+               ucontext->invalidate_range = NULL;
+
+#endif
+
        resp.num_comp_vectors = file->device->num_comp_vectors;
 
        ret = get_unused_fd_flags(O_CLOEXEC);
@@ -371,6 +392,7 @@ err_fd:
        put_unused_fd(resp.async_fd);
 
 err_free:
+       put_pid(ucontext->tgid);
        ibdev->dealloc_ucontext(ucontext);
 
 err:
@@ -378,6 +400,52 @@ err:
        return ret;
 }
 
+static void copy_query_dev_fields(struct ib_uverbs_file *file,
+                                 struct ib_uverbs_query_device_resp *resp,
+                                 struct ib_device_attr *attr)
+{
+       resp->fw_ver            = attr->fw_ver;
+       resp->node_guid         = file->device->ib_dev->node_guid;
+       resp->sys_image_guid    = attr->sys_image_guid;
+       resp->max_mr_size       = attr->max_mr_size;
+       resp->page_size_cap     = attr->page_size_cap;
+       resp->vendor_id         = attr->vendor_id;
+       resp->vendor_part_id    = attr->vendor_part_id;
+       resp->hw_ver            = attr->hw_ver;
+       resp->max_qp            = attr->max_qp;
+       resp->max_qp_wr         = attr->max_qp_wr;
+       resp->device_cap_flags  = attr->device_cap_flags;
+       resp->max_sge           = attr->max_sge;
+       resp->max_sge_rd        = attr->max_sge_rd;
+       resp->max_cq            = attr->max_cq;
+       resp->max_cqe           = attr->max_cqe;
+       resp->max_mr            = attr->max_mr;
+       resp->max_pd            = attr->max_pd;
+       resp->max_qp_rd_atom    = attr->max_qp_rd_atom;
+       resp->max_ee_rd_atom    = attr->max_ee_rd_atom;
+       resp->max_res_rd_atom   = attr->max_res_rd_atom;
+       resp->max_qp_init_rd_atom       = attr->max_qp_init_rd_atom;
+       resp->max_ee_init_rd_atom       = attr->max_ee_init_rd_atom;
+       resp->atomic_cap                = attr->atomic_cap;
+       resp->max_ee                    = attr->max_ee;
+       resp->max_rdd                   = attr->max_rdd;
+       resp->max_mw                    = attr->max_mw;
+       resp->max_raw_ipv6_qp           = attr->max_raw_ipv6_qp;
+       resp->max_raw_ethy_qp           = attr->max_raw_ethy_qp;
+       resp->max_mcast_grp             = attr->max_mcast_grp;
+       resp->max_mcast_qp_attach       = attr->max_mcast_qp_attach;
+       resp->max_total_mcast_qp_attach = attr->max_total_mcast_qp_attach;
+       resp->max_ah                    = attr->max_ah;
+       resp->max_fmr                   = attr->max_fmr;
+       resp->max_map_per_fmr           = attr->max_map_per_fmr;
+       resp->max_srq                   = attr->max_srq;
+       resp->max_srq_wr                = attr->max_srq_wr;
+       resp->max_srq_sge               = attr->max_srq_sge;
+       resp->max_pkeys                 = attr->max_pkeys;
+       resp->local_ca_ack_delay        = attr->local_ca_ack_delay;
+       resp->phys_port_cnt             = file->device->ib_dev->phys_port_cnt;
+}
+
 ssize_t ib_uverbs_query_device(struct ib_uverbs_file *file,
                               const char __user *buf,
                               int in_len, int out_len)
@@ -398,47 +466,7 @@ ssize_t ib_uverbs_query_device(struct ib_uverbs_file *file,
                return ret;
 
        memset(&resp, 0, sizeof resp);
-
-       resp.fw_ver                    = attr.fw_ver;
-       resp.node_guid                 = file->device->ib_dev->node_guid;
-       resp.sys_image_guid            = attr.sys_image_guid;
-       resp.max_mr_size               = attr.max_mr_size;
-       resp.page_size_cap             = attr.page_size_cap;
-       resp.vendor_id                 = attr.vendor_id;
-       resp.vendor_part_id            = attr.vendor_part_id;
-       resp.hw_ver                    = attr.hw_ver;
-       resp.max_qp                    = attr.max_qp;
-       resp.max_qp_wr                 = attr.max_qp_wr;
-       resp.device_cap_flags          = attr.device_cap_flags;
-       resp.max_sge                   = attr.max_sge;
-       resp.max_sge_rd                = attr.max_sge_rd;
-       resp.max_cq                    = attr.max_cq;
-       resp.max_cqe                   = attr.max_cqe;
-       resp.max_mr                    = attr.max_mr;
-       resp.max_pd                    = attr.max_pd;
-       resp.max_qp_rd_atom            = attr.max_qp_rd_atom;
-       resp.max_ee_rd_atom            = attr.max_ee_rd_atom;
-       resp.max_res_rd_atom           = attr.max_res_rd_atom;
-       resp.max_qp_init_rd_atom       = attr.max_qp_init_rd_atom;
-       resp.max_ee_init_rd_atom       = attr.max_ee_init_rd_atom;
-       resp.atomic_cap                = attr.atomic_cap;
-       resp.max_ee                    = attr.max_ee;
-       resp.max_rdd                   = attr.max_rdd;
-       resp.max_mw                    = attr.max_mw;
-       resp.max_raw_ipv6_qp           = attr.max_raw_ipv6_qp;
-       resp.max_raw_ethy_qp           = attr.max_raw_ethy_qp;
-       resp.max_mcast_grp             = attr.max_mcast_grp;
-       resp.max_mcast_qp_attach       = attr.max_mcast_qp_attach;
-       resp.max_total_mcast_qp_attach = attr.max_total_mcast_qp_attach;
-       resp.max_ah                    = attr.max_ah;
-       resp.max_fmr                   = attr.max_fmr;
-       resp.max_map_per_fmr           = attr.max_map_per_fmr;
-       resp.max_srq                   = attr.max_srq;
-       resp.max_srq_wr                = attr.max_srq_wr;
-       resp.max_srq_sge               = attr.max_srq_sge;
-       resp.max_pkeys                 = attr.max_pkeys;
-       resp.local_ca_ack_delay        = attr.local_ca_ack_delay;
-       resp.phys_port_cnt             = file->device->ib_dev->phys_port_cnt;
+       copy_query_dev_fields(file, &resp, &attr);
 
        if (copy_to_user((void __user *) (unsigned long) cmd.response,
                         &resp, sizeof resp))
@@ -947,6 +975,18 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file,
                goto err_free;
        }
 
+       if (cmd.access_flags & IB_ACCESS_ON_DEMAND) {
+               struct ib_device_attr attr;
+
+               ret = ib_query_device(pd->device, &attr);
+               if (ret || !(attr.device_cap_flags &
+                               IB_DEVICE_ON_DEMAND_PAGING)) {
+                       pr_debug("ODP support not available\n");
+                       ret = -EINVAL;
+                       goto err_put;
+               }
+       }
+
        mr = pd->device->reg_user_mr(pd, cmd.start, cmd.length, cmd.hca_va,
                                     cmd.access_flags, &udata);
        if (IS_ERR(mr)) {
@@ -3253,3 +3293,52 @@ ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file,
 
        return ret ? ret : in_len;
 }
+
+int ib_uverbs_ex_query_device(struct ib_uverbs_file *file,
+                             struct ib_udata *ucore,
+                             struct ib_udata *uhw)
+{
+       struct ib_uverbs_ex_query_device_resp resp;
+       struct ib_uverbs_ex_query_device  cmd;
+       struct ib_device_attr attr;
+       struct ib_device *device;
+       int err;
+
+       device = file->device->ib_dev;
+       if (ucore->inlen < sizeof(cmd))
+               return -EINVAL;
+
+       err = ib_copy_from_udata(&cmd, ucore, sizeof(cmd));
+       if (err)
+               return err;
+
+       if (cmd.reserved)
+               return -EINVAL;
+
+       err = device->query_device(device, &attr);
+       if (err)
+               return err;
+
+       memset(&resp, 0, sizeof(resp));
+       copy_query_dev_fields(file, &resp.base, &attr);
+       resp.comp_mask = 0;
+
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+       if (cmd.comp_mask & IB_USER_VERBS_EX_QUERY_DEVICE_ODP) {
+               resp.odp_caps.general_caps = attr.odp_caps.general_caps;
+               resp.odp_caps.per_transport_caps.rc_odp_caps =
+                       attr.odp_caps.per_transport_caps.rc_odp_caps;
+               resp.odp_caps.per_transport_caps.uc_odp_caps =
+                       attr.odp_caps.per_transport_caps.uc_odp_caps;
+               resp.odp_caps.per_transport_caps.ud_odp_caps =
+                       attr.odp_caps.per_transport_caps.ud_odp_caps;
+               resp.comp_mask |= IB_USER_VERBS_EX_QUERY_DEVICE_ODP;
+       }
+#endif
+
+       err = ib_copy_to_udata(ucore, &resp, sizeof(resp));
+       if (err)
+               return err;
+
+       return 0;
+}
index 71ab83f..e6c23b9 100644 (file)
@@ -122,7 +122,8 @@ static int (*uverbs_ex_cmd_table[])(struct ib_uverbs_file *file,
                                    struct ib_udata *ucore,
                                    struct ib_udata *uhw) = {
        [IB_USER_VERBS_EX_CMD_CREATE_FLOW]      = ib_uverbs_ex_create_flow,
-       [IB_USER_VERBS_EX_CMD_DESTROY_FLOW]     = ib_uverbs_ex_destroy_flow
+       [IB_USER_VERBS_EX_CMD_DESTROY_FLOW]     = ib_uverbs_ex_destroy_flow,
+       [IB_USER_VERBS_EX_CMD_QUERY_DEVICE]     = ib_uverbs_ex_query_device
 };
 
 static void ib_uverbs_add_one(struct ib_device *device);
@@ -296,6 +297,8 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
                kfree(uobj);
        }
 
+       put_pid(context->tgid);
+
        return context->device->dealloc_ucontext(context);
 }
 
index c2b89cc..f93eb8d 100644 (file)
@@ -879,7 +879,8 @@ int ib_resolve_eth_l2_attrs(struct ib_qp *qp,
                if (rdma_link_local_addr((struct in6_addr *)qp_attr->ah_attr.grh.dgid.raw)) {
                        rdma_get_ll_mac((struct in6_addr *)qp_attr->ah_attr.grh.dgid.raw, qp_attr->ah_attr.dmac);
                        rdma_get_ll_mac((struct in6_addr *)sgid.raw, qp_attr->smac);
-                       qp_attr->vlan_id = rdma_get_vlan_id(&sgid);
+                       if (!(*qp_attr_mask & IB_QP_VID))
+                               qp_attr->vlan_id = rdma_get_vlan_id(&sgid);
                } else {
                        ret = rdma_addr_find_dmac_by_grh(&sgid, &qp_attr->ah_attr.grh.dgid,
                                        qp_attr->ah_attr.dmac, &qp_attr->vlan_id);
index 2d5cbf4..bdf3507 100644 (file)
@@ -476,7 +476,7 @@ static struct ib_mr *c2_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
                                         c2mr->umem->page_size,
                                         i,
                                         length,
-                                        c2mr->umem->offset,
+                                        ib_umem_offset(c2mr->umem),
                                         &kva,
                                         c2_convert_access(acc),
                                         c2mr);
index 4b8c611..9edc200 100644 (file)
@@ -1640,7 +1640,8 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb)
                __state_set(&ep->com, MPA_REQ_RCVD);
 
                /* drive upcall */
-               mutex_lock(&ep->parent_ep->com.mutex);
+               mutex_lock_nested(&ep->parent_ep->com.mutex,
+                                 SINGLE_DEPTH_NESTING);
                if (ep->parent_ep->com.state != DEAD) {
                        if (connect_request_upcall(ep))
                                abort_connection(ep, skb, GFP_KERNEL);
@@ -3126,6 +3127,8 @@ static int create_server6(struct c4iw_dev *dev, struct c4iw_listen_ep *ep)
                err = c4iw_wait_for_reply(&ep->com.dev->rdev,
                                          &ep->com.wr_wait,
                                          0, 0, __func__);
+       else if (err > 0)
+               err = net_xmit_errno(err);
        if (err)
                pr_err("cxgb4_create_server6/filter failed err %d stid %d laddr %pI6 lport %d\n",
                       err, ep->stid,
@@ -3159,6 +3162,8 @@ static int create_server4(struct c4iw_dev *dev, struct c4iw_listen_ep *ep)
                        err = c4iw_wait_for_reply(&ep->com.dev->rdev,
                                                  &ep->com.wr_wait,
                                                  0, 0, __func__);
+               else if (err > 0)
+                       err = net_xmit_errno(err);
        }
        if (err)
                pr_err("cxgb4_create_server/filter failed err %d stid %d laddr %pI4 lport %d\n"
index 72f1f05..eb5df4e 100644 (file)
@@ -670,7 +670,7 @@ static int ep_open(struct inode *inode, struct file *file)
        idr_for_each(&epd->devp->stid_idr, count_idrs, &count);
        spin_unlock_irq(&epd->devp->lock);
 
-       epd->bufsize = count * 160;
+       epd->bufsize = count * 240;
        epd->buf = vmalloc(epd->bufsize);
        if (!epd->buf) {
                ret = -ENOMEM;
index 0744455..cb43c22 100644 (file)
@@ -50,6 +50,13 @@ static int inline_threshold = C4IW_INLINE_THRESHOLD;
 module_param(inline_threshold, int, 0644);
 MODULE_PARM_DESC(inline_threshold, "inline vs dsgl threshold (default=128)");
 
+static int mr_exceeds_hw_limits(struct c4iw_dev *dev, u64 length)
+{
+       return (is_t4(dev->rdev.lldi.adapter_type) ||
+               is_t5(dev->rdev.lldi.adapter_type)) &&
+               length >= 8*1024*1024*1024ULL;
+}
+
 static int _c4iw_write_mem_dma_aligned(struct c4iw_rdev *rdev, u32 addr,
                                       u32 len, dma_addr_t data, int wait)
 {
@@ -369,9 +376,11 @@ static int register_mem(struct c4iw_dev *rhp, struct c4iw_pd *php,
        int ret;
 
        ret = write_tpt_entry(&rhp->rdev, 0, &stag, 1, mhp->attr.pdid,
-                             FW_RI_STAG_NSMR, mhp->attr.perms,
+                             FW_RI_STAG_NSMR, mhp->attr.len ?
+                             mhp->attr.perms : 0,
                              mhp->attr.mw_bind_enable, mhp->attr.zbva,
-                             mhp->attr.va_fbo, mhp->attr.len, shift - 12,
+                             mhp->attr.va_fbo, mhp->attr.len ?
+                             mhp->attr.len : -1, shift - 12,
                              mhp->attr.pbl_size, mhp->attr.pbl_addr);
        if (ret)
                return ret;
@@ -536,6 +545,11 @@ int c4iw_reregister_phys_mem(struct ib_mr *mr, int mr_rereg_mask,
                        return ret;
        }
 
+       if (mr_exceeds_hw_limits(rhp, total_size)) {
+               kfree(page_list);
+               return -EINVAL;
+       }
+
        ret = reregister_mem(rhp, php, &mh, shift, npages);
        kfree(page_list);
        if (ret)
@@ -596,6 +610,12 @@ struct ib_mr *c4iw_register_phys_mem(struct ib_pd *pd,
        if (ret)
                goto err;
 
+       if (mr_exceeds_hw_limits(rhp, total_size)) {
+               kfree(page_list);
+               ret = -EINVAL;
+               goto err;
+       }
+
        ret = alloc_pbl(mhp, npages);
        if (ret) {
                kfree(page_list);
@@ -699,6 +719,10 @@ struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
 
        php = to_c4iw_pd(pd);
        rhp = php->rhp;
+
+       if (mr_exceeds_hw_limits(rhp, length))
+               return ERR_PTR(-EINVAL);
+
        mhp = kzalloc(sizeof(*mhp), GFP_KERNEL);
        if (!mhp)
                return ERR_PTR(-ENOMEM);
index 2ed3ece..bb85d47 100644 (file)
@@ -1538,9 +1538,9 @@ err:
        set_state(qhp, C4IW_QP_STATE_ERROR);
        free = 1;
        abort = 1;
-       wake_up(&qhp->wait);
        BUG_ON(!ep);
        flush_qp(qhp);
+       wake_up(&qhp->wait);
 out:
        mutex_unlock(&qhp->mutex);
 
index 3488e8c..f914b30 100644 (file)
@@ -399,7 +399,7 @@ reg_user_mr_fallback:
        pginfo.num_kpages = num_kpages;
        pginfo.num_hwpages = num_hwpages;
        pginfo.u.usr.region = e_mr->umem;
-       pginfo.next_hwpage = e_mr->umem->offset / hwpage_size;
+       pginfo.next_hwpage = ib_umem_offset(e_mr->umem) / hwpage_size;
        pginfo.u.usr.next_sg = pginfo.u.usr.region->sg_head.sgl;
        ret = ehca_reg_mr(shca, e_mr, (u64 *)virt, length, mr_access_flags,
                          e_pd, &pginfo, &e_mr->ib.ib_mr.lkey,
index 5e61e9b..c7278f6 100644 (file)
@@ -214,7 +214,7 @@ struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
        mr->mr.user_base = start;
        mr->mr.iova = virt_addr;
        mr->mr.length = length;
-       mr->mr.offset = umem->offset;
+       mr->mr.offset = ib_umem_offset(umem);
        mr->mr.access_flags = mr_access_flags;
        mr->mr.max_segs = n;
        mr->umem = umem;
index 8f9325c..c36ccbd 100644 (file)
@@ -223,7 +223,6 @@ int mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags,
 
        if (flags & IB_MR_REREG_TRANS) {
                int shift;
-               int err;
                int n;
 
                mlx4_mr_rereg_mem_cleanup(dev->dev, &mmr->mmr);
index 4ea0135..27a7015 100644 (file)
@@ -1,3 +1,4 @@
 obj-$(CONFIG_MLX5_INFINIBAND)  += mlx5_ib.o
 
 mlx5_ib-y :=   main.o cq.o doorbell.o qp.o mem.o srq.o mr.o ah.o mad.o
+mlx5_ib-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += odp.o
index 1ba6c42..8a87404 100644 (file)
@@ -244,6 +244,12 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
                                           props->max_mcast_grp;
        props->max_map_per_fmr = INT_MAX; /* no limit in ConnectIB */
 
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+       if (dev->mdev->caps.gen.flags & MLX5_DEV_CAP_FLAG_ON_DMND_PG)
+               props->device_cap_flags |= IB_DEVICE_ON_DEMAND_PAGING;
+       props->odp_caps = dev->odp_caps;
+#endif
+
 out:
        kfree(in_mad);
        kfree(out_mad);
@@ -568,6 +574,10 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
                        goto out_count;
        }
 
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+       context->ibucontext.invalidate_range = &mlx5_ib_invalidate_range;
+#endif
+
        INIT_LIST_HEAD(&context->db_page_list);
        mutex_init(&context->db_page_mutex);
 
@@ -858,7 +868,7 @@ static ssize_t show_reg_pages(struct device *device,
        struct mlx5_ib_dev *dev =
                container_of(device, struct mlx5_ib_dev, ib_dev.dev);
 
-       return sprintf(buf, "%d\n", dev->mdev->priv.reg_pages);
+       return sprintf(buf, "%d\n", atomic_read(&dev->mdev->priv.reg_pages));
 }
 
 static ssize_t show_hca(struct device *device, struct device_attribute *attr,
@@ -1321,6 +1331,8 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
                (1ull << IB_USER_VERBS_CMD_DESTROY_SRQ)         |
                (1ull << IB_USER_VERBS_CMD_CREATE_XSRQ)         |
                (1ull << IB_USER_VERBS_CMD_OPEN_QP);
+       dev->ib_dev.uverbs_ex_cmd_mask =
+               (1ull << IB_USER_VERBS_EX_CMD_QUERY_DEVICE);
 
        dev->ib_dev.query_device        = mlx5_ib_query_device;
        dev->ib_dev.query_port          = mlx5_ib_query_port;
@@ -1366,6 +1378,8 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
        dev->ib_dev.free_fast_reg_page_list  = mlx5_ib_free_fast_reg_page_list;
        dev->ib_dev.check_mr_status     = mlx5_ib_check_mr_status;
 
+       mlx5_ib_internal_query_odp_caps(dev);
+
        if (mdev->caps.gen.flags & MLX5_DEV_CAP_FLAG_XRC) {
                dev->ib_dev.alloc_xrcd = mlx5_ib_alloc_xrcd;
                dev->ib_dev.dealloc_xrcd = mlx5_ib_dealloc_xrcd;
@@ -1379,16 +1393,19 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
                goto err_eqs;
 
        mutex_init(&dev->cap_mask_mutex);
-       spin_lock_init(&dev->mr_lock);
 
        err = create_dev_resources(&dev->devr);
        if (err)
                goto err_eqs;
 
-       err = ib_register_device(&dev->ib_dev, NULL);
+       err = mlx5_ib_odp_init_one(dev);
        if (err)
                goto err_rsrc;
 
+       err = ib_register_device(&dev->ib_dev, NULL);
+       if (err)
+               goto err_odp;
+
        err = create_umr_res(dev);
        if (err)
                goto err_dev;
@@ -1410,6 +1427,9 @@ err_umrc:
 err_dev:
        ib_unregister_device(&dev->ib_dev);
 
+err_odp:
+       mlx5_ib_odp_remove_one(dev);
+
 err_rsrc:
        destroy_dev_resources(&dev->devr);
 
@@ -1425,8 +1445,10 @@ err_dealloc:
 static void mlx5_ib_remove(struct mlx5_core_dev *mdev, void *context)
 {
        struct mlx5_ib_dev *dev = context;
+
        ib_unregister_device(&dev->ib_dev);
        destroy_umrc_res(dev);
+       mlx5_ib_odp_remove_one(dev);
        destroy_dev_resources(&dev->devr);
        free_comp_eqs(dev);
        ib_dealloc_device(&dev->ib_dev);
@@ -1440,15 +1462,30 @@ static struct mlx5_interface mlx5_ib_interface = {
 
 static int __init mlx5_ib_init(void)
 {
+       int err;
+
        if (deprecated_prof_sel != 2)
                pr_warn("prof_sel is deprecated for mlx5_ib, set it for mlx5_core\n");
 
-       return mlx5_register_interface(&mlx5_ib_interface);
+       err = mlx5_ib_odp_init();
+       if (err)
+               return err;
+
+       err = mlx5_register_interface(&mlx5_ib_interface);
+       if (err)
+               goto clean_odp;
+
+       return err;
+
+clean_odp:
+       mlx5_ib_odp_cleanup();
+       return err;
 }
 
 static void __exit mlx5_ib_cleanup(void)
 {
        mlx5_unregister_interface(&mlx5_ib_interface);
+       mlx5_ib_odp_cleanup();
 }
 
 module_init(mlx5_ib_init);
index dae07ea..b56e4c5 100644 (file)
@@ -32,6 +32,7 @@
 
 #include <linux/module.h>
 #include <rdma/ib_umem.h>
+#include <rdma/ib_umem_odp.h>
 #include "mlx5_ib.h"
 
 /* @umem: umem object to scan
@@ -57,6 +58,17 @@ void mlx5_ib_cont_pages(struct ib_umem *umem, u64 addr, int *count, int *shift,
        int entry;
        unsigned long page_shift = ilog2(umem->page_size);
 
+       /* With ODP we must always match OS page size. */
+       if (umem->odp_data) {
+               *count = ib_umem_page_count(umem);
+               *shift = PAGE_SHIFT;
+               *ncont = *count;
+               if (order)
+                       *order = ilog2(roundup_pow_of_two(*count));
+
+               return;
+       }
+
        addr = addr >> page_shift;
        tmp = (unsigned long)addr;
        m = find_first_bit(&tmp, sizeof(tmp));
@@ -108,8 +120,36 @@ void mlx5_ib_cont_pages(struct ib_umem *umem, u64 addr, int *count, int *shift,
        *count = i;
 }
 
-void mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
-                         int page_shift, __be64 *pas, int umr)
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+static u64 umem_dma_to_mtt(dma_addr_t umem_dma)
+{
+       u64 mtt_entry = umem_dma & ODP_DMA_ADDR_MASK;
+
+       if (umem_dma & ODP_READ_ALLOWED_BIT)
+               mtt_entry |= MLX5_IB_MTT_READ;
+       if (umem_dma & ODP_WRITE_ALLOWED_BIT)
+               mtt_entry |= MLX5_IB_MTT_WRITE;
+
+       return mtt_entry;
+}
+#endif
+
+/*
+ * Populate the given array with bus addresses from the umem.
+ *
+ * dev - mlx5_ib device
+ * umem - umem to use to fill the pages
+ * page_shift - determines the page size used in the resulting array
+ * offset - offset into the umem to start from,
+ *          only implemented for ODP umems
+ * num_pages - total number of pages to fill
+ * pas - bus addresses array to fill
+ * access_flags - access flags to set on all present pages.
+                 use enum mlx5_ib_mtt_access_flags for this.
+ */
+void __mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
+                           int page_shift, size_t offset, size_t num_pages,
+                           __be64 *pas, int access_flags)
 {
        unsigned long umem_page_shift = ilog2(umem->page_size);
        int shift = page_shift - umem_page_shift;
@@ -120,6 +160,21 @@ void mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
        int len;
        struct scatterlist *sg;
        int entry;
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+       const bool odp = umem->odp_data != NULL;
+
+       if (odp) {
+               WARN_ON(shift != 0);
+               WARN_ON(access_flags != (MLX5_IB_MTT_READ | MLX5_IB_MTT_WRITE));
+
+               for (i = 0; i < num_pages; ++i) {
+                       dma_addr_t pa = umem->odp_data->dma_list[offset + i];
+
+                       pas[i] = cpu_to_be64(umem_dma_to_mtt(pa));
+               }
+               return;
+       }
+#endif
 
        i = 0;
        for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
@@ -128,8 +183,7 @@ void mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
                for (k = 0; k < len; k++) {
                        if (!(i & mask)) {
                                cur = base + (k << umem_page_shift);
-                               if (umr)
-                                       cur |= 3;
+                               cur |= access_flags;
 
                                pas[i >> shift] = cpu_to_be64(cur);
                                mlx5_ib_dbg(dev, "pas[%d] 0x%llx\n",
@@ -142,6 +196,13 @@ void mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
        }
 }
 
+void mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
+                         int page_shift, __be64 *pas, int access_flags)
+{
+       return __mlx5_ib_populate_pas(dev, umem, page_shift, 0,
+                                     ib_umem_num_pages(umem), pas,
+                                     access_flags);
+}
 int mlx5_ib_get_buf_offset(u64 addr, int page_shift, u32 *offset)
 {
        u64 page_size;
index 386780f..83f22fe 100644 (file)
@@ -111,6 +111,8 @@ struct mlx5_ib_pd {
  */
 
 #define MLX5_IB_SEND_UMR_UNREG IB_SEND_RESERVED_START
+#define MLX5_IB_SEND_UMR_FAIL_IF_FREE (IB_SEND_RESERVED_START << 1)
+#define MLX5_IB_SEND_UMR_UPDATE_MTT (IB_SEND_RESERVED_START << 2)
 #define MLX5_IB_QPT_REG_UMR    IB_QPT_RESERVED1
 #define MLX5_IB_WR_UMR         IB_WR_RESERVED1
 
@@ -147,6 +149,29 @@ enum {
        MLX5_QP_EMPTY
 };
 
+/*
+ * Connect-IB can trigger up to four concurrent pagefaults
+ * per-QP.
+ */
+enum mlx5_ib_pagefault_context {
+       MLX5_IB_PAGEFAULT_RESPONDER_READ,
+       MLX5_IB_PAGEFAULT_REQUESTOR_READ,
+       MLX5_IB_PAGEFAULT_RESPONDER_WRITE,
+       MLX5_IB_PAGEFAULT_REQUESTOR_WRITE,
+       MLX5_IB_PAGEFAULT_CONTEXTS
+};
+
+static inline enum mlx5_ib_pagefault_context
+       mlx5_ib_get_pagefault_context(struct mlx5_pagefault *pagefault)
+{
+       return pagefault->flags & (MLX5_PFAULT_REQUESTOR | MLX5_PFAULT_WRITE);
+}
+
+struct mlx5_ib_pfault {
+       struct work_struct      work;
+       struct mlx5_pagefault   mpfault;
+};
+
 struct mlx5_ib_qp {
        struct ib_qp            ibqp;
        struct mlx5_core_qp     mqp;
@@ -192,6 +217,21 @@ struct mlx5_ib_qp {
 
        /* Store signature errors */
        bool                    signature_en;
+
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+       /*
+        * A flag that is true for QP's that are in a state that doesn't
+        * allow page faults, and shouldn't schedule any more faults.
+        */
+       int                     disable_page_faults;
+       /*
+        * The disable_page_faults_lock protects a QP's disable_page_faults
+        * field, allowing for a thread to atomically check whether the QP
+        * allows page faults, and if so schedule a page fault.
+        */
+       spinlock_t              disable_page_faults_lock;
+       struct mlx5_ib_pfault   pagefaults[MLX5_IB_PAGEFAULT_CONTEXTS];
+#endif
 };
 
 struct mlx5_ib_cq_buf {
@@ -206,6 +246,19 @@ enum mlx5_ib_qp_flags {
        MLX5_IB_QP_SIGNATURE_HANDLING           = 1 << 1,
 };
 
+struct mlx5_umr_wr {
+       union {
+               u64                     virt_addr;
+               u64                     offset;
+       } target;
+       struct ib_pd                   *pd;
+       unsigned int                    page_shift;
+       unsigned int                    npages;
+       u32                             length;
+       int                             access_flags;
+       u32                             mkey;
+};
+
 struct mlx5_shared_mr_info {
        int mr_id;
        struct ib_umem          *umem;
@@ -253,6 +306,13 @@ struct mlx5_ib_xrcd {
        u32                     xrcdn;
 };
 
+enum mlx5_ib_mtt_access_flags {
+       MLX5_IB_MTT_READ  = (1 << 0),
+       MLX5_IB_MTT_WRITE = (1 << 1),
+};
+
+#define MLX5_IB_MTT_PRESENT (MLX5_IB_MTT_READ | MLX5_IB_MTT_WRITE)
+
 struct mlx5_ib_mr {
        struct ib_mr            ibmr;
        struct mlx5_core_mr     mmr;
@@ -261,12 +321,11 @@ struct mlx5_ib_mr {
        struct list_head        list;
        int                     order;
        int                     umred;
-       __be64                  *pas;
-       dma_addr_t              dma;
        int                     npages;
        struct mlx5_ib_dev     *dev;
        struct mlx5_create_mkey_mbox_out out;
        struct mlx5_core_sig_ctx    *sig;
+       int                     live;
 };
 
 struct mlx5_ib_fast_reg_page_list {
@@ -372,11 +431,18 @@ struct mlx5_ib_dev {
        struct umr_common               umrc;
        /* sync used page count stats
         */
-       spinlock_t                      mr_lock;
        struct mlx5_ib_resources        devr;
        struct mlx5_mr_cache            cache;
        struct timer_list               delay_timer;
        int                             fill_delay;
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+       struct ib_odp_caps      odp_caps;
+       /*
+        * Sleepable RCU that prevents destruction of MRs while they are still
+        * being used by a page fault handler.
+        */
+       struct srcu_struct      mr_srcu;
+#endif
 };
 
 static inline struct mlx5_ib_cq *to_mibcq(struct mlx5_core_cq *mcq)
@@ -490,6 +556,8 @@ int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
 int mlx5_ib_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
                      struct ib_recv_wr **bad_wr);
 void *mlx5_get_send_wqe(struct mlx5_ib_qp *qp, int n);
+int mlx5_ib_read_user_wqe(struct mlx5_ib_qp *qp, int send, int wqe_index,
+                         void *buffer, u32 length);
 struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries,
                                int vector, struct ib_ucontext *context,
                                struct ib_udata *udata);
@@ -502,6 +570,8 @@ struct ib_mr *mlx5_ib_get_dma_mr(struct ib_pd *pd, int acc);
 struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
                                  u64 virt_addr, int access_flags,
                                  struct ib_udata *udata);
+int mlx5_ib_update_mtt(struct mlx5_ib_mr *mr, u64 start_page_index,
+                      int npages, int zap);
 int mlx5_ib_dereg_mr(struct ib_mr *ibmr);
 int mlx5_ib_destroy_mr(struct ib_mr *ibmr);
 struct ib_mr *mlx5_ib_create_mr(struct ib_pd *pd,
@@ -533,8 +603,11 @@ int mlx5_ib_init_fmr(struct mlx5_ib_dev *dev);
 void mlx5_ib_cleanup_fmr(struct mlx5_ib_dev *dev);
 void mlx5_ib_cont_pages(struct ib_umem *umem, u64 addr, int *count, int *shift,
                        int *ncont, int *order);
+void __mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
+                           int page_shift, size_t offset, size_t num_pages,
+                           __be64 *pas, int access_flags);
 void mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
-                         int page_shift, __be64 *pas, int umr);
+                         int page_shift, __be64 *pas, int access_flags);
 void mlx5_ib_copy_pas(u64 *old, u64 *new, int step, int num);
 int mlx5_ib_get_cqe_size(struct mlx5_ib_dev *dev, struct ib_cq *ibcq);
 int mlx5_mr_cache_init(struct mlx5_ib_dev *dev);
@@ -544,6 +617,38 @@ void mlx5_umr_cq_handler(struct ib_cq *cq, void *cq_context);
 int mlx5_ib_check_mr_status(struct ib_mr *ibmr, u32 check_mask,
                            struct ib_mr_status *mr_status);
 
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+extern struct workqueue_struct *mlx5_ib_page_fault_wq;
+
+int mlx5_ib_internal_query_odp_caps(struct mlx5_ib_dev *dev);
+void mlx5_ib_mr_pfault_handler(struct mlx5_ib_qp *qp,
+                              struct mlx5_ib_pfault *pfault);
+void mlx5_ib_odp_create_qp(struct mlx5_ib_qp *qp);
+int mlx5_ib_odp_init_one(struct mlx5_ib_dev *ibdev);
+void mlx5_ib_odp_remove_one(struct mlx5_ib_dev *ibdev);
+int __init mlx5_ib_odp_init(void);
+void mlx5_ib_odp_cleanup(void);
+void mlx5_ib_qp_disable_pagefaults(struct mlx5_ib_qp *qp);
+void mlx5_ib_qp_enable_pagefaults(struct mlx5_ib_qp *qp);
+void mlx5_ib_invalidate_range(struct ib_umem *umem, unsigned long start,
+                             unsigned long end);
+
+#else /* CONFIG_INFINIBAND_ON_DEMAND_PAGING */
+static inline int mlx5_ib_internal_query_odp_caps(struct mlx5_ib_dev *dev)
+{
+       return 0;
+}
+
+static inline void mlx5_ib_odp_create_qp(struct mlx5_ib_qp *qp)                {}
+static inline int mlx5_ib_odp_init_one(struct mlx5_ib_dev *ibdev) { return 0; }
+static inline void mlx5_ib_odp_remove_one(struct mlx5_ib_dev *ibdev)   {}
+static inline int mlx5_ib_odp_init(void) { return 0; }
+static inline void mlx5_ib_odp_cleanup(void)                           {}
+static inline void mlx5_ib_qp_disable_pagefaults(struct mlx5_ib_qp *qp) {}
+static inline void mlx5_ib_qp_enable_pagefaults(struct mlx5_ib_qp *qp)  {}
+
+#endif /* CONFIG_INFINIBAND_ON_DEMAND_PAGING */
+
 static inline void init_query_mad(struct ib_smp *mad)
 {
        mad->base_version  = 1;
@@ -561,4 +666,7 @@ static inline u8 convert_access(int acc)
               MLX5_PERM_LOCAL_READ;
 }
 
+#define MLX5_MAX_UMR_SHIFT 16
+#define MLX5_MAX_UMR_PAGES (1 << MLX5_MAX_UMR_SHIFT)
+
 #endif /* MLX5_IB_H */
index 5a80dd9..32a28bd 100644 (file)
 #include <linux/export.h>
 #include <linux/delay.h>
 #include <rdma/ib_umem.h>
+#include <rdma/ib_umem_odp.h>
+#include <rdma/ib_verbs.h>
 #include "mlx5_ib.h"
 
 enum {
        MAX_PENDING_REG_MR = 8,
 };
 
-enum {
-       MLX5_UMR_ALIGN  = 2048
-};
+#define MLX5_UMR_ALIGN 2048
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+static __be64 mlx5_ib_update_mtt_emergency_buffer[
+               MLX5_UMR_MTT_MIN_CHUNK_SIZE/sizeof(__be64)]
+       __aligned(MLX5_UMR_ALIGN);
+static DEFINE_MUTEX(mlx5_ib_update_mtt_emergency_buffer_mutex);
+#endif
+
+static int clean_mr(struct mlx5_ib_mr *mr);
 
-static __be64 *mr_align(__be64 *ptr, int align)
+static int destroy_mkey(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
 {
-       unsigned long mask = align - 1;
+       int err = mlx5_core_destroy_mkey(dev->mdev, &mr->mmr);
 
-       return (__be64 *)(((unsigned long)ptr + mask) & ~mask);
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+       /* Wait until all page fault handlers using the mr complete. */
+       synchronize_srcu(&dev->mr_srcu);
+#endif
+
+       return err;
 }
 
 static int order2idx(struct mlx5_ib_dev *dev, int order)
@@ -146,7 +159,7 @@ static int add_keys(struct mlx5_ib_dev *dev, int c, int num)
                mr->order = ent->order;
                mr->umred = 1;
                mr->dev = dev;
-               in->seg.status = 1 << 6;
+               in->seg.status = MLX5_MKEY_STATUS_FREE;
                in->seg.xlt_oct_size = cpu_to_be32((npages + 1) / 2);
                in->seg.qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
                in->seg.flags = MLX5_ACCESS_MODE_MTT | MLX5_PERM_UMR_EN;
@@ -191,7 +204,7 @@ static void remove_keys(struct mlx5_ib_dev *dev, int c, int num)
                ent->cur--;
                ent->size--;
                spin_unlock_irq(&ent->lock);
-               err = mlx5_core_destroy_mkey(dev->mdev, &mr->mmr);
+               err = destroy_mkey(dev, mr);
                if (err)
                        mlx5_ib_warn(dev, "failed destroy mkey\n");
                else
@@ -482,7 +495,7 @@ static void clean_keys(struct mlx5_ib_dev *dev, int c)
                ent->cur--;
                ent->size--;
                spin_unlock_irq(&ent->lock);
-               err = mlx5_core_destroy_mkey(dev->mdev, &mr->mmr);
+               err = destroy_mkey(dev, mr);
                if (err)
                        mlx5_ib_warn(dev, "failed destroy mkey\n");
                else
@@ -668,7 +681,7 @@ static int get_octo_len(u64 addr, u64 len, int page_size)
 
 static int use_umr(int order)
 {
-       return order <= 17;
+       return order <= MLX5_MAX_UMR_SHIFT;
 }
 
 static void prep_umr_reg_wqe(struct ib_pd *pd, struct ib_send_wr *wr,
@@ -678,6 +691,7 @@ static void prep_umr_reg_wqe(struct ib_pd *pd, struct ib_send_wr *wr,
 {
        struct mlx5_ib_dev *dev = to_mdev(pd->device);
        struct ib_mr *mr = dev->umrc.mr;
+       struct mlx5_umr_wr *umrwr = (struct mlx5_umr_wr *)&wr->wr.fast_reg;
 
        sg->addr = dma;
        sg->length = ALIGN(sizeof(u64) * n, 64);
@@ -692,21 +706,24 @@ static void prep_umr_reg_wqe(struct ib_pd *pd, struct ib_send_wr *wr,
                wr->num_sge = 0;
 
        wr->opcode = MLX5_IB_WR_UMR;
-       wr->wr.fast_reg.page_list_len = n;
-       wr->wr.fast_reg.page_shift = page_shift;
-       wr->wr.fast_reg.rkey = key;
-       wr->wr.fast_reg.iova_start = virt_addr;
-       wr->wr.fast_reg.length = len;
-       wr->wr.fast_reg.access_flags = access_flags;
-       wr->wr.fast_reg.page_list = (struct ib_fast_reg_page_list *)pd;
+
+       umrwr->npages = n;
+       umrwr->page_shift = page_shift;
+       umrwr->mkey = key;
+       umrwr->target.virt_addr = virt_addr;
+       umrwr->length = len;
+       umrwr->access_flags = access_flags;
+       umrwr->pd = pd;
 }
 
 static void prep_umr_unreg_wqe(struct mlx5_ib_dev *dev,
                               struct ib_send_wr *wr, u32 key)
 {
-       wr->send_flags = MLX5_IB_SEND_UMR_UNREG;
+       struct mlx5_umr_wr *umrwr = (struct mlx5_umr_wr *)&wr->wr.fast_reg;
+
+       wr->send_flags = MLX5_IB_SEND_UMR_UNREG | MLX5_IB_SEND_UMR_FAIL_IF_FREE;
        wr->opcode = MLX5_IB_WR_UMR;
-       wr->wr.fast_reg.rkey = key;
+       umrwr->mkey = key;
 }
 
 void mlx5_umr_cq_handler(struct ib_cq *cq, void *cq_context)
@@ -742,7 +759,10 @@ static struct mlx5_ib_mr *reg_umr(struct ib_pd *pd, struct ib_umem *umem,
        struct ib_send_wr wr, *bad;
        struct mlx5_ib_mr *mr;
        struct ib_sge sg;
-       int size = sizeof(u64) * npages;
+       int size;
+       __be64 *mr_pas;
+       __be64 *pas;
+       dma_addr_t dma;
        int err = 0;
        int i;
 
@@ -761,25 +781,31 @@ static struct mlx5_ib_mr *reg_umr(struct ib_pd *pd, struct ib_umem *umem,
        if (!mr)
                return ERR_PTR(-EAGAIN);
 
-       mr->pas = kmalloc(size + MLX5_UMR_ALIGN - 1, GFP_KERNEL);
-       if (!mr->pas) {
+       /* UMR copies MTTs in units of MLX5_UMR_MTT_ALIGNMENT bytes.
+        * To avoid copying garbage after the pas array, we allocate
+        * a little more. */
+       size = ALIGN(sizeof(u64) * npages, MLX5_UMR_MTT_ALIGNMENT);
+       mr_pas = kmalloc(size + MLX5_UMR_ALIGN - 1, GFP_KERNEL);
+       if (!mr_pas) {
                err = -ENOMEM;
                goto free_mr;
        }
 
-       mlx5_ib_populate_pas(dev, umem, page_shift,
-                            mr_align(mr->pas, MLX5_UMR_ALIGN), 1);
+       pas = PTR_ALIGN(mr_pas, MLX5_UMR_ALIGN);
+       mlx5_ib_populate_pas(dev, umem, page_shift, pas, MLX5_IB_MTT_PRESENT);
+       /* Clear padding after the actual pages. */
+       memset(pas + npages, 0, size - npages * sizeof(u64));
 
-       mr->dma = dma_map_single(ddev, mr_align(mr->pas, MLX5_UMR_ALIGN), size,
-                                DMA_TO_DEVICE);
-       if (dma_mapping_error(ddev, mr->dma)) {
+       dma = dma_map_single(ddev, pas, size, DMA_TO_DEVICE);
+       if (dma_mapping_error(ddev, dma)) {
                err = -ENOMEM;
                goto free_pas;
        }
 
        memset(&wr, 0, sizeof(wr));
        wr.wr_id = (u64)(unsigned long)&umr_context;
-       prep_umr_reg_wqe(pd, &wr, &sg, mr->dma, npages, mr->mmr.key, page_shift, virt_addr, len, access_flags);
+       prep_umr_reg_wqe(pd, &wr, &sg, dma, npages, mr->mmr.key, page_shift,
+                        virt_addr, len, access_flags);
 
        mlx5_ib_init_umr_context(&umr_context);
        down(&umrc->sem);
@@ -799,12 +825,14 @@ static struct mlx5_ib_mr *reg_umr(struct ib_pd *pd, struct ib_umem *umem,
        mr->mmr.size = len;
        mr->mmr.pd = to_mpd(pd)->pdn;
 
+       mr->live = 1;
+
 unmap_dma:
        up(&umrc->sem);
-       dma_unmap_single(ddev, mr->dma, size, DMA_TO_DEVICE);
+       dma_unmap_single(ddev, dma, size, DMA_TO_DEVICE);
 
 free_pas:
-       kfree(mr->pas);
+       kfree(mr_pas);
 
 free_mr:
        if (err) {
@@ -815,6 +843,128 @@ free_mr:
        return mr;
 }
 
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+int mlx5_ib_update_mtt(struct mlx5_ib_mr *mr, u64 start_page_index, int npages,
+                      int zap)
+{
+       struct mlx5_ib_dev *dev = mr->dev;
+       struct device *ddev = dev->ib_dev.dma_device;
+       struct umr_common *umrc = &dev->umrc;
+       struct mlx5_ib_umr_context umr_context;
+       struct ib_umem *umem = mr->umem;
+       int size;
+       __be64 *pas;
+       dma_addr_t dma;
+       struct ib_send_wr wr, *bad;
+       struct mlx5_umr_wr *umrwr = (struct mlx5_umr_wr *)&wr.wr.fast_reg;
+       struct ib_sge sg;
+       int err = 0;
+       const int page_index_alignment = MLX5_UMR_MTT_ALIGNMENT / sizeof(u64);
+       const int page_index_mask = page_index_alignment - 1;
+       size_t pages_mapped = 0;
+       size_t pages_to_map = 0;
+       size_t pages_iter = 0;
+       int use_emergency_buf = 0;
+
+       /* UMR copies MTTs in units of MLX5_UMR_MTT_ALIGNMENT bytes,
+        * so we need to align the offset and length accordingly */
+       if (start_page_index & page_index_mask) {
+               npages += start_page_index & page_index_mask;
+               start_page_index &= ~page_index_mask;
+       }
+
+       pages_to_map = ALIGN(npages, page_index_alignment);
+
+       if (start_page_index + pages_to_map > MLX5_MAX_UMR_PAGES)
+               return -EINVAL;
+
+       size = sizeof(u64) * pages_to_map;
+       size = min_t(int, PAGE_SIZE, size);
+       /* We allocate with GFP_ATOMIC to avoid recursion into page-reclaim
+        * code, when we are called from an invalidation. The pas buffer must
+        * be 2k-aligned for Connect-IB. */
+       pas = (__be64 *)get_zeroed_page(GFP_ATOMIC);
+       if (!pas) {
+               mlx5_ib_warn(dev, "unable to allocate memory during MTT update, falling back to slower chunked mechanism.\n");
+               pas = mlx5_ib_update_mtt_emergency_buffer;
+               size = MLX5_UMR_MTT_MIN_CHUNK_SIZE;
+               use_emergency_buf = 1;
+               mutex_lock(&mlx5_ib_update_mtt_emergency_buffer_mutex);
+               memset(pas, 0, size);
+       }
+       pages_iter = size / sizeof(u64);
+       dma = dma_map_single(ddev, pas, size, DMA_TO_DEVICE);
+       if (dma_mapping_error(ddev, dma)) {
+               mlx5_ib_err(dev, "unable to map DMA during MTT update.\n");
+               err = -ENOMEM;
+               goto free_pas;
+       }
+
+       for (pages_mapped = 0;
+            pages_mapped < pages_to_map && !err;
+            pages_mapped += pages_iter, start_page_index += pages_iter) {
+               dma_sync_single_for_cpu(ddev, dma, size, DMA_TO_DEVICE);
+
+               npages = min_t(size_t,
+                              pages_iter,
+                              ib_umem_num_pages(umem) - start_page_index);
+
+               if (!zap) {
+                       __mlx5_ib_populate_pas(dev, umem, PAGE_SHIFT,
+                                              start_page_index, npages, pas,
+                                              MLX5_IB_MTT_PRESENT);
+                       /* Clear padding after the pages brought from the
+                        * umem. */
+                       memset(pas + npages, 0, size - npages * sizeof(u64));
+               }
+
+               dma_sync_single_for_device(ddev, dma, size, DMA_TO_DEVICE);
+
+               memset(&wr, 0, sizeof(wr));
+               wr.wr_id = (u64)(unsigned long)&umr_context;
+
+               sg.addr = dma;
+               sg.length = ALIGN(npages * sizeof(u64),
+                               MLX5_UMR_MTT_ALIGNMENT);
+               sg.lkey = dev->umrc.mr->lkey;
+
+               wr.send_flags = MLX5_IB_SEND_UMR_FAIL_IF_FREE |
+                               MLX5_IB_SEND_UMR_UPDATE_MTT;
+               wr.sg_list = &sg;
+               wr.num_sge = 1;
+               wr.opcode = MLX5_IB_WR_UMR;
+               umrwr->npages = sg.length / sizeof(u64);
+               umrwr->page_shift = PAGE_SHIFT;
+               umrwr->mkey = mr->mmr.key;
+               umrwr->target.offset = start_page_index;
+
+               mlx5_ib_init_umr_context(&umr_context);
+               down(&umrc->sem);
+               err = ib_post_send(umrc->qp, &wr, &bad);
+               if (err) {
+                       mlx5_ib_err(dev, "UMR post send failed, err %d\n", err);
+               } else {
+                       wait_for_completion(&umr_context.done);
+                       if (umr_context.status != IB_WC_SUCCESS) {
+                               mlx5_ib_err(dev, "UMR completion failed, code %d\n",
+                                           umr_context.status);
+                               err = -EFAULT;
+                       }
+               }
+               up(&umrc->sem);
+       }
+       dma_unmap_single(ddev, dma, size, DMA_TO_DEVICE);
+
+free_pas:
+       if (!use_emergency_buf)
+               free_page((unsigned long)pas);
+       else
+               mutex_unlock(&mlx5_ib_update_mtt_emergency_buffer_mutex);
+
+       return err;
+}
+#endif
+
 static struct mlx5_ib_mr *reg_create(struct ib_pd *pd, u64 virt_addr,
                                     u64 length, struct ib_umem *umem,
                                     int npages, int page_shift,
@@ -825,6 +975,8 @@ static struct mlx5_ib_mr *reg_create(struct ib_pd *pd, u64 virt_addr,
        struct mlx5_ib_mr *mr;
        int inlen;
        int err;
+       bool pg_cap = !!(dev->mdev->caps.gen.flags &
+                        MLX5_DEV_CAP_FLAG_ON_DMND_PG);
 
        mr = kzalloc(sizeof(*mr), GFP_KERNEL);
        if (!mr)
@@ -836,8 +988,12 @@ static struct mlx5_ib_mr *reg_create(struct ib_pd *pd, u64 virt_addr,
                err = -ENOMEM;
                goto err_1;
        }
-       mlx5_ib_populate_pas(dev, umem, page_shift, in->pas, 0);
+       mlx5_ib_populate_pas(dev, umem, page_shift, in->pas,
+                            pg_cap ? MLX5_IB_MTT_PRESENT : 0);
 
+       /* The MLX5_MKEY_INBOX_PG_ACCESS bit allows setting the access flags
+        * in the page list submitted with the command. */
+       in->flags = pg_cap ? cpu_to_be32(MLX5_MKEY_INBOX_PG_ACCESS) : 0;
        in->seg.flags = convert_access(access_flags) |
                MLX5_ACCESS_MODE_MTT;
        in->seg.flags_pd = cpu_to_be32(to_mpd(pd)->pdn);
@@ -856,6 +1012,7 @@ static struct mlx5_ib_mr *reg_create(struct ib_pd *pd, u64 virt_addr,
                goto err_2;
        }
        mr->umem = umem;
+       mr->live = 1;
        kvfree(in);
 
        mlx5_ib_dbg(dev, "mkey = 0x%x\n", mr->mmr.key);
@@ -910,6 +1067,10 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
                        mlx5_ib_dbg(dev, "cache empty for order %d", order);
                        mr = NULL;
                }
+       } else if (access_flags & IB_ACCESS_ON_DEMAND) {
+               err = -EINVAL;
+               pr_err("Got MR registration for ODP MR > 512MB, not supported for Connect-IB");
+               goto error;
        }
 
        if (!mr)
@@ -925,16 +1086,51 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
 
        mr->umem = umem;
        mr->npages = npages;
-       spin_lock(&dev->mr_lock);
-       dev->mdev->priv.reg_pages += npages;
-       spin_unlock(&dev->mr_lock);
+       atomic_add(npages, &dev->mdev->priv.reg_pages);
        mr->ibmr.lkey = mr->mmr.key;
        mr->ibmr.rkey = mr->mmr.key;
 
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+       if (umem->odp_data) {
+               /*
+                * This barrier prevents the compiler from moving the
+                * setting of umem->odp_data->private to point to our
+                * MR, before reg_umr finished, to ensure that the MR
+                * initialization have finished before starting to
+                * handle invalidations.
+                */
+               smp_wmb();
+               mr->umem->odp_data->private = mr;
+               /*
+                * Make sure we will see the new
+                * umem->odp_data->private value in the invalidation
+                * routines, before we can get page faults on the
+                * MR. Page faults can happen once we put the MR in
+                * the tree, below this line. Without the barrier,
+                * there can be a fault handling and an invalidation
+                * before umem->odp_data->private == mr is visible to
+                * the invalidation handler.
+                */
+               smp_wmb();
+       }
+#endif
+
        return &mr->ibmr;
 
 error:
+       /*
+        * Destroy the umem *before* destroying the MR, to ensure we
+        * will not have any in-flight notifiers when destroying the
+        * MR.
+        *
+        * As the MR is completely invalid to begin with, and this
+        * error path is only taken if we can't push the mr entry into
+        * the pagefault tree, this is safe.
+        */
+
        ib_umem_release(umem);
+       /* Kill the MR, and return an error code. */
+       clean_mr(mr);
        return ERR_PTR(err);
 }
 
@@ -971,17 +1167,14 @@ error:
        return err;
 }
 
-int mlx5_ib_dereg_mr(struct ib_mr *ibmr)
+static int clean_mr(struct mlx5_ib_mr *mr)
 {
-       struct mlx5_ib_dev *dev = to_mdev(ibmr->device);
-       struct mlx5_ib_mr *mr = to_mmr(ibmr);
-       struct ib_umem *umem = mr->umem;
-       int npages = mr->npages;
+       struct mlx5_ib_dev *dev = to_mdev(mr->ibmr.device);
        int umred = mr->umred;
        int err;
 
        if (!umred) {
-               err = mlx5_core_destroy_mkey(dev->mdev, &mr->mmr);
+               err = destroy_mkey(dev, mr);
                if (err) {
                        mlx5_ib_warn(dev, "failed to destroy mkey 0x%x (%d)\n",
                                     mr->mmr.key, err);
@@ -996,15 +1189,47 @@ int mlx5_ib_dereg_mr(struct ib_mr *ibmr)
                free_cached_mr(dev, mr);
        }
 
-       if (umem) {
+       if (!umred)
+               kfree(mr);
+
+       return 0;
+}
+
+int mlx5_ib_dereg_mr(struct ib_mr *ibmr)
+{
+       struct mlx5_ib_dev *dev = to_mdev(ibmr->device);
+       struct mlx5_ib_mr *mr = to_mmr(ibmr);
+       int npages = mr->npages;
+       struct ib_umem *umem = mr->umem;
+
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+       if (umem && umem->odp_data) {
+               /* Prevent new page faults from succeeding */
+               mr->live = 0;
+               /* Wait for all running page-fault handlers to finish. */
+               synchronize_srcu(&dev->mr_srcu);
+               /* Destroy all page mappings */
+               mlx5_ib_invalidate_range(umem, ib_umem_start(umem),
+                                        ib_umem_end(umem));
+               /*
+                * We kill the umem before the MR for ODP,
+                * so that there will not be any invalidations in
+                * flight, looking at the *mr struct.
+                */
                ib_umem_release(umem);
-               spin_lock(&dev->mr_lock);
-               dev->mdev->priv.reg_pages -= npages;
-               spin_unlock(&dev->mr_lock);
+               atomic_sub(npages, &dev->mdev->priv.reg_pages);
+
+               /* Avoid double-freeing the umem. */
+               umem = NULL;
        }
+#endif
 
-       if (!umred)
-               kfree(mr);
+       clean_mr(mr);
+
+       if (umem) {
+               ib_umem_release(umem);
+               atomic_sub(npages, &dev->mdev->priv.reg_pages);
+       }
 
        return 0;
 }
@@ -1028,7 +1253,7 @@ struct ib_mr *mlx5_ib_create_mr(struct ib_pd *pd,
                goto err_free;
        }
 
-       in->seg.status = 1 << 6; /* free */
+       in->seg.status = MLX5_MKEY_STATUS_FREE;
        in->seg.xlt_oct_size = cpu_to_be32(ndescs);
        in->seg.qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
        in->seg.flags_pd = cpu_to_be32(to_mpd(pd)->pdn);
@@ -1113,7 +1338,7 @@ int mlx5_ib_destroy_mr(struct ib_mr *ibmr)
                kfree(mr->sig);
        }
 
-       err = mlx5_core_destroy_mkey(dev->mdev, &mr->mmr);
+       err = destroy_mkey(dev, mr);
        if (err) {
                mlx5_ib_warn(dev, "failed to destroy mkey 0x%x (%d)\n",
                             mr->mmr.key, err);
@@ -1143,7 +1368,7 @@ struct ib_mr *mlx5_ib_alloc_fast_reg_mr(struct ib_pd *pd,
                goto err_free;
        }
 
-       in->seg.status = 1 << 6; /* free */
+       in->seg.status = MLX5_MKEY_STATUS_FREE;
        in->seg.xlt_oct_size = cpu_to_be32((max_page_list_len + 1) / 2);
        in->seg.qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
        in->seg.flags = MLX5_PERM_UMR_EN | MLX5_ACCESS_MODE_MTT;
diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c
new file mode 100644 (file)
index 0000000..a2c541c
--- /dev/null
@@ -0,0 +1,798 @@
+/*
+ * Copyright (c) 2014 Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <rdma/ib_umem.h>
+#include <rdma/ib_umem_odp.h>
+
+#include "mlx5_ib.h"
+
+#define MAX_PREFETCH_LEN (4*1024*1024U)
+
+/* Timeout in ms to wait for an active mmu notifier to complete when handling
+ * a pagefault. */
+#define MMU_NOTIFIER_TIMEOUT 1000
+
+struct workqueue_struct *mlx5_ib_page_fault_wq;
+
+void mlx5_ib_invalidate_range(struct ib_umem *umem, unsigned long start,
+                             unsigned long end)
+{
+       struct mlx5_ib_mr *mr;
+       const u64 umr_block_mask = (MLX5_UMR_MTT_ALIGNMENT / sizeof(u64)) - 1;
+       u64 idx = 0, blk_start_idx = 0;
+       int in_block = 0;
+       u64 addr;
+
+       if (!umem || !umem->odp_data) {
+               pr_err("invalidation called on NULL umem or non-ODP umem\n");
+               return;
+       }
+
+       mr = umem->odp_data->private;
+
+       if (!mr || !mr->ibmr.pd)
+               return;
+
+       start = max_t(u64, ib_umem_start(umem), start);
+       end = min_t(u64, ib_umem_end(umem), end);
+
+       /*
+        * Iteration one - zap the HW's MTTs. The notifiers_count ensures that
+        * while we are doing the invalidation, no page fault will attempt to
+        * overwrite the same MTTs.  Concurent invalidations might race us,
+        * but they will write 0s as well, so no difference in the end result.
+        */
+
+       for (addr = start; addr < end; addr += (u64)umem->page_size) {
+               idx = (addr - ib_umem_start(umem)) / PAGE_SIZE;
+               /*
+                * Strive to write the MTTs in chunks, but avoid overwriting
+                * non-existing MTTs. The huristic here can be improved to
+                * estimate the cost of another UMR vs. the cost of bigger
+                * UMR.
+                */
+               if (umem->odp_data->dma_list[idx] &
+                   (ODP_READ_ALLOWED_BIT | ODP_WRITE_ALLOWED_BIT)) {
+                       if (!in_block) {
+                               blk_start_idx = idx;
+                               in_block = 1;
+                       }
+               } else {
+                       u64 umr_offset = idx & umr_block_mask;
+
+                       if (in_block && umr_offset == 0) {
+                               mlx5_ib_update_mtt(mr, blk_start_idx,
+                                                  idx - blk_start_idx, 1);
+                               in_block = 0;
+                       }
+               }
+       }
+       if (in_block)
+               mlx5_ib_update_mtt(mr, blk_start_idx, idx - blk_start_idx + 1,
+                                  1);
+
+       /*
+        * We are now sure that the device will not access the
+        * memory. We can safely unmap it, and mark it as dirty if
+        * needed.
+        */
+
+       ib_umem_odp_unmap_dma_pages(umem, start, end);
+}
+
+#define COPY_ODP_BIT_MLX_TO_IB(reg, ib_caps, field_name, bit_name) do {        \
+       if (be32_to_cpu(reg.field_name) & MLX5_ODP_SUPPORT_##bit_name)  \
+               ib_caps->field_name |= IB_ODP_SUPPORT_##bit_name;       \
+} while (0)
+
+int mlx5_ib_internal_query_odp_caps(struct mlx5_ib_dev *dev)
+{
+       int err;
+       struct mlx5_odp_caps hw_caps;
+       struct ib_odp_caps *caps = &dev->odp_caps;
+
+       memset(caps, 0, sizeof(*caps));
+
+       if (!(dev->mdev->caps.gen.flags & MLX5_DEV_CAP_FLAG_ON_DMND_PG))
+               return 0;
+
+       err = mlx5_query_odp_caps(dev->mdev, &hw_caps);
+       if (err)
+               goto out;
+
+       caps->general_caps = IB_ODP_SUPPORT;
+       COPY_ODP_BIT_MLX_TO_IB(hw_caps, caps, per_transport_caps.ud_odp_caps,
+                              SEND);
+       COPY_ODP_BIT_MLX_TO_IB(hw_caps, caps, per_transport_caps.rc_odp_caps,
+                              SEND);
+       COPY_ODP_BIT_MLX_TO_IB(hw_caps, caps, per_transport_caps.rc_odp_caps,
+                              RECV);
+       COPY_ODP_BIT_MLX_TO_IB(hw_caps, caps, per_transport_caps.rc_odp_caps,
+                              WRITE);
+       COPY_ODP_BIT_MLX_TO_IB(hw_caps, caps, per_transport_caps.rc_odp_caps,
+                              READ);
+
+out:
+       return err;
+}
+
+static struct mlx5_ib_mr *mlx5_ib_odp_find_mr_lkey(struct mlx5_ib_dev *dev,
+                                                  u32 key)
+{
+       u32 base_key = mlx5_base_mkey(key);
+       struct mlx5_core_mr *mmr = __mlx5_mr_lookup(dev->mdev, base_key);
+       struct mlx5_ib_mr *mr = container_of(mmr, struct mlx5_ib_mr, mmr);
+
+       if (!mmr || mmr->key != key || !mr->live)
+               return NULL;
+
+       return container_of(mmr, struct mlx5_ib_mr, mmr);
+}
+
+static void mlx5_ib_page_fault_resume(struct mlx5_ib_qp *qp,
+                                     struct mlx5_ib_pfault *pfault,
+                                     int error) {
+       struct mlx5_ib_dev *dev = to_mdev(qp->ibqp.pd->device);
+       int ret = mlx5_core_page_fault_resume(dev->mdev, qp->mqp.qpn,
+                                             pfault->mpfault.flags,
+                                             error);
+       if (ret)
+               pr_err("Failed to resolve the page fault on QP 0x%x\n",
+                      qp->mqp.qpn);
+}
+
+/*
+ * Handle a single data segment in a page-fault WQE.
+ *
+ * Returns number of pages retrieved on success. The caller will continue to
+ * the next data segment.
+ * Can return the following error codes:
+ * -EAGAIN to designate a temporary error. The caller will abort handling the
+ *  page fault and resolve it.
+ * -EFAULT when there's an error mapping the requested pages. The caller will
+ *  abort the page fault handling and possibly move the QP to an error state.
+ * On other errors the QP should also be closed with an error.
+ */
+static int pagefault_single_data_segment(struct mlx5_ib_qp *qp,
+                                        struct mlx5_ib_pfault *pfault,
+                                        u32 key, u64 io_virt, size_t bcnt,
+                                        u32 *bytes_mapped)
+{
+       struct mlx5_ib_dev *mib_dev = to_mdev(qp->ibqp.pd->device);
+       int srcu_key;
+       unsigned int current_seq;
+       u64 start_idx;
+       int npages = 0, ret = 0;
+       struct mlx5_ib_mr *mr;
+       u64 access_mask = ODP_READ_ALLOWED_BIT;
+
+       srcu_key = srcu_read_lock(&mib_dev->mr_srcu);
+       mr = mlx5_ib_odp_find_mr_lkey(mib_dev, key);
+       /*
+        * If we didn't find the MR, it means the MR was closed while we were
+        * handling the ODP event. In this case we return -EFAULT so that the
+        * QP will be closed.
+        */
+       if (!mr || !mr->ibmr.pd) {
+               pr_err("Failed to find relevant mr for lkey=0x%06x, probably the MR was destroyed\n",
+                      key);
+               ret = -EFAULT;
+               goto srcu_unlock;
+       }
+       if (!mr->umem->odp_data) {
+               pr_debug("skipping non ODP MR (lkey=0x%06x) in page fault handler.\n",
+                        key);
+               if (bytes_mapped)
+                       *bytes_mapped +=
+                               (bcnt - pfault->mpfault.bytes_committed);
+               goto srcu_unlock;
+       }
+       if (mr->ibmr.pd != qp->ibqp.pd) {
+               pr_err("Page-fault with different PDs for QP and MR.\n");
+               ret = -EFAULT;
+               goto srcu_unlock;
+       }
+
+       current_seq = ACCESS_ONCE(mr->umem->odp_data->notifiers_seq);
+       /*
+        * Ensure the sequence number is valid for some time before we call
+        * gup.
+        */
+       smp_rmb();
+
+       /*
+        * Avoid branches - this code will perform correctly
+        * in all iterations (in iteration 2 and above,
+        * bytes_committed == 0).
+        */
+       io_virt += pfault->mpfault.bytes_committed;
+       bcnt -= pfault->mpfault.bytes_committed;
+
+       start_idx = (io_virt - (mr->mmr.iova & PAGE_MASK)) >> PAGE_SHIFT;
+
+       if (mr->umem->writable)
+               access_mask |= ODP_WRITE_ALLOWED_BIT;
+       npages = ib_umem_odp_map_dma_pages(mr->umem, io_virt, bcnt,
+                                          access_mask, current_seq);
+       if (npages < 0) {
+               ret = npages;
+               goto srcu_unlock;
+       }
+
+       if (npages > 0) {
+               mutex_lock(&mr->umem->odp_data->umem_mutex);
+               if (!ib_umem_mmu_notifier_retry(mr->umem, current_seq)) {
+                       /*
+                        * No need to check whether the MTTs really belong to
+                        * this MR, since ib_umem_odp_map_dma_pages already
+                        * checks this.
+                        */
+                       ret = mlx5_ib_update_mtt(mr, start_idx, npages, 0);
+               } else {
+                       ret = -EAGAIN;
+               }
+               mutex_unlock(&mr->umem->odp_data->umem_mutex);
+               if (ret < 0) {
+                       if (ret != -EAGAIN)
+                               pr_err("Failed to update mkey page tables\n");
+                       goto srcu_unlock;
+               }
+
+               if (bytes_mapped) {
+                       u32 new_mappings = npages * PAGE_SIZE -
+                               (io_virt - round_down(io_virt, PAGE_SIZE));
+                       *bytes_mapped += min_t(u32, new_mappings, bcnt);
+               }
+       }
+
+srcu_unlock:
+       if (ret == -EAGAIN) {
+               if (!mr->umem->odp_data->dying) {
+                       struct ib_umem_odp *odp_data = mr->umem->odp_data;
+                       unsigned long timeout =
+                               msecs_to_jiffies(MMU_NOTIFIER_TIMEOUT);
+
+                       if (!wait_for_completion_timeout(
+                                       &odp_data->notifier_completion,
+                                       timeout)) {
+                               pr_warn("timeout waiting for mmu notifier completion\n");
+                       }
+               } else {
+                       /* The MR is being killed, kill the QP as well. */
+                       ret = -EFAULT;
+               }
+       }
+       srcu_read_unlock(&mib_dev->mr_srcu, srcu_key);
+       pfault->mpfault.bytes_committed = 0;
+       return ret ? ret : npages;
+}
+
+/**
+ * Parse a series of data segments for page fault handling.
+ *
+ * @qp the QP on which the fault occurred.
+ * @pfault contains page fault information.
+ * @wqe points at the first data segment in the WQE.
+ * @wqe_end points after the end of the WQE.
+ * @bytes_mapped receives the number of bytes that the function was able to
+ *               map. This allows the caller to decide intelligently whether
+ *               enough memory was mapped to resolve the page fault
+ *               successfully (e.g. enough for the next MTU, or the entire
+ *               WQE).
+ * @total_wqe_bytes receives the total data size of this WQE in bytes (minus
+ *                  the committed bytes).
+ *
+ * Returns the number of pages loaded if positive, zero for an empty WQE, or a
+ * negative error code.
+ */
+static int pagefault_data_segments(struct mlx5_ib_qp *qp,
+                                  struct mlx5_ib_pfault *pfault, void *wqe,
+                                  void *wqe_end, u32 *bytes_mapped,
+                                  u32 *total_wqe_bytes, int receive_queue)
+{
+       int ret = 0, npages = 0;
+       u64 io_virt;
+       u32 key;
+       u32 byte_count;
+       size_t bcnt;
+       int inline_segment;
+
+       /* Skip SRQ next-WQE segment. */
+       if (receive_queue && qp->ibqp.srq)
+               wqe += sizeof(struct mlx5_wqe_srq_next_seg);
+
+       if (bytes_mapped)
+               *bytes_mapped = 0;
+       if (total_wqe_bytes)
+               *total_wqe_bytes = 0;
+
+       while (wqe < wqe_end) {
+               struct mlx5_wqe_data_seg *dseg = wqe;
+
+               io_virt = be64_to_cpu(dseg->addr);
+               key = be32_to_cpu(dseg->lkey);
+               byte_count = be32_to_cpu(dseg->byte_count);
+               inline_segment = !!(byte_count &  MLX5_INLINE_SEG);
+               bcnt           = byte_count & ~MLX5_INLINE_SEG;
+
+               if (inline_segment) {
+                       bcnt = bcnt & MLX5_WQE_INLINE_SEG_BYTE_COUNT_MASK;
+                       wqe += ALIGN(sizeof(struct mlx5_wqe_inline_seg) + bcnt,
+                                    16);
+               } else {
+                       wqe += sizeof(*dseg);
+               }
+
+               /* receive WQE end of sg list. */
+               if (receive_queue && bcnt == 0 && key == MLX5_INVALID_LKEY &&
+                   io_virt == 0)
+                       break;
+
+               if (!inline_segment && total_wqe_bytes) {
+                       *total_wqe_bytes += bcnt - min_t(size_t, bcnt,
+                                       pfault->mpfault.bytes_committed);
+               }
+
+               /* A zero length data segment designates a length of 2GB. */
+               if (bcnt == 0)
+                       bcnt = 1U << 31;
+
+               if (inline_segment || bcnt <= pfault->mpfault.bytes_committed) {
+                       pfault->mpfault.bytes_committed -=
+                               min_t(size_t, bcnt,
+                                     pfault->mpfault.bytes_committed);
+                       continue;
+               }
+
+               ret = pagefault_single_data_segment(qp, pfault, key, io_virt,
+                                                   bcnt, bytes_mapped);
+               if (ret < 0)
+                       break;
+               npages += ret;
+       }
+
+       return ret < 0 ? ret : npages;
+}
+
+/*
+ * Parse initiator WQE. Advances the wqe pointer to point at the
+ * scatter-gather list, and set wqe_end to the end of the WQE.
+ */
+static int mlx5_ib_mr_initiator_pfault_handler(
+       struct mlx5_ib_qp *qp, struct mlx5_ib_pfault *pfault,
+       void **wqe, void **wqe_end, int wqe_length)
+{
+       struct mlx5_ib_dev *dev = to_mdev(qp->ibqp.pd->device);
+       struct mlx5_wqe_ctrl_seg *ctrl = *wqe;
+       u16 wqe_index = pfault->mpfault.wqe.wqe_index;
+       unsigned ds, opcode;
+#if defined(DEBUG)
+       u32 ctrl_wqe_index, ctrl_qpn;
+#endif
+
+       ds = be32_to_cpu(ctrl->qpn_ds) & MLX5_WQE_CTRL_DS_MASK;
+       if (ds * MLX5_WQE_DS_UNITS > wqe_length) {
+               mlx5_ib_err(dev, "Unable to read the complete WQE. ds = 0x%x, ret = 0x%x\n",
+                           ds, wqe_length);
+               return -EFAULT;
+       }
+
+       if (ds == 0) {
+               mlx5_ib_err(dev, "Got WQE with zero DS. wqe_index=%x, qpn=%x\n",
+                           wqe_index, qp->mqp.qpn);
+               return -EFAULT;
+       }
+
+#if defined(DEBUG)
+       ctrl_wqe_index = (be32_to_cpu(ctrl->opmod_idx_opcode) &
+                       MLX5_WQE_CTRL_WQE_INDEX_MASK) >>
+                       MLX5_WQE_CTRL_WQE_INDEX_SHIFT;
+       if (wqe_index != ctrl_wqe_index) {
+               mlx5_ib_err(dev, "Got WQE with invalid wqe_index. wqe_index=0x%x, qpn=0x%x ctrl->wqe_index=0x%x\n",
+                           wqe_index, qp->mqp.qpn,
+                           ctrl_wqe_index);
+               return -EFAULT;
+       }
+
+       ctrl_qpn = (be32_to_cpu(ctrl->qpn_ds) & MLX5_WQE_CTRL_QPN_MASK) >>
+               MLX5_WQE_CTRL_QPN_SHIFT;
+       if (qp->mqp.qpn != ctrl_qpn) {
+               mlx5_ib_err(dev, "Got WQE with incorrect QP number. wqe_index=0x%x, qpn=0x%x ctrl->qpn=0x%x\n",
+                           wqe_index, qp->mqp.qpn,
+                           ctrl_qpn);
+               return -EFAULT;
+       }
+#endif /* DEBUG */
+
+       *wqe_end = *wqe + ds * MLX5_WQE_DS_UNITS;
+       *wqe += sizeof(*ctrl);
+
+       opcode = be32_to_cpu(ctrl->opmod_idx_opcode) &
+                MLX5_WQE_CTRL_OPCODE_MASK;
+       switch (qp->ibqp.qp_type) {
+       case IB_QPT_RC:
+               switch (opcode) {
+               case MLX5_OPCODE_SEND:
+               case MLX5_OPCODE_SEND_IMM:
+               case MLX5_OPCODE_SEND_INVAL:
+                       if (!(dev->odp_caps.per_transport_caps.rc_odp_caps &
+                             IB_ODP_SUPPORT_SEND))
+                               goto invalid_transport_or_opcode;
+                       break;
+               case MLX5_OPCODE_RDMA_WRITE:
+               case MLX5_OPCODE_RDMA_WRITE_IMM:
+                       if (!(dev->odp_caps.per_transport_caps.rc_odp_caps &
+                             IB_ODP_SUPPORT_WRITE))
+                               goto invalid_transport_or_opcode;
+                       *wqe += sizeof(struct mlx5_wqe_raddr_seg);
+                       break;
+               case MLX5_OPCODE_RDMA_READ:
+                       if (!(dev->odp_caps.per_transport_caps.rc_odp_caps &
+                             IB_ODP_SUPPORT_READ))
+                               goto invalid_transport_or_opcode;
+                       *wqe += sizeof(struct mlx5_wqe_raddr_seg);
+                       break;
+               default:
+                       goto invalid_transport_or_opcode;
+               }
+               break;
+       case IB_QPT_UD:
+               switch (opcode) {
+               case MLX5_OPCODE_SEND:
+               case MLX5_OPCODE_SEND_IMM:
+                       if (!(dev->odp_caps.per_transport_caps.ud_odp_caps &
+                             IB_ODP_SUPPORT_SEND))
+                               goto invalid_transport_or_opcode;
+                       *wqe += sizeof(struct mlx5_wqe_datagram_seg);
+                       break;
+               default:
+                       goto invalid_transport_or_opcode;
+               }
+               break;
+       default:
+invalid_transport_or_opcode:
+               mlx5_ib_err(dev, "ODP fault on QP of an unsupported opcode or transport. transport: 0x%x opcode: 0x%x.\n",
+                           qp->ibqp.qp_type, opcode);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+/*
+ * Parse responder WQE. Advances the wqe pointer to point at the
+ * scatter-gather list, and set wqe_end to the end of the WQE.
+ */
+static int mlx5_ib_mr_responder_pfault_handler(
+       struct mlx5_ib_qp *qp, struct mlx5_ib_pfault *pfault,
+       void **wqe, void **wqe_end, int wqe_length)
+{
+       struct mlx5_ib_dev *dev = to_mdev(qp->ibqp.pd->device);
+       struct mlx5_ib_wq *wq = &qp->rq;
+       int wqe_size = 1 << wq->wqe_shift;
+
+       if (qp->ibqp.srq) {
+               mlx5_ib_err(dev, "ODP fault on SRQ is not supported\n");
+               return -EFAULT;
+       }
+
+       if (qp->wq_sig) {
+               mlx5_ib_err(dev, "ODP fault with WQE signatures is not supported\n");
+               return -EFAULT;
+       }
+
+       if (wqe_size > wqe_length) {
+               mlx5_ib_err(dev, "Couldn't read all of the receive WQE's content\n");
+               return -EFAULT;
+       }
+
+       switch (qp->ibqp.qp_type) {
+       case IB_QPT_RC:
+               if (!(dev->odp_caps.per_transport_caps.rc_odp_caps &
+                     IB_ODP_SUPPORT_RECV))
+                       goto invalid_transport_or_opcode;
+               break;
+       default:
+invalid_transport_or_opcode:
+               mlx5_ib_err(dev, "ODP fault on QP of an unsupported transport. transport: 0x%x\n",
+                           qp->ibqp.qp_type);
+               return -EFAULT;
+       }
+
+       *wqe_end = *wqe + wqe_size;
+
+       return 0;
+}
+
+static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_qp *qp,
+                                         struct mlx5_ib_pfault *pfault)
+{
+       struct mlx5_ib_dev *dev = to_mdev(qp->ibqp.pd->device);
+       int ret;
+       void *wqe, *wqe_end;
+       u32 bytes_mapped, total_wqe_bytes;
+       char *buffer = NULL;
+       int resume_with_error = 0;
+       u16 wqe_index = pfault->mpfault.wqe.wqe_index;
+       int requestor = pfault->mpfault.flags & MLX5_PFAULT_REQUESTOR;
+
+       buffer = (char *)__get_free_page(GFP_KERNEL);
+       if (!buffer) {
+               mlx5_ib_err(dev, "Error allocating memory for IO page fault handling.\n");
+               resume_with_error = 1;
+               goto resolve_page_fault;
+       }
+
+       ret = mlx5_ib_read_user_wqe(qp, requestor, wqe_index, buffer,
+                                   PAGE_SIZE);
+       if (ret < 0) {
+               mlx5_ib_err(dev, "Failed reading a WQE following page fault, error=%x, wqe_index=%x, qpn=%x\n",
+                           -ret, wqe_index, qp->mqp.qpn);
+               resume_with_error = 1;
+               goto resolve_page_fault;
+       }
+
+       wqe = buffer;
+       if (requestor)
+               ret = mlx5_ib_mr_initiator_pfault_handler(qp, pfault, &wqe,
+                                                         &wqe_end, ret);
+       else
+               ret = mlx5_ib_mr_responder_pfault_handler(qp, pfault, &wqe,
+                                                         &wqe_end, ret);
+       if (ret < 0) {
+               resume_with_error = 1;
+               goto resolve_page_fault;
+       }
+
+       if (wqe >= wqe_end) {
+               mlx5_ib_err(dev, "ODP fault on invalid WQE.\n");
+               resume_with_error = 1;
+               goto resolve_page_fault;
+       }
+
+       ret = pagefault_data_segments(qp, pfault, wqe, wqe_end, &bytes_mapped,
+                                     &total_wqe_bytes, !requestor);
+       if (ret == -EAGAIN) {
+               goto resolve_page_fault;
+       } else if (ret < 0 || total_wqe_bytes > bytes_mapped) {
+               mlx5_ib_err(dev, "Error getting user pages for page fault. Error: 0x%x\n",
+                           -ret);
+               resume_with_error = 1;
+               goto resolve_page_fault;
+       }
+
+resolve_page_fault:
+       mlx5_ib_page_fault_resume(qp, pfault, resume_with_error);
+       mlx5_ib_dbg(dev, "PAGE FAULT completed. QP 0x%x resume_with_error=%d, flags: 0x%x\n",
+                   qp->mqp.qpn, resume_with_error, pfault->mpfault.flags);
+
+       free_page((unsigned long)buffer);
+}
+
+static int pages_in_range(u64 address, u32 length)
+{
+       return (ALIGN(address + length, PAGE_SIZE) -
+               (address & PAGE_MASK)) >> PAGE_SHIFT;
+}
+
+static void mlx5_ib_mr_rdma_pfault_handler(struct mlx5_ib_qp *qp,
+                                          struct mlx5_ib_pfault *pfault)
+{
+       struct mlx5_pagefault *mpfault = &pfault->mpfault;
+       u64 address;
+       u32 length;
+       u32 prefetch_len = mpfault->bytes_committed;
+       int prefetch_activated = 0;
+       u32 rkey = mpfault->rdma.r_key;
+       int ret;
+
+       /* The RDMA responder handler handles the page fault in two parts.
+        * First it brings the necessary pages for the current packet
+        * (and uses the pfault context), and then (after resuming the QP)
+        * prefetches more pages. The second operation cannot use the pfault
+        * context and therefore uses the dummy_pfault context allocated on
+        * the stack */
+       struct mlx5_ib_pfault dummy_pfault = {};
+
+       dummy_pfault.mpfault.bytes_committed = 0;
+
+       mpfault->rdma.rdma_va += mpfault->bytes_committed;
+       mpfault->rdma.rdma_op_len -= min(mpfault->bytes_committed,
+                                        mpfault->rdma.rdma_op_len);
+       mpfault->bytes_committed = 0;
+
+       address = mpfault->rdma.rdma_va;
+       length  = mpfault->rdma.rdma_op_len;
+
+       /* For some operations, the hardware cannot tell the exact message
+        * length, and in those cases it reports zero. Use prefetch
+        * logic. */
+       if (length == 0) {
+               prefetch_activated = 1;
+               length = mpfault->rdma.packet_size;
+               prefetch_len = min(MAX_PREFETCH_LEN, prefetch_len);
+       }
+
+       ret = pagefault_single_data_segment(qp, pfault, rkey, address, length,
+                                           NULL);
+       if (ret == -EAGAIN) {
+               /* We're racing with an invalidation, don't prefetch */
+               prefetch_activated = 0;
+       } else if (ret < 0 || pages_in_range(address, length) > ret) {
+               mlx5_ib_page_fault_resume(qp, pfault, 1);
+               return;
+       }
+
+       mlx5_ib_page_fault_resume(qp, pfault, 0);
+
+       /* At this point, there might be a new pagefault already arriving in
+        * the eq, switch to the dummy pagefault for the rest of the
+        * processing. We're still OK with the objects being alive as the
+        * work-queue is being fenced. */
+
+       if (prefetch_activated) {
+               ret = pagefault_single_data_segment(qp, &dummy_pfault, rkey,
+                                                   address,
+                                                   prefetch_len,
+                                                   NULL);
+               if (ret < 0) {
+                       pr_warn("Prefetch failed (ret = %d, prefetch_activated = %d) for QPN %d, address: 0x%.16llx, length = 0x%.16x\n",
+                               ret, prefetch_activated,
+                               qp->ibqp.qp_num, address, prefetch_len);
+               }
+       }
+}
+
+void mlx5_ib_mr_pfault_handler(struct mlx5_ib_qp *qp,
+                              struct mlx5_ib_pfault *pfault)
+{
+       u8 event_subtype = pfault->mpfault.event_subtype;
+
+       switch (event_subtype) {
+       case MLX5_PFAULT_SUBTYPE_WQE:
+               mlx5_ib_mr_wqe_pfault_handler(qp, pfault);
+               break;
+       case MLX5_PFAULT_SUBTYPE_RDMA:
+               mlx5_ib_mr_rdma_pfault_handler(qp, pfault);
+               break;
+       default:
+               pr_warn("Invalid page fault event subtype: 0x%x\n",
+                       event_subtype);
+               mlx5_ib_page_fault_resume(qp, pfault, 1);
+               break;
+       }
+}
+
+static void mlx5_ib_qp_pfault_action(struct work_struct *work)
+{
+       struct mlx5_ib_pfault *pfault = container_of(work,
+                                                    struct mlx5_ib_pfault,
+                                                    work);
+       enum mlx5_ib_pagefault_context context =
+               mlx5_ib_get_pagefault_context(&pfault->mpfault);
+       struct mlx5_ib_qp *qp = container_of(pfault, struct mlx5_ib_qp,
+                                            pagefaults[context]);
+       mlx5_ib_mr_pfault_handler(qp, pfault);
+}
+
+void mlx5_ib_qp_disable_pagefaults(struct mlx5_ib_qp *qp)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&qp->disable_page_faults_lock, flags);
+       qp->disable_page_faults = 1;
+       spin_unlock_irqrestore(&qp->disable_page_faults_lock, flags);
+
+       /*
+        * Note that at this point, we are guarenteed that no more
+        * work queue elements will be posted to the work queue with
+        * the QP we are closing.
+        */
+       flush_workqueue(mlx5_ib_page_fault_wq);
+}
+
+void mlx5_ib_qp_enable_pagefaults(struct mlx5_ib_qp *qp)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&qp->disable_page_faults_lock, flags);
+       qp->disable_page_faults = 0;
+       spin_unlock_irqrestore(&qp->disable_page_faults_lock, flags);
+}
+
+static void mlx5_ib_pfault_handler(struct mlx5_core_qp *qp,
+                                  struct mlx5_pagefault *pfault)
+{
+       /*
+        * Note that we will only get one fault event per QP per context
+        * (responder/initiator, read/write), until we resolve the page fault
+        * with the mlx5_ib_page_fault_resume command. Since this function is
+        * called from within the work element, there is no risk of missing
+        * events.
+        */
+       struct mlx5_ib_qp *mibqp = to_mibqp(qp);
+       enum mlx5_ib_pagefault_context context =
+               mlx5_ib_get_pagefault_context(pfault);
+       struct mlx5_ib_pfault *qp_pfault = &mibqp->pagefaults[context];
+
+       qp_pfault->mpfault = *pfault;
+
+       /* No need to stop interrupts here since we are in an interrupt */
+       spin_lock(&mibqp->disable_page_faults_lock);
+       if (!mibqp->disable_page_faults)
+               queue_work(mlx5_ib_page_fault_wq, &qp_pfault->work);
+       spin_unlock(&mibqp->disable_page_faults_lock);
+}
+
+void mlx5_ib_odp_create_qp(struct mlx5_ib_qp *qp)
+{
+       int i;
+
+       qp->disable_page_faults = 1;
+       spin_lock_init(&qp->disable_page_faults_lock);
+
+       qp->mqp.pfault_handler  = mlx5_ib_pfault_handler;
+
+       for (i = 0; i < MLX5_IB_PAGEFAULT_CONTEXTS; ++i)
+               INIT_WORK(&qp->pagefaults[i].work, mlx5_ib_qp_pfault_action);
+}
+
+int mlx5_ib_odp_init_one(struct mlx5_ib_dev *ibdev)
+{
+       int ret;
+
+       ret = init_srcu_struct(&ibdev->mr_srcu);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+void mlx5_ib_odp_remove_one(struct mlx5_ib_dev *ibdev)
+{
+       cleanup_srcu_struct(&ibdev->mr_srcu);
+}
+
+int __init mlx5_ib_odp_init(void)
+{
+       mlx5_ib_page_fault_wq =
+               create_singlethread_workqueue("mlx5_ib_page_faults");
+       if (!mlx5_ib_page_fault_wq)
+               return -ENOMEM;
+
+       return 0;
+}
+
+void mlx5_ib_odp_cleanup(void)
+{
+       destroy_workqueue(mlx5_ib_page_fault_wq);
+}
index 1cae1c7..be0cd35 100644 (file)
@@ -70,15 +70,6 @@ static const u32 mlx5_ib_opcode[] = {
        [MLX5_IB_WR_UMR]                        = MLX5_OPCODE_UMR,
 };
 
-struct umr_wr {
-       u64                             virt_addr;
-       struct ib_pd                   *pd;
-       unsigned int                    page_shift;
-       unsigned int                    npages;
-       u32                             length;
-       int                             access_flags;
-       u32                             mkey;
-};
 
 static int is_qp0(enum ib_qp_type qp_type)
 {
@@ -110,6 +101,77 @@ void *mlx5_get_send_wqe(struct mlx5_ib_qp *qp, int n)
        return get_wqe(qp, qp->sq.offset + (n << MLX5_IB_SQ_STRIDE));
 }
 
+/**
+ * mlx5_ib_read_user_wqe() - Copy a user-space WQE to kernel space.
+ *
+ * @qp: QP to copy from.
+ * @send: copy from the send queue when non-zero, use the receive queue
+ *       otherwise.
+ * @wqe_index:  index to start copying from. For send work queues, the
+ *             wqe_index is in units of MLX5_SEND_WQE_BB.
+ *             For receive work queue, it is the number of work queue
+ *             element in the queue.
+ * @buffer: destination buffer.
+ * @length: maximum number of bytes to copy.
+ *
+ * Copies at least a single WQE, but may copy more data.
+ *
+ * Return: the number of bytes copied, or an error code.
+ */
+int mlx5_ib_read_user_wqe(struct mlx5_ib_qp *qp, int send, int wqe_index,
+                         void *buffer, u32 length)
+{
+       struct ib_device *ibdev = qp->ibqp.device;
+       struct mlx5_ib_dev *dev = to_mdev(ibdev);
+       struct mlx5_ib_wq *wq = send ? &qp->sq : &qp->rq;
+       size_t offset;
+       size_t wq_end;
+       struct ib_umem *umem = qp->umem;
+       u32 first_copy_length;
+       int wqe_length;
+       int ret;
+
+       if (wq->wqe_cnt == 0) {
+               mlx5_ib_dbg(dev, "mlx5_ib_read_user_wqe for a QP with wqe_cnt == 0. qp_type: 0x%x\n",
+                           qp->ibqp.qp_type);
+               return -EINVAL;
+       }
+
+       offset = wq->offset + ((wqe_index % wq->wqe_cnt) << wq->wqe_shift);
+       wq_end = wq->offset + (wq->wqe_cnt << wq->wqe_shift);
+
+       if (send && length < sizeof(struct mlx5_wqe_ctrl_seg))
+               return -EINVAL;
+
+       if (offset > umem->length ||
+           (send && offset + sizeof(struct mlx5_wqe_ctrl_seg) > umem->length))
+               return -EINVAL;
+
+       first_copy_length = min_t(u32, offset + length, wq_end) - offset;
+       ret = ib_umem_copy_from(buffer, umem, offset, first_copy_length);
+       if (ret)
+               return ret;
+
+       if (send) {
+               struct mlx5_wqe_ctrl_seg *ctrl = buffer;
+               int ds = be32_to_cpu(ctrl->qpn_ds) & MLX5_WQE_CTRL_DS_MASK;
+
+               wqe_length = ds * MLX5_WQE_DS_UNITS;
+       } else {
+               wqe_length = 1 << wq->wqe_shift;
+       }
+
+       if (wqe_length <= first_copy_length)
+               return first_copy_length;
+
+       ret = ib_umem_copy_from(buffer + first_copy_length, umem, wq->offset,
+                               wqe_length - first_copy_length);
+       if (ret)
+               return ret;
+
+       return wqe_length;
+}
+
 static void mlx5_ib_qp_event(struct mlx5_core_qp *qp, int type)
 {
        struct ib_qp *ibqp = &to_mibqp(qp)->ibqp;
@@ -814,6 +876,8 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd,
        int inlen = sizeof(*in);
        int err;
 
+       mlx5_ib_odp_create_qp(qp);
+
        gen = &dev->mdev->caps.gen;
        mutex_init(&qp->mutex);
        spin_lock_init(&qp->sq.lock);
@@ -1098,11 +1162,13 @@ static void destroy_qp_common(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp)
        in = kzalloc(sizeof(*in), GFP_KERNEL);
        if (!in)
                return;
-       if (qp->state != IB_QPS_RESET)
+       if (qp->state != IB_QPS_RESET) {
+               mlx5_ib_qp_disable_pagefaults(qp);
                if (mlx5_core_qp_modify(dev->mdev, to_mlx5_state(qp->state),
                                        MLX5_QP_STATE_RST, in, sizeof(*in), &qp->mqp))
                        mlx5_ib_warn(dev, "mlx5_ib: modify QP %06x to RESET failed\n",
                                     qp->mqp.qpn);
+       }
 
        get_cqs(qp, &send_cq, &recv_cq);
 
@@ -1650,6 +1716,15 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
        if (mlx5_st < 0)
                goto out;
 
+       /* If moving to a reset or error state, we must disable page faults on
+        * this QP and flush all current page faults. Otherwise a stale page
+        * fault may attempt to work on this QP after it is reset and moved
+        * again to RTS, and may cause the driver and the device to get out of
+        * sync. */
+       if (cur_state != IB_QPS_RESET && cur_state != IB_QPS_ERR &&
+           (new_state == IB_QPS_RESET || new_state == IB_QPS_ERR))
+               mlx5_ib_qp_disable_pagefaults(qp);
+
        optpar = ib_mask_to_mlx5_opt(attr_mask);
        optpar &= opt_mask[mlx5_cur][mlx5_new][mlx5_st];
        in->optparam = cpu_to_be32(optpar);
@@ -1659,6 +1734,9 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
        if (err)
                goto out;
 
+       if (cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT)
+               mlx5_ib_qp_enable_pagefaults(qp);
+
        qp->state = new_state;
 
        if (attr_mask & IB_QP_ACCESS_FLAGS)
@@ -1848,37 +1926,70 @@ static void set_frwr_umr_segment(struct mlx5_wqe_umr_ctrl_seg *umr,
        umr->mkey_mask = frwr_mkey_mask();
 }
 
+static __be64 get_umr_reg_mr_mask(void)
+{
+       u64 result;
+
+       result = MLX5_MKEY_MASK_LEN             |
+                MLX5_MKEY_MASK_PAGE_SIZE       |
+                MLX5_MKEY_MASK_START_ADDR      |
+                MLX5_MKEY_MASK_PD              |
+                MLX5_MKEY_MASK_LR              |
+                MLX5_MKEY_MASK_LW              |
+                MLX5_MKEY_MASK_KEY             |
+                MLX5_MKEY_MASK_RR              |
+                MLX5_MKEY_MASK_RW              |
+                MLX5_MKEY_MASK_A               |
+                MLX5_MKEY_MASK_FREE;
+
+       return cpu_to_be64(result);
+}
+
+static __be64 get_umr_unreg_mr_mask(void)
+{
+       u64 result;
+
+       result = MLX5_MKEY_MASK_FREE;
+
+       return cpu_to_be64(result);
+}
+
+static __be64 get_umr_update_mtt_mask(void)
+{
+       u64 result;
+
+       result = MLX5_MKEY_MASK_FREE;
+
+       return cpu_to_be64(result);
+}
+
 static void set_reg_umr_segment(struct mlx5_wqe_umr_ctrl_seg *umr,
                                struct ib_send_wr *wr)
 {
-       struct umr_wr *umrwr = (struct umr_wr *)&wr->wr.fast_reg;
-       u64 mask;
+       struct mlx5_umr_wr *umrwr = (struct mlx5_umr_wr *)&wr->wr.fast_reg;
 
        memset(umr, 0, sizeof(*umr));
 
+       if (wr->send_flags & MLX5_IB_SEND_UMR_FAIL_IF_FREE)
+               umr->flags = MLX5_UMR_CHECK_FREE; /* fail if free */
+       else
+               umr->flags = MLX5_UMR_CHECK_NOT_FREE; /* fail if not free */
+
        if (!(wr->send_flags & MLX5_IB_SEND_UMR_UNREG)) {
-               umr->flags = 1 << 5; /* fail if not free */
                umr->klm_octowords = get_klm_octo(umrwr->npages);
-               mask =  MLX5_MKEY_MASK_LEN              |
-                       MLX5_MKEY_MASK_PAGE_SIZE        |
-                       MLX5_MKEY_MASK_START_ADDR       |
-                       MLX5_MKEY_MASK_PD               |
-                       MLX5_MKEY_MASK_LR               |
-                       MLX5_MKEY_MASK_LW               |
-                       MLX5_MKEY_MASK_KEY              |
-                       MLX5_MKEY_MASK_RR               |
-                       MLX5_MKEY_MASK_RW               |
-                       MLX5_MKEY_MASK_A                |
-                       MLX5_MKEY_MASK_FREE;
-               umr->mkey_mask = cpu_to_be64(mask);
+               if (wr->send_flags & MLX5_IB_SEND_UMR_UPDATE_MTT) {
+                       umr->mkey_mask = get_umr_update_mtt_mask();
+                       umr->bsf_octowords = get_klm_octo(umrwr->target.offset);
+                       umr->flags |= MLX5_UMR_TRANSLATION_OFFSET_EN;
+               } else {
+                       umr->mkey_mask = get_umr_reg_mr_mask();
+               }
        } else {
-               umr->flags = 2 << 5; /* fail if free */
-               mask = MLX5_MKEY_MASK_FREE;
-               umr->mkey_mask = cpu_to_be64(mask);
+               umr->mkey_mask = get_umr_unreg_mr_mask();
        }
 
        if (!wr->num_sge)
-               umr->flags |= (1 << 7); /* inline */
+               umr->flags |= MLX5_UMR_INLINE;
 }
 
 static u8 get_umr_flags(int acc)
@@ -1895,7 +2006,7 @@ static void set_mkey_segment(struct mlx5_mkey_seg *seg, struct ib_send_wr *wr,
 {
        memset(seg, 0, sizeof(*seg));
        if (li) {
-               seg->status = 1 << 6;
+               seg->status = MLX5_MKEY_STATUS_FREE;
                return;
        }
 
@@ -1912,19 +2023,23 @@ static void set_mkey_segment(struct mlx5_mkey_seg *seg, struct ib_send_wr *wr,
 
 static void set_reg_mkey_segment(struct mlx5_mkey_seg *seg, struct ib_send_wr *wr)
 {
+       struct mlx5_umr_wr *umrwr = (struct mlx5_umr_wr *)&wr->wr.fast_reg;
+
        memset(seg, 0, sizeof(*seg));
        if (wr->send_flags & MLX5_IB_SEND_UMR_UNREG) {
-               seg->status = 1 << 6;
+               seg->status = MLX5_MKEY_STATUS_FREE;
                return;
        }
 
-       seg->flags = convert_access(wr->wr.fast_reg.access_flags);
-       seg->flags_pd = cpu_to_be32(to_mpd((struct ib_pd *)wr->wr.fast_reg.page_list)->pdn);
-       seg->start_addr = cpu_to_be64(wr->wr.fast_reg.iova_start);
-       seg->len = cpu_to_be64(wr->wr.fast_reg.length);
-       seg->log2_page_size = wr->wr.fast_reg.page_shift;
+       seg->flags = convert_access(umrwr->access_flags);
+       if (!(wr->send_flags & MLX5_IB_SEND_UMR_UPDATE_MTT)) {
+               seg->flags_pd = cpu_to_be32(to_mpd(umrwr->pd)->pdn);
+               seg->start_addr = cpu_to_be64(umrwr->target.virt_addr);
+       }
+       seg->len = cpu_to_be64(umrwr->length);
+       seg->log2_page_size = umrwr->page_shift;
        seg->qpn_mkey7_0 = cpu_to_be32(0xffffff00 |
-                                      mlx5_mkey_variant(wr->wr.fast_reg.rkey));
+                                      mlx5_mkey_variant(umrwr->mkey));
 }
 
 static void set_frwr_pages(struct mlx5_wqe_data_seg *dseg,
@@ -2927,6 +3042,14 @@ int mlx5_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, int qp_attr
        int mlx5_state;
        int err = 0;
 
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+       /*
+        * Wait for any outstanding page faults, in case the user frees memory
+        * based upon this query's result.
+        */
+       flush_workqueue(mlx5_ib_page_fault_wq);
+#endif
+
        mutex_lock(&qp->mutex);
        outb = kzalloc(sizeof(*outb), GFP_KERNEL);
        if (!outb) {
index fef067c..c0d0296 100644 (file)
@@ -2341,9 +2341,9 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
        nes_debug(NES_DBG_MR, "User base = 0x%lX, Virt base = 0x%lX, length = %u,"
                        " offset = %u, page size = %u.\n",
                        (unsigned long int)start, (unsigned long int)virt, (u32)length,
-                       region->offset, region->page_size);
+                       ib_umem_offset(region), region->page_size);
 
-       skip_pages = ((u32)region->offset) >> 12;
+       skip_pages = ((u32)ib_umem_offset(region)) >> 12;
 
        if (ib_copy_from_udata(&req, udata, sizeof(req))) {
                ib_umem_release(region);
@@ -2408,7 +2408,7 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
                                region_length -= skip_pages << 12;
                                for (page_index = skip_pages; page_index < chunk_pages; page_index++) {
                                        skip_pages = 0;
-                                       if ((page_count != 0) && (page_count<<12)-(region->offset&(4096-1)) >= region->length)
+                                       if ((page_count != 0) && (page_count << 12) - (ib_umem_offset(region) & (4096 - 1)) >= region->length)
                                                goto enough_pages;
                                        if ((page_count&0x01FF) == 0) {
                                                if (page_count >= 1024 * 512) {
index ac02ce4..f3cc8c9 100644 (file)
@@ -96,7 +96,6 @@ struct ib_ah *ocrdma_create_ah(struct ib_pd *ibpd, struct ib_ah_attr *attr)
        struct ocrdma_pd *pd = get_ocrdma_pd(ibpd);
        struct ocrdma_dev *dev = get_ocrdma_dev(ibpd->device);
        union ib_gid sgid;
-       u8 zmac[ETH_ALEN];
 
        if (!(attr->ah_flags & IB_AH_GRH))
                return ERR_PTR(-EINVAL);
@@ -118,9 +117,7 @@ struct ib_ah *ocrdma_create_ah(struct ib_pd *ibpd, struct ib_ah_attr *attr)
                goto av_conf_err;
        }
 
-       memset(&zmac, 0, ETH_ALEN);
-       if (pd->uctx &&
-           memcmp(attr->dmac, &zmac, ETH_ALEN)) {
+       if (pd->uctx) {
                status = rdma_addr_find_dmac_by_grh(&sgid, &attr->grh.dgid,
                                         attr->dmac, &attr->vlan_id);
                if (status) {
index 4c68305..fb8d8c4 100644 (file)
@@ -805,7 +805,7 @@ struct ib_mr *ocrdma_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len,
                goto umem_err;
 
        mr->hwmr.pbe_size = mr->umem->page_size;
-       mr->hwmr.fbo = mr->umem->offset;
+       mr->hwmr.fbo = ib_umem_offset(mr->umem);
        mr->hwmr.va = usr_addr;
        mr->hwmr.len = len;
        mr->hwmr.remote_wr = (acc & IB_ACCESS_REMOTE_WRITE) ? 1 : 0;
@@ -1410,6 +1410,8 @@ int ocrdma_query_qp(struct ib_qp *ibqp,
        mutex_unlock(&dev->dev_lock);
        if (status)
                goto mbx_err;
+       if (qp->qp_type == IB_QPT_UD)
+               qp_attr->qkey = params.qkey;
        qp_attr->qp_state = get_ibqp_state(IB_QPS_INIT);
        qp_attr->cur_qp_state = get_ibqp_state(IB_QPS_INIT);
        qp_attr->path_mtu =
index 9bbb553..a77fb4f 100644 (file)
@@ -258,7 +258,7 @@ struct ib_mr *qib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
        mr->mr.user_base = start;
        mr->mr.iova = virt_addr;
        mr->mr.length = length;
-       mr->mr.offset = umem->offset;
+       mr->mr.offset = ib_umem_offset(umem);
        mr->mr.access_flags = mr_access_flags;
        mr->umem = umem;
 
index d7562be..8ba80a6 100644 (file)
@@ -98,9 +98,15 @@ enum {
 
        IPOIB_MCAST_FLAG_FOUND    = 0,  /* used in set_multicast_list */
        IPOIB_MCAST_FLAG_SENDONLY = 1,
-       IPOIB_MCAST_FLAG_BUSY     = 2,  /* joining or already joined */
+       /*
+        * For IPOIB_MCAST_FLAG_BUSY
+        * When set, in flight join and mcast->mc is unreliable
+        * When clear and mcast->mc IS_ERR_OR_NULL, need to restart or
+        *   haven't started yet
+        * When clear and mcast->mc is valid pointer, join was successful
+        */
+       IPOIB_MCAST_FLAG_BUSY     = 2,
        IPOIB_MCAST_FLAG_ATTACHED = 3,
-       IPOIB_MCAST_JOIN_STARTED  = 4,
 
        MAX_SEND_CQE              = 16,
        IPOIB_CM_COPYBREAK        = 256,
@@ -317,6 +323,7 @@ struct ipoib_dev_priv {
        struct list_head multicast_list;
        struct rb_root multicast_tree;
 
+       struct workqueue_struct *wq;
        struct delayed_work mcast_task;
        struct work_struct carrier_on_task;
        struct work_struct flush_light;
@@ -477,10 +484,10 @@ void ipoib_ib_dev_flush_heavy(struct work_struct *work);
 void ipoib_pkey_event(struct work_struct *work);
 void ipoib_ib_dev_cleanup(struct net_device *dev);
 
-int ipoib_ib_dev_open(struct net_device *dev, int flush);
+int ipoib_ib_dev_open(struct net_device *dev);
 int ipoib_ib_dev_up(struct net_device *dev);
-int ipoib_ib_dev_down(struct net_device *dev, int flush);
-int ipoib_ib_dev_stop(struct net_device *dev, int flush);
+int ipoib_ib_dev_down(struct net_device *dev);
+int ipoib_ib_dev_stop(struct net_device *dev);
 void ipoib_pkey_dev_check_presence(struct net_device *dev);
 
 int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port);
@@ -492,7 +499,7 @@ void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb);
 
 void ipoib_mcast_restart_task(struct work_struct *work);
 int ipoib_mcast_start_thread(struct net_device *dev);
-int ipoib_mcast_stop_thread(struct net_device *dev, int flush);
+int ipoib_mcast_stop_thread(struct net_device *dev);
 
 void ipoib_mcast_dev_down(struct net_device *dev);
 void ipoib_mcast_dev_flush(struct net_device *dev);
index 933efce..56959ad 100644 (file)
@@ -474,7 +474,7 @@ static int ipoib_cm_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *even
        }
 
        spin_lock_irq(&priv->lock);
-       queue_delayed_work(ipoib_workqueue,
+       queue_delayed_work(priv->wq,
                           &priv->cm.stale_task, IPOIB_CM_RX_DELAY);
        /* Add this entry to passive ids list head, but do not re-add it
         * if IB_EVENT_QP_LAST_WQE_REACHED has moved it to flush list. */
@@ -576,7 +576,7 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
                        spin_lock_irqsave(&priv->lock, flags);
                        list_splice_init(&priv->cm.rx_drain_list, &priv->cm.rx_reap_list);
                        ipoib_cm_start_rx_drain(priv);
-                       queue_work(ipoib_workqueue, &priv->cm.rx_reap_task);
+                       queue_work(priv->wq, &priv->cm.rx_reap_task);
                        spin_unlock_irqrestore(&priv->lock, flags);
                } else
                        ipoib_warn(priv, "cm recv completion event with wrid %d (> %d)\n",
@@ -603,7 +603,7 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
                                spin_lock_irqsave(&priv->lock, flags);
                                list_move(&p->list, &priv->cm.rx_reap_list);
                                spin_unlock_irqrestore(&priv->lock, flags);
-                               queue_work(ipoib_workqueue, &priv->cm.rx_reap_task);
+                               queue_work(priv->wq, &priv->cm.rx_reap_task);
                        }
                        return;
                }
@@ -827,7 +827,7 @@ void ipoib_cm_handle_tx_wc(struct net_device *dev, struct ib_wc *wc)
 
                if (test_and_clear_bit(IPOIB_FLAG_INITIALIZED, &tx->flags)) {
                        list_move(&tx->list, &priv->cm.reap_list);
-                       queue_work(ipoib_workqueue, &priv->cm.reap_task);
+                       queue_work(priv->wq, &priv->cm.reap_task);
                }
 
                clear_bit(IPOIB_FLAG_OPER_UP, &tx->flags);
@@ -1255,7 +1255,7 @@ static int ipoib_cm_tx_handler(struct ib_cm_id *cm_id,
 
                if (test_and_clear_bit(IPOIB_FLAG_INITIALIZED, &tx->flags)) {
                        list_move(&tx->list, &priv->cm.reap_list);
-                       queue_work(ipoib_workqueue, &priv->cm.reap_task);
+                       queue_work(priv->wq, &priv->cm.reap_task);
                }
 
                spin_unlock_irqrestore(&priv->lock, flags);
@@ -1284,7 +1284,7 @@ struct ipoib_cm_tx *ipoib_cm_create_tx(struct net_device *dev, struct ipoib_path
        tx->dev = dev;
        list_add(&tx->list, &priv->cm.start_list);
        set_bit(IPOIB_FLAG_INITIALIZED, &tx->flags);
-       queue_work(ipoib_workqueue, &priv->cm.start_task);
+       queue_work(priv->wq, &priv->cm.start_task);
        return tx;
 }
 
@@ -1295,7 +1295,7 @@ void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx)
        if (test_and_clear_bit(IPOIB_FLAG_INITIALIZED, &tx->flags)) {
                spin_lock_irqsave(&priv->lock, flags);
                list_move(&tx->list, &priv->cm.reap_list);
-               queue_work(ipoib_workqueue, &priv->cm.reap_task);
+               queue_work(priv->wq, &priv->cm.reap_task);
                ipoib_dbg(priv, "Reap connection for gid %pI6\n",
                          tx->neigh->daddr + 4);
                tx->neigh = NULL;
@@ -1417,7 +1417,7 @@ void ipoib_cm_skb_too_long(struct net_device *dev, struct sk_buff *skb,
 
        skb_queue_tail(&priv->cm.skb_queue, skb);
        if (e)
-               queue_work(ipoib_workqueue, &priv->cm.skb_task);
+               queue_work(priv->wq, &priv->cm.skb_task);
 }
 
 static void ipoib_cm_rx_reap(struct work_struct *work)
@@ -1450,7 +1450,7 @@ static void ipoib_cm_stale_task(struct work_struct *work)
        }
 
        if (!list_empty(&priv->cm.passive_ids))
-               queue_delayed_work(ipoib_workqueue,
+               queue_delayed_work(priv->wq,
                                   &priv->cm.stale_task, IPOIB_CM_RX_DELAY);
        spin_unlock_irq(&priv->lock);
 }
index 72626c3..fe65abb 100644 (file)
@@ -655,7 +655,7 @@ void ipoib_reap_ah(struct work_struct *work)
        __ipoib_reap_ah(dev);
 
        if (!test_bit(IPOIB_STOP_REAPER, &priv->flags))
-               queue_delayed_work(ipoib_workqueue, &priv->ah_reap_task,
+               queue_delayed_work(priv->wq, &priv->ah_reap_task,
                                   round_jiffies_relative(HZ));
 }
 
@@ -664,7 +664,7 @@ static void ipoib_ib_tx_timer_func(unsigned long ctx)
        drain_tx_cq((struct net_device *)ctx);
 }
 
-int ipoib_ib_dev_open(struct net_device *dev, int flush)
+int ipoib_ib_dev_open(struct net_device *dev)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
        int ret;
@@ -696,7 +696,7 @@ int ipoib_ib_dev_open(struct net_device *dev, int flush)
        }
 
        clear_bit(IPOIB_STOP_REAPER, &priv->flags);
-       queue_delayed_work(ipoib_workqueue, &priv->ah_reap_task,
+       queue_delayed_work(priv->wq, &priv->ah_reap_task,
                           round_jiffies_relative(HZ));
 
        if (!test_and_set_bit(IPOIB_FLAG_INITIALIZED, &priv->flags))
@@ -706,7 +706,7 @@ int ipoib_ib_dev_open(struct net_device *dev, int flush)
 dev_stop:
        if (!test_and_set_bit(IPOIB_FLAG_INITIALIZED, &priv->flags))
                napi_enable(&priv->napi);
-       ipoib_ib_dev_stop(dev, flush);
+       ipoib_ib_dev_stop(dev);
        return -1;
 }
 
@@ -738,7 +738,7 @@ int ipoib_ib_dev_up(struct net_device *dev)
        return ipoib_mcast_start_thread(dev);
 }
 
-int ipoib_ib_dev_down(struct net_device *dev, int flush)
+int ipoib_ib_dev_down(struct net_device *dev)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
 
@@ -747,7 +747,7 @@ int ipoib_ib_dev_down(struct net_device *dev, int flush)
        clear_bit(IPOIB_FLAG_OPER_UP, &priv->flags);
        netif_carrier_off(dev);
 
-       ipoib_mcast_stop_thread(dev, flush);
+       ipoib_mcast_stop_thread(dev);
        ipoib_mcast_dev_flush(dev);
 
        ipoib_flush_paths(dev);
@@ -807,7 +807,7 @@ void ipoib_drain_cq(struct net_device *dev)
        local_bh_enable();
 }
 
-int ipoib_ib_dev_stop(struct net_device *dev, int flush)
+int ipoib_ib_dev_stop(struct net_device *dev)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
        struct ib_qp_attr qp_attr;
@@ -880,8 +880,7 @@ timeout:
        /* Wait for all AHs to be reaped */
        set_bit(IPOIB_STOP_REAPER, &priv->flags);
        cancel_delayed_work(&priv->ah_reap_task);
-       if (flush)
-               flush_workqueue(ipoib_workqueue);
+       flush_workqueue(priv->wq);
 
        begin = jiffies;
 
@@ -918,7 +917,7 @@ int ipoib_ib_dev_init(struct net_device *dev, struct ib_device *ca, int port)
                    (unsigned long) dev);
 
        if (dev->flags & IFF_UP) {
-               if (ipoib_ib_dev_open(dev, 1)) {
+               if (ipoib_ib_dev_open(dev)) {
                        ipoib_transport_dev_cleanup(dev);
                        return -ENODEV;
                }
@@ -1040,12 +1039,12 @@ static void __ipoib_ib_dev_flush(struct ipoib_dev_priv *priv,
        }
 
        if (level >= IPOIB_FLUSH_NORMAL)
-               ipoib_ib_dev_down(dev, 0);
+               ipoib_ib_dev_down(dev);
 
        if (level == IPOIB_FLUSH_HEAVY) {
                if (test_bit(IPOIB_FLAG_INITIALIZED, &priv->flags))
-                       ipoib_ib_dev_stop(dev, 0);
-               if (ipoib_ib_dev_open(dev, 0) != 0)
+                       ipoib_ib_dev_stop(dev);
+               if (ipoib_ib_dev_open(dev) != 0)
                        return;
                if (netif_queue_stopped(dev))
                        netif_start_queue(dev);
@@ -1097,7 +1096,7 @@ void ipoib_ib_dev_cleanup(struct net_device *dev)
         */
        ipoib_flush_paths(dev);
 
-       ipoib_mcast_stop_thread(dev, 1);
+       ipoib_mcast_stop_thread(dev);
        ipoib_mcast_dev_flush(dev);
 
        ipoib_transport_dev_cleanup(dev);
index 58b5aa3..6bad17d 100644 (file)
@@ -108,7 +108,7 @@ int ipoib_open(struct net_device *dev)
 
        set_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags);
 
-       if (ipoib_ib_dev_open(dev, 1)) {
+       if (ipoib_ib_dev_open(dev)) {
                if (!test_bit(IPOIB_PKEY_ASSIGNED, &priv->flags))
                        return 0;
                goto err_disable;
@@ -139,7 +139,7 @@ int ipoib_open(struct net_device *dev)
        return 0;
 
 err_stop:
-       ipoib_ib_dev_stop(dev, 1);
+       ipoib_ib_dev_stop(dev);
 
 err_disable:
        clear_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags);
@@ -157,8 +157,8 @@ static int ipoib_stop(struct net_device *dev)
 
        netif_stop_queue(dev);
 
-       ipoib_ib_dev_down(dev, 1);
-       ipoib_ib_dev_stop(dev, 0);
+       ipoib_ib_dev_down(dev);
+       ipoib_ib_dev_stop(dev);
 
        if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) {
                struct ipoib_dev_priv *cpriv;
@@ -839,7 +839,7 @@ static void ipoib_set_mcast_list(struct net_device *dev)
                return;
        }
 
-       queue_work(ipoib_workqueue, &priv->restart_task);
+       queue_work(priv->wq, &priv->restart_task);
 }
 
 static u32 ipoib_addr_hash(struct ipoib_neigh_hash *htbl, u8 *daddr)
@@ -954,7 +954,7 @@ static void ipoib_reap_neigh(struct work_struct *work)
        __ipoib_reap_neigh(priv);
 
        if (!test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
-               queue_delayed_work(ipoib_workqueue, &priv->neigh_reap_task,
+               queue_delayed_work(priv->wq, &priv->neigh_reap_task,
                                   arp_tbl.gc_interval);
 }
 
@@ -1133,7 +1133,7 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
 
        /* start garbage collection */
        clear_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
-       queue_delayed_work(ipoib_workqueue, &priv->neigh_reap_task,
+       queue_delayed_work(priv->wq, &priv->neigh_reap_task,
                           arp_tbl.gc_interval);
 
        return 0;
@@ -1262,15 +1262,13 @@ int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
 
-       if (ipoib_neigh_hash_init(priv) < 0)
-               goto out;
        /* Allocate RX/TX "rings" to hold queued skbs */
        priv->rx_ring = kzalloc(ipoib_recvq_size * sizeof *priv->rx_ring,
                                GFP_KERNEL);
        if (!priv->rx_ring) {
                printk(KERN_WARNING "%s: failed to allocate RX ring (%d entries)\n",
                       ca->name, ipoib_recvq_size);
-               goto out_neigh_hash_cleanup;
+               goto out;
        }
 
        priv->tx_ring = vzalloc(ipoib_sendq_size * sizeof *priv->tx_ring);
@@ -1285,16 +1283,24 @@ int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port)
        if (ipoib_ib_dev_init(dev, ca, port))
                goto out_tx_ring_cleanup;
 
+       /*
+        * Must be after ipoib_ib_dev_init so we can allocate a per
+        * device wq there and use it here
+        */
+       if (ipoib_neigh_hash_init(priv) < 0)
+               goto out_dev_uninit;
+
        return 0;
 
+out_dev_uninit:
+       ipoib_ib_dev_cleanup(dev);
+
 out_tx_ring_cleanup:
        vfree(priv->tx_ring);
 
 out_rx_ring_cleanup:
        kfree(priv->rx_ring);
 
-out_neigh_hash_cleanup:
-       ipoib_neigh_hash_uninit(dev);
 out:
        return -ENOMEM;
 }
@@ -1317,6 +1323,12 @@ void ipoib_dev_cleanup(struct net_device *dev)
        }
        unregister_netdevice_many(&head);
 
+       /*
+        * Must be before ipoib_ib_dev_cleanup or we delete an in use
+        * work queue
+        */
+       ipoib_neigh_hash_uninit(dev);
+
        ipoib_ib_dev_cleanup(dev);
 
        kfree(priv->rx_ring);
@@ -1324,8 +1336,6 @@ void ipoib_dev_cleanup(struct net_device *dev)
 
        priv->rx_ring = NULL;
        priv->tx_ring = NULL;
-
-       ipoib_neigh_hash_uninit(dev);
 }
 
 static const struct header_ops ipoib_header_ops = {
@@ -1636,7 +1646,7 @@ register_failed:
        /* Stop GC if started before flush */
        set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
        cancel_delayed_work(&priv->neigh_reap_task);
-       flush_workqueue(ipoib_workqueue);
+       flush_workqueue(priv->wq);
 
 event_failed:
        ipoib_dev_cleanup(priv->dev);
@@ -1707,7 +1717,7 @@ static void ipoib_remove_one(struct ib_device *device)
                /* Stop GC */
                set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
                cancel_delayed_work(&priv->neigh_reap_task);
-               flush_workqueue(ipoib_workqueue);
+               flush_workqueue(priv->wq);
 
                unregister_netdev(priv->dev);
                free_netdev(priv->dev);
@@ -1748,8 +1758,13 @@ static int __init ipoib_init_module(void)
         * unregister_netdev() and linkwatch_event take the rtnl lock,
         * so flush_scheduled_work() can deadlock during device
         * removal.
+        *
+        * In addition, bringing one device up and another down at the
+        * same time can deadlock a single workqueue, so we have this
+        * global fallback workqueue, but we also attempt to open a
+        * per device workqueue each time we bring an interface up
         */
-       ipoib_workqueue = create_singlethread_workqueue("ipoib");
+       ipoib_workqueue = create_singlethread_workqueue("ipoib_flush");
        if (!ipoib_workqueue) {
                ret = -ENOMEM;
                goto err_fs;
index ffb83b5..bc50dd0 100644 (file)
@@ -190,12 +190,6 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast,
                spin_unlock_irq(&priv->lock);
                priv->tx_wr.wr.ud.remote_qkey = priv->qkey;
                set_qkey = 1;
-
-               if (!ipoib_cm_admin_enabled(dev)) {
-                       rtnl_lock();
-                       dev_set_mtu(dev, min(priv->mcast_mtu, priv->admin_mtu));
-                       rtnl_unlock();
-               }
        }
 
        if (!test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) {
@@ -277,16 +271,27 @@ ipoib_mcast_sendonly_join_complete(int status,
        struct ipoib_mcast *mcast = multicast->context;
        struct net_device *dev = mcast->dev;
 
+       /*
+        * We have to take the mutex to force mcast_sendonly_join to
+        * return from ib_sa_multicast_join and set mcast->mc to a
+        * valid value.  Otherwise we were racing with ourselves in
+        * that we might fail here, but get a valid return from
+        * ib_sa_multicast_join after we had cleared mcast->mc here,
+        * resulting in mis-matched joins and leaves and a deadlock
+        */
+       mutex_lock(&mcast_mutex);
+
        /* We trap for port events ourselves. */
        if (status == -ENETRESET)
-               return 0;
+               goto out;
 
        if (!status)
                status = ipoib_mcast_join_finish(mcast, &multicast->rec);
 
        if (status) {
                if (mcast->logcount++ < 20)
-                       ipoib_dbg_mcast(netdev_priv(dev), "multicast join failed for %pI6, status %d\n",
+                       ipoib_dbg_mcast(netdev_priv(dev), "sendonly multicast "
+                                       "join failed for %pI6, status %d\n",
                                        mcast->mcmember.mgid.raw, status);
 
                /* Flush out any queued packets */
@@ -296,11 +301,15 @@ ipoib_mcast_sendonly_join_complete(int status,
                        dev_kfree_skb_any(skb_dequeue(&mcast->pkt_queue));
                }
                netif_tx_unlock_bh(dev);
-
-               /* Clear the busy flag so we try again */
-               status = test_and_clear_bit(IPOIB_MCAST_FLAG_BUSY,
-                                           &mcast->flags);
        }
+out:
+       clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
+       if (status)
+               mcast->mc = NULL;
+       complete(&mcast->done);
+       if (status == -ENETRESET)
+               status = 0;
+       mutex_unlock(&mcast_mutex);
        return status;
 }
 
@@ -318,12 +327,14 @@ static int ipoib_mcast_sendonly_join(struct ipoib_mcast *mcast)
        int ret = 0;
 
        if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) {
-               ipoib_dbg_mcast(priv, "device shutting down, no multicast joins\n");
+               ipoib_dbg_mcast(priv, "device shutting down, no sendonly "
+                               "multicast joins\n");
                return -ENODEV;
        }
 
-       if (test_and_set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) {
-               ipoib_dbg_mcast(priv, "multicast entry busy, skipping\n");
+       if (test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) {
+               ipoib_dbg_mcast(priv, "multicast entry busy, skipping "
+                               "sendonly join\n");
                return -EBUSY;
        }
 
@@ -331,6 +342,9 @@ static int ipoib_mcast_sendonly_join(struct ipoib_mcast *mcast)
        rec.port_gid = priv->local_gid;
        rec.pkey     = cpu_to_be16(priv->pkey);
 
+       mutex_lock(&mcast_mutex);
+       init_completion(&mcast->done);
+       set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
        mcast->mc = ib_sa_join_multicast(&ipoib_sa_client, priv->ca,
                                         priv->port, &rec,
                                         IB_SA_MCMEMBER_REC_MGID        |
@@ -343,12 +357,14 @@ static int ipoib_mcast_sendonly_join(struct ipoib_mcast *mcast)
        if (IS_ERR(mcast->mc)) {
                ret = PTR_ERR(mcast->mc);
                clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
-               ipoib_warn(priv, "ib_sa_join_multicast failed (ret = %d)\n",
-                          ret);
+               complete(&mcast->done);
+               ipoib_warn(priv, "ib_sa_join_multicast for sendonly join "
+                          "failed (ret = %d)\n", ret);
        } else {
-               ipoib_dbg_mcast(priv, "no multicast record for %pI6, starting join\n",
-                               mcast->mcmember.mgid.raw);
+               ipoib_dbg_mcast(priv, "no multicast record for %pI6, starting "
+                               "sendonly join\n", mcast->mcmember.mgid.raw);
        }
+       mutex_unlock(&mcast_mutex);
 
        return ret;
 }
@@ -359,18 +375,29 @@ void ipoib_mcast_carrier_on_task(struct work_struct *work)
                                                   carrier_on_task);
        struct ib_port_attr attr;
 
-       /*
-        * Take rtnl_lock to avoid racing with ipoib_stop() and
-        * turning the carrier back on while a device is being
-        * removed.
-        */
        if (ib_query_port(priv->ca, priv->port, &attr) ||
            attr.state != IB_PORT_ACTIVE) {
                ipoib_dbg(priv, "Keeping carrier off until IB port is active\n");
                return;
        }
 
-       rtnl_lock();
+       /*
+        * Take rtnl_lock to avoid racing with ipoib_stop() and
+        * turning the carrier back on while a device is being
+        * removed.  However, ipoib_stop() will attempt to flush
+        * the workqueue while holding the rtnl lock, so loop
+        * on trylock until either we get the lock or we see
+        * FLAG_ADMIN_UP go away as that signals that we are bailing
+        * and can safely ignore the carrier on work.
+        */
+       while (!rtnl_trylock()) {
+               if (!test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags))
+                       return;
+               else
+                       msleep(20);
+       }
+       if (!ipoib_cm_admin_enabled(priv->dev))
+               dev_set_mtu(priv->dev, min(priv->mcast_mtu, priv->admin_mtu));
        netif_carrier_on(priv->dev);
        rtnl_unlock();
 }
@@ -385,60 +412,63 @@ static int ipoib_mcast_join_complete(int status,
        ipoib_dbg_mcast(priv, "join completion for %pI6 (status %d)\n",
                        mcast->mcmember.mgid.raw, status);
 
+       /*
+        * We have to take the mutex to force mcast_join to
+        * return from ib_sa_multicast_join and set mcast->mc to a
+        * valid value.  Otherwise we were racing with ourselves in
+        * that we might fail here, but get a valid return from
+        * ib_sa_multicast_join after we had cleared mcast->mc here,
+        * resulting in mis-matched joins and leaves and a deadlock
+        */
+       mutex_lock(&mcast_mutex);
+
        /* We trap for port events ourselves. */
-       if (status == -ENETRESET) {
-               status = 0;
+       if (status == -ENETRESET)
                goto out;
-       }
 
        if (!status)
                status = ipoib_mcast_join_finish(mcast, &multicast->rec);
 
        if (!status) {
                mcast->backoff = 1;
-               mutex_lock(&mcast_mutex);
                if (test_bit(IPOIB_MCAST_RUN, &priv->flags))
-                       queue_delayed_work(ipoib_workqueue,
-                                          &priv->mcast_task, 0);
-               mutex_unlock(&mcast_mutex);
+                       queue_delayed_work(priv->wq, &priv->mcast_task, 0);
 
                /*
-                * Defer carrier on work to ipoib_workqueue to avoid a
+                * Defer carrier on work to priv->wq to avoid a
                 * deadlock on rtnl_lock here.
                 */
                if (mcast == priv->broadcast)
-                       queue_work(ipoib_workqueue, &priv->carrier_on_task);
-
-               status = 0;
-               goto out;
-       }
-
-       if (mcast->logcount++ < 20) {
-               if (status == -ETIMEDOUT || status == -EAGAIN) {
-                       ipoib_dbg_mcast(priv, "multicast join failed for %pI6, status %d\n",
-                                       mcast->mcmember.mgid.raw, status);
-               } else {
-                       ipoib_warn(priv, "multicast join failed for %pI6, status %d\n",
-                                  mcast->mcmember.mgid.raw, status);
+                       queue_work(priv->wq, &priv->carrier_on_task);
+       } else {
+               if (mcast->logcount++ < 20) {
+                       if (status == -ETIMEDOUT || status == -EAGAIN) {
+                               ipoib_dbg_mcast(priv, "multicast join failed for %pI6, status %d\n",
+                                               mcast->mcmember.mgid.raw, status);
+                       } else {
+                               ipoib_warn(priv, "multicast join failed for %pI6, status %d\n",
+                                          mcast->mcmember.mgid.raw, status);
+                       }
                }
-       }
-
-       mcast->backoff *= 2;
-       if (mcast->backoff > IPOIB_MAX_BACKOFF_SECONDS)
-               mcast->backoff = IPOIB_MAX_BACKOFF_SECONDS;
 
-       /* Clear the busy flag so we try again */
-       status = test_and_clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
-
-       mutex_lock(&mcast_mutex);
+               mcast->backoff *= 2;
+               if (mcast->backoff > IPOIB_MAX_BACKOFF_SECONDS)
+                       mcast->backoff = IPOIB_MAX_BACKOFF_SECONDS;
+       }
+out:
        spin_lock_irq(&priv->lock);
-       if (test_bit(IPOIB_MCAST_RUN, &priv->flags))
-               queue_delayed_work(ipoib_workqueue, &priv->mcast_task,
+       clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
+       if (status)
+               mcast->mc = NULL;
+       complete(&mcast->done);
+       if (status == -ENETRESET)
+               status = 0;
+       if (status && test_bit(IPOIB_MCAST_RUN, &priv->flags))
+               queue_delayed_work(priv->wq, &priv->mcast_task,
                                   mcast->backoff * HZ);
        spin_unlock_irq(&priv->lock);
        mutex_unlock(&mcast_mutex);
-out:
-       complete(&mcast->done);
+
        return status;
 }
 
@@ -487,10 +517,9 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast,
                rec.hop_limit     = priv->broadcast->mcmember.hop_limit;
        }
 
-       set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
+       mutex_lock(&mcast_mutex);
        init_completion(&mcast->done);
-       set_bit(IPOIB_MCAST_JOIN_STARTED, &mcast->flags);
-
+       set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
        mcast->mc = ib_sa_join_multicast(&ipoib_sa_client, priv->ca, priv->port,
                                         &rec, comp_mask, GFP_KERNEL,
                                         ipoib_mcast_join_complete, mcast);
@@ -504,13 +533,11 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast,
                if (mcast->backoff > IPOIB_MAX_BACKOFF_SECONDS)
                        mcast->backoff = IPOIB_MAX_BACKOFF_SECONDS;
 
-               mutex_lock(&mcast_mutex);
                if (test_bit(IPOIB_MCAST_RUN, &priv->flags))
-                       queue_delayed_work(ipoib_workqueue,
-                                          &priv->mcast_task,
+                       queue_delayed_work(priv->wq, &priv->mcast_task,
                                           mcast->backoff * HZ);
-               mutex_unlock(&mcast_mutex);
        }
+       mutex_unlock(&mcast_mutex);
 }
 
 void ipoib_mcast_join_task(struct work_struct *work)
@@ -547,8 +574,8 @@ void ipoib_mcast_join_task(struct work_struct *work)
                        ipoib_warn(priv, "failed to allocate broadcast group\n");
                        mutex_lock(&mcast_mutex);
                        if (test_bit(IPOIB_MCAST_RUN, &priv->flags))
-                               queue_delayed_work(ipoib_workqueue,
-                                                  &priv->mcast_task, HZ);
+                               queue_delayed_work(priv->wq, &priv->mcast_task,
+                                                  HZ);
                        mutex_unlock(&mcast_mutex);
                        return;
                }
@@ -563,7 +590,8 @@ void ipoib_mcast_join_task(struct work_struct *work)
        }
 
        if (!test_bit(IPOIB_MCAST_FLAG_ATTACHED, &priv->broadcast->flags)) {
-               if (!test_bit(IPOIB_MCAST_FLAG_BUSY, &priv->broadcast->flags))
+               if (IS_ERR_OR_NULL(priv->broadcast->mc) &&
+                   !test_bit(IPOIB_MCAST_FLAG_BUSY, &priv->broadcast->flags))
                        ipoib_mcast_join(dev, priv->broadcast, 0);
                return;
        }
@@ -571,23 +599,33 @@ void ipoib_mcast_join_task(struct work_struct *work)
        while (1) {
                struct ipoib_mcast *mcast = NULL;
 
+               /*
+                * Need the mutex so our flags are consistent, need the
+                * priv->lock so we don't race with list removals in either
+                * mcast_dev_flush or mcast_restart_task
+                */
+               mutex_lock(&mcast_mutex);
                spin_lock_irq(&priv->lock);
                list_for_each_entry(mcast, &priv->multicast_list, list) {
-                       if (!test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)
-                           && !test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)
-                           && !test_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) {
+                       if (IS_ERR_OR_NULL(mcast->mc) &&
+                           !test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags) &&
+                           !test_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) {
                                /* Found the next unjoined group */
                                break;
                        }
                }
                spin_unlock_irq(&priv->lock);
+               mutex_unlock(&mcast_mutex);
 
                if (&mcast->list == &priv->multicast_list) {
                        /* All done */
                        break;
                }
 
-               ipoib_mcast_join(dev, mcast, 1);
+               if (test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags))
+                       ipoib_mcast_sendonly_join(mcast);
+               else
+                       ipoib_mcast_join(dev, mcast, 1);
                return;
        }
 
@@ -604,13 +642,13 @@ int ipoib_mcast_start_thread(struct net_device *dev)
 
        mutex_lock(&mcast_mutex);
        if (!test_and_set_bit(IPOIB_MCAST_RUN, &priv->flags))
-               queue_delayed_work(ipoib_workqueue, &priv->mcast_task, 0);
+               queue_delayed_work(priv->wq, &priv->mcast_task, 0);
        mutex_unlock(&mcast_mutex);
 
        return 0;
 }
 
-int ipoib_mcast_stop_thread(struct net_device *dev, int flush)
+int ipoib_mcast_stop_thread(struct net_device *dev)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
 
@@ -621,8 +659,7 @@ int ipoib_mcast_stop_thread(struct net_device *dev, int flush)
        cancel_delayed_work(&priv->mcast_task);
        mutex_unlock(&mcast_mutex);
 
-       if (flush)
-               flush_workqueue(ipoib_workqueue);
+       flush_workqueue(priv->wq);
 
        return 0;
 }
@@ -633,6 +670,9 @@ static int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast)
        int ret = 0;
 
        if (test_and_clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags))
+               ipoib_warn(priv, "ipoib_mcast_leave on an in-flight join\n");
+
+       if (!IS_ERR_OR_NULL(mcast->mc))
                ib_sa_free_multicast(mcast->mc);
 
        if (test_and_clear_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) {
@@ -685,6 +725,8 @@ void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb)
                memcpy(mcast->mcmember.mgid.raw, mgid, sizeof (union ib_gid));
                __ipoib_mcast_add(dev, mcast);
                list_add_tail(&mcast->list, &priv->multicast_list);
+               if (!test_and_set_bit(IPOIB_MCAST_RUN, &priv->flags))
+                       queue_delayed_work(priv->wq, &priv->mcast_task, 0);
        }
 
        if (!mcast->ah) {
@@ -698,8 +740,6 @@ void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb)
                if (test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags))
                        ipoib_dbg_mcast(priv, "no address vector, "
                                        "but multicast join already started\n");
-               else if (test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags))
-                       ipoib_mcast_sendonly_join(mcast);
 
                /*
                 * If lookup completes between here and out:, don't
@@ -759,9 +799,12 @@ void ipoib_mcast_dev_flush(struct net_device *dev)
 
        spin_unlock_irqrestore(&priv->lock, flags);
 
-       /* seperate between the wait to the leave*/
+       /*
+        * make sure the in-flight joins have finished before we attempt
+        * to leave
+        */
        list_for_each_entry_safe(mcast, tmcast, &remove_list, list)
-               if (test_bit(IPOIB_MCAST_JOIN_STARTED, &mcast->flags))
+               if (test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags))
                        wait_for_completion(&mcast->done);
 
        list_for_each_entry_safe(mcast, tmcast, &remove_list, list) {
@@ -794,8 +837,6 @@ void ipoib_mcast_restart_task(struct work_struct *work)
 
        ipoib_dbg_mcast(priv, "restarting multicast task\n");
 
-       ipoib_mcast_stop_thread(dev, 0);
-
        local_irq_save(flags);
        netif_addr_lock(dev);
        spin_lock(&priv->lock);
@@ -880,14 +921,38 @@ void ipoib_mcast_restart_task(struct work_struct *work)
        netif_addr_unlock(dev);
        local_irq_restore(flags);
 
-       /* We have to cancel outside of the spinlock */
+       /*
+        * make sure the in-flight joins have finished before we attempt
+        * to leave
+        */
+       list_for_each_entry_safe(mcast, tmcast, &remove_list, list)
+               if (test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags))
+                       wait_for_completion(&mcast->done);
+
+       /*
+        * We have to cancel outside of the spinlock, but we have to
+        * take the rtnl lock or else we race with the removal of
+        * entries from the remove list in mcast_dev_flush as part
+        * of ipoib_stop().  We detect the drop of the ADMIN_UP flag
+        * to signal that we have hit this particular race, and we
+        * return since we know we don't need to do anything else
+        * anyway.
+        */
+       while (!rtnl_trylock()) {
+               if (!test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags))
+                       return;
+               else
+                       msleep(20);
+       }
        list_for_each_entry_safe(mcast, tmcast, &remove_list, list) {
                ipoib_mcast_leave(mcast->dev, mcast);
                ipoib_mcast_free(mcast);
        }
-
-       if (test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags))
-               ipoib_mcast_start_thread(dev);
+       /*
+        * Restart our join task if needed
+        */
+       ipoib_mcast_start_thread(dev);
+       rtnl_unlock();
 }
 
 #ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
index c56d5d4..b72a753 100644 (file)
@@ -145,10 +145,20 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca)
        int ret, size;
        int i;
 
+       /*
+        * the various IPoIB tasks assume they will never race against
+        * themselves, so always use a single thread workqueue
+        */
+       priv->wq = create_singlethread_workqueue("ipoib_wq");
+       if (!priv->wq) {
+               printk(KERN_WARNING "ipoib: failed to allocate device WQ\n");
+               return -ENODEV;
+       }
+
        priv->pd = ib_alloc_pd(priv->ca);
        if (IS_ERR(priv->pd)) {
                printk(KERN_WARNING "%s: failed to allocate PD\n", ca->name);
-               return -ENODEV;
+               goto out_free_wq;
        }
 
        priv->mr = ib_get_dma_mr(priv->pd, IB_ACCESS_LOCAL_WRITE);
@@ -242,6 +252,10 @@ out_free_mr:
 
 out_free_pd:
        ib_dealloc_pd(priv->pd);
+
+out_free_wq:
+       destroy_workqueue(priv->wq);
+       priv->wq = NULL;
        return -ENODEV;
 }
 
@@ -270,6 +284,12 @@ void ipoib_transport_dev_cleanup(struct net_device *dev)
 
        if (ib_dealloc_pd(priv->pd))
                ipoib_warn(priv, "ib_dealloc_pd failed\n");
+
+       if (priv->wq) {
+               flush_workqueue(priv->wq);
+               destroy_workqueue(priv->wq);
+               priv->wq = NULL;
+       }
 }
 
 void ipoib_event(struct ib_event_handler *handler,
index 20ca6a6..6a594aa 100644 (file)
@@ -97,7 +97,7 @@ module_param_named(pi_enable, iser_pi_enable, bool, 0644);
 MODULE_PARM_DESC(pi_enable, "Enable T10-PI offload support (default:disabled)");
 
 module_param_named(pi_guard, iser_pi_guard, int, 0644);
-MODULE_PARM_DESC(pi_guard, "T10-PI guard_type, 0:CRC|1:IP_CSUM (default:IP_CSUM)");
+MODULE_PARM_DESC(pi_guard, "T10-PI guard_type [deprecated]");
 
 static struct workqueue_struct *release_wq;
 struct iser_global ig;
@@ -164,18 +164,42 @@ iscsi_iser_pdu_alloc(struct iscsi_task *task, uint8_t opcode)
        return 0;
 }
 
-int iser_initialize_task_headers(struct iscsi_task *task,
-                                               struct iser_tx_desc *tx_desc)
+/**
+ * iser_initialize_task_headers() - Initialize task headers
+ * @task:       iscsi task
+ * @tx_desc:    iser tx descriptor
+ *
+ * Notes:
+ * This routine may race with iser teardown flow for scsi
+ * error handling TMFs. So for TMF we should acquire the
+ * state mutex to avoid dereferencing the IB device which
+ * may have already been terminated.
+ */
+int
+iser_initialize_task_headers(struct iscsi_task *task,
+                            struct iser_tx_desc *tx_desc)
 {
-       struct iser_conn       *iser_conn   = task->conn->dd_data;
+       struct iser_conn *iser_conn = task->conn->dd_data;
        struct iser_device *device = iser_conn->ib_conn.device;
        struct iscsi_iser_task *iser_task = task->dd_data;
        u64 dma_addr;
+       const bool mgmt_task = !task->sc && !in_interrupt();
+       int ret = 0;
+
+       if (unlikely(mgmt_task))
+               mutex_lock(&iser_conn->state_mutex);
+
+       if (unlikely(iser_conn->state != ISER_CONN_UP)) {
+               ret = -ENODEV;
+               goto out;
+       }
 
        dma_addr = ib_dma_map_single(device->ib_device, (void *)tx_desc,
                                ISER_HEADERS_LEN, DMA_TO_DEVICE);
-       if (ib_dma_mapping_error(device->ib_device, dma_addr))
-               return -ENOMEM;
+       if (ib_dma_mapping_error(device->ib_device, dma_addr)) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
        tx_desc->dma_addr = dma_addr;
        tx_desc->tx_sg[0].addr   = tx_desc->dma_addr;
@@ -183,7 +207,11 @@ int iser_initialize_task_headers(struct iscsi_task *task,
        tx_desc->tx_sg[0].lkey   = device->mr->lkey;
 
        iser_task->iser_conn = iser_conn;
-       return 0;
+out:
+       if (unlikely(mgmt_task))
+               mutex_unlock(&iser_conn->state_mutex);
+
+       return ret;
 }
 
 /**
@@ -199,9 +227,14 @@ static int
 iscsi_iser_task_init(struct iscsi_task *task)
 {
        struct iscsi_iser_task *iser_task = task->dd_data;
+       int ret;
 
-       if (iser_initialize_task_headers(task, &iser_task->desc))
-                       return -ENOMEM;
+       ret = iser_initialize_task_headers(task, &iser_task->desc);
+       if (ret) {
+               iser_err("Failed to init task %p, err = %d\n",
+                        iser_task, ret);
+               return ret;
+       }
 
        /* mgmt task */
        if (!task->sc)
@@ -508,8 +541,8 @@ iscsi_iser_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
         */
        if (iser_conn) {
                mutex_lock(&iser_conn->state_mutex);
-               iscsi_conn_stop(cls_conn, flag);
                iser_conn_terminate(iser_conn);
+               iscsi_conn_stop(cls_conn, flag);
 
                /* unbind */
                iser_conn->iscsi_conn = NULL;
@@ -541,12 +574,13 @@ iscsi_iser_session_destroy(struct iscsi_cls_session *cls_session)
 static inline unsigned int
 iser_dif_prot_caps(int prot_caps)
 {
-       return ((prot_caps & IB_PROT_T10DIF_TYPE_1) ? SHOST_DIF_TYPE1_PROTECTION |
-                                                     SHOST_DIX_TYPE1_PROTECTION : 0) |
-              ((prot_caps & IB_PROT_T10DIF_TYPE_2) ? SHOST_DIF_TYPE2_PROTECTION |
-                                                     SHOST_DIX_TYPE2_PROTECTION : 0) |
-              ((prot_caps & IB_PROT_T10DIF_TYPE_3) ? SHOST_DIF_TYPE3_PROTECTION |
-                                                     SHOST_DIX_TYPE3_PROTECTION : 0);
+       return ((prot_caps & IB_PROT_T10DIF_TYPE_1) ?
+               SHOST_DIF_TYPE1_PROTECTION | SHOST_DIX_TYPE0_PROTECTION |
+               SHOST_DIX_TYPE1_PROTECTION : 0) |
+              ((prot_caps & IB_PROT_T10DIF_TYPE_2) ?
+               SHOST_DIF_TYPE2_PROTECTION | SHOST_DIX_TYPE2_PROTECTION : 0) |
+              ((prot_caps & IB_PROT_T10DIF_TYPE_3) ?
+               SHOST_DIF_TYPE3_PROTECTION | SHOST_DIX_TYPE3_PROTECTION : 0);
 }
 
 /**
@@ -569,6 +603,7 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep,
        struct Scsi_Host *shost;
        struct iser_conn *iser_conn = NULL;
        struct ib_conn *ib_conn;
+       u16 max_cmds;
 
        shost = iscsi_host_alloc(&iscsi_iser_sht, 0, 0);
        if (!shost)
@@ -586,26 +621,41 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep,
         */
        if (ep) {
                iser_conn = ep->dd_data;
+               max_cmds = iser_conn->max_cmds;
+
+               mutex_lock(&iser_conn->state_mutex);
+               if (iser_conn->state != ISER_CONN_UP) {
+                       iser_err("iser conn %p already started teardown\n",
+                                iser_conn);
+                       mutex_unlock(&iser_conn->state_mutex);
+                       goto free_host;
+               }
+
                ib_conn = &iser_conn->ib_conn;
                if (ib_conn->pi_support) {
                        u32 sig_caps = ib_conn->device->dev_attr.sig_prot_cap;
 
                        scsi_host_set_prot(shost, iser_dif_prot_caps(sig_caps));
-                       if (iser_pi_guard)
-                               scsi_host_set_guard(shost, SHOST_DIX_GUARD_IP);
-                       else
-                               scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC);
+                       scsi_host_set_guard(shost, SHOST_DIX_GUARD_IP |
+                                                  SHOST_DIX_GUARD_CRC);
                }
-       }
 
-       if (iscsi_host_add(shost, ep ?
-                          ib_conn->device->ib_device->dma_device : NULL))
-               goto free_host;
+               if (iscsi_host_add(shost,
+                                  ib_conn->device->ib_device->dma_device)) {
+                       mutex_unlock(&iser_conn->state_mutex);
+                       goto free_host;
+               }
+               mutex_unlock(&iser_conn->state_mutex);
+       } else {
+               max_cmds = ISER_DEF_XMIT_CMDS_MAX;
+               if (iscsi_host_add(shost, NULL))
+                       goto free_host;
+       }
 
-       if (cmds_max > ISER_DEF_XMIT_CMDS_MAX) {
+       if (cmds_max > max_cmds) {
                iser_info("cmds_max changed from %u to %u\n",
-                         cmds_max, ISER_DEF_XMIT_CMDS_MAX);
-               cmds_max = ISER_DEF_XMIT_CMDS_MAX;
+                         cmds_max, max_cmds);
+               cmds_max = max_cmds;
        }
 
        cls_session = iscsi_session_setup(&iscsi_iser_transport, shost,
index cd4174c..5ce2681 100644 (file)
 
 #define DRV_NAME       "iser"
 #define PFX            DRV_NAME ": "
-#define DRV_VER                "1.4.8"
+#define DRV_VER                "1.5"
 
 #define iser_dbg(fmt, arg...)                           \
        do {                                             \
-               if (iser_debug_level > 2)                \
+               if (unlikely(iser_debug_level > 2))      \
                        printk(KERN_DEBUG PFX "%s: " fmt,\
                                __func__ , ## arg);      \
        } while (0)
 
 #define iser_warn(fmt, arg...)                         \
        do {                                            \
-               if (iser_debug_level > 0)               \
+               if (unlikely(iser_debug_level > 0))     \
                        pr_warn(PFX "%s: " fmt,         \
                                __func__ , ## arg);     \
        } while (0)
 
 #define iser_info(fmt, arg...)                         \
        do {                                            \
-               if (iser_debug_level > 1)               \
+               if (unlikely(iser_debug_level > 1))     \
                        pr_info(PFX "%s: " fmt,         \
                                __func__ , ## arg);     \
        } while (0)
 
-#define iser_err(fmt, arg...)                          \
-       do {                                            \
-               printk(KERN_ERR PFX "%s: " fmt,         \
-                      __func__ , ## arg);              \
-       } while (0)
+#define iser_err(fmt, arg...) \
+       pr_err(PFX "%s: " fmt, __func__ , ## arg)
 
 #define SHIFT_4K       12
 #define SIZE_4K        (1ULL << SHIFT_4K)
                                        ISER_MAX_TX_MISC_PDUS         + \
                                        ISER_MAX_RX_MISC_PDUS)
 
+#define ISER_GET_MAX_XMIT_CMDS(send_wr) ((send_wr                      \
+                                        - ISER_MAX_TX_MISC_PDUS        \
+                                        - ISER_MAX_RX_MISC_PDUS) /     \
+                                        (1 + ISER_INFLIGHT_DATAOUTS))
+
 #define ISER_WC_BATCH_COUNT   16
 #define ISER_SIGNAL_CMD_COUNT 32
 
@@ -247,7 +249,6 @@ struct iscsi_endpoint;
  * @va:           MR start address (buffer va)
  * @len:          MR length
  * @mem_h:        pointer to registration context (FMR/Fastreg)
- * @is_mr:        indicates weather we registered the buffer
  */
 struct iser_mem_reg {
        u32  lkey;
@@ -255,7 +256,6 @@ struct iser_mem_reg {
        u64  va;
        u64  len;
        void *mem_h;
-       int  is_mr;
 };
 
 /**
@@ -323,8 +323,6 @@ struct iser_rx_desc {
        char                         pad[ISER_RX_PAD_SIZE];
 } __attribute__((packed));
 
-#define ISER_MAX_CQ 4
-
 struct iser_conn;
 struct ib_conn;
 struct iscsi_iser_task;
@@ -375,7 +373,7 @@ struct iser_device {
        struct list_head             ig_list;
        int                          refcount;
        int                          comps_used;
-       struct iser_comp             comps[ISER_MAX_CQ];
+       struct iser_comp             *comps;
        int                          (*iser_alloc_rdma_reg_res)(struct ib_conn *ib_conn,
                                                                unsigned cmds_max);
        void                         (*iser_free_rdma_reg_res)(struct ib_conn *ib_conn);
@@ -432,6 +430,7 @@ struct fast_reg_descriptor {
  * @cma_id:              rdma_cm connection maneger handle
  * @qp:                  Connection Queue-pair
  * @post_recv_buf_count: post receive counter
+ * @sig_count:           send work request signal count
  * @rx_wr:               receive work request for batch posts
  * @device:              reference to iser device
  * @comp:                iser completion context
@@ -452,6 +451,7 @@ struct ib_conn {
        struct rdma_cm_id           *cma_id;
        struct ib_qp                *qp;
        int                          post_recv_buf_count;
+       u8                           sig_count;
        struct ib_recv_wr            rx_wr[ISER_MIN_POSTED_RX];
        struct iser_device          *device;
        struct iser_comp            *comp;
@@ -482,6 +482,7 @@ struct ib_conn {
  *                    to max number of post recvs
  * @qp_max_recv_dtos_mask: (qp_max_recv_dtos - 1)
  * @min_posted_rx:    (qp_max_recv_dtos >> 2)
+ * @max_cmds:         maximum cmds allowed for this connection
  * @name:             connection peer portal
  * @release_work:     deffered work for release job
  * @state_mutex:      protects iser onnection state
@@ -507,6 +508,7 @@ struct iser_conn {
        unsigned                     qp_max_recv_dtos;
        unsigned                     qp_max_recv_dtos_mask;
        unsigned                     min_posted_rx;
+       u16                          max_cmds;
        char                         name[ISER_OBJECT_NAME_SIZE];
        struct work_struct           release_work;
        struct mutex                 state_mutex;
index 5a489ea..3821633 100644 (file)
@@ -369,7 +369,7 @@ static int iser_post_rx_bufs(struct iscsi_conn *conn, struct iscsi_hdr *req)
        return 0;
 }
 
-static inline bool iser_signal_comp(int sig_count)
+static inline bool iser_signal_comp(u8 sig_count)
 {
        return ((sig_count % ISER_SIGNAL_CMD_COUNT) == 0);
 }
@@ -388,7 +388,7 @@ int iser_send_command(struct iscsi_conn *conn,
        struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)task->hdr;
        struct scsi_cmnd *sc  =  task->sc;
        struct iser_tx_desc *tx_desc = &iser_task->desc;
-       static unsigned sig_count;
+       u8 sig_count = ++iser_conn->ib_conn.sig_count;
 
        edtl = ntohl(hdr->data_length);
 
@@ -435,7 +435,7 @@ int iser_send_command(struct iscsi_conn *conn,
        iser_task->status = ISER_TASK_STATUS_STARTED;
 
        err = iser_post_send(&iser_conn->ib_conn, tx_desc,
-                            iser_signal_comp(++sig_count));
+                            iser_signal_comp(sig_count));
        if (!err)
                return 0;
 
index 6c5ce35..abce933 100644 (file)
@@ -73,7 +73,6 @@ static int iser_start_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
 
        if (cmd_dir == ISER_DIR_OUT) {
                /* copy the unaligned sg the buffer which is used for RDMA */
-               int i;
                char *p, *from;
 
                sgl = (struct scatterlist *)data->buf;
@@ -409,7 +408,6 @@ int iser_reg_rdma_mem_fmr(struct iscsi_iser_task *iser_task,
                regd_buf->reg.rkey = device->mr->rkey;
                regd_buf->reg.len  = ib_sg_dma_len(ibdev, &sg[0]);
                regd_buf->reg.va   = ib_sg_dma_address(ibdev, &sg[0]);
-               regd_buf->reg.is_mr = 0;
 
                iser_dbg("PHYSICAL Mem.register: lkey: 0x%08X rkey: 0x%08X  "
                         "va: 0x%08lX sz: %ld]\n",
@@ -440,13 +438,13 @@ int iser_reg_rdma_mem_fmr(struct iscsi_iser_task *iser_task,
        return 0;
 }
 
-static inline void
+static void
 iser_set_dif_domain(struct scsi_cmnd *sc, struct ib_sig_attrs *sig_attrs,
                    struct ib_sig_domain *domain)
 {
        domain->sig_type = IB_SIG_TYPE_T10_DIF;
-       domain->sig.dif.pi_interval = sc->device->sector_size;
-       domain->sig.dif.ref_tag = scsi_get_lba(sc) & 0xffffffff;
+       domain->sig.dif.pi_interval = scsi_prot_interval(sc);
+       domain->sig.dif.ref_tag = scsi_prot_ref_tag(sc);
        /*
         * At the moment we hard code those, but in the future
         * we will take them from sc.
@@ -454,8 +452,7 @@ iser_set_dif_domain(struct scsi_cmnd *sc, struct ib_sig_attrs *sig_attrs,
        domain->sig.dif.apptag_check_mask = 0xffff;
        domain->sig.dif.app_escape = true;
        domain->sig.dif.ref_escape = true;
-       if (scsi_get_prot_type(sc) == SCSI_PROT_DIF_TYPE1 ||
-           scsi_get_prot_type(sc) == SCSI_PROT_DIF_TYPE2)
+       if (sc->prot_flags & SCSI_PROT_REF_INCREMENT)
                domain->sig.dif.ref_remap = true;
 };
 
@@ -473,26 +470,16 @@ iser_set_sig_attrs(struct scsi_cmnd *sc, struct ib_sig_attrs *sig_attrs)
        case SCSI_PROT_WRITE_STRIP:
                sig_attrs->wire.sig_type = IB_SIG_TYPE_NONE;
                iser_set_dif_domain(sc, sig_attrs, &sig_attrs->mem);
-               /*
-                * At the moment we use this modparam to tell what is
-                * the memory bg_type, in the future we will take it
-                * from sc.
-                */
-               sig_attrs->mem.sig.dif.bg_type = iser_pi_guard ? IB_T10DIF_CSUM :
-                                                IB_T10DIF_CRC;
+               sig_attrs->mem.sig.dif.bg_type = sc->prot_flags & SCSI_PROT_IP_CHECKSUM ?
+                                               IB_T10DIF_CSUM : IB_T10DIF_CRC;
                break;
        case SCSI_PROT_READ_PASS:
        case SCSI_PROT_WRITE_PASS:
                iser_set_dif_domain(sc, sig_attrs, &sig_attrs->wire);
                sig_attrs->wire.sig.dif.bg_type = IB_T10DIF_CRC;
                iser_set_dif_domain(sc, sig_attrs, &sig_attrs->mem);
-               /*
-                * At the moment we use this modparam to tell what is
-                * the memory bg_type, in the future we will take it
-                * from sc.
-                */
-               sig_attrs->mem.sig.dif.bg_type = iser_pi_guard ? IB_T10DIF_CSUM :
-                                                IB_T10DIF_CRC;
+               sig_attrs->mem.sig.dif.bg_type = sc->prot_flags & SCSI_PROT_IP_CHECKSUM ?
+                                               IB_T10DIF_CSUM : IB_T10DIF_CRC;
                break;
        default:
                iser_err("Unsupported PI operation %d\n",
@@ -503,26 +490,28 @@ iser_set_sig_attrs(struct scsi_cmnd *sc, struct ib_sig_attrs *sig_attrs)
        return 0;
 }
 
-static int
+static inline void
 iser_set_prot_checks(struct scsi_cmnd *sc, u8 *mask)
 {
-       switch (scsi_get_prot_type(sc)) {
-       case SCSI_PROT_DIF_TYPE0:
-               break;
-       case SCSI_PROT_DIF_TYPE1:
-       case SCSI_PROT_DIF_TYPE2:
-               *mask = ISER_CHECK_GUARD | ISER_CHECK_REFTAG;
-               break;
-       case SCSI_PROT_DIF_TYPE3:
-               *mask = ISER_CHECK_GUARD;
-               break;
-       default:
-               iser_err("Unsupported protection type %d\n",
-                        scsi_get_prot_type(sc));
-               return -EINVAL;
-       }
+       *mask = 0;
+       if (sc->prot_flags & SCSI_PROT_REF_CHECK)
+               *mask |= ISER_CHECK_REFTAG;
+       if (sc->prot_flags & SCSI_PROT_GUARD_CHECK)
+               *mask |= ISER_CHECK_GUARD;
+}
 
-       return 0;
+static void
+iser_inv_rkey(struct ib_send_wr *inv_wr, struct ib_mr *mr)
+{
+       u32 rkey;
+
+       memset(inv_wr, 0, sizeof(*inv_wr));
+       inv_wr->opcode = IB_WR_LOCAL_INV;
+       inv_wr->wr_id = ISER_FASTREG_LI_WRID;
+       inv_wr->ex.invalidate_rkey = mr->rkey;
+
+       rkey = ib_inc_rkey(mr->rkey);
+       ib_update_fast_reg_key(mr, rkey);
 }
 
 static int
@@ -536,26 +525,17 @@ iser_reg_sig_mr(struct iscsi_iser_task *iser_task,
        struct ib_send_wr *bad_wr, *wr = NULL;
        struct ib_sig_attrs sig_attrs;
        int ret;
-       u32 key;
 
        memset(&sig_attrs, 0, sizeof(sig_attrs));
        ret = iser_set_sig_attrs(iser_task->sc, &sig_attrs);
        if (ret)
                goto err;
 
-       ret = iser_set_prot_checks(iser_task->sc, &sig_attrs.check_mask);
-       if (ret)
-               goto err;
+       iser_set_prot_checks(iser_task->sc, &sig_attrs.check_mask);
 
        if (!(desc->reg_indicators & ISER_SIG_KEY_VALID)) {
-               memset(&inv_wr, 0, sizeof(inv_wr));
-               inv_wr.opcode = IB_WR_LOCAL_INV;
-               inv_wr.wr_id = ISER_FASTREG_LI_WRID;
-               inv_wr.ex.invalidate_rkey = pi_ctx->sig_mr->rkey;
+               iser_inv_rkey(&inv_wr, pi_ctx->sig_mr);
                wr = &inv_wr;
-               /* Bump the key */
-               key = (u8)(pi_ctx->sig_mr->rkey & 0x000000FF);
-               ib_update_fast_reg_key(pi_ctx->sig_mr, ++key);
        }
 
        memset(&sig_wr, 0, sizeof(sig_wr));
@@ -585,12 +565,7 @@ iser_reg_sig_mr(struct iscsi_iser_task *iser_task,
 
        sig_sge->lkey = pi_ctx->sig_mr->lkey;
        sig_sge->addr = 0;
-       sig_sge->length = data_sge->length + prot_sge->length;
-       if (scsi_get_prot_op(iser_task->sc) == SCSI_PROT_WRITE_INSERT ||
-           scsi_get_prot_op(iser_task->sc) == SCSI_PROT_READ_STRIP) {
-               sig_sge->length += (data_sge->length /
-                                  iser_task->sc->device->sector_size) * 8;
-       }
+       sig_sge->length = scsi_transfer_length(iser_task->sc);
 
        iser_dbg("sig_sge: addr: 0x%llx  length: %u lkey: 0x%x\n",
                 sig_sge->addr, sig_sge->length,
@@ -613,7 +588,6 @@ static int iser_fast_reg_mr(struct iscsi_iser_task *iser_task,
        struct ib_fast_reg_page_list *frpl;
        struct ib_send_wr fastreg_wr, inv_wr;
        struct ib_send_wr *bad_wr, *wr = NULL;
-       u8 key;
        int ret, offset, size, plen;
 
        /* if there a single dma entry, dma mr suffices */
@@ -645,14 +619,8 @@ static int iser_fast_reg_mr(struct iscsi_iser_task *iser_task,
        }
 
        if (!(desc->reg_indicators & ind)) {
-               memset(&inv_wr, 0, sizeof(inv_wr));
-               inv_wr.wr_id = ISER_FASTREG_LI_WRID;
-               inv_wr.opcode = IB_WR_LOCAL_INV;
-               inv_wr.ex.invalidate_rkey = mr->rkey;
+               iser_inv_rkey(&inv_wr, mr);
                wr = &inv_wr;
-               /* Bump the key */
-               key = (u8)(mr->rkey & 0x000000FF);
-               ib_update_fast_reg_key(mr, ++key);
        }
 
        /* Prepare FASTREG WR */
@@ -770,15 +738,11 @@ int iser_reg_rdma_mem_fastreg(struct iscsi_iser_task *iser_task,
                regd_buf->reg.rkey = desc->pi_ctx->sig_mr->rkey;
                regd_buf->reg.va = sig_sge.addr;
                regd_buf->reg.len = sig_sge.length;
-               regd_buf->reg.is_mr = 1;
        } else {
-               if (desc) {
+               if (desc)
                        regd_buf->reg.rkey = desc->data_mr->rkey;
-                       regd_buf->reg.is_mr = 1;
-               } else {
+               else
                        regd_buf->reg.rkey = device->mr->rkey;
-                       regd_buf->reg.is_mr = 0;
-               }
 
                regd_buf->reg.lkey = data_sge.lkey;
                regd_buf->reg.va = data_sge.addr;
index 67225bb..695a270 100644 (file)
@@ -76,7 +76,7 @@ static void iser_event_handler(struct ib_event_handler *handler,
 static int iser_create_device_ib_res(struct iser_device *device)
 {
        struct ib_device_attr *dev_attr = &device->dev_attr;
-       int ret, i;
+       int ret, i, max_cqe;
 
        ret = ib_query_device(device->ib_device, dev_attr);
        if (ret) {
@@ -104,11 +104,19 @@ static int iser_create_device_ib_res(struct iser_device *device)
                return -1;
        }
 
-       device->comps_used = min(ISER_MAX_CQ,
+       device->comps_used = min_t(int, num_online_cpus(),
                                 device->ib_device->num_comp_vectors);
-       iser_info("using %d CQs, device %s supports %d vectors\n",
+
+       device->comps = kcalloc(device->comps_used, sizeof(*device->comps),
+                               GFP_KERNEL);
+       if (!device->comps)
+               goto comps_err;
+
+       max_cqe = min(ISER_MAX_CQ_LEN, dev_attr->max_cqe);
+
+       iser_info("using %d CQs, device %s supports %d vectors max_cqe %d\n",
                  device->comps_used, device->ib_device->name,
-                 device->ib_device->num_comp_vectors);
+                 device->ib_device->num_comp_vectors, max_cqe);
 
        device->pd = ib_alloc_pd(device->ib_device);
        if (IS_ERR(device->pd))
@@ -122,7 +130,7 @@ static int iser_create_device_ib_res(struct iser_device *device)
                                        iser_cq_callback,
                                        iser_cq_event_callback,
                                        (void *)comp,
-                                       ISER_MAX_CQ_LEN, i);
+                                       max_cqe, i);
                if (IS_ERR(comp->cq)) {
                        comp->cq = NULL;
                        goto cq_err;
@@ -162,6 +170,8 @@ cq_err:
        }
        ib_dealloc_pd(device->pd);
 pd_err:
+       kfree(device->comps);
+comps_err:
        iser_err("failed to allocate an IB resource\n");
        return -1;
 }
@@ -187,6 +197,9 @@ static void iser_free_device_ib_res(struct iser_device *device)
        (void)ib_dereg_mr(device->mr);
        (void)ib_dealloc_pd(device->pd);
 
+       kfree(device->comps);
+       device->comps = NULL;
+
        device->mr = NULL;
        device->pd = NULL;
 }
@@ -425,7 +438,10 @@ void iser_free_fastreg_pool(struct ib_conn *ib_conn)
  */
 static int iser_create_ib_conn_res(struct ib_conn *ib_conn)
 {
+       struct iser_conn *iser_conn = container_of(ib_conn, struct iser_conn,
+                                                  ib_conn);
        struct iser_device      *device;
+       struct ib_device_attr *dev_attr;
        struct ib_qp_init_attr  init_attr;
        int                     ret = -ENOMEM;
        int index, min_index = 0;
@@ -433,6 +449,7 @@ static int iser_create_ib_conn_res(struct ib_conn *ib_conn)
        BUG_ON(ib_conn->device == NULL);
 
        device = ib_conn->device;
+       dev_attr = &device->dev_attr;
 
        memset(&init_attr, 0, sizeof init_attr);
 
@@ -460,8 +477,20 @@ static int iser_create_ib_conn_res(struct ib_conn *ib_conn)
        if (ib_conn->pi_support) {
                init_attr.cap.max_send_wr = ISER_QP_SIG_MAX_REQ_DTOS + 1;
                init_attr.create_flags |= IB_QP_CREATE_SIGNATURE_EN;
+               iser_conn->max_cmds =
+                       ISER_GET_MAX_XMIT_CMDS(ISER_QP_SIG_MAX_REQ_DTOS);
        } else {
-               init_attr.cap.max_send_wr  = ISER_QP_MAX_REQ_DTOS + 1;
+               if (dev_attr->max_qp_wr > ISER_QP_MAX_REQ_DTOS) {
+                       init_attr.cap.max_send_wr  = ISER_QP_MAX_REQ_DTOS + 1;
+                       iser_conn->max_cmds =
+                               ISER_GET_MAX_XMIT_CMDS(ISER_QP_MAX_REQ_DTOS);
+               } else {
+                       init_attr.cap.max_send_wr = dev_attr->max_qp_wr;
+                       iser_conn->max_cmds =
+                               ISER_GET_MAX_XMIT_CMDS(dev_attr->max_qp_wr);
+                       iser_dbg("device %s supports max_send_wr %d\n",
+                                device->ib_device->name, dev_attr->max_qp_wr);
+               }
        }
 
        ret = rdma_create_qp(ib_conn->cma_id, device->pd, &init_attr);
@@ -475,7 +504,11 @@ static int iser_create_ib_conn_res(struct ib_conn *ib_conn)
        return ret;
 
 out_err:
+       mutex_lock(&ig.connlist_mutex);
+       ib_conn->comp->active_qps--;
+       mutex_unlock(&ig.connlist_mutex);
        iser_err("unable to alloc mem or create resource, err %d\n", ret);
+
        return ret;
 }
 
@@ -610,9 +643,11 @@ void iser_conn_release(struct iser_conn *iser_conn)
        mutex_unlock(&ig.connlist_mutex);
 
        mutex_lock(&iser_conn->state_mutex);
-       if (iser_conn->state != ISER_CONN_DOWN)
+       if (iser_conn->state != ISER_CONN_DOWN) {
                iser_warn("iser conn %p state %d, expected state down.\n",
                          iser_conn, iser_conn->state);
+               iser_conn->state = ISER_CONN_DOWN;
+       }
        /*
         * In case we never got to bind stage, we still need to
         * release IB resources (which is safe to call more than once).
@@ -662,8 +697,10 @@ int iser_conn_terminate(struct iser_conn *iser_conn)
 
                /* post an indication that all flush errors were consumed */
                err = ib_post_send(ib_conn->qp, &ib_conn->beacon, &bad_wr);
-               if (err)
+               if (err) {
                        iser_err("conn %p failed to post beacon", ib_conn);
+                       return 1;
+               }
 
                wait_for_completion(&ib_conn->flush_comp);
        }
@@ -846,20 +883,21 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve
                break;
        case RDMA_CM_EVENT_DISCONNECTED:
        case RDMA_CM_EVENT_ADDR_CHANGE:
-               iser_disconnected_handler(cma_id);
+       case RDMA_CM_EVENT_TIMEWAIT_EXIT:
+               iser_cleanup_handler(cma_id, false);
                break;
        case RDMA_CM_EVENT_DEVICE_REMOVAL:
                /*
                 * we *must* destroy the device as we cannot rely
                 * on iscsid to be around to initiate error handling.
-                * also implicitly destroy the cma_id.
+                * also if we are not in state DOWN implicitly destroy
+                * the cma_id.
                 */
                iser_cleanup_handler(cma_id, true);
-               iser_conn->ib_conn.cma_id = NULL;
-               ret = 1;
-               break;
-       case RDMA_CM_EVENT_TIMEWAIT_EXIT:
-               iser_cleanup_handler(cma_id, false);
+               if (iser_conn->state != ISER_CONN_DOWN) {
+                       iser_conn->ib_conn.cma_id = NULL;
+                       ret = 1;
+               }
                break;
        default:
                iser_err("Unexpected RDMA CM event (%d)\n", event->event);
@@ -981,7 +1019,6 @@ int iser_reg_page_vec(struct ib_conn *ib_conn,
        mem_reg->rkey  = mem->fmr->rkey;
        mem_reg->len   = page_vec->length * SIZE_4K;
        mem_reg->va    = io_addr;
-       mem_reg->is_mr = 1;
        mem_reg->mem_h = (void *)mem;
 
        mem_reg->va   += page_vec->offset;
@@ -1008,7 +1045,7 @@ void iser_unreg_mem_fmr(struct iscsi_iser_task *iser_task,
        struct iser_mem_reg *reg = &iser_task->rdma_regd[cmd_dir].reg;
        int ret;
 
-       if (!reg->is_mr)
+       if (!reg->mem_h)
                return;
 
        iser_dbg("PHYSICAL Mem.Unregister mem_h %p\n",reg->mem_h);
@@ -1028,11 +1065,10 @@ void iser_unreg_mem_fastreg(struct iscsi_iser_task *iser_task,
        struct ib_conn *ib_conn = &iser_conn->ib_conn;
        struct fast_reg_descriptor *desc = reg->mem_h;
 
-       if (!reg->is_mr)
+       if (!desc)
                return;
 
        reg->mem_h = NULL;
-       reg->is_mr = 0;
        spin_lock_bh(&ib_conn->lock);
        list_add_tail(&desc->list, &ib_conn->fastreg.pool);
        spin_unlock_bh(&ib_conn->lock);
@@ -1049,7 +1085,7 @@ int iser_post_recvl(struct iser_conn *iser_conn)
        sge.length = ISER_RX_LOGIN_SIZE;
        sge.lkey   = ib_conn->device->mr->lkey;
 
-       rx_wr.wr_id   = (unsigned long)iser_conn->login_resp_buf;
+       rx_wr.wr_id   = (uintptr_t)iser_conn->login_resp_buf;
        rx_wr.sg_list = &sge;
        rx_wr.num_sge = 1;
        rx_wr.next    = NULL;
@@ -1073,7 +1109,7 @@ int iser_post_recvm(struct iser_conn *iser_conn, int count)
 
        for (rx_wr = ib_conn->rx_wr, i = 0; i < count; i++, rx_wr++) {
                rx_desc         = &iser_conn->rx_descs[my_rx_head];
-               rx_wr->wr_id    = (unsigned long)rx_desc;
+               rx_wr->wr_id    = (uintptr_t)rx_desc;
                rx_wr->sg_list  = &rx_desc->rx_sg;
                rx_wr->num_sge  = 1;
                rx_wr->next     = rx_wr + 1;
@@ -1110,7 +1146,7 @@ int iser_post_send(struct ib_conn *ib_conn, struct iser_tx_desc *tx_desc,
                                      DMA_TO_DEVICE);
 
        send_wr.next       = NULL;
-       send_wr.wr_id      = (unsigned long)tx_desc;
+       send_wr.wr_id      = (uintptr_t)tx_desc;
        send_wr.sg_list    = tx_desc->tx_sg;
        send_wr.num_sge    = tx_desc->num_sge;
        send_wr.opcode     = IB_WR_SEND;
@@ -1160,6 +1196,7 @@ static void
 iser_handle_comp_error(struct ib_conn *ib_conn,
                       struct ib_wc *wc)
 {
+       void *wr_id = (void *)(uintptr_t)wc->wr_id;
        struct iser_conn *iser_conn = container_of(ib_conn, struct iser_conn,
                                                   ib_conn);
 
@@ -1168,8 +1205,8 @@ iser_handle_comp_error(struct ib_conn *ib_conn,
                        iscsi_conn_failure(iser_conn->iscsi_conn,
                                           ISCSI_ERR_CONN_FAILED);
 
-       if (is_iser_tx_desc(iser_conn, (void *)wc->wr_id)) {
-               struct iser_tx_desc *desc = (struct iser_tx_desc *)wc->wr_id;
+       if (is_iser_tx_desc(iser_conn, wr_id)) {
+               struct iser_tx_desc *desc = wr_id;
 
                if (desc->type == ISCSI_TX_DATAOUT)
                        kmem_cache_free(ig.desc_cache, desc);
@@ -1193,14 +1230,14 @@ static void iser_handle_wc(struct ib_wc *wc)
        struct iser_rx_desc *rx_desc;
 
        ib_conn = wc->qp->qp_context;
-       if (wc->status == IB_WC_SUCCESS) {
+       if (likely(wc->status == IB_WC_SUCCESS)) {
                if (wc->opcode == IB_WC_RECV) {
-                       rx_desc = (struct iser_rx_desc *)wc->wr_id;
+                       rx_desc = (struct iser_rx_desc *)(uintptr_t)wc->wr_id;
                        iser_rcv_completion(rx_desc, wc->byte_len,
                                            ib_conn);
                } else
                if (wc->opcode == IB_WC_SEND) {
-                       tx_desc = (struct iser_tx_desc *)wc->wr_id;
+                       tx_desc = (struct iser_tx_desc *)(uintptr_t)wc->wr_id;
                        iser_snd_completion(tx_desc, ib_conn);
                } else {
                        iser_err("Unknown wc opcode %d\n", wc->opcode);
index 5461924..db3c8c8 100644 (file)
@@ -2929,7 +2929,7 @@ static int srp_parse_options(const char *buf, struct srp_target_port *target)
                return -ENOMEM;
 
        sep_opt = options;
-       while ((p = strsep(&sep_opt, ",")) != NULL) {
+       while ((p = strsep(&sep_opt, ",\n")) != NULL) {
                if (!*p)
                        continue;
 
index 8a8ba11..7ea1ea4 100644 (file)
@@ -203,7 +203,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
                fwnode_property_read_string(child, "linux,default-trigger",
                                            &led.default_trigger);
 
-               if (!fwnode_property_read_string(child, "linux,default_state",
+               if (!fwnode_property_read_string(child, "default-state",
                                                 &state)) {
                        if (!strcmp(state, "keep"))
                                led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
index 3c89fcb..49cd308 100644 (file)
@@ -160,7 +160,6 @@ source "drivers/media/usb/Kconfig"
 source "drivers/media/pci/Kconfig"
 source "drivers/media/platform/Kconfig"
 source "drivers/media/mmc/Kconfig"
-source "drivers/media/parport/Kconfig"
 source "drivers/media/radio/Kconfig"
 
 comment "Supported FireWire (IEEE 1394) Adapters"
index 620f275..e608bbc 100644 (file)
@@ -28,6 +28,6 @@ obj-y += rc/
 # Finally, merge the drivers that require the core
 #
 
-obj-y += common/ platform/ pci/ usb/ mmc/ firewire/ parport/
+obj-y += common/ platform/ pci/ usb/ mmc/ firewire/
 obj-$(CONFIG_VIDEO_DEV) += radio/
 
index f40b4cf..205d713 100644 (file)
@@ -284,15 +284,6 @@ config VIDEO_SAA711X
          To compile this driver as a module, choose M here: the
          module will be called saa7115.
 
-config VIDEO_SAA7191
-       tristate "Philips SAA7191 video decoder"
-       depends on VIDEO_V4L2 && I2C
-       ---help---
-         Support for the Philips SAA7191 video decoder.
-
-         To compile this driver as a module, choose M here: the
-         module will be called saa7191.
-
 config VIDEO_TVP514X
        tristate "Texas Instruments TVP514x video decoder"
        depends on VIDEO_V4L2 && I2C
index 01ae932..9858900 100644 (file)
@@ -18,7 +18,6 @@ obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o
 obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o
 obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
 obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
-obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
 obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o
 obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
 obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
diff --git a/drivers/media/i2c/saa7191.c b/drivers/media/i2c/saa7191.c
deleted file mode 100644 (file)
index 8e96992..0000000
+++ /dev/null
@@ -1,649 +0,0 @@
-/*
- *  saa7191.c - Philips SAA7191 video decoder driver
- *
- *  Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
- *  Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- */
-
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-
-#include <linux/videodev2.h>
-#include <linux/i2c.h>
-#include <media/v4l2-device.h>
-
-#include "saa7191.h"
-
-#define SAA7191_MODULE_VERSION "0.0.5"
-
-MODULE_DESCRIPTION("Philips SAA7191 video decoder driver");
-MODULE_VERSION(SAA7191_MODULE_VERSION);
-MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
-MODULE_LICENSE("GPL");
-
-
-// #define SAA7191_DEBUG
-
-#ifdef SAA7191_DEBUG
-#define dprintk(x...) printk("SAA7191: " x);
-#else
-#define dprintk(x...)
-#endif
-
-#define SAA7191_SYNC_COUNT     30
-#define SAA7191_SYNC_DELAY     100     /* milliseconds */
-
-struct saa7191 {
-       struct v4l2_subdev sd;
-
-       /* the register values are stored here as the actual
-        * I2C-registers are write-only */
-       u8 reg[25];
-
-       int input;
-       v4l2_std_id norm;
-};
-
-static inline struct saa7191 *to_saa7191(struct v4l2_subdev *sd)
-{
-       return container_of(sd, struct saa7191, sd);
-}
-
-static const u8 initseq[] = {
-       0,      /* Subaddress */
-
-       0x50,   /* (0x50) SAA7191_REG_IDEL */
-
-       /* 50 Hz signal timing */
-       0x30,   /* (0x30) SAA7191_REG_HSYB */
-       0x00,   /* (0x00) SAA7191_REG_HSYS */
-       0xe8,   /* (0xe8) SAA7191_REG_HCLB */
-       0xb6,   /* (0xb6) SAA7191_REG_HCLS */
-       0xf4,   /* (0xf4) SAA7191_REG_HPHI */
-
-       /* control */
-       SAA7191_LUMA_APER_1,    /* (0x01) SAA7191_REG_LUMA - CVBS mode */
-       0x00,   /* (0x00) SAA7191_REG_HUEC */
-       0xf8,   /* (0xf8) SAA7191_REG_CKTQ */
-       0xf8,   /* (0xf8) SAA7191_REG_CKTS */
-       0x90,   /* (0x90) SAA7191_REG_PLSE */
-       0x90,   /* (0x90) SAA7191_REG_SESE */
-       0x00,   /* (0x00) SAA7191_REG_GAIN */
-       SAA7191_STDC_NFEN | SAA7191_STDC_HRMV,  /* (0x0c) SAA7191_REG_STDC
-                                                * - not SECAM,
-                                                * slow time constant */
-       SAA7191_IOCK_OEDC | SAA7191_IOCK_OEHS | SAA7191_IOCK_OEVS
-       | SAA7191_IOCK_OEDY,    /* (0x78) SAA7191_REG_IOCK
-                                * - chroma from CVBS, GPSW1 & 2 off */
-       SAA7191_CTL3_AUFD | SAA7191_CTL3_SCEN | SAA7191_CTL3_OFTS
-       | SAA7191_CTL3_YDEL0,   /* (0x99) SAA7191_REG_CTL3
-                                * - automatic field detection */
-       0x00,   /* (0x00) SAA7191_REG_CTL4 */
-       0x2c,   /* (0x2c) SAA7191_REG_CHCV - PAL nominal value */
-       0x00,   /* unused */
-       0x00,   /* unused */
-
-       /* 60 Hz signal timing */
-       0x34,   /* (0x34) SAA7191_REG_HS6B */
-       0x0a,   /* (0x0a) SAA7191_REG_HS6S */
-       0xf4,   /* (0xf4) SAA7191_REG_HC6B */
-       0xce,   /* (0xce) SAA7191_REG_HC6S */
-       0xf4,   /* (0xf4) SAA7191_REG_HP6I */
-};
-
-/* SAA7191 register handling */
-
-static u8 saa7191_read_reg(struct v4l2_subdev *sd, u8 reg)
-{
-       return to_saa7191(sd)->reg[reg];
-}
-
-static int saa7191_read_status(struct v4l2_subdev *sd, u8 *value)
-{
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-       int ret;
-
-       ret = i2c_master_recv(client, value, 1);
-       if (ret < 0) {
-               printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-
-static int saa7191_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value)
-{
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-
-       to_saa7191(sd)->reg[reg] = value;
-       return i2c_smbus_write_byte_data(client, reg, value);
-}
-
-/* the first byte of data must be the first subaddress number (register) */
-static int saa7191_write_block(struct v4l2_subdev *sd,
-                              u8 length, const u8 *data)
-{
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-       struct saa7191 *decoder = to_saa7191(sd);
-       int i;
-       int ret;
-
-       for (i = 0; i < (length - 1); i++) {
-               decoder->reg[data[0] + i] = data[i + 1];
-       }
-
-       ret = i2c_master_send(client, data, length);
-       if (ret < 0) {
-               printk(KERN_ERR "SAA7191: saa7191_write_block(): "
-                      "write failed\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-/* Helper functions */
-
-static int saa7191_s_routing(struct v4l2_subdev *sd,
-                            u32 input, u32 output, u32 config)
-{
-       struct saa7191 *decoder = to_saa7191(sd);
-       u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA);
-       u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK);
-       int err;
-
-       switch (input) {
-       case SAA7191_INPUT_COMPOSITE: /* Set Composite input */
-               iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1
-                         | SAA7191_IOCK_GPSW2);
-               /* Chrominance trap active */
-               luma &= ~SAA7191_LUMA_BYPS;
-               break;
-       case SAA7191_INPUT_SVIDEO: /* Set S-Video input */
-               iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2;
-               /* Chrominance trap bypassed */
-               luma |= SAA7191_LUMA_BYPS;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       err = saa7191_write_reg(sd, SAA7191_REG_LUMA, luma);
-       if (err)
-               return -EIO;
-       err = saa7191_write_reg(sd, SAA7191_REG_IOCK, iock);
-       if (err)
-               return -EIO;
-
-       decoder->input = input;
-
-       return 0;
-}
-
-static int saa7191_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
-{
-       struct saa7191 *decoder = to_saa7191(sd);
-       u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC);
-       u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3);
-       u8 chcv = saa7191_read_reg(sd, SAA7191_REG_CHCV);
-       int err;
-
-       if (norm & V4L2_STD_PAL) {
-               stdc &= ~SAA7191_STDC_SECS;
-               ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);
-               chcv = SAA7191_CHCV_PAL;
-       } else if (norm & V4L2_STD_NTSC) {
-               stdc &= ~SAA7191_STDC_SECS;
-               ctl3 &= ~SAA7191_CTL3_AUFD;
-               ctl3 |= SAA7191_CTL3_FSEL;
-               chcv = SAA7191_CHCV_NTSC;
-       } else if (norm & V4L2_STD_SECAM) {
-               stdc |= SAA7191_STDC_SECS;
-               ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);
-               chcv = SAA7191_CHCV_PAL;
-       } else {
-               return -EINVAL;
-       }
-
-       err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
-       if (err)
-               return -EIO;
-       err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc);
-       if (err)
-               return -EIO;
-       err = saa7191_write_reg(sd, SAA7191_REG_CHCV, chcv);
-       if (err)
-               return -EIO;
-
-       decoder->norm = norm;
-
-       dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3,
-               stdc, chcv);
-       dprintk("norm: %llx\n", norm);
-
-       return 0;
-}
-
-static int saa7191_wait_for_signal(struct v4l2_subdev *sd, u8 *status)
-{
-       int i = 0;
-
-       dprintk("Checking for signal...\n");
-
-       for (i = 0; i < SAA7191_SYNC_COUNT; i++) {
-               if (saa7191_read_status(sd, status))
-                       return -EIO;
-
-               if (((*status) & SAA7191_STATUS_HLCK) == 0) {
-                       dprintk("Signal found\n");
-                       return 0;
-               }
-
-               msleep(SAA7191_SYNC_DELAY);
-       }
-
-       dprintk("No signal\n");
-
-       return -EBUSY;
-}
-
-static int saa7191_querystd(struct v4l2_subdev *sd, v4l2_std_id *norm)
-{
-       struct saa7191 *decoder = to_saa7191(sd);
-       u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC);
-       u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3);
-       u8 status;
-       v4l2_std_id old_norm = decoder->norm;
-       int err = 0;
-
-       dprintk("SAA7191 extended signal auto-detection...\n");
-
-       *norm &= V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
-       stdc &= ~SAA7191_STDC_SECS;
-       ctl3 &= ~(SAA7191_CTL3_FSEL);
-
-       err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc);
-       if (err) {
-               err = -EIO;
-               goto out;
-       }
-       err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
-       if (err) {
-               err = -EIO;
-               goto out;
-       }
-
-       ctl3 |= SAA7191_CTL3_AUFD;
-       err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
-       if (err) {
-               err = -EIO;
-               goto out;
-       }
-
-       msleep(SAA7191_SYNC_DELAY);
-
-       err = saa7191_wait_for_signal(sd, &status);
-       if (err)
-               goto out;
-
-       if (status & SAA7191_STATUS_FIDT) {
-               /* 60Hz signal -> NTSC */
-               dprintk("60Hz signal: NTSC\n");
-               *norm &= V4L2_STD_NTSC;
-               return 0;
-       }
-
-       /* 50Hz signal */
-       dprintk("50Hz signal: Trying PAL...\n");
-
-       /* try PAL first */
-       err = saa7191_s_std(sd, V4L2_STD_PAL);
-       if (err)
-               goto out;
-
-       msleep(SAA7191_SYNC_DELAY);
-
-       err = saa7191_wait_for_signal(sd, &status);
-       if (err)
-               goto out;
-
-       /* not 50Hz ? */
-       if (status & SAA7191_STATUS_FIDT) {
-               dprintk("No 50Hz signal\n");
-               saa7191_s_std(sd, old_norm);
-               *norm = V4L2_STD_UNKNOWN;
-               return 0;
-       }
-
-       if (status & SAA7191_STATUS_CODE) {
-               dprintk("PAL\n");
-               *norm &= V4L2_STD_PAL;
-               return saa7191_s_std(sd, old_norm);
-       }
-
-       dprintk("No color detected with PAL - Trying SECAM...\n");
-
-       /* no color detected ? -> try SECAM */
-       err = saa7191_s_std(sd, V4L2_STD_SECAM);
-       if (err)
-               goto out;
-
-       msleep(SAA7191_SYNC_DELAY);
-
-       err = saa7191_wait_for_signal(sd, &status);
-       if (err)
-               goto out;
-
-       /* not 50Hz ? */
-       if (status & SAA7191_STATUS_FIDT) {
-               dprintk("No 50Hz signal\n");
-               *norm = V4L2_STD_UNKNOWN;
-               goto out;
-       }
-
-       if (status & SAA7191_STATUS_CODE) {
-               /* Color detected -> SECAM */
-               dprintk("SECAM\n");
-               *norm &= V4L2_STD_SECAM;
-               return saa7191_s_std(sd, old_norm);
-       }
-
-       dprintk("No color detected with SECAM - Going back to PAL.\n");
-       *norm = V4L2_STD_UNKNOWN;
-
-out:
-       return saa7191_s_std(sd, old_norm);
-}
-
-static int saa7191_autodetect_norm(struct v4l2_subdev *sd)
-{
-       u8 status;
-
-       dprintk("SAA7191 signal auto-detection...\n");
-
-       dprintk("Reading status...\n");
-
-       if (saa7191_read_status(sd, &status))
-               return -EIO;
-
-       dprintk("Checking for signal...\n");
-
-       /* no signal ? */
-       if (status & SAA7191_STATUS_HLCK) {
-               dprintk("No signal\n");
-               return -EBUSY;
-       }
-
-       dprintk("Signal found\n");
-
-       if (status & SAA7191_STATUS_FIDT) {
-               /* 60hz signal -> NTSC */
-               dprintk("NTSC\n");
-               return saa7191_s_std(sd, V4L2_STD_NTSC);
-       } else {
-               /* 50hz signal -> PAL */
-               dprintk("PAL\n");
-               return saa7191_s_std(sd, V4L2_STD_PAL);
-       }
-}
-
-static int saa7191_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       u8 reg;
-       int ret = 0;
-
-       switch (ctrl->id) {
-       case SAA7191_CONTROL_BANDPASS:
-       case SAA7191_CONTROL_BANDPASS_WEIGHT:
-       case SAA7191_CONTROL_CORING:
-               reg = saa7191_read_reg(sd, SAA7191_REG_LUMA);
-               switch (ctrl->id) {
-               case SAA7191_CONTROL_BANDPASS:
-                       ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK)
-                               >> SAA7191_LUMA_BPSS_SHIFT;
-                       break;
-               case SAA7191_CONTROL_BANDPASS_WEIGHT:
-                       ctrl->value = ((s32)reg & SAA7191_LUMA_APER_MASK)
-                               >> SAA7191_LUMA_APER_SHIFT;
-                       break;
-               case SAA7191_CONTROL_CORING:
-                       ctrl->value = ((s32)reg & SAA7191_LUMA_CORI_MASK)
-                               >> SAA7191_LUMA_CORI_SHIFT;
-                       break;
-               }
-               break;
-       case SAA7191_CONTROL_FORCE_COLOUR:
-       case SAA7191_CONTROL_CHROMA_GAIN:
-               reg = saa7191_read_reg(sd, SAA7191_REG_GAIN);
-               if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR)
-                       ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0;
-               else
-                       ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK)
-                               >> SAA7191_GAIN_LFIS_SHIFT;
-               break;
-       case V4L2_CID_HUE:
-               reg = saa7191_read_reg(sd, SAA7191_REG_HUEC);
-               if (reg < 0x80)
-                       reg += 0x80;
-               else
-                       reg -= 0x80;
-               ctrl->value = (s32)reg;
-               break;
-       case SAA7191_CONTROL_VTRC:
-               reg = saa7191_read_reg(sd, SAA7191_REG_STDC);
-               ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0;
-               break;
-       case SAA7191_CONTROL_LUMA_DELAY:
-               reg = saa7191_read_reg(sd, SAA7191_REG_CTL3);
-               ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK)
-                       >> SAA7191_CTL3_YDEL_SHIFT;
-               if (ctrl->value >= 4)
-                       ctrl->value -= 8;
-               break;
-       case SAA7191_CONTROL_VNR:
-               reg = saa7191_read_reg(sd, SAA7191_REG_CTL4);
-               ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK)
-                       >> SAA7191_CTL4_VNOI_SHIFT;
-               break;
-       default:
-               ret = -EINVAL;
-       }
-
-       return ret;
-}
-
-static int saa7191_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       u8 reg;
-       int ret = 0;
-
-       switch (ctrl->id) {
-       case SAA7191_CONTROL_BANDPASS:
-       case SAA7191_CONTROL_BANDPASS_WEIGHT:
-       case SAA7191_CONTROL_CORING:
-               reg = saa7191_read_reg(sd, SAA7191_REG_LUMA);
-               switch (ctrl->id) {
-               case SAA7191_CONTROL_BANDPASS:
-                       reg &= ~SAA7191_LUMA_BPSS_MASK;
-                       reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT)
-                               & SAA7191_LUMA_BPSS_MASK;
-                       break;
-               case SAA7191_CONTROL_BANDPASS_WEIGHT:
-                       reg &= ~SAA7191_LUMA_APER_MASK;
-                       reg |= (ctrl->value << SAA7191_LUMA_APER_SHIFT)
-                               & SAA7191_LUMA_APER_MASK;
-                       break;
-               case SAA7191_CONTROL_CORING:
-                       reg &= ~SAA7191_LUMA_CORI_MASK;
-                       reg |= (ctrl->value << SAA7191_LUMA_CORI_SHIFT)
-                               & SAA7191_LUMA_CORI_MASK;
-                       break;
-               }
-               ret = saa7191_write_reg(sd, SAA7191_REG_LUMA, reg);
-               break;
-       case SAA7191_CONTROL_FORCE_COLOUR:
-       case SAA7191_CONTROL_CHROMA_GAIN:
-               reg = saa7191_read_reg(sd, SAA7191_REG_GAIN);
-               if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) {
-                       if (ctrl->value)
-                               reg |= SAA7191_GAIN_COLO;
-                       else
-                               reg &= ~SAA7191_GAIN_COLO;
-               } else {
-                       reg &= ~SAA7191_GAIN_LFIS_MASK;
-                       reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT)
-                               & SAA7191_GAIN_LFIS_MASK;
-               }
-               ret = saa7191_write_reg(sd, SAA7191_REG_GAIN, reg);
-               break;
-       case V4L2_CID_HUE:
-               reg = ctrl->value & 0xff;
-               if (reg < 0x80)
-                       reg += 0x80;
-               else
-                       reg -= 0x80;
-               ret = saa7191_write_reg(sd, SAA7191_REG_HUEC, reg);
-               break;
-       case SAA7191_CONTROL_VTRC:
-               reg = saa7191_read_reg(sd, SAA7191_REG_STDC);
-               if (ctrl->value)
-                       reg |= SAA7191_STDC_VTRC;
-               else
-                       reg &= ~SAA7191_STDC_VTRC;
-               ret = saa7191_write_reg(sd, SAA7191_REG_STDC, reg);
-               break;
-       case SAA7191_CONTROL_LUMA_DELAY: {
-               s32 value = ctrl->value;
-               if (value < 0)
-                       value += 8;
-               reg = saa7191_read_reg(sd, SAA7191_REG_CTL3);
-               reg &= ~SAA7191_CTL3_YDEL_MASK;
-               reg |= (value << SAA7191_CTL3_YDEL_SHIFT)
-                       & SAA7191_CTL3_YDEL_MASK;
-               ret = saa7191_write_reg(sd, SAA7191_REG_CTL3, reg);
-               break;
-       }
-       case SAA7191_CONTROL_VNR:
-               reg = saa7191_read_reg(sd, SAA7191_REG_CTL4);
-               reg &= ~SAA7191_CTL4_VNOI_MASK;
-               reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT)
-                       & SAA7191_CTL4_VNOI_MASK;
-               ret = saa7191_write_reg(sd, SAA7191_REG_CTL4, reg);
-               break;
-       default:
-               ret = -EINVAL;
-       }
-
-       return ret;
-}
-
-/* I2C-interface */
-
-static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status)
-{
-       u8 status_reg;
-       int res = V4L2_IN_ST_NO_SIGNAL;
-
-       if (saa7191_read_status(sd, &status_reg))
-               return -EIO;
-       if ((status_reg & SAA7191_STATUS_HLCK) == 0)
-               res = 0;
-       if (!(status_reg & SAA7191_STATUS_CODE))
-               res |= V4L2_IN_ST_NO_COLOR;
-       *status = res;
-       return 0;
-}
-
-
-/* ----------------------------------------------------------------------- */
-
-static const struct v4l2_subdev_core_ops saa7191_core_ops = {
-       .g_ctrl = saa7191_g_ctrl,
-       .s_ctrl = saa7191_s_ctrl,
-};
-
-static const struct v4l2_subdev_video_ops saa7191_video_ops = {
-       .s_std = saa7191_s_std,
-       .s_routing = saa7191_s_routing,
-       .querystd = saa7191_querystd,
-       .g_input_status = saa7191_g_input_status,
-};
-
-static const struct v4l2_subdev_ops saa7191_ops = {
-       .core = &saa7191_core_ops,
-       .video = &saa7191_video_ops,
-};
-
-static int saa7191_probe(struct i2c_client *client,
-                         const struct i2c_device_id *id)
-{
-       int err = 0;
-       struct saa7191 *decoder;
-       struct v4l2_subdev *sd;
-
-       v4l_info(client, "chip found @ 0x%x (%s)\n",
-                       client->addr << 1, client->adapter->name);
-
-       decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
-       if (!decoder)
-               return -ENOMEM;
-
-       sd = &decoder->sd;
-       v4l2_i2c_subdev_init(sd, client, &saa7191_ops);
-
-       err = saa7191_write_block(sd, sizeof(initseq), initseq);
-       if (err) {
-               printk(KERN_ERR "SAA7191 initialization failed\n");
-               return err;
-       }
-
-       printk(KERN_INFO "SAA7191 initialized\n");
-
-       decoder->input = SAA7191_INPUT_COMPOSITE;
-       decoder->norm = V4L2_STD_PAL;
-
-       err = saa7191_autodetect_norm(sd);
-       if (err && (err != -EBUSY))
-               printk(KERN_ERR "SAA7191: Signal auto-detection failed\n");
-
-       return 0;
-}
-
-static int saa7191_remove(struct i2c_client *client)
-{
-       struct v4l2_subdev *sd = i2c_get_clientdata(client);
-
-       v4l2_device_unregister_subdev(sd);
-       return 0;
-}
-
-static const struct i2c_device_id saa7191_id[] = {
-       { "saa7191", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, saa7191_id);
-
-static struct i2c_driver saa7191_driver = {
-       .driver = {
-               .owner  = THIS_MODULE,
-               .name   = "saa7191",
-       },
-       .probe          = saa7191_probe,
-       .remove         = saa7191_remove,
-       .id_table       = saa7191_id,
-};
-
-module_i2c_driver(saa7191_driver);
diff --git a/drivers/media/i2c/saa7191.h b/drivers/media/i2c/saa7191.h
deleted file mode 100644 (file)
index 803c74d..0000000
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- *  saa7191.h - Philips SAA7191 video decoder driver
- *
- *  Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
- *  Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- */
-
-#ifndef _SAA7191_H_
-#define _SAA7191_H_
-
-/* Philips SAA7191 DMSD I2C bus address */
-#define SAA7191_ADDR           0x8a
-
-/* Register subaddresses. */
-#define SAA7191_REG_IDEL       0x00
-#define SAA7191_REG_HSYB       0x01
-#define SAA7191_REG_HSYS       0x02
-#define SAA7191_REG_HCLB       0x03
-#define SAA7191_REG_HCLS       0x04
-#define SAA7191_REG_HPHI       0x05
-#define SAA7191_REG_LUMA       0x06
-#define SAA7191_REG_HUEC       0x07
-#define SAA7191_REG_CKTQ       0x08 /* bits 3-7 */
-#define SAA7191_REG_CKTS       0x09 /* bits 3-7 */
-#define SAA7191_REG_PLSE       0x0a
-#define SAA7191_REG_SESE       0x0b
-#define SAA7191_REG_GAIN       0x0c
-#define SAA7191_REG_STDC       0x0d
-#define SAA7191_REG_IOCK       0x0e
-#define SAA7191_REG_CTL3       0x0f
-#define SAA7191_REG_CTL4       0x10
-#define SAA7191_REG_CHCV       0x11
-#define SAA7191_REG_HS6B       0x14
-#define SAA7191_REG_HS6S       0x15
-#define SAA7191_REG_HC6B       0x16
-#define SAA7191_REG_HC6S       0x17
-#define SAA7191_REG_HP6I       0x18
-#define SAA7191_REG_STATUS     0xff    /* not really a subaddress */
-
-/* Status Register definitions */
-#define SAA7191_STATUS_CODE    0x01    /* color detected flag */
-#define SAA7191_STATUS_FIDT    0x20    /* signal type 50/60 Hz */
-#define SAA7191_STATUS_HLCK    0x40    /* PLL unlocked(1)/locked(0) */
-#define SAA7191_STATUS_STTC    0x80    /* tv/vtr time constant */
-
-/* Luminance Control Register definitions */
-/* input mode select bit:
- * 0=CVBS (chrominance trap active), 1=S-Video (trap bypassed) */
-#define SAA7191_LUMA_BYPS      0x80
-/* pre-filter (only when chrominance trap is active) */
-#define SAA7191_LUMA_PREF      0x40
-/* aperture bandpass to select different characteristics with maximums
- * (bits 4-5) */
-#define SAA7191_LUMA_BPSS_MASK 0x30
-#define SAA7191_LUMA_BPSS_SHIFT        4
-#define SAA7191_LUMA_BPSS_3    0x30
-#define SAA7191_LUMA_BPSS_2    0x20
-#define SAA7191_LUMA_BPSS_1    0x10
-#define SAA7191_LUMA_BPSS_0    0x00
-/* coring range for high frequency components according to 8-bit luminance
- * (bits 2-3)
- * 0=coring off, n= (+-)n LSB */
-#define SAA7191_LUMA_CORI_MASK 0x0c
-#define SAA7191_LUMA_CORI_SHIFT        2
-#define SAA7191_LUMA_CORI_3    0x0c
-#define SAA7191_LUMA_CORI_2    0x08
-#define SAA7191_LUMA_CORI_1    0x04
-#define SAA7191_LUMA_CORI_0    0x00
-/* aperture bandpass filter weights high frequency components of luminance
- * signal (bits 0-1)
- * 0=factor 0, 1=0.25, 2=0.5, 3=1 */
-#define SAA7191_LUMA_APER_MASK 0x03
-#define SAA7191_LUMA_APER_SHIFT        0
-#define SAA7191_LUMA_APER_3    0x03
-#define SAA7191_LUMA_APER_2    0x02
-#define SAA7191_LUMA_APER_1    0x01
-#define SAA7191_LUMA_APER_0    0x00
-
-/* Chrominance Gain Control Settings Register definitions */
-/* colour on: 0=automatic colour-killer enabled, 1=forced colour on */
-#define SAA7191_GAIN_COLO      0x80
-/* chrominance gain control (AGC filter)
- * 0=loop filter time constant slow, 1=medium, 2=fast, 3=actual gain */
-#define SAA7191_GAIN_LFIS_MASK 0x60
-#define SAA7191_GAIN_LFIS_SHIFT        5
-#define SAA7191_GAIN_LFIS_3    0x60
-#define SAA7191_GAIN_LFIS_2    0x40
-#define SAA7191_GAIN_LFIS_1    0x20
-#define SAA7191_GAIN_LFIS_0    0x00
-
-/* Standard/Mode Control Register definitions */
-/* tv/vtr mode bit: 0=TV mode (slow time constant),
- * 1=VTR mode (fast time constant) */
-#define SAA7191_STDC_VTRC      0x80
-/* SAA7191B-specific functions enable (RTCO, ODD and GPSW0 outputs)
- * 0=outputs set to high-impedance (circuit equals SAA7191), 1=enabled */
-#define SAA7191_STDC_NFEN      0x08
-/* HREF generation: 0=like SAA7191, 1=HREF is 8xLLC2 clocks earlier */
-#define SAA7191_STDC_HRMV      0x04
-/* general purpose switch 0
- * (not used with VINO afaik) */
-#define SAA7191_STDC_GPSW0     0x02
-/* SECAM mode bit: 0=other standards, 1=SECAM */
-#define SAA7191_STDC_SECS      0x01
-
-/* I/O and Clock Control Register definitions */
-/* horizontal clock PLL: 0=PLL closed,
- * 1=PLL circuit open and horizontal freq fixed */
-#define SAA7191_IOCK_HPLL      0x80
-/* colour-difference output enable (outputs UV0-UV7) */
-#define SAA7191_IOCK_OEDC      0x40
-/* H-sync output enable */
-#define SAA7191_IOCK_OEHS      0x20
-/* V-sync output enable */
-#define SAA7191_IOCK_OEVS      0x10
-/* luminance output enable (outputs Y0-Y7) */
-#define SAA7191_IOCK_OEDY      0x08
-/* S-VHS bit (chrominance from CVBS or from chrominance input):
- * 0=controlled by BYPS-bit, 1=from chrominance input */
-#define SAA7191_IOCK_CHRS      0x04
-/* general purpose switch 2
- * VINO-specific: 0=used with CVBS, 1=used with S-Video */
-#define SAA7191_IOCK_GPSW2     0x02
-/* general purpose switch 1 */
-/* VINO-specific: 0=always, 1=not used!*/
-#define SAA7191_IOCK_GPSW1     0x01
-
-/* Miscellaneous Control #1 Register definitions */
-/* automatic field detection (50/60Hz standard) */
-#define SAA7191_CTL3_AUFD      0x80
-/* field select: (if AUFD=0)
- * 0=50Hz (625 lines), 1=60Hz (525 lines) */
-#define SAA7191_CTL3_FSEL      0x40
-/* SECAM cross-colour reduction enable */
-#define SAA7191_CTL3_SXCR      0x20
-/* sync and clamping pulse enable (HCL and HSY outputs) */
-#define SAA7191_CTL3_SCEN      0x10
-/* output format: 0=4:1:1, 1=4:2:2 (4:2:2 for VINO) */
-#define SAA7191_CTL3_OFTS      0x08
-/* luminance delay compensation
- * 0=0*2/LLC,  1=+1*2/LLC, 2=+2*2/LLC, 3=+3*2/LLC,
- * 4=-4*2/LLC, 5=-3*2/LLC, 6=-2*2/LLC, 7=-1*2/LLC
- * step size = 2/LLC = 67.8ns for 50Hz, 81.5ns for 60Hz */
-#define SAA7191_CTL3_YDEL_MASK 0x07
-#define SAA7191_CTL3_YDEL_SHIFT        0
-#define SAA7191_CTL3_YDEL2     0x04
-#define SAA7191_CTL3_YDEL1     0x02
-#define SAA7191_CTL3_YDEL0     0x01
-
-/* Miscellaneous Control #2 Register definitions */
-/* select HREF position
- * 0=normal, HREF is matched to YUV output port,
- * 1=HREF is matched to CVBS input port */
-#define SAA7191_CTL4_HRFS      0x04
-/* vertical noise reduction
- * 0=normal, 1=searching window, 2=auto-deflection, 3=reduction bypassed */
-#define SAA7191_CTL4_VNOI_MASK 0x03
-#define SAA7191_CTL4_VNOI_SHIFT        0
-#define SAA7191_CTL4_VNOI_3    0x03
-#define SAA7191_CTL4_VNOI_2    0x02
-#define SAA7191_CTL4_VNOI_1    0x01
-#define SAA7191_CTL4_VNOI_0    0x00
-
-/* Chrominance Gain Control Register definitions
- * - for QAM-modulated input signals, effects output amplitude
- * (SECAM gain fixed)
- * (nominal values for UV CCIR level) */
-#define SAA7191_CHCV_NTSC      0x2c
-#define SAA7191_CHCV_PAL       0x59
-
-/* Driver interface definitions */
-#define SAA7191_INPUT_COMPOSITE        0
-#define SAA7191_INPUT_SVIDEO   1
-
-#define SAA7191_NORM_PAL       1
-#define SAA7191_NORM_NTSC      2
-#define SAA7191_NORM_SECAM     3
-
-struct saa7191_status {
-       /* 0=no signal, 1=signal detected */
-       int signal;
-       /* 0=50hz (pal) signal, 1=60hz (ntsc) signal */
-       int signal_60hz;
-       /* 0=no color detected, 1=color detected */
-       int color;
-
-       /* current SAA7191_INPUT_ */
-       int input;
-       /* current SAA7191_NORM_ */
-       int norm;
-};
-
-#define SAA7191_BANDPASS_MIN           0x00
-#define SAA7191_BANDPASS_MAX           0x03
-#define SAA7191_BANDPASS_DEFAULT       0x00
-
-#define SAA7191_BANDPASS_WEIGHT_MIN    0x00
-#define SAA7191_BANDPASS_WEIGHT_MAX    0x03
-#define SAA7191_BANDPASS_WEIGHT_DEFAULT        0x01
-
-#define SAA7191_CORING_MIN             0x00
-#define SAA7191_CORING_MAX             0x03
-#define SAA7191_CORING_DEFAULT         0x00
-
-#define SAA7191_HUE_MIN                        0x00
-#define SAA7191_HUE_MAX                        0xff
-#define SAA7191_HUE_DEFAULT            0x80
-
-#define SAA7191_VTRC_MIN               0x00
-#define SAA7191_VTRC_MAX               0x01
-#define SAA7191_VTRC_DEFAULT           0x00
-
-#define SAA7191_FORCE_COLOUR_MIN       0x00
-#define SAA7191_FORCE_COLOUR_MAX       0x01
-#define SAA7191_FORCE_COLOUR_DEFAULT   0x00
-
-#define SAA7191_CHROMA_GAIN_MIN                0x00
-#define SAA7191_CHROMA_GAIN_MAX                0x03
-#define SAA7191_CHROMA_GAIN_DEFAULT    0x00
-
-#define SAA7191_LUMA_DELAY_MIN         -0x04
-#define SAA7191_LUMA_DELAY_MAX         0x03
-#define SAA7191_LUMA_DELAY_DEFAULT     0x01
-
-#define SAA7191_VNR_MIN                        0x00
-#define SAA7191_VNR_MAX                        0x03
-#define SAA7191_VNR_DEFAULT            0x00
-
-#define SAA7191_CONTROL_BANDPASS       (V4L2_CID_PRIVATE_BASE + 0)
-#define SAA7191_CONTROL_BANDPASS_WEIGHT        (V4L2_CID_PRIVATE_BASE + 1)
-#define SAA7191_CONTROL_CORING         (V4L2_CID_PRIVATE_BASE + 2)
-#define SAA7191_CONTROL_FORCE_COLOUR   (V4L2_CID_PRIVATE_BASE + 3)
-#define SAA7191_CONTROL_CHROMA_GAIN    (V4L2_CID_PRIVATE_BASE + 4)
-#define SAA7191_CONTROL_VTRC           (V4L2_CID_PRIVATE_BASE + 5)
-#define SAA7191_CONTROL_LUMA_DELAY     (V4L2_CID_PRIVATE_BASE + 6)
-#define SAA7191_CONTROL_VNR            (V4L2_CID_PRIVATE_BASE + 7)
-
-#define        DECODER_SAA7191_GET_STATUS      _IOR('d', 195, struct saa7191_status)
-#define        DECODER_SAA7191_SET_NORM        _IOW('d', 196, int)
-
-#endif
diff --git a/drivers/media/parport/Kconfig b/drivers/media/parport/Kconfig
deleted file mode 100644 (file)
index 948c981..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-menuconfig MEDIA_PARPORT_SUPPORT
-       bool "ISA and parallel port devices"
-       depends on (ISA || PARPORT) && MEDIA_CAMERA_SUPPORT
-       help
-         Enables drivers for ISA and parallel port bus. If you
-         need media drivers using those legacy buses, say Y.
-
-if MEDIA_PARPORT_SUPPORT
-config VIDEO_BWQCAM
-       tristate "Quickcam BW Video For Linux"
-       depends on PARPORT && VIDEO_V4L2
-       select VIDEOBUF2_VMALLOC
-       help
-         Say Y have if you the black and white version of the QuickCam
-         camera. See the next option for the color version.
-
-         To compile this driver as a module, choose M here: the
-         module will be called bw-qcam.
-
-config VIDEO_CQCAM
-       tristate "QuickCam Colour Video For Linux"
-       depends on PARPORT && VIDEO_V4L2
-       help
-         This is the video4linux driver for the colour version of the
-         Connectix QuickCam.  If you have one of these cameras, say Y here,
-         otherwise say N.  This driver does not work with the original
-         monochrome QuickCam, QuickCam VC or QuickClip.  It is also available
-         as a module (c-qcam).
-         Read <file:Documentation/video4linux/CQcam.txt> for more information.
-
-config VIDEO_PMS
-       tristate "Mediavision Pro Movie Studio Video For Linux"
-       depends on ISA && VIDEO_V4L2
-       help
-         Say Y if you have the ISA Mediavision Pro Movie Studio
-         capture card.
-
-         To compile this driver as a module, choose M here: the
-         module will be called pms.
-
-config VIDEO_W9966
-       tristate "W9966CF Webcam (FlyCam Supra and others) Video For Linux"
-       depends on PARPORT_1284 && PARPORT && VIDEO_V4L2
-       help
-         Video4linux driver for Winbond's w9966 based Webcams.
-         Currently tested with the LifeView FlyCam Supra.
-         If you have one of these cameras, say Y here
-         otherwise say N.
-         This driver is also available as a module (w9966).
-
-         Check out <file:Documentation/video4linux/w9966.txt> for more
-         information.
-endif
diff --git a/drivers/media/parport/Makefile b/drivers/media/parport/Makefile
deleted file mode 100644 (file)
index 4eea06d..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o
-obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o
-obj-$(CONFIG_VIDEO_W9966) += w9966.o
-obj-$(CONFIG_VIDEO_PMS) += pms.o
diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c
deleted file mode 100644 (file)
index 67b9da1..0000000
+++ /dev/null
@@ -1,1177 +0,0 @@
-/*
- *    QuickCam Driver For Video4Linux.
- *
- *     Video4Linux conversion work by Alan Cox.
- *     Parport compatibility by Phil Blundell.
- *     Busy loop avoidance by Mark Cooke.
- *
- *    Module parameters:
- *
- *     maxpoll=<1 - 5000>
- *
- *       When polling the QuickCam for a response, busy-wait for a
- *       maximum of this many loops. The default of 250 gives little
- *       impact on interactive response.
- *
- *       NOTE: If this parameter is set too high, the processor
- *             will busy wait until this loop times out, and then
- *             slowly poll for a further 5 seconds before failing
- *             the transaction. You have been warned.
- *
- *     yieldlines=<1 - 250>
- *
- *       When acquiring a frame from the camera, the data gathering
- *       loop will yield back to the scheduler after completing
- *       this many lines. The default of 4 provides a trade-off
- *       between increased frame acquisition time and impact on
- *       interactive response.
- */
-
-/* qcam-lib.c -- Library for programming with the Connectix QuickCam.
- * See the included documentation for usage instructions and details
- * of the protocol involved. */
-
-
-/* Version 0.5, August 4, 1996 */
-/* Version 0.7, August 27, 1996 */
-/* Version 0.9, November 17, 1996 */
-
-
-/******************************************************************
-
-Copyright (C) 1996 by Scott Laird
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-******************************************************************/
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/parport.h>
-#include <linux/sched.h>
-#include <linux/videodev2.h>
-#include <linux/mutex.h>
-#include <asm/uaccess.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-event.h>
-#include <media/videobuf2-vmalloc.h>
-
-/* One from column A... */
-#define QC_NOTSET 0
-#define QC_UNIDIR 1
-#define QC_BIDIR  2
-#define QC_SERIAL 3
-
-/* ... and one from column B */
-#define QC_ANY          0x00
-#define QC_FORCE_UNIDIR 0x10
-#define QC_FORCE_BIDIR  0x20
-#define QC_FORCE_SERIAL 0x30
-/* in the port_mode member */
-
-#define QC_MODE_MASK    0x07
-#define QC_FORCE_MASK   0x70
-
-#define MAX_HEIGHT 243
-#define MAX_WIDTH 336
-
-/* Bit fields for status flags */
-#define QC_PARAM_CHANGE        0x01 /* Camera status change has occurred */
-
-struct qcam {
-       struct v4l2_device v4l2_dev;
-       struct video_device vdev;
-       struct v4l2_ctrl_handler hdl;
-       struct vb2_queue vb_vidq;
-       struct pardevice *pdev;
-       struct parport *pport;
-       struct mutex lock;
-       struct mutex queue_lock;
-       int width, height;
-       int bpp;
-       int mode;
-       int contrast, brightness, whitebal;
-       int port_mode;
-       int transfer_scale;
-       int top, left;
-       int status;
-       unsigned int saved_bits;
-       unsigned long in_use;
-};
-
-static unsigned int maxpoll = 250;   /* Maximum busy-loop count for qcam I/O */
-static unsigned int yieldlines = 4;  /* Yield after this many during capture */
-static int video_nr = -1;
-static unsigned int force_init;                /* Whether to probe aggressively */
-
-module_param(maxpoll, int, 0);
-module_param(yieldlines, int, 0);
-module_param(video_nr, int, 0);
-
-/* Set force_init=1 to avoid detection by polling status register and
- * immediately attempt to initialize qcam */
-module_param(force_init, int, 0);
-
-#define MAX_CAMS 4
-static struct qcam *qcams[MAX_CAMS];
-static unsigned int num_cams;
-
-static inline int read_lpstatus(struct qcam *q)
-{
-       return parport_read_status(q->pport);
-}
-
-static inline int read_lpdata(struct qcam *q)
-{
-       return parport_read_data(q->pport);
-}
-
-static inline void write_lpdata(struct qcam *q, int d)
-{
-       parport_write_data(q->pport, d);
-}
-
-static void write_lpcontrol(struct qcam *q, int d)
-{
-       if (d & 0x20) {
-               /* Set bidirectional mode to reverse (data in) */
-               parport_data_reverse(q->pport);
-       } else {
-               /* Set bidirectional mode to forward (data out) */
-               parport_data_forward(q->pport);
-       }
-
-       /* Now issue the regular port command, but strip out the
-        * direction flag */
-       d &= ~0x20;
-       parport_write_control(q->pport, d);
-}
-
-
-/* qc_waithand busy-waits for a handshake signal from the QuickCam.
- * Almost all communication with the camera requires handshaking. */
-
-static int qc_waithand(struct qcam *q, int val)
-{
-       int status;
-       int runs = 0;
-
-       if (val) {
-               while (!((status = read_lpstatus(q)) & 8)) {
-                       /* 1000 is enough spins on the I/O for all normal
-                          cases, at that point we start to poll slowly
-                          until the camera wakes up. However, we are
-                          busy blocked until the camera responds, so
-                          setting it lower is much better for interactive
-                          response. */
-
-                       if (runs++ > maxpoll)
-                               msleep_interruptible(5);
-                       if (runs > (maxpoll + 1000)) /* 5 seconds */
-                               return -1;
-               }
-       } else {
-               while (((status = read_lpstatus(q)) & 8)) {
-                       /* 1000 is enough spins on the I/O for all normal
-                          cases, at that point we start to poll slowly
-                          until the camera wakes up. However, we are
-                          busy blocked until the camera responds, so
-                          setting it lower is much better for interactive
-                          response. */
-
-                       if (runs++ > maxpoll)
-                               msleep_interruptible(5);
-                       if (runs++ > (maxpoll + 1000)) /* 5 seconds */
-                               return -1;
-               }
-       }
-
-       return status;
-}
-
-/* Waithand2 is used when the qcam is in bidirectional mode, and the
- * handshaking signal is CamRdy2 (bit 0 of data reg) instead of CamRdy1
- * (bit 3 of status register).  It also returns the last value read,
- * since this data is useful. */
-
-static unsigned int qc_waithand2(struct qcam *q, int val)
-{
-       unsigned int status;
-       int runs = 0;
-
-       do {
-               status = read_lpdata(q);
-               /* 1000 is enough spins on the I/O for all normal
-                  cases, at that point we start to poll slowly
-                  until the camera wakes up. However, we are
-                  busy blocked until the camera responds, so
-                  setting it lower is much better for interactive
-                  response. */
-
-               if (runs++ > maxpoll)
-                       msleep_interruptible(5);
-               if (runs++ > (maxpoll + 1000)) /* 5 seconds */
-                       return 0;
-       } while ((status & 1) != val);
-
-       return status;
-}
-
-/* qc_command is probably a bit of a misnomer -- it's used to send
- * bytes *to* the camera.  Generally, these bytes are either commands
- * or arguments to commands, so the name fits, but it still bugs me a
- * bit.  See the documentation for a list of commands. */
-
-static int qc_command(struct qcam *q, int command)
-{
-       int n1, n2;
-       int cmd;
-
-       write_lpdata(q, command);
-       write_lpcontrol(q, 6);
-
-       n1 = qc_waithand(q, 1);
-
-       write_lpcontrol(q, 0xe);
-       n2 = qc_waithand(q, 0);
-
-       cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
-       return cmd;
-}
-
-static int qc_readparam(struct qcam *q)
-{
-       int n1, n2;
-       int cmd;
-
-       write_lpcontrol(q, 6);
-       n1 = qc_waithand(q, 1);
-
-       write_lpcontrol(q, 0xe);
-       n2 = qc_waithand(q, 0);
-
-       cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
-       return cmd;
-}
-
-
-/* Try to detect a QuickCam.  It appears to flash the upper 4 bits of
-   the status register at 5-10 Hz.  This is only used in the autoprobe
-   code.  Be aware that this isn't the way Connectix detects the
-   camera (they send a reset and try to handshake), but this should be
-   almost completely safe, while their method screws up my printer if
-   I plug it in before the camera. */
-
-static int qc_detect(struct qcam *q)
-{
-       int reg, lastreg;
-       int count = 0;
-       int i;
-
-       if (force_init)
-               return 1;
-
-       lastreg = reg = read_lpstatus(q) & 0xf0;
-
-       for (i = 0; i < 500; i++) {
-               reg = read_lpstatus(q) & 0xf0;
-               if (reg != lastreg)
-                       count++;
-               lastreg = reg;
-               mdelay(2);
-       }
-
-
-#if 0
-       /* Force camera detection during testing. Sometimes the camera
-          won't be flashing these bits. Possibly unloading the module
-          in the middle of a grab? Or some timeout condition?
-          I've seen this parameter as low as 19 on my 450Mhz box - mpc */
-       printk(KERN_DEBUG "Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count);
-       return 1;
-#endif
-
-       /* Be (even more) liberal in what you accept...  */
-
-       if (count > 20 && count < 400) {
-               return 1;       /* found */
-       } else {
-               printk(KERN_ERR "No Quickcam found on port %s\n",
-                               q->pport->name);
-               printk(KERN_DEBUG "Quickcam detection counter: %u\n", count);
-               return 0;       /* not found */
-       }
-}
-
-/* Decide which scan mode to use.  There's no real requirement that
- * the scanmode match the resolution in q->height and q-> width -- the
- * camera takes the picture at the resolution specified in the
- * "scanmode" and then returns the image at the resolution specified
- * with the resolution commands.  If the scan is bigger than the
- * requested resolution, the upper-left hand corner of the scan is
- * returned.  If the scan is smaller, then the rest of the image
- * returned contains garbage. */
-
-static int qc_setscanmode(struct qcam *q)
-{
-       int old_mode = q->mode;
-
-       switch (q->transfer_scale) {
-       case 1:
-               q->mode = 0;
-               break;
-       case 2:
-               q->mode = 4;
-               break;
-       case 4:
-               q->mode = 8;
-               break;
-       }
-
-       switch (q->bpp) {
-       case 4:
-               break;
-       case 6:
-               q->mode += 2;
-               break;
-       }
-
-       switch (q->port_mode & QC_MODE_MASK) {
-       case QC_BIDIR:
-               q->mode += 1;
-               break;
-       case QC_NOTSET:
-       case QC_UNIDIR:
-               break;
-       }
-
-       if (q->mode != old_mode)
-               q->status |= QC_PARAM_CHANGE;
-
-       return 0;
-}
-
-
-/* Reset the QuickCam.  This uses the same sequence the Windows
- * QuickPic program uses.  Someone with a bi-directional port should
- * check that bi-directional mode is detected right, and then
- * implement bi-directional mode in qc_readbyte(). */
-
-static void qc_reset(struct qcam *q)
-{
-       switch (q->port_mode & QC_FORCE_MASK) {
-       case QC_FORCE_UNIDIR:
-               q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR;
-               break;
-
-       case QC_FORCE_BIDIR:
-               q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR;
-               break;
-
-       case QC_ANY:
-               write_lpcontrol(q, 0x20);
-               write_lpdata(q, 0x75);
-
-               if (read_lpdata(q) != 0x75)
-                       q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR;
-               else
-                       q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR;
-               break;
-       }
-
-       write_lpcontrol(q, 0xb);
-       udelay(250);
-       write_lpcontrol(q, 0xe);
-       qc_setscanmode(q);              /* in case port_mode changed */
-}
-
-
-
-/* Reset the QuickCam and program for brightness, contrast,
- * white-balance, and resolution. */
-
-static void qc_set(struct qcam *q)
-{
-       int val;
-       int val2;
-
-       /* Set the brightness.  Yes, this is repetitive, but it works.
-        * Shorter versions seem to fail subtly.  Feel free to try :-). */
-       /* I think the problem was in qc_command, not here -- bls */
-
-       qc_command(q, 0xb);
-       qc_command(q, q->brightness);
-
-       val = q->height / q->transfer_scale;
-       qc_command(q, 0x11);
-       qc_command(q, val);
-       if ((q->port_mode & QC_MODE_MASK) == QC_UNIDIR && q->bpp == 6) {
-               /* The normal "transfers per line" calculation doesn't seem to work
-                  as expected here (and yet it works fine in qc_scan).  No idea
-                  why this case is the odd man out.  Fortunately, Laird's original
-                  working version gives me a good way to guess at working values.
-                  -- bls */
-               val = q->width;
-               val2 = q->transfer_scale * 4;
-       } else {
-               val = q->width * q->bpp;
-               val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) *
-                       q->transfer_scale;
-       }
-       val = DIV_ROUND_UP(val, val2);
-       qc_command(q, 0x13);
-       qc_command(q, val);
-
-       /* Setting top and left -- bls */
-       qc_command(q, 0xd);
-       qc_command(q, q->top);
-       qc_command(q, 0xf);
-       qc_command(q, q->left / 2);
-
-       qc_command(q, 0x19);
-       qc_command(q, q->contrast);
-       qc_command(q, 0x1f);
-       qc_command(q, q->whitebal);
-
-       /* Clear flag that we must update the grabbing parameters on the camera
-          before we grab the next frame */
-       q->status &= (~QC_PARAM_CHANGE);
-}
-
-/* Qc_readbytes reads some bytes from the QC and puts them in
-   the supplied buffer.  It returns the number of bytes read,
-   or -1 on error. */
-
-static inline int qc_readbytes(struct qcam *q, char buffer[])
-{
-       int ret = 1;
-       unsigned int hi, lo;
-       unsigned int hi2, lo2;
-       static int state;
-
-       if (buffer == NULL) {
-               state = 0;
-               return 0;
-       }
-
-       switch (q->port_mode & QC_MODE_MASK) {
-       case QC_BIDIR:          /* Bi-directional Port */
-               write_lpcontrol(q, 0x26);
-               lo = (qc_waithand2(q, 1) >> 1);
-               hi = (read_lpstatus(q) >> 3) & 0x1f;
-               write_lpcontrol(q, 0x2e);
-               lo2 = (qc_waithand2(q, 0) >> 1);
-               hi2 = (read_lpstatus(q) >> 3) & 0x1f;
-               switch (q->bpp) {
-               case 4:
-                       buffer[0] = lo & 0xf;
-                       buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3);
-                       buffer[2] = (hi & 0x1e) >> 1;
-                       buffer[3] = lo2 & 0xf;
-                       buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3);
-                       buffer[5] = (hi2 & 0x1e) >> 1;
-                       ret = 6;
-                       break;
-               case 6:
-                       buffer[0] = lo & 0x3f;
-                       buffer[1] = ((lo & 0x40) >> 6) | (hi << 1);
-                       buffer[2] = lo2 & 0x3f;
-                       buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1);
-                       ret = 4;
-                       break;
-               }
-               break;
-
-       case QC_UNIDIR: /* Unidirectional Port */
-               write_lpcontrol(q, 6);
-               lo = (qc_waithand(q, 1) & 0xf0) >> 4;
-               write_lpcontrol(q, 0xe);
-               hi = (qc_waithand(q, 0) & 0xf0) >> 4;
-
-               switch (q->bpp) {
-               case 4:
-                       buffer[0] = lo;
-                       buffer[1] = hi;
-                       ret = 2;
-                       break;
-               case 6:
-                       switch (state) {
-                       case 0:
-                               buffer[0] = (lo << 2) | ((hi & 0xc) >> 2);
-                               q->saved_bits = (hi & 3) << 4;
-                               state = 1;
-                               ret = 1;
-                               break;
-                       case 1:
-                               buffer[0] = lo | q->saved_bits;
-                               q->saved_bits = hi << 2;
-                               state = 2;
-                               ret = 1;
-                               break;
-                       case 2:
-                               buffer[0] = ((lo & 0xc) >> 2) | q->saved_bits;
-                               buffer[1] = ((lo & 3) << 4) | hi;
-                               state = 0;
-                               ret = 2;
-                               break;
-                       }
-                       break;
-               }
-               break;
-       }
-       return ret;
-}
-
-/* requests a scan from the camera.  It sends the correct instructions
- * to the camera and then reads back the correct number of bytes.  In
- * previous versions of this routine the return structure contained
- * the raw output from the camera, and there was a 'qc_convertscan'
- * function that converted that to a useful format.  In version 0.3 I
- * rolled qc_convertscan into qc_scan and now I only return the
- * converted scan.  The format is just an one-dimensional array of
- * characters, one for each pixel, with 0=black up to n=white, where
- * n=2^(bit depth)-1.  Ask me for more details if you don't understand
- * this. */
-
-static long qc_capture(struct qcam *q, u8 *buf, unsigned long len)
-{
-       int i, j, k, yield;
-       int bytes;
-       int linestotrans, transperline;
-       int divisor;
-       int pixels_per_line;
-       int pixels_read = 0;
-       int got = 0;
-       char buffer[6];
-       int  shift = 8 - q->bpp;
-       char invert;
-
-       if (q->mode == -1)
-               return -ENXIO;
-
-       qc_command(q, 0x7);
-       qc_command(q, q->mode);
-
-       if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) {
-               write_lpcontrol(q, 0x2e);       /* turn port around */
-               write_lpcontrol(q, 0x26);
-               qc_waithand(q, 1);
-               write_lpcontrol(q, 0x2e);
-               qc_waithand(q, 0);
-       }
-
-       /* strange -- should be 15:63 below, but 4bpp is odd */
-       invert = (q->bpp == 4) ? 16 : 63;
-
-       linestotrans = q->height / q->transfer_scale;
-       pixels_per_line = q->width / q->transfer_scale;
-       transperline = q->width * q->bpp;
-       divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) *
-               q->transfer_scale;
-       transperline = DIV_ROUND_UP(transperline, divisor);
-
-       for (i = 0, yield = yieldlines; i < linestotrans; i++) {
-               for (pixels_read = j = 0; j < transperline; j++) {
-                       bytes = qc_readbytes(q, buffer);
-                       for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++) {
-                               int o;
-                               if (buffer[k] == 0 && invert == 16) {
-                                       /* 4bpp is odd (again) -- inverter is 16, not 15, but output
-                                          must be 0-15 -- bls */
-                                       buffer[k] = 16;
-                               }
-                               o = i * pixels_per_line + pixels_read + k;
-                               if (o < len) {
-                                       u8 ch = invert - buffer[k];
-                                       got++;
-                                       buf[o] = ch << shift;
-                               }
-                       }
-                       pixels_read += bytes;
-               }
-               qc_readbytes(q, NULL);  /* reset state machine */
-
-               /* Grabbing an entire frame from the quickcam is a lengthy
-                  process. We don't (usually) want to busy-block the
-                  processor for the entire frame. yieldlines is a module
-                  parameter. If we yield every line, the minimum frame
-                  time will be 240 / 200 = 1.2 seconds. The compile-time
-                  default is to yield every 4 lines. */
-               if (i >= yield) {
-                       msleep_interruptible(5);
-                       yield = i + yieldlines;
-               }
-       }
-
-       if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) {
-               write_lpcontrol(q, 2);
-               write_lpcontrol(q, 6);
-               udelay(3);
-               write_lpcontrol(q, 0xe);
-       }
-       if (got < len)
-               return got;
-       return len;
-}
-
-/* ------------------------------------------------------------------
-       Videobuf operations
-   ------------------------------------------------------------------*/
-static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
-                               unsigned int *nbuffers, unsigned int *nplanes,
-                               unsigned int sizes[], void *alloc_ctxs[])
-{
-       struct qcam *dev = vb2_get_drv_priv(vq);
-
-       if (0 == *nbuffers)
-               *nbuffers = 3;
-       *nplanes = 1;
-       mutex_lock(&dev->lock);
-       if (fmt)
-               sizes[0] = fmt->fmt.pix.width * fmt->fmt.pix.height;
-       else
-               sizes[0] = (dev->width / dev->transfer_scale) *
-                  (dev->height / dev->transfer_scale);
-       mutex_unlock(&dev->lock);
-       return 0;
-}
-
-static void buffer_queue(struct vb2_buffer *vb)
-{
-       vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
-}
-
-static void buffer_finish(struct vb2_buffer *vb)
-{
-       struct qcam *qcam = vb2_get_drv_priv(vb->vb2_queue);
-       void *vbuf = vb2_plane_vaddr(vb, 0);
-       int size = vb->vb2_queue->plane_sizes[0];
-       int len;
-
-       if (!vb2_is_streaming(vb->vb2_queue))
-               return;
-
-       mutex_lock(&qcam->lock);
-       parport_claim_or_block(qcam->pdev);
-
-       qc_reset(qcam);
-
-       /* Update the camera parameters if we need to */
-       if (qcam->status & QC_PARAM_CHANGE)
-               qc_set(qcam);
-
-       len = qc_capture(qcam, vbuf, size);
-
-       parport_release(qcam->pdev);
-       mutex_unlock(&qcam->lock);
-       v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
-       if (len != size)
-               vb->state = VB2_BUF_STATE_ERROR;
-       vb2_set_plane_payload(vb, 0, len);
-}
-
-static struct vb2_ops qcam_video_qops = {
-       .queue_setup            = queue_setup,
-       .buf_queue              = buffer_queue,
-       .buf_finish             = buffer_finish,
-       .wait_prepare           = vb2_ops_wait_prepare,
-       .wait_finish            = vb2_ops_wait_finish,
-};
-
-/*
- *     Video4linux interfacing
- */
-
-static int qcam_querycap(struct file *file, void  *priv,
-                                       struct v4l2_capability *vcap)
-{
-       struct qcam *qcam = video_drvdata(file);
-
-       strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
-       strlcpy(vcap->card, "Connectix B&W Quickcam", sizeof(vcap->card));
-       strlcpy(vcap->bus_info, qcam->pport->name, sizeof(vcap->bus_info));
-       vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
-                               V4L2_CAP_STREAMING;
-       vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
-       return 0;
-}
-
-static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
-{
-       if (vin->index > 0)
-               return -EINVAL;
-       strlcpy(vin->name, "Camera", sizeof(vin->name));
-       vin->type = V4L2_INPUT_TYPE_CAMERA;
-       vin->audioset = 0;
-       vin->tuner = 0;
-       vin->std = 0;
-       vin->status = 0;
-       return 0;
-}
-
-static int qcam_g_input(struct file *file, void *fh, unsigned int *inp)
-{
-       *inp = 0;
-       return 0;
-}
-
-static int qcam_s_input(struct file *file, void *fh, unsigned int inp)
-{
-       return (inp > 0) ? -EINVAL : 0;
-}
-
-static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
-       struct qcam *qcam = video_drvdata(file);
-       struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
-       pix->width = qcam->width / qcam->transfer_scale;
-       pix->height = qcam->height / qcam->transfer_scale;
-       pix->pixelformat = (qcam->bpp == 4) ? V4L2_PIX_FMT_Y4 : V4L2_PIX_FMT_Y6;
-       pix->field = V4L2_FIELD_NONE;
-       pix->bytesperline = pix->width;
-       pix->sizeimage = pix->width * pix->height;
-       /* Just a guess */
-       pix->colorspace = V4L2_COLORSPACE_SRGB;
-       return 0;
-}
-
-static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
-       struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
-       if (pix->height <= 60 || pix->width <= 80) {
-               pix->height = 60;
-               pix->width = 80;
-       } else if (pix->height <= 120 || pix->width <= 160) {
-               pix->height = 120;
-               pix->width = 160;
-       } else {
-               pix->height = 240;
-               pix->width = 320;
-       }
-       if (pix->pixelformat != V4L2_PIX_FMT_Y4 &&
-           pix->pixelformat != V4L2_PIX_FMT_Y6)
-               pix->pixelformat = V4L2_PIX_FMT_Y4;
-       pix->field = V4L2_FIELD_NONE;
-       pix->bytesperline = pix->width;
-       pix->sizeimage = pix->width * pix->height;
-       /* Just a guess */
-       pix->colorspace = V4L2_COLORSPACE_SRGB;
-       return 0;
-}
-
-static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
-       struct qcam *qcam = video_drvdata(file);
-       struct v4l2_pix_format *pix = &fmt->fmt.pix;
-       int ret = qcam_try_fmt_vid_cap(file, fh, fmt);
-
-       if (ret)
-               return ret;
-       if (vb2_is_busy(&qcam->vb_vidq))
-               return -EBUSY;
-       qcam->width = 320;
-       qcam->height = 240;
-       if (pix->height == 60)
-               qcam->transfer_scale = 4;
-       else if (pix->height == 120)
-               qcam->transfer_scale = 2;
-       else
-               qcam->transfer_scale = 1;
-       if (pix->pixelformat == V4L2_PIX_FMT_Y6)
-               qcam->bpp = 6;
-       else
-               qcam->bpp = 4;
-
-       qc_setscanmode(qcam);
-       /* We must update the camera before we grab. We could
-          just have changed the grab size */
-       qcam->status |= QC_PARAM_CHANGE;
-       return 0;
-}
-
-static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
-{
-       static struct v4l2_fmtdesc formats[] = {
-               { 0, 0, 0,
-                 "4-Bit Monochrome", V4L2_PIX_FMT_Y4,
-                 { 0, 0, 0, 0 }
-               },
-               { 1, 0, 0,
-                 "6-Bit Monochrome", V4L2_PIX_FMT_Y6,
-                 { 0, 0, 0, 0 }
-               },
-       };
-       enum v4l2_buf_type type = fmt->type;
-
-       if (fmt->index > 1)
-               return -EINVAL;
-
-       *fmt = formats[fmt->index];
-       fmt->type = type;
-       return 0;
-}
-
-static int qcam_enum_framesizes(struct file *file, void *fh,
-                                        struct v4l2_frmsizeenum *fsize)
-{
-       static const struct v4l2_frmsize_discrete sizes[] = {
-               {  80,  60 },
-               { 160, 120 },
-               { 320, 240 },
-       };
-
-       if (fsize->index > 2)
-               return -EINVAL;
-       if (fsize->pixel_format != V4L2_PIX_FMT_Y4 &&
-           fsize->pixel_format != V4L2_PIX_FMT_Y6)
-               return -EINVAL;
-       fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
-       fsize->discrete = sizes[fsize->index];
-       return 0;
-}
-
-static int qcam_s_ctrl(struct v4l2_ctrl *ctrl)
-{
-       struct qcam *qcam =
-               container_of(ctrl->handler, struct qcam, hdl);
-       int ret = 0;
-
-       switch (ctrl->id) {
-       case V4L2_CID_BRIGHTNESS:
-               qcam->brightness = ctrl->val;
-               break;
-       case V4L2_CID_CONTRAST:
-               qcam->contrast = ctrl->val;
-               break;
-       case V4L2_CID_GAMMA:
-               qcam->whitebal = ctrl->val;
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-       if (ret == 0)
-               qcam->status |= QC_PARAM_CHANGE;
-       return ret;
-}
-
-static const struct v4l2_file_operations qcam_fops = {
-       .owner          = THIS_MODULE,
-       .open           = v4l2_fh_open,
-       .release        = vb2_fop_release,
-       .poll           = vb2_fop_poll,
-       .unlocked_ioctl = video_ioctl2,
-       .read           = vb2_fop_read,
-       .mmap           = vb2_fop_mmap,
-};
-
-static const struct v4l2_ioctl_ops qcam_ioctl_ops = {
-       .vidioc_querycap                    = qcam_querycap,
-       .vidioc_g_input                     = qcam_g_input,
-       .vidioc_s_input                     = qcam_s_input,
-       .vidioc_enum_input                  = qcam_enum_input,
-       .vidioc_enum_fmt_vid_cap            = qcam_enum_fmt_vid_cap,
-       .vidioc_enum_framesizes             = qcam_enum_framesizes,
-       .vidioc_g_fmt_vid_cap               = qcam_g_fmt_vid_cap,
-       .vidioc_s_fmt_vid_cap               = qcam_s_fmt_vid_cap,
-       .vidioc_try_fmt_vid_cap             = qcam_try_fmt_vid_cap,
-       .vidioc_reqbufs                     = vb2_ioctl_reqbufs,
-       .vidioc_create_bufs                 = vb2_ioctl_create_bufs,
-       .vidioc_prepare_buf                 = vb2_ioctl_prepare_buf,
-       .vidioc_querybuf                    = vb2_ioctl_querybuf,
-       .vidioc_qbuf                        = vb2_ioctl_qbuf,
-       .vidioc_dqbuf                       = vb2_ioctl_dqbuf,
-       .vidioc_streamon                    = vb2_ioctl_streamon,
-       .vidioc_streamoff                   = vb2_ioctl_streamoff,
-       .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 qcam_ctrl_ops = {
-       .s_ctrl = qcam_s_ctrl,
-};
-
-/* Initialize the QuickCam driver control structure.  This is where
- * defaults are set for people who don't have a config file.*/
-
-static struct qcam *qcam_init(struct parport *port)
-{
-       struct qcam *qcam;
-       struct v4l2_device *v4l2_dev;
-       struct vb2_queue *q;
-       int err;
-
-       qcam = kzalloc(sizeof(struct qcam), GFP_KERNEL);
-       if (qcam == NULL)
-               return NULL;
-
-       v4l2_dev = &qcam->v4l2_dev;
-       snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "bw-qcam%u", num_cams);
-
-       if (v4l2_device_register(port->dev, v4l2_dev) < 0) {
-               v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
-               kfree(qcam);
-               return NULL;
-       }
-
-       v4l2_ctrl_handler_init(&qcam->hdl, 3);
-       v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
-                         V4L2_CID_BRIGHTNESS, 0, 255, 1, 180);
-       v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
-                         V4L2_CID_CONTRAST, 0, 255, 1, 192);
-       v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
-                         V4L2_CID_GAMMA, 0, 255, 1, 105);
-       if (qcam->hdl.error) {
-               v4l2_err(v4l2_dev, "couldn't register controls\n");
-               goto exit;
-       }
-
-       mutex_init(&qcam->lock);
-       mutex_init(&qcam->queue_lock);
-
-       /* initialize queue */
-       q = &qcam->vb_vidq;
-       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-       q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
-       q->drv_priv = qcam;
-       q->ops = &qcam_video_qops;
-       q->mem_ops = &vb2_vmalloc_memops;
-       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-       err = vb2_queue_init(q);
-       if (err < 0) {
-               v4l2_err(v4l2_dev, "couldn't init vb2_queue for %s.\n", port->name);
-               goto exit;
-       }
-       qcam->vdev.queue = q;
-       qcam->vdev.queue->lock = &qcam->queue_lock;
-
-       qcam->pport = port;
-       qcam->pdev = parport_register_device(port, v4l2_dev->name, NULL, NULL,
-                       NULL, 0, NULL);
-       if (qcam->pdev == NULL) {
-               v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name);
-               goto exit;
-       }
-
-       strlcpy(qcam->vdev.name, "Connectix QuickCam", sizeof(qcam->vdev.name));
-       qcam->vdev.v4l2_dev = v4l2_dev;
-       qcam->vdev.ctrl_handler = &qcam->hdl;
-       qcam->vdev.fops = &qcam_fops;
-       qcam->vdev.lock = &qcam->lock;
-       qcam->vdev.ioctl_ops = &qcam_ioctl_ops;
-       qcam->vdev.release = video_device_release_empty;
-       video_set_drvdata(&qcam->vdev, qcam);
-
-       qcam->port_mode = (QC_ANY | QC_NOTSET);
-       qcam->width = 320;
-       qcam->height = 240;
-       qcam->bpp = 4;
-       qcam->transfer_scale = 2;
-       qcam->contrast = 192;
-       qcam->brightness = 180;
-       qcam->whitebal = 105;
-       qcam->top = 1;
-       qcam->left = 14;
-       qcam->mode = -1;
-       qcam->status = QC_PARAM_CHANGE;
-       return qcam;
-
-exit:
-       v4l2_ctrl_handler_free(&qcam->hdl);
-       kfree(qcam);
-       return NULL;
-}
-
-static int qc_calibrate(struct qcam *q)
-{
-       /*
-        *      Bugfix by Hanno Mueller hmueller@kabel.de, Mai 21 96
-        *      The white balance is an individual value for each
-        *      quickcam.
-        */
-
-       int value;
-       int count = 0;
-
-       qc_command(q, 27);      /* AutoAdjustOffset */
-       qc_command(q, 0);       /* Dummy Parameter, ignored by the camera */
-
-       /* GetOffset (33) will read 255 until autocalibration */
-       /* is finished. After that, a value of 1-254 will be */
-       /* returned. */
-
-       do {
-               qc_command(q, 33);
-               value = qc_readparam(q);
-               mdelay(1);
-               schedule();
-               count++;
-       } while (value == 0xff && count < 2048);
-
-       q->whitebal = value;
-       return value;
-}
-
-static int init_bwqcam(struct parport *port)
-{
-       struct qcam *qcam;
-
-       if (num_cams == MAX_CAMS) {
-               printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS);
-               return -ENOSPC;
-       }
-
-       qcam = qcam_init(port);
-       if (qcam == NULL)
-               return -ENODEV;
-
-       parport_claim_or_block(qcam->pdev);
-
-       qc_reset(qcam);
-
-       if (qc_detect(qcam) == 0) {
-               parport_release(qcam->pdev);
-               parport_unregister_device(qcam->pdev);
-               kfree(qcam);
-               return -ENODEV;
-       }
-       qc_calibrate(qcam);
-       v4l2_ctrl_handler_setup(&qcam->hdl);
-
-       parport_release(qcam->pdev);
-
-       v4l2_info(&qcam->v4l2_dev, "Connectix Quickcam on %s\n", qcam->pport->name);
-
-       if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
-               parport_unregister_device(qcam->pdev);
-               kfree(qcam);
-               return -ENODEV;
-       }
-
-       qcams[num_cams++] = qcam;
-
-       return 0;
-}
-
-static void close_bwqcam(struct qcam *qcam)
-{
-       video_unregister_device(&qcam->vdev);
-       v4l2_ctrl_handler_free(&qcam->hdl);
-       parport_unregister_device(qcam->pdev);
-       kfree(qcam);
-}
-
-/* The parport parameter controls which parports will be scanned.
- * Scanning all parports causes some printers to print a garbage page.
- *       -- March 14, 1999  Billy Donahue <billy@escape.com> */
-#ifdef MODULE
-static char *parport[MAX_CAMS] = { NULL, };
-module_param_array(parport, charp, NULL, 0);
-#endif
-
-static int accept_bwqcam(struct parport *port)
-{
-#ifdef MODULE
-       int n;
-
-       if (parport[0] && strncmp(parport[0], "auto", 4) != 0) {
-               /* user gave parport parameters */
-               for (n = 0; n < MAX_CAMS && parport[n]; n++) {
-                       char *ep;
-                       unsigned long r;
-                       r = simple_strtoul(parport[n], &ep, 0);
-                       if (ep == parport[n]) {
-                               printk(KERN_ERR
-                                       "bw-qcam: bad port specifier \"%s\"\n",
-                                       parport[n]);
-                               continue;
-                       }
-                       if (r == port->number)
-                               return 1;
-               }
-               return 0;
-       }
-#endif
-       return 1;
-}
-
-static void bwqcam_attach(struct parport *port)
-{
-       if (accept_bwqcam(port))
-               init_bwqcam(port);
-}
-
-static void bwqcam_detach(struct parport *port)
-{
-       int i;
-       for (i = 0; i < num_cams; i++) {
-               struct qcam *qcam = qcams[i];
-               if (qcam && qcam->pdev->port == port) {
-                       qcams[i] = NULL;
-                       close_bwqcam(qcam);
-               }
-       }
-}
-
-static struct parport_driver bwqcam_driver = {
-       .name   = "bw-qcam",
-       .attach = bwqcam_attach,
-       .detach = bwqcam_detach,
-};
-
-static void __exit exit_bw_qcams(void)
-{
-       parport_unregister_driver(&bwqcam_driver);
-}
-
-static int __init init_bw_qcams(void)
-{
-#ifdef MODULE
-       /* Do some sanity checks on the module parameters. */
-       if (maxpoll > 5000) {
-               printk(KERN_INFO "Connectix Quickcam max-poll was above 5000. Using 5000.\n");
-               maxpoll = 5000;
-       }
-
-       if (yieldlines < 1) {
-               printk(KERN_INFO "Connectix Quickcam yieldlines was less than 1. Using 1.\n");
-               yieldlines = 1;
-       }
-#endif
-       return parport_register_driver(&bwqcam_driver);
-}
-
-module_init(init_bw_qcams);
-module_exit(exit_bw_qcams);
-
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.3");
diff --git a/drivers/media/parport/c-qcam.c b/drivers/media/parport/c-qcam.c
deleted file mode 100644 (file)
index b9010bd..0000000
+++ /dev/null
@@ -1,882 +0,0 @@
-/*
- *     Video4Linux Colour QuickCam driver
- *     Copyright 1997-2000 Philip Blundell <philb@gnu.org>
- *
- *    Module parameters:
- *
- *     parport=auto      -- probe all parports (default)
- *     parport=0         -- parport0 becomes qcam1
- *     parport=2,0,1     -- parports 2,0,1 are tried in that order
- *
- *     probe=0           -- do no probing, assume camera is present
- *     probe=1           -- use IEEE-1284 autoprobe data only (default)
- *     probe=2           -- probe aggressively for cameras
- *
- *     force_rgb=1       -- force data format to RGB (default is BGR)
- *
- * The parport parameter controls which parports will be scanned.
- * Scanning all parports causes some printers to print a garbage page.
- *       -- March 14, 1999  Billy Donahue <billy@escape.com>
- *
- * Fixed data format to BGR, added force_rgb parameter. Added missing
- * parport_unregister_driver() on module removal.
- *       -- May 28, 2000  Claudio Matsuoka <claudio@conectiva.com>
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/parport.h>
-#include <linux/sched.h>
-#include <linux/mutex.h>
-#include <linux/jiffies.h>
-#include <linux/videodev2.h>
-#include <asm/uaccess.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-event.h>
-
-struct qcam {
-       struct v4l2_device v4l2_dev;
-       struct video_device vdev;
-       struct v4l2_ctrl_handler hdl;
-       struct pardevice *pdev;
-       struct parport *pport;
-       int width, height;
-       int ccd_width, ccd_height;
-       int mode;
-       int contrast, brightness, whitebal;
-       int top, left;
-       unsigned int bidirectional;
-       struct mutex lock;
-};
-
-/* cameras maximum */
-#define MAX_CAMS 4
-
-/* The three possible QuickCam modes */
-#define QC_MILLIONS    0x18
-#define QC_BILLIONS    0x10
-#define QC_THOUSANDS   0x08    /* with VIDEC compression (not supported) */
-
-/* The three possible decimations */
-#define QC_DECIMATION_1                0
-#define QC_DECIMATION_2                2
-#define QC_DECIMATION_4                4
-
-#define BANNER "Colour QuickCam for Video4Linux v0.06"
-
-static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 };
-static int probe = 2;
-static bool force_rgb;
-static int video_nr = -1;
-
-/* FIXME: parport=auto would never have worked, surely? --RR */
-MODULE_PARM_DESC(parport, "parport=<auto|n[,n]...> for port detection method\n"
-                         "probe=<0|1|2> for camera detection method\n"
-                         "force_rgb=<0|1> for RGB data format (default BGR)");
-module_param_array(parport, int, NULL, 0);
-module_param(probe, int, 0);
-module_param(force_rgb, bool, 0);
-module_param(video_nr, int, 0);
-
-static struct qcam *qcams[MAX_CAMS];
-static unsigned int num_cams;
-
-static inline void qcam_set_ack(struct qcam *qcam, unsigned int i)
-{
-       /* note: the QC specs refer to the PCAck pin by voltage, not
-          software level.  PC ports have builtin inverters. */
-       parport_frob_control(qcam->pport, 8, i ? 8 : 0);
-}
-
-static inline unsigned int qcam_ready1(struct qcam *qcam)
-{
-       return (parport_read_status(qcam->pport) & 0x8) ? 1 : 0;
-}
-
-static inline unsigned int qcam_ready2(struct qcam *qcam)
-{
-       return (parport_read_data(qcam->pport) & 0x1) ? 1 : 0;
-}
-
-static unsigned int qcam_await_ready1(struct qcam *qcam, int value)
-{
-       struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
-       unsigned long oldjiffies = jiffies;
-       unsigned int i;
-
-       for (oldjiffies = jiffies;
-            time_before(jiffies, oldjiffies + msecs_to_jiffies(40));)
-               if (qcam_ready1(qcam) == value)
-                       return 0;
-
-       /* If the camera didn't respond within 1/25 second, poll slowly
-          for a while. */
-       for (i = 0; i < 50; i++) {
-               if (qcam_ready1(qcam) == value)
-                       return 0;
-               msleep_interruptible(100);
-       }
-
-       /* Probably somebody pulled the plug out.  Not much we can do. */
-       v4l2_err(v4l2_dev, "ready1 timeout (%d) %x %x\n", value,
-              parport_read_status(qcam->pport),
-              parport_read_control(qcam->pport));
-       return 1;
-}
-
-static unsigned int qcam_await_ready2(struct qcam *qcam, int value)
-{
-       struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
-       unsigned long oldjiffies = jiffies;
-       unsigned int i;
-
-       for (oldjiffies = jiffies;
-            time_before(jiffies, oldjiffies + msecs_to_jiffies(40));)
-               if (qcam_ready2(qcam) == value)
-                       return 0;
-
-       /* If the camera didn't respond within 1/25 second, poll slowly
-          for a while. */
-       for (i = 0; i < 50; i++) {
-               if (qcam_ready2(qcam) == value)
-                       return 0;
-               msleep_interruptible(100);
-       }
-
-       /* Probably somebody pulled the plug out.  Not much we can do. */
-       v4l2_err(v4l2_dev, "ready2 timeout (%d) %x %x %x\n", value,
-              parport_read_status(qcam->pport),
-              parport_read_control(qcam->pport),
-              parport_read_data(qcam->pport));
-       return 1;
-}
-
-static int qcam_read_data(struct qcam *qcam)
-{
-       unsigned int idata;
-
-       qcam_set_ack(qcam, 0);
-       if (qcam_await_ready1(qcam, 1))
-               return -1;
-       idata = parport_read_status(qcam->pport) & 0xf0;
-       qcam_set_ack(qcam, 1);
-       if (qcam_await_ready1(qcam, 0))
-               return -1;
-       idata |= parport_read_status(qcam->pport) >> 4;
-       return idata;
-}
-
-static int qcam_write_data(struct qcam *qcam, unsigned int data)
-{
-       struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
-       unsigned int idata;
-
-       parport_write_data(qcam->pport, data);
-       idata = qcam_read_data(qcam);
-       if (data != idata) {
-               v4l2_warn(v4l2_dev, "sent %x but received %x\n", data,
-                      idata);
-               return 1;
-       }
-       return 0;
-}
-
-static inline int qcam_set(struct qcam *qcam, unsigned int cmd, unsigned int data)
-{
-       if (qcam_write_data(qcam, cmd))
-               return -1;
-       if (qcam_write_data(qcam, data))
-               return -1;
-       return 0;
-}
-
-static inline int qcam_get(struct qcam *qcam, unsigned int cmd)
-{
-       if (qcam_write_data(qcam, cmd))
-               return -1;
-       return qcam_read_data(qcam);
-}
-
-static int qc_detect(struct qcam *qcam)
-{
-       unsigned int stat, ostat, i, count = 0;
-
-       /* The probe routine below is not very reliable.  The IEEE-1284
-          probe takes precedence. */
-       /* XXX Currently parport provides no way to distinguish between
-          "the IEEE probe was not done" and "the probe was done, but
-          no device was found".  Fix this one day. */
-       if (qcam->pport->probe_info[0].class == PARPORT_CLASS_MEDIA
-           && qcam->pport->probe_info[0].model
-           && !strcmp(qcam->pdev->port->probe_info[0].model,
-                      "Color QuickCam 2.0")) {
-               printk(KERN_DEBUG "QuickCam: Found by IEEE1284 probe.\n");
-               return 1;
-       }
-
-       if (probe < 2)
-               return 0;
-
-       parport_write_control(qcam->pport, 0xc);
-
-       /* look for a heartbeat */
-       ostat = stat = parport_read_status(qcam->pport);
-       for (i = 0; i < 250; i++) {
-               mdelay(1);
-               stat = parport_read_status(qcam->pport);
-               if (ostat != stat) {
-                       if (++count >= 3)
-                               return 1;
-                       ostat = stat;
-               }
-       }
-
-       /* Reset the camera and try again */
-       parport_write_control(qcam->pport, 0xc);
-       parport_write_control(qcam->pport, 0x8);
-       mdelay(1);
-       parport_write_control(qcam->pport, 0xc);
-       mdelay(1);
-       count = 0;
-
-       ostat = stat = parport_read_status(qcam->pport);
-       for (i = 0; i < 250; i++) {
-               mdelay(1);
-               stat = parport_read_status(qcam->pport);
-               if (ostat != stat) {
-                       if (++count >= 3)
-                               return 1;
-                       ostat = stat;
-               }
-       }
-
-       /* no (or flatline) camera, give up */
-       return 0;
-}
-
-static void qc_reset(struct qcam *qcam)
-{
-       parport_write_control(qcam->pport, 0xc);
-       parport_write_control(qcam->pport, 0x8);
-       mdelay(1);
-       parport_write_control(qcam->pport, 0xc);
-       mdelay(1);
-}
-
-/* Reset the QuickCam and program for brightness, contrast,
- * white-balance, and resolution. */
-
-static void qc_setup(struct qcam *qcam)
-{
-       qc_reset(qcam);
-
-       /* Set the brightness. */
-       qcam_set(qcam, 11, qcam->brightness);
-
-       /* Set the height and width.  These refer to the actual
-          CCD area *before* applying the selected decimation.  */
-       qcam_set(qcam, 17, qcam->ccd_height);
-       qcam_set(qcam, 19, qcam->ccd_width / 2);
-
-       /* Set top and left.  */
-       qcam_set(qcam, 0xd, qcam->top);
-       qcam_set(qcam, 0xf, qcam->left);
-
-       /* Set contrast and white balance.  */
-       qcam_set(qcam, 0x19, qcam->contrast);
-       qcam_set(qcam, 0x1f, qcam->whitebal);
-
-       /* Set the speed.  */
-       qcam_set(qcam, 45, 2);
-}
-
-/* Read some bytes from the camera and put them in the buffer.
-   nbytes should be a multiple of 3, because bidirectional mode gives
-   us three bytes at a time.  */
-
-static unsigned int qcam_read_bytes(struct qcam *qcam, unsigned char *buf, unsigned int nbytes)
-{
-       unsigned int bytes = 0;
-
-       qcam_set_ack(qcam, 0);
-       if (qcam->bidirectional) {
-               /* It's a bidirectional port */
-               while (bytes < nbytes) {
-                       unsigned int lo1, hi1, lo2, hi2;
-                       unsigned char r, g, b;
-
-                       if (qcam_await_ready2(qcam, 1))
-                               return bytes;
-                       lo1 = parport_read_data(qcam->pport) >> 1;
-                       hi1 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
-                       qcam_set_ack(qcam, 1);
-                       if (qcam_await_ready2(qcam, 0))
-                               return bytes;
-                       lo2 = parport_read_data(qcam->pport) >> 1;
-                       hi2 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
-                       qcam_set_ack(qcam, 0);
-                       r = lo1 | ((hi1 & 1) << 7);
-                       g = ((hi1 & 0x1e) << 3) | ((hi2 & 0x1e) >> 1);
-                       b = lo2 | ((hi2 & 1) << 7);
-                       if (force_rgb) {
-                               buf[bytes++] = r;
-                               buf[bytes++] = g;
-                               buf[bytes++] = b;
-                       } else {
-                               buf[bytes++] = b;
-                               buf[bytes++] = g;
-                               buf[bytes++] = r;
-                       }
-               }
-       } else {
-               /* It's a unidirectional port */
-               int i = 0, n = bytes;
-               unsigned char rgb[3];
-
-               while (bytes < nbytes) {
-                       unsigned int hi, lo;
-
-                       if (qcam_await_ready1(qcam, 1))
-                               return bytes;
-                       hi = (parport_read_status(qcam->pport) & 0xf0);
-                       qcam_set_ack(qcam, 1);
-                       if (qcam_await_ready1(qcam, 0))
-                               return bytes;
-                       lo = (parport_read_status(qcam->pport) & 0xf0);
-                       qcam_set_ack(qcam, 0);
-                       /* flip some bits */
-                       rgb[(i = bytes++ % 3)] = (hi | (lo >> 4)) ^ 0x88;
-                       if (i >= 2) {
-get_fragment:
-                               if (force_rgb) {
-                                       buf[n++] = rgb[0];
-                                       buf[n++] = rgb[1];
-                                       buf[n++] = rgb[2];
-                               } else {
-                                       buf[n++] = rgb[2];
-                                       buf[n++] = rgb[1];
-                                       buf[n++] = rgb[0];
-                               }
-                       }
-               }
-               if (i) {
-                       i = 0;
-                       goto get_fragment;
-               }
-       }
-       return bytes;
-}
-
-#define BUFSZ  150
-
-static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len)
-{
-       struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
-       unsigned lines, pixelsperline;
-       unsigned int is_bi_dir = qcam->bidirectional;
-       size_t wantlen, outptr = 0;
-       char tmpbuf[BUFSZ];
-
-       if (!access_ok(VERIFY_WRITE, buf, len))
-               return -EFAULT;
-
-       /* Wait for camera to become ready */
-       for (;;) {
-               int i = qcam_get(qcam, 41);
-
-               if (i == -1) {
-                       qc_setup(qcam);
-                       return -EIO;
-               }
-               if ((i & 0x80) == 0)
-                       break;
-               schedule();
-       }
-
-       if (qcam_set(qcam, 7, (qcam->mode | (is_bi_dir ? 1 : 0)) + 1))
-               return -EIO;
-
-       lines = qcam->height;
-       pixelsperline = qcam->width;
-
-       if (is_bi_dir) {
-               /* Turn the port around */
-               parport_data_reverse(qcam->pport);
-               mdelay(3);
-               qcam_set_ack(qcam, 0);
-               if (qcam_await_ready1(qcam, 1)) {
-                       qc_setup(qcam);
-                       return -EIO;
-               }
-               qcam_set_ack(qcam, 1);
-               if (qcam_await_ready1(qcam, 0)) {
-                       qc_setup(qcam);
-                       return -EIO;
-               }
-       }
-
-       wantlen = lines * pixelsperline * 24 / 8;
-
-       while (wantlen) {
-               size_t t, s;
-
-               s = (wantlen > BUFSZ) ? BUFSZ : wantlen;
-               t = qcam_read_bytes(qcam, tmpbuf, s);
-               if (outptr < len) {
-                       size_t sz = len - outptr;
-
-                       if (sz > t)
-                               sz = t;
-                       if (__copy_to_user(buf + outptr, tmpbuf, sz))
-                               break;
-                       outptr += sz;
-               }
-               wantlen -= t;
-               if (t < s)
-                       break;
-               cond_resched();
-       }
-
-       len = outptr;
-
-       if (wantlen) {
-               v4l2_err(v4l2_dev, "short read.\n");
-               if (is_bi_dir)
-                       parport_data_forward(qcam->pport);
-               qc_setup(qcam);
-               return len;
-       }
-
-       if (is_bi_dir) {
-               int l;
-
-               do {
-                       l = qcam_read_bytes(qcam, tmpbuf, 3);
-                       cond_resched();
-               } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e));
-               if (force_rgb) {
-                       if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
-                               v4l2_err(v4l2_dev, "bad EOF\n");
-               } else {
-                       if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
-                               v4l2_err(v4l2_dev, "bad EOF\n");
-               }
-               qcam_set_ack(qcam, 0);
-               if (qcam_await_ready1(qcam, 1)) {
-                       v4l2_err(v4l2_dev, "no ack after EOF\n");
-                       parport_data_forward(qcam->pport);
-                       qc_setup(qcam);
-                       return len;
-               }
-               parport_data_forward(qcam->pport);
-               mdelay(3);
-               qcam_set_ack(qcam, 1);
-               if (qcam_await_ready1(qcam, 0)) {
-                       v4l2_err(v4l2_dev, "no ack to port turnaround\n");
-                       qc_setup(qcam);
-                       return len;
-               }
-       } else {
-               int l;
-
-               do {
-                       l = qcam_read_bytes(qcam, tmpbuf, 1);
-                       cond_resched();
-               } while (l && tmpbuf[0] == 0x7e);
-               l = qcam_read_bytes(qcam, tmpbuf + 1, 2);
-               if (force_rgb) {
-                       if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
-                               v4l2_err(v4l2_dev, "bad EOF\n");
-               } else {
-                       if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
-                               v4l2_err(v4l2_dev, "bad EOF\n");
-               }
-       }
-
-       qcam_write_data(qcam, 0);
-       return len;
-}
-
-/*
- *     Video4linux interfacing
- */
-
-static int qcam_querycap(struct file *file, void  *priv,
-                                       struct v4l2_capability *vcap)
-{
-       struct qcam *qcam = video_drvdata(file);
-
-       strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
-       strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card));
-       strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
-       vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
-       vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
-       return 0;
-}
-
-static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
-{
-       if (vin->index > 0)
-               return -EINVAL;
-       strlcpy(vin->name, "Camera", sizeof(vin->name));
-       vin->type = V4L2_INPUT_TYPE_CAMERA;
-       vin->audioset = 0;
-       vin->tuner = 0;
-       vin->std = 0;
-       vin->status = 0;
-       return 0;
-}
-
-static int qcam_g_input(struct file *file, void *fh, unsigned int *inp)
-{
-       *inp = 0;
-       return 0;
-}
-
-static int qcam_s_input(struct file *file, void *fh, unsigned int inp)
-{
-       return (inp > 0) ? -EINVAL : 0;
-}
-
-static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
-       struct qcam *qcam = video_drvdata(file);
-       struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
-       pix->width = qcam->width;
-       pix->height = qcam->height;
-       pix->pixelformat = V4L2_PIX_FMT_RGB24;
-       pix->field = V4L2_FIELD_NONE;
-       pix->bytesperline = 3 * qcam->width;
-       pix->sizeimage = 3 * qcam->width * qcam->height;
-       /* Just a guess */
-       pix->colorspace = V4L2_COLORSPACE_SRGB;
-       return 0;
-}
-
-static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
-       struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
-       if (pix->height < 60 || pix->width < 80) {
-               pix->height = 60;
-               pix->width = 80;
-       } else if (pix->height < 120 || pix->width < 160) {
-               pix->height = 120;
-               pix->width = 160;
-       } else {
-               pix->height = 240;
-               pix->width = 320;
-       }
-       pix->pixelformat = V4L2_PIX_FMT_RGB24;
-       pix->field = V4L2_FIELD_NONE;
-       pix->bytesperline = 3 * pix->width;
-       pix->sizeimage = 3 * pix->width * pix->height;
-       /* Just a guess */
-       pix->colorspace = V4L2_COLORSPACE_SRGB;
-       return 0;
-}
-
-static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
-       struct qcam *qcam = video_drvdata(file);
-       struct v4l2_pix_format *pix = &fmt->fmt.pix;
-       int ret = qcam_try_fmt_vid_cap(file, fh, fmt);
-
-       if (ret)
-               return ret;
-       switch (pix->height) {
-       case 60:
-               qcam->mode = QC_DECIMATION_4;
-               break;
-       case 120:
-               qcam->mode = QC_DECIMATION_2;
-               break;
-       default:
-               qcam->mode = QC_DECIMATION_1;
-               break;
-       }
-
-       mutex_lock(&qcam->lock);
-       qcam->mode |= QC_MILLIONS;
-       qcam->height = pix->height;
-       qcam->width = pix->width;
-       parport_claim_or_block(qcam->pdev);
-       qc_setup(qcam);
-       parport_release(qcam->pdev);
-       mutex_unlock(&qcam->lock);
-       return 0;
-}
-
-static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
-{
-       static struct v4l2_fmtdesc formats[] = {
-               { 0, 0, 0,
-                 "RGB 8:8:8", V4L2_PIX_FMT_RGB24,
-                 { 0, 0, 0, 0 }
-               },
-       };
-       enum v4l2_buf_type type = fmt->type;
-
-       if (fmt->index > 0)
-               return -EINVAL;
-
-       *fmt = formats[fmt->index];
-       fmt->type = type;
-       return 0;
-}
-
-static ssize_t qcam_read(struct file *file, char __user *buf,
-                        size_t count, loff_t *ppos)
-{
-       struct qcam *qcam = video_drvdata(file);
-       int len;
-
-       mutex_lock(&qcam->lock);
-       parport_claim_or_block(qcam->pdev);
-       /* Probably should have a semaphore against multiple users */
-       len = qc_capture(qcam, buf, count);
-       parport_release(qcam->pdev);
-       mutex_unlock(&qcam->lock);
-       return len;
-}
-
-static int qcam_s_ctrl(struct v4l2_ctrl *ctrl)
-{
-       struct qcam *qcam =
-               container_of(ctrl->handler, struct qcam, hdl);
-       int ret = 0;
-
-       mutex_lock(&qcam->lock);
-       switch (ctrl->id) {
-       case V4L2_CID_BRIGHTNESS:
-               qcam->brightness = ctrl->val;
-               break;
-       case V4L2_CID_CONTRAST:
-               qcam->contrast = ctrl->val;
-               break;
-       case V4L2_CID_GAMMA:
-               qcam->whitebal = ctrl->val;
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-       if (ret == 0) {
-               parport_claim_or_block(qcam->pdev);
-               qc_setup(qcam);
-               parport_release(qcam->pdev);
-       }
-       mutex_unlock(&qcam->lock);
-       return ret;
-}
-
-static const struct v4l2_file_operations qcam_fops = {
-       .owner          = THIS_MODULE,
-       .open           = v4l2_fh_open,
-       .release        = v4l2_fh_release,
-       .poll           = v4l2_ctrl_poll,
-       .unlocked_ioctl = video_ioctl2,
-       .read           = qcam_read,
-};
-
-static const struct v4l2_ioctl_ops qcam_ioctl_ops = {
-       .vidioc_querycap                    = qcam_querycap,
-       .vidioc_g_input                     = qcam_g_input,
-       .vidioc_s_input                     = qcam_s_input,
-       .vidioc_enum_input                  = qcam_enum_input,
-       .vidioc_enum_fmt_vid_cap            = qcam_enum_fmt_vid_cap,
-       .vidioc_g_fmt_vid_cap               = qcam_g_fmt_vid_cap,
-       .vidioc_s_fmt_vid_cap               = qcam_s_fmt_vid_cap,
-       .vidioc_try_fmt_vid_cap             = qcam_try_fmt_vid_cap,
-       .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 qcam_ctrl_ops = {
-       .s_ctrl = qcam_s_ctrl,
-};
-
-/* Initialize the QuickCam driver control structure. */
-
-static struct qcam *qcam_init(struct parport *port)
-{
-       struct qcam *qcam;
-       struct v4l2_device *v4l2_dev;
-
-       qcam = kzalloc(sizeof(*qcam), GFP_KERNEL);
-       if (qcam == NULL)
-               return NULL;
-
-       v4l2_dev = &qcam->v4l2_dev;
-       strlcpy(v4l2_dev->name, "c-qcam", sizeof(v4l2_dev->name));
-
-       if (v4l2_device_register(NULL, v4l2_dev) < 0) {
-               v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
-               kfree(qcam);
-               return NULL;
-       }
-
-       v4l2_ctrl_handler_init(&qcam->hdl, 3);
-       v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
-                         V4L2_CID_BRIGHTNESS, 0, 255, 1, 240);
-       v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
-                         V4L2_CID_CONTRAST, 0, 255, 1, 192);
-       v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
-                         V4L2_CID_GAMMA, 0, 255, 1, 128);
-       if (qcam->hdl.error) {
-               v4l2_err(v4l2_dev, "couldn't register controls\n");
-               v4l2_ctrl_handler_free(&qcam->hdl);
-               kfree(qcam);
-               return NULL;
-       }
-
-       qcam->pport = port;
-       qcam->pdev = parport_register_device(port, "c-qcam", NULL, NULL,
-                                         NULL, 0, NULL);
-
-       qcam->bidirectional = (qcam->pport->modes & PARPORT_MODE_TRISTATE) ? 1 : 0;
-
-       if (qcam->pdev == NULL) {
-               v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name);
-               v4l2_ctrl_handler_free(&qcam->hdl);
-               kfree(qcam);
-               return NULL;
-       }
-
-       strlcpy(qcam->vdev.name, "Colour QuickCam", sizeof(qcam->vdev.name));
-       qcam->vdev.v4l2_dev = v4l2_dev;
-       qcam->vdev.fops = &qcam_fops;
-       qcam->vdev.ioctl_ops = &qcam_ioctl_ops;
-       qcam->vdev.release = video_device_release_empty;
-       qcam->vdev.ctrl_handler = &qcam->hdl;
-       video_set_drvdata(&qcam->vdev, qcam);
-
-       mutex_init(&qcam->lock);
-       qcam->width = qcam->ccd_width = 320;
-       qcam->height = qcam->ccd_height = 240;
-       qcam->mode = QC_MILLIONS | QC_DECIMATION_1;
-       qcam->contrast = 192;
-       qcam->brightness = 240;
-       qcam->whitebal = 128;
-       qcam->top = 1;
-       qcam->left = 14;
-       return qcam;
-}
-
-static int init_cqcam(struct parport *port)
-{
-       struct qcam *qcam;
-       struct v4l2_device *v4l2_dev;
-
-       if (parport[0] != -1) {
-               /* The user gave specific instructions */
-               int i, found = 0;
-
-               for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) {
-                       if (parport[0] == port->number)
-                               found = 1;
-               }
-               if (!found)
-                       return -ENODEV;
-       }
-
-       if (num_cams == MAX_CAMS)
-               return -ENOSPC;
-
-       qcam = qcam_init(port);
-       if (qcam == NULL)
-               return -ENODEV;
-
-       v4l2_dev = &qcam->v4l2_dev;
-
-       parport_claim_or_block(qcam->pdev);
-
-       qc_reset(qcam);
-
-       if (probe && qc_detect(qcam) == 0) {
-               parport_release(qcam->pdev);
-               parport_unregister_device(qcam->pdev);
-               kfree(qcam);
-               return -ENODEV;
-       }
-
-       qc_setup(qcam);
-
-       parport_release(qcam->pdev);
-
-       if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
-               v4l2_err(v4l2_dev, "Unable to register Colour QuickCam on %s\n",
-                      qcam->pport->name);
-               parport_unregister_device(qcam->pdev);
-               kfree(qcam);
-               return -ENODEV;
-       }
-
-       v4l2_info(v4l2_dev, "%s: Colour QuickCam found on %s\n",
-              video_device_node_name(&qcam->vdev), qcam->pport->name);
-
-       qcams[num_cams++] = qcam;
-
-       return 0;
-}
-
-static void close_cqcam(struct qcam *qcam)
-{
-       video_unregister_device(&qcam->vdev);
-       v4l2_ctrl_handler_free(&qcam->hdl);
-       parport_unregister_device(qcam->pdev);
-       kfree(qcam);
-}
-
-static void cq_attach(struct parport *port)
-{
-       init_cqcam(port);
-}
-
-static void cq_detach(struct parport *port)
-{
-       /* Write this some day. */
-}
-
-static struct parport_driver cqcam_driver = {
-       .name = "cqcam",
-       .attach = cq_attach,
-       .detach = cq_detach,
-};
-
-static int __init cqcam_init(void)
-{
-       printk(KERN_INFO BANNER "\n");
-
-       return parport_register_driver(&cqcam_driver);
-}
-
-static void __exit cqcam_cleanup(void)
-{
-       unsigned int i;
-
-       for (i = 0; i < num_cams; i++)
-               close_cqcam(qcams[i]);
-
-       parport_unregister_driver(&cqcam_driver);
-}
-
-MODULE_AUTHOR("Philip Blundell <philb@gnu.org>");
-MODULE_DESCRIPTION(BANNER);
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.4");
-
-module_init(cqcam_init);
-module_exit(cqcam_cleanup);
diff --git a/drivers/media/parport/pms.c b/drivers/media/parport/pms.c
deleted file mode 100644 (file)
index e6b4975..0000000
+++ /dev/null
@@ -1,1156 +0,0 @@
-/*
- *     Media Vision Pro Movie Studio
- *                     or
- *     "all you need is an I2C bus some RAM and a prayer"
- *
- *     This draws heavily on code
- *
- *     (c) Wolfgang Koehler,  wolf@first.gmd.de, Dec. 1994
- *     Kiefernring 15
- *     14478 Potsdam, Germany
- *
- *     Most of this code is directly derived from his userspace driver.
- *     His driver works so send any reports to alan@lxorguk.ukuu.org.uk
- *     unless the userspace driver also doesn't work for you...
- *
- *      Changes:
- *     25-11-2009      Hans Verkuil <hverkuil@xs4all.nl>
- *                     - converted to version 2 of the V4L API.
- *      08/07/2003      Daniele Bellucci <bellucda@tiscali.it>
- *                      - pms_capture: report back -EFAULT
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/mutex.h>
-#include <linux/uaccess.h>
-#include <linux/isa.h>
-#include <asm/io.h>
-
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-device.h>
-
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.5");
-
-#define MOTOROLA       1
-#define PHILIPS2       2               /* SAA7191 */
-#define PHILIPS1       3
-#define MVVMEMORYWIDTH 0x40            /* 512 bytes */
-
-struct i2c_info {
-       u8 slave;
-       u8 sub;
-       u8 data;
-       u8 hits;
-};
-
-struct pms {
-       struct v4l2_device v4l2_dev;
-       struct video_device vdev;
-       struct v4l2_ctrl_handler hdl;
-       int height;
-       int width;
-       int depth;
-       int input;
-       struct mutex lock;
-       int i2c_count;
-       struct i2c_info i2cinfo[64];
-
-       int decoder;
-       int standard;   /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
-       v4l2_std_id std;
-       int io;
-       int data;
-       void __iomem *mem;
-};
-
-/*
- *     I/O ports and Shared Memory
- */
-
-static int io_port = 0x250;
-module_param(io_port, int, 0);
-
-static int mem_base = 0xc8000;
-module_param(mem_base, int, 0);
-
-static int video_nr = -1;
-module_param(video_nr, int, 0);
-
-
-static inline void mvv_write(struct pms *dev, u8 index, u8 value)
-{
-       outw(index | (value << 8), dev->io);
-}
-
-static inline u8 mvv_read(struct pms *dev, u8 index)
-{
-       outb(index, dev->io);
-       return inb(dev->data);
-}
-
-static int pms_i2c_stat(struct pms *dev, u8 slave)
-{
-       int counter = 0;
-       int i;
-
-       outb(0x28, dev->io);
-
-       while ((inb(dev->data) & 0x01) == 0)
-               if (counter++ == 256)
-                       break;
-
-       while ((inb(dev->data) & 0x01) != 0)
-               if (counter++ == 256)
-                       break;
-
-       outb(slave, dev->io);
-
-       counter = 0;
-       while ((inb(dev->data) & 0x01) == 0)
-               if (counter++ == 256)
-                       break;
-
-       while ((inb(dev->data) & 0x01) != 0)
-               if (counter++ == 256)
-                       break;
-
-       for (i = 0; i < 12; i++) {
-               char st = inb(dev->data);
-
-               if ((st & 2) != 0)
-                       return -1;
-               if ((st & 1) == 0)
-                       break;
-       }
-       outb(0x29, dev->io);
-       return inb(dev->data);
-}
-
-static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data)
-{
-       int skip = 0;
-       int count;
-       int i;
-
-       for (i = 0; i < dev->i2c_count; i++) {
-               if ((dev->i2cinfo[i].slave == slave) &&
-                   (dev->i2cinfo[i].sub == sub)) {
-                       if (dev->i2cinfo[i].data == data)
-                               skip = 1;
-                       dev->i2cinfo[i].data = data;
-                       i = dev->i2c_count + 1;
-               }
-       }
-
-       if (i == dev->i2c_count && dev->i2c_count < 64) {
-               dev->i2cinfo[dev->i2c_count].slave = slave;
-               dev->i2cinfo[dev->i2c_count].sub = sub;
-               dev->i2cinfo[dev->i2c_count].data = data;
-               dev->i2c_count++;
-       }
-
-       if (skip)
-               return 0;
-
-       mvv_write(dev, 0x29, sub);
-       mvv_write(dev, 0x2A, data);
-       mvv_write(dev, 0x28, slave);
-
-       outb(0x28, dev->io);
-
-       count = 0;
-       while ((inb(dev->data) & 1) == 0)
-               if (count > 255)
-                       break;
-       while ((inb(dev->data) & 1) != 0)
-               if (count > 255)
-                       break;
-
-       count = inb(dev->data);
-
-       if (count & 2)
-               return -1;
-       return count;
-}
-
-static int pms_i2c_read(struct pms *dev, int slave, int sub)
-{
-       int i;
-
-       for (i = 0; i < dev->i2c_count; i++) {
-               if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub)
-                       return dev->i2cinfo[i].data;
-       }
-       return 0;
-}
-
-
-static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or)
-{
-       u8 tmp;
-
-       tmp = pms_i2c_read(dev, slave, sub);
-       tmp = (tmp & and) | or;
-       pms_i2c_write(dev, slave, sub, tmp);
-}
-
-/*
- *     Control functions
- */
-
-
-static void pms_videosource(struct pms *dev, short source)
-{
-       switch (dev->decoder) {
-       case MOTOROLA:
-               break;
-       case PHILIPS2:
-               pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0);
-               break;
-       case PHILIPS1:
-               break;
-       }
-       mvv_write(dev, 0x2E, 0x31);
-       /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30);
-          But could not make this work correctly. Only Composite input
-          worked for me. */
-}
-
-static void pms_hue(struct pms *dev, short hue)
-{
-       switch (dev->decoder) {
-       case MOTOROLA:
-               pms_i2c_write(dev, 0x8a, 0x00, hue);
-               break;
-       case PHILIPS2:
-               pms_i2c_write(dev, 0x8a, 0x07, hue);
-               break;
-       case PHILIPS1:
-               pms_i2c_write(dev, 0x42, 0x07, hue);
-               break;
-       }
-}
-
-static void pms_saturation(struct pms *dev, short sat)
-{
-       switch (dev->decoder) {
-       case MOTOROLA:
-               pms_i2c_write(dev, 0x8a, 0x00, sat);
-               break;
-       case PHILIPS1:
-               pms_i2c_write(dev, 0x42, 0x12, sat);
-               break;
-       }
-}
-
-
-static void pms_contrast(struct pms *dev, short contrast)
-{
-       switch (dev->decoder) {
-       case MOTOROLA:
-               pms_i2c_write(dev, 0x8a, 0x00, contrast);
-               break;
-       case PHILIPS1:
-               pms_i2c_write(dev, 0x42, 0x13, contrast);
-               break;
-       }
-}
-
-static void pms_brightness(struct pms *dev, short brightness)
-{
-       switch (dev->decoder) {
-       case MOTOROLA:
-               pms_i2c_write(dev, 0x8a, 0x00, brightness);
-               pms_i2c_write(dev, 0x8a, 0x00, brightness);
-               pms_i2c_write(dev, 0x8a, 0x00, brightness);
-               break;
-       case PHILIPS1:
-               pms_i2c_write(dev, 0x42, 0x19, brightness);
-               break;
-       }
-}
-
-
-static void pms_format(struct pms *dev, short format)
-{
-       int target;
-
-       dev->standard = format;
-
-       if (dev->decoder == PHILIPS1)
-               target = 0x42;
-       else if (dev->decoder == PHILIPS2)
-               target = 0x8a;
-       else
-               return;
-
-       switch (format) {
-       case 0: /* Auto */
-               pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
-               pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80);
-               break;
-       case 1: /* NTSC */
-               pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
-               pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40);
-               break;
-       case 2: /* PAL */
-               pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
-               pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
-               break;
-       case 3: /* SECAM */
-               pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01);
-               pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
-               break;
-       }
-}
-
-#ifdef FOR_FUTURE_EXPANSION
-
-/*
- *     These features of the PMS card are not currently exposes. They
- *     could become a private v4l ioctl for PMSCONFIG or somesuch if
- *     people need it. We also don't yet use the PMS interrupt.
- */
-
-static void pms_hstart(struct pms *dev, short start)
-{
-       switch (dev->decoder) {
-       case PHILIPS1:
-               pms_i2c_write(dev, 0x8a, 0x05, start);
-               pms_i2c_write(dev, 0x8a, 0x18, start);
-               break;
-       case PHILIPS2:
-               pms_i2c_write(dev, 0x42, 0x05, start);
-               pms_i2c_write(dev, 0x42, 0x18, start);
-               break;
-       }
-}
-
-/*
- *     Bandpass filters
- */
-
-static void pms_bandpass(struct pms *dev, short pass)
-{
-       if (dev->decoder == PHILIPS2)
-               pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4);
-       else if (dev->decoder == PHILIPS1)
-               pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4);
-}
-
-static void pms_antisnow(struct pms *dev, short snow)
-{
-       if (dev->decoder == PHILIPS2)
-               pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2);
-       else if (dev->decoder == PHILIPS1)
-               pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2);
-}
-
-static void pms_sharpness(struct pms *dev, short sharp)
-{
-       if (dev->decoder == PHILIPS2)
-               pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03);
-       else if (dev->decoder == PHILIPS1)
-               pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03);
-}
-
-static void pms_chromaagc(struct pms *dev, short agc)
-{
-       if (dev->decoder == PHILIPS2)
-               pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5);
-       else if (dev->decoder == PHILIPS1)
-               pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5);
-}
-
-static void pms_vertnoise(struct pms *dev, short noise)
-{
-       if (dev->decoder == PHILIPS2)
-               pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3);
-       else if (dev->decoder == PHILIPS1)
-               pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3);
-}
-
-static void pms_forcecolour(struct pms *dev, short colour)
-{
-       if (dev->decoder == PHILIPS2)
-               pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7);
-       else if (dev->decoder == PHILIPS1)
-               pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7);
-}
-
-static void pms_antigamma(struct pms *dev, short gamma)
-{
-       if (dev->decoder == PHILIPS2)
-               pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7);
-       else if (dev->decoder == PHILIPS1)
-               pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7);
-}
-
-static void pms_prefilter(struct pms *dev, short filter)
-{
-       if (dev->decoder == PHILIPS2)
-               pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6);
-       else if (dev->decoder == PHILIPS1)
-               pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6);
-}
-
-static void pms_hfilter(struct pms *dev, short filter)
-{
-       if (dev->decoder == PHILIPS2)
-               pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5);
-       else if (dev->decoder == PHILIPS1)
-               pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5);
-}
-
-static void pms_vfilter(struct pms *dev, short filter)
-{
-       if (dev->decoder == PHILIPS2)
-               pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5);
-       else if (dev->decoder == PHILIPS1)
-               pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5);
-}
-
-static void pms_killcolour(struct pms *dev, short colour)
-{
-       if (dev->decoder == PHILIPS2) {
-               pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3);
-               pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3);
-       } else if (dev->decoder == PHILIPS1) {
-               pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3);
-               pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3);
-       }
-}
-
-static void pms_chromagain(struct pms *dev, short chroma)
-{
-       if (dev->decoder == PHILIPS2)
-               pms_i2c_write(dev, 0x8a, 0x11, chroma);
-       else if (dev->decoder == PHILIPS1)
-               pms_i2c_write(dev, 0x42, 0x11, chroma);
-}
-
-
-static void pms_spacialcompl(struct pms *dev, short data)
-{
-       mvv_write(dev, 0x3b, data);
-}
-
-static void pms_spacialcomph(struct pms *dev, short data)
-{
-       mvv_write(dev, 0x3a, data);
-}
-
-static void pms_vstart(struct pms *dev, short start)
-{
-       mvv_write(dev, 0x16, start);
-       mvv_write(dev, 0x17, (start >> 8) & 0x01);
-}
-
-#endif
-
-static void pms_secamcross(struct pms *dev, short cross)
-{
-       if (dev->decoder == PHILIPS2)
-               pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5);
-       else if (dev->decoder == PHILIPS1)
-               pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5);
-}
-
-
-static void pms_swsense(struct pms *dev, short sense)
-{
-       if (dev->decoder == PHILIPS2) {
-               pms_i2c_write(dev, 0x8a, 0x0a, sense);
-               pms_i2c_write(dev, 0x8a, 0x0b, sense);
-       } else if (dev->decoder == PHILIPS1) {
-               pms_i2c_write(dev, 0x42, 0x0a, sense);
-               pms_i2c_write(dev, 0x42, 0x0b, sense);
-       }
-}
-
-
-static void pms_framerate(struct pms *dev, short frr)
-{
-       int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25;
-
-       if (frr == 0)
-               return;
-       fps = fps/frr;
-       mvv_write(dev, 0x14, 0x80 | fps);
-       mvv_write(dev, 0x15, 1);
-}
-
-static void pms_vert(struct pms *dev, u8 deciden, u8 decinum)
-{
-       mvv_write(dev, 0x1c, deciden);  /* Denominator */
-       mvv_write(dev, 0x1d, decinum);  /* Numerator */
-}
-
-/*
- *     Turn 16bit ratios into best small ratio the chipset can grok
- */
-
-static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden)
-{
-       /* Knock it down by / 5 once */
-       if (decinum % 5 == 0) {
-               deciden /= 5;
-               decinum /= 5;
-       }
-       /*
-        *      3's
-        */
-       while (decinum % 3 == 0 && deciden % 3 == 0) {
-               deciden /= 3;
-               decinum /= 3;
-       }
-       /*
-        *      2's
-        */
-       while (decinum % 2 == 0 && deciden % 2 == 0) {
-               decinum /= 2;
-               deciden /= 2;
-       }
-       /*
-        *      Fudgyify
-        */
-       while (deciden > 32) {
-               deciden /= 2;
-               decinum = (decinum + 1) / 2;
-       }
-       if (deciden == 32)
-               deciden--;
-       pms_vert(dev, deciden, decinum);
-}
-
-static void pms_horzdeci(struct pms *dev, short decinum, short deciden)
-{
-       if (decinum <= 512) {
-               if (decinum % 5 == 0) {
-                       decinum /= 5;
-                       deciden /= 5;
-               }
-       } else {
-               decinum = 512;
-               deciden = 640;  /* 768 would be ideal */
-       }
-
-       while (((decinum | deciden) & 1) == 0) {
-               decinum >>= 1;
-               deciden >>= 1;
-       }
-       while (deciden > 32) {
-               deciden >>= 1;
-               decinum = (decinum + 1) >> 1;
-       }
-       if (deciden == 32)
-               deciden--;
-
-       mvv_write(dev, 0x24, 0x80 | deciden);
-       mvv_write(dev, 0x25, decinum);
-}
-
-static void pms_resolution(struct pms *dev, short width, short height)
-{
-       int fg_height;
-
-       fg_height = height;
-       if (fg_height > 280)
-               fg_height = 280;
-
-       mvv_write(dev, 0x18, fg_height);
-       mvv_write(dev, 0x19, fg_height >> 8);
-
-       if (dev->std & V4L2_STD_525_60) {
-               mvv_write(dev, 0x1a, 0xfc);
-               mvv_write(dev, 0x1b, 0x00);
-               if (height > fg_height)
-                       pms_vertdeci(dev, 240, 240);
-               else
-                       pms_vertdeci(dev, fg_height, 240);
-       } else {
-               mvv_write(dev, 0x1a, 0x1a);
-               mvv_write(dev, 0x1b, 0x01);
-               if (fg_height > 256)
-                       pms_vertdeci(dev, 270, 270);
-               else
-                       pms_vertdeci(dev, fg_height, 270);
-       }
-       mvv_write(dev, 0x12, 0);
-       mvv_write(dev, 0x13, MVVMEMORYWIDTH);
-       mvv_write(dev, 0x42, 0x00);
-       mvv_write(dev, 0x43, 0x00);
-       mvv_write(dev, 0x44, MVVMEMORYWIDTH);
-
-       mvv_write(dev, 0x22, width + 8);
-       mvv_write(dev, 0x23, (width + 8) >> 8);
-
-       if (dev->std & V4L2_STD_525_60)
-               pms_horzdeci(dev, width, 640);
-       else
-               pms_horzdeci(dev, width + 8, 768);
-
-       mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe);
-       mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01);
-       mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd);
-       mvv_write(dev, 0x32, 0x00);
-       mvv_write(dev, 0x33, MVVMEMORYWIDTH);
-}
-
-
-/*
- *     Set Input
- */
-
-static void pms_vcrinput(struct pms *dev, short input)
-{
-       if (dev->decoder == PHILIPS2)
-               pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7);
-       else if (dev->decoder == PHILIPS1)
-               pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7);
-}
-
-
-static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count)
-{
-       int y;
-       int dw = 2 * dev->width;
-       char *tmp; /* using a temp buffer is faster than direct  */
-       int cnt = 0;
-       int len = 0;
-       unsigned char r8 = 0x5;  /* value for reg8  */
-
-       tmp = kmalloc(dw + 32, GFP_KERNEL);
-       if (!tmp)
-               return 0;
-
-       if (rgb555)
-               r8 |= 0x20; /* else use untranslated rgb = 565 */
-       mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */
-
-/*     printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
-
-       for (y = 0; y < dev->height; y++) {
-               writeb(0, dev->mem);  /* synchronisiert neue Zeile */
-
-               /*
-                *      This is in truth a fifo, be very careful as if you
-                *      forgot this odd things will occur 8)
-                */
-
-               memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word   */
-               cnt -= dev->height;
-               while (cnt <= 0) {
-                       /*
-                        *      Don't copy too far
-                        */
-                       int dt = dw;
-                       if (dt + len > count)
-                               dt = count - len;
-                       cnt += dev->height;
-                       if (copy_to_user(buf, tmp + 32, dt))
-                               return len ? len : -EFAULT;
-                       buf += dt;
-                       len += dt;
-               }
-       }
-       kfree(tmp);
-       return len;
-}
-
-
-/*
- *     Video4linux interfacing
- */
-
-static int pms_querycap(struct file *file, void  *priv,
-                                       struct v4l2_capability *vcap)
-{
-       struct pms *dev = video_drvdata(file);
-
-       strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver));
-       strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card));
-       snprintf(vcap->bus_info, sizeof(vcap->bus_info),
-                       "ISA:%s", dev->v4l2_dev.name);
-       vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
-       vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
-       return 0;
-}
-
-static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
-{
-       static const char *inputs[4] = {
-               "Composite",
-               "S-Video",
-               "Composite (VCR)",
-               "S-Video (VCR)"
-       };
-
-       if (vin->index > 3)
-               return -EINVAL;
-       strlcpy(vin->name, inputs[vin->index], sizeof(vin->name));
-       vin->type = V4L2_INPUT_TYPE_CAMERA;
-       vin->audioset = 0;
-       vin->tuner = 0;
-       vin->std = V4L2_STD_ALL;
-       vin->status = 0;
-       return 0;
-}
-
-static int pms_g_input(struct file *file, void *fh, unsigned int *inp)
-{
-       struct pms *dev = video_drvdata(file);
-
-       *inp = dev->input;
-       return 0;
-}
-
-static int pms_s_input(struct file *file, void *fh, unsigned int inp)
-{
-       struct pms *dev = video_drvdata(file);
-
-       if (inp > 3)
-               return -EINVAL;
-
-       dev->input = inp;
-       pms_videosource(dev, inp & 1);
-       pms_vcrinput(dev, inp >> 1);
-       return 0;
-}
-
-static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std)
-{
-       struct pms *dev = video_drvdata(file);
-
-       *std = dev->std;
-       return 0;
-}
-
-static int pms_s_std(struct file *file, void *fh, v4l2_std_id std)
-{
-       struct pms *dev = video_drvdata(file);
-       int ret = 0;
-
-       dev->std = std;
-       if (dev->std & V4L2_STD_NTSC) {
-               pms_framerate(dev, 30);
-               pms_secamcross(dev, 0);
-               pms_format(dev, 1);
-       } else if (dev->std & V4L2_STD_PAL) {
-               pms_framerate(dev, 25);
-               pms_secamcross(dev, 0);
-               pms_format(dev, 2);
-       } else if (dev->std & V4L2_STD_SECAM) {
-               pms_framerate(dev, 25);
-               pms_secamcross(dev, 1);
-               pms_format(dev, 2);
-       } else {
-               ret = -EINVAL;
-       }
-       /*
-       switch (v->mode) {
-       case VIDEO_MODE_AUTO:
-               pms_framerate(dev, 25);
-               pms_secamcross(dev, 0);
-               pms_format(dev, 0);
-               break;
-       }*/
-       return ret;
-}
-
-static int pms_s_ctrl(struct v4l2_ctrl *ctrl)
-{
-       struct pms *dev = container_of(ctrl->handler, struct pms, hdl);
-       int ret = 0;
-
-       switch (ctrl->id) {
-       case V4L2_CID_BRIGHTNESS:
-               pms_brightness(dev, ctrl->val);
-               break;
-       case V4L2_CID_CONTRAST:
-               pms_contrast(dev, ctrl->val);
-               break;
-       case V4L2_CID_SATURATION:
-               pms_saturation(dev, ctrl->val);
-               break;
-       case V4L2_CID_HUE:
-               pms_hue(dev, ctrl->val);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
-
-static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
-       struct pms *dev = video_drvdata(file);
-       struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
-       pix->width = dev->width;
-       pix->height = dev->height;
-       pix->pixelformat = dev->width == 15 ?
-                           V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565;
-       pix->field = V4L2_FIELD_NONE;
-       pix->bytesperline = 2 * dev->width;
-       pix->sizeimage = 2 * dev->width * dev->height;
-       /* Just a guess */
-       pix->colorspace = V4L2_COLORSPACE_SRGB;
-       return 0;
-}
-
-static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
-       struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
-       if (pix->height < 16 || pix->height > 480)
-               return -EINVAL;
-       if (pix->width < 16 || pix->width > 640)
-               return -EINVAL;
-       if (pix->pixelformat != V4L2_PIX_FMT_RGB555 &&
-           pix->pixelformat != V4L2_PIX_FMT_RGB565)
-               return -EINVAL;
-       pix->field = V4L2_FIELD_NONE;
-       pix->bytesperline = 2 * pix->width;
-       pix->sizeimage = 2 * pix->width * pix->height;
-       /* Just a guess */
-       pix->colorspace = V4L2_COLORSPACE_SRGB;
-       return 0;
-}
-
-static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
-       struct pms *dev = video_drvdata(file);
-       struct v4l2_pix_format *pix = &fmt->fmt.pix;
-       int ret = pms_try_fmt_vid_cap(file, fh, fmt);
-
-       if (ret)
-               return ret;
-       dev->width = pix->width;
-       dev->height = pix->height;
-       dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16;
-       pms_resolution(dev, dev->width, dev->height);
-       /* Ok we figured out what to use from our wide choice */
-       return 0;
-}
-
-static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
-{
-       static struct v4l2_fmtdesc formats[] = {
-               { 0, 0, 0,
-                 "RGB 5:5:5", V4L2_PIX_FMT_RGB555,
-                 { 0, 0, 0, 0 }
-               },
-               { 1, 0, 0,
-                 "RGB 5:6:5", V4L2_PIX_FMT_RGB565,
-                 { 0, 0, 0, 0 }
-               },
-       };
-       enum v4l2_buf_type type = fmt->type;
-
-       if (fmt->index > 1)
-               return -EINVAL;
-
-       *fmt = formats[fmt->index];
-       fmt->type = type;
-       return 0;
-}
-
-static ssize_t pms_read(struct file *file, char __user *buf,
-                   size_t count, loff_t *ppos)
-{
-       struct pms *dev = video_drvdata(file);
-       int len;
-
-       len = pms_capture(dev, buf, (dev->depth == 15), count);
-       return len;
-}
-
-static unsigned int pms_poll(struct file *file, struct poll_table_struct *wait)
-{
-       struct v4l2_fh *fh = file->private_data;
-       unsigned int res = POLLIN | POLLRDNORM;
-
-       if (v4l2_event_pending(fh))
-               res |= POLLPRI;
-       poll_wait(file, &fh->wait, wait);
-       return res;
-}
-
-static const struct v4l2_file_operations pms_fops = {
-       .owner          = THIS_MODULE,
-       .open           = v4l2_fh_open,
-       .release        = v4l2_fh_release,
-       .poll           = pms_poll,
-       .unlocked_ioctl = video_ioctl2,
-       .read           = pms_read,
-};
-
-static const struct v4l2_ioctl_ops pms_ioctl_ops = {
-       .vidioc_querycap            = pms_querycap,
-       .vidioc_g_input             = pms_g_input,
-       .vidioc_s_input             = pms_s_input,
-       .vidioc_enum_input          = pms_enum_input,
-       .vidioc_g_std               = pms_g_std,
-       .vidioc_s_std               = pms_s_std,
-       .vidioc_enum_fmt_vid_cap    = pms_enum_fmt_vid_cap,
-       .vidioc_g_fmt_vid_cap       = pms_g_fmt_vid_cap,
-       .vidioc_s_fmt_vid_cap       = pms_s_fmt_vid_cap,
-       .vidioc_try_fmt_vid_cap     = pms_try_fmt_vid_cap,
-       .vidioc_subscribe_event     = v4l2_ctrl_subscribe_event,
-       .vidioc_unsubscribe_event   = v4l2_event_unsubscribe,
-};
-
-/*
- *     Probe for and initialise the Mediavision PMS
- */
-
-static int init_mediavision(struct pms *dev)
-{
-       int idec, decst;
-       int i;
-       static const unsigned char i2c_defs[] = {
-               0x4c, 0x30, 0x00, 0xe8,
-               0xb6, 0xe2, 0x00, 0x00,
-               0xff, 0xff, 0x00, 0x00,
-               0x00, 0x00, 0x78, 0x98,
-               0x00, 0x00, 0x00, 0x00,
-               0x34, 0x0a, 0xf4, 0xce,
-               0xe4
-       };
-
-       dev->mem = ioremap(mem_base, 0x800);
-       if (!dev->mem)
-               return -ENOMEM;
-
-       if (!request_region(0x9a01, 1, "Mediavision PMS config")) {
-               printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n");
-               iounmap(dev->mem);
-               return -EBUSY;
-       }
-       if (!request_region(dev->io, 3, "Mediavision PMS")) {
-               printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io);
-               release_region(0x9a01, 1);
-               iounmap(dev->mem);
-               return -EBUSY;
-       }
-       outb(0xb8, 0x9a01);             /* Unlock */
-       outb(dev->io >> 4, 0x9a01);     /* Set IO port */
-
-
-       decst = pms_i2c_stat(dev, 0x43);
-
-       if (decst != -1)
-               idec = 2;
-       else if (pms_i2c_stat(dev, 0xb9) != -1)
-               idec = 3;
-       else if (pms_i2c_stat(dev, 0x8b) != -1)
-               idec = 1;
-       else
-               idec = 0;
-
-       printk(KERN_INFO "PMS type is %d\n", idec);
-       if (idec == 0) {
-               release_region(dev->io, 3);
-               release_region(0x9a01, 1);
-               iounmap(dev->mem);
-               return -ENODEV;
-       }
-
-       /*
-        *      Ok we have a PMS of some sort
-        */
-
-       mvv_write(dev, 0x04, mem_base >> 12);   /* Set the memory area */
-
-       /* Ok now load the defaults */
-
-       for (i = 0; i < 0x19; i++) {
-               if (i2c_defs[i] == 0xff)
-                       pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00);
-               else
-                       pms_i2c_write(dev, 0x8a, i, i2c_defs[i]);
-       }
-
-       pms_i2c_write(dev, 0xb8, 0x00, 0x12);
-       pms_i2c_write(dev, 0xb8, 0x04, 0x00);
-       pms_i2c_write(dev, 0xb8, 0x07, 0x00);
-       pms_i2c_write(dev, 0xb8, 0x08, 0x00);
-       pms_i2c_write(dev, 0xb8, 0x09, 0xff);
-       pms_i2c_write(dev, 0xb8, 0x0a, 0x00);
-       pms_i2c_write(dev, 0xb8, 0x0b, 0x10);
-       pms_i2c_write(dev, 0xb8, 0x10, 0x03);
-
-       mvv_write(dev, 0x01, 0x00);
-       mvv_write(dev, 0x05, 0xa0);
-       mvv_write(dev, 0x08, 0x25);
-       mvv_write(dev, 0x09, 0x00);
-       mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH);
-
-       mvv_write(dev, 0x10, 0x02);
-       mvv_write(dev, 0x1e, 0x0c);
-       mvv_write(dev, 0x1f, 0x03);
-       mvv_write(dev, 0x26, 0x06);
-
-       mvv_write(dev, 0x2b, 0x00);
-       mvv_write(dev, 0x2c, 0x20);
-       mvv_write(dev, 0x2d, 0x00);
-       mvv_write(dev, 0x2f, 0x70);
-       mvv_write(dev, 0x32, 0x00);
-       mvv_write(dev, 0x33, MVVMEMORYWIDTH);
-       mvv_write(dev, 0x34, 0x00);
-       mvv_write(dev, 0x35, 0x00);
-       mvv_write(dev, 0x3a, 0x80);
-       mvv_write(dev, 0x3b, 0x10);
-       mvv_write(dev, 0x20, 0x00);
-       mvv_write(dev, 0x21, 0x00);
-       mvv_write(dev, 0x30, 0x22);
-       return 0;
-}
-
-/*
- *     Initialization and module stuff
- */
-
-#ifndef MODULE
-static int enable;
-module_param(enable, int, 0);
-#endif
-
-static const struct v4l2_ctrl_ops pms_ctrl_ops = {
-       .s_ctrl = pms_s_ctrl,
-};
-
-static int pms_probe(struct device *pdev, unsigned int card)
-{
-       struct pms *dev;
-       struct v4l2_device *v4l2_dev;
-       struct v4l2_ctrl_handler *hdl;
-       int res;
-
-#ifndef MODULE
-       if (!enable) {
-               pr_err("PMS: not enabled, use pms.enable=1 to probe\n");
-               return -ENODEV;
-       }
-#endif
-
-       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-       if (dev == NULL)
-               return -ENOMEM;
-
-       dev->decoder = PHILIPS2;
-       dev->io = io_port;
-       dev->data = io_port + 1;
-       v4l2_dev = &dev->v4l2_dev;
-       hdl = &dev->hdl;
-
-       res = v4l2_device_register(pdev, v4l2_dev);
-       if (res < 0) {
-               v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
-               goto free_dev;
-       }
-       v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.05\n");
-
-       res = init_mediavision(dev);
-       if (res) {
-               v4l2_err(v4l2_dev, "Board not found.\n");
-               goto free_io;
-       }
-
-       v4l2_ctrl_handler_init(hdl, 4);
-       v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
-                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 139);
-       v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
-                       V4L2_CID_CONTRAST, 0, 255, 1, 70);
-       v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
-                       V4L2_CID_SATURATION, 0, 255, 1, 64);
-       v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
-                       V4L2_CID_HUE, 0, 255, 1, 0);
-       if (hdl->error) {
-               res = hdl->error;
-               goto free_hdl;
-       }
-
-       mutex_init(&dev->lock);
-       strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
-       dev->vdev.v4l2_dev = v4l2_dev;
-       dev->vdev.ctrl_handler = hdl;
-       dev->vdev.fops = &pms_fops;
-       dev->vdev.ioctl_ops = &pms_ioctl_ops;
-       dev->vdev.release = video_device_release_empty;
-       dev->vdev.lock = &dev->lock;
-       dev->vdev.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
-       video_set_drvdata(&dev->vdev, dev);
-       dev->std = V4L2_STD_NTSC_M;
-       dev->height = 240;
-       dev->width = 320;
-       dev->depth = 16;
-       pms_swsense(dev, 75);
-       pms_resolution(dev, 320, 240);
-       pms_videosource(dev, 0);
-       pms_vcrinput(dev, 0);
-       v4l2_ctrl_handler_setup(hdl);
-       res = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr);
-       if (res >= 0)
-               return 0;
-
-free_hdl:
-       v4l2_ctrl_handler_free(hdl);
-       v4l2_device_unregister(&dev->v4l2_dev);
-free_io:
-       release_region(dev->io, 3);
-       release_region(0x9a01, 1);
-       iounmap(dev->mem);
-free_dev:
-       kfree(dev);
-       return res;
-}
-
-static int pms_remove(struct device *pdev, unsigned int card)
-{
-       struct pms *dev = dev_get_drvdata(pdev);
-
-       video_unregister_device(&dev->vdev);
-       v4l2_ctrl_handler_free(&dev->hdl);
-       release_region(dev->io, 3);
-       release_region(0x9a01, 1);
-       iounmap(dev->mem);
-       return 0;
-}
-
-static struct isa_driver pms_driver = {
-       .probe          = pms_probe,
-       .remove         = pms_remove,
-       .driver         = {
-               .name   = "pms",
-       },
-};
-
-static int __init pms_init(void)
-{
-       return isa_register_driver(&pms_driver, 1);
-}
-
-static void __exit pms_exit(void)
-{
-       isa_unregister_driver(&pms_driver);
-}
-
-module_init(pms_init);
-module_exit(pms_exit);
diff --git a/drivers/media/parport/w9966.c b/drivers/media/parport/w9966.c
deleted file mode 100644 (file)
index f7502f3..0000000
+++ /dev/null
@@ -1,980 +0,0 @@
-/*
-       Winbond w9966cf Webcam parport driver.
-
-       Version 0.33
-
-       Copyright (C) 2001 Jakob Kemi <jakob.kemi@post.utfors.se>
-
-       This program is free software; you can redistribute it and/or modify
-       it under the terms of the GNU General Public License as published by
-       the Free Software Foundation; either version 2 of the License, or
-       (at your option) any later version.
-
-       This program is distributed in the hope that it will be useful,
-       but WITHOUT ANY WARRANTY; without even the implied warranty of
-       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-       GNU General Public License for more details.
-
-       You should have received a copy of the GNU General Public License
-       along with this program; if not, write to the Free Software
-       Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-/*
-       Supported devices:
-       *Lifeview FlyCam Supra (using the Philips saa7111a chip)
-
-       Does any other model using the w9966 interface chip exist ?
-
-       Todo:
-
-       *Add a working EPP mode, since DMA ECP read isn't implemented
-       in the parport drivers. (That's why it's so sloow)
-
-       *Add support for other ccd-control chips than the saa7111
-       please send me feedback on what kind of chips you have.
-
-       *Add proper probing. I don't know what's wrong with the IEEE1284
-       parport drivers but (IEEE1284_MODE_NIBBLE|IEEE1284_DEVICE_ID)
-       and nibble read seems to be broken for some peripherals.
-
-       *Add probing for onboard SRAM, port directions etc. (if possible)
-
-       *Add support for the hardware compressed modes (maybe using v4l2)
-
-       *Fix better support for the capture window (no skewed images, v4l
-       interface to capt. window)
-
-       *Probably some bugs that I don't know of
-
-       Please support me by sending feedback!
-
-       Changes:
-
-       Alan Cox:       Removed RGB mode for kernel merge, added THIS_MODULE
-                       and owner support for newer module locks
-*/
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/videodev2.h>
-#include <linux/slab.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-event.h>
-#include <linux/parport.h>
-
-/*#define DEBUG*/                              /* Undef me for production */
-
-#ifdef DEBUG
-#define DPRINTF(x, a...) printk(KERN_DEBUG "W9966: %s(): "x, __func__ , ##a)
-#else
-#define DPRINTF(x...)
-#endif
-
-/*
- *     Defines, simple typedefs etc.
- */
-
-#define W9966_DRIVERNAME       "W9966CF Webcam"
-#define W9966_MAXCAMS          4       /* Maximum number of cameras */
-#define W9966_RBUFFER          2048    /* Read buffer (must be an even number) */
-#define W9966_SRAMSIZE         131072  /* 128kb */
-#define W9966_SRAMID           0x02    /* check w9966cf.pdf */
-
-/* Empirically determined window limits */
-#define W9966_WND_MIN_X                16
-#define W9966_WND_MIN_Y                14
-#define W9966_WND_MAX_X                705
-#define W9966_WND_MAX_Y                253
-#define W9966_WND_MAX_W                (W9966_WND_MAX_X - W9966_WND_MIN_X)
-#define W9966_WND_MAX_H                (W9966_WND_MAX_Y - W9966_WND_MIN_Y)
-
-/* Keep track of our current state */
-#define W9966_STATE_PDEV       0x01
-#define W9966_STATE_CLAIMED    0x02
-#define W9966_STATE_VDEV       0x04
-
-#define W9966_I2C_W_ID         0x48
-#define W9966_I2C_R_ID         0x49
-#define W9966_I2C_R_DATA       0x08
-#define W9966_I2C_R_CLOCK      0x04
-#define W9966_I2C_W_DATA       0x02
-#define W9966_I2C_W_CLOCK      0x01
-
-struct w9966 {
-       struct v4l2_device v4l2_dev;
-       struct v4l2_ctrl_handler hdl;
-       unsigned char dev_state;
-       unsigned char i2c_state;
-       unsigned short ppmode;
-       struct parport *pport;
-       struct pardevice *pdev;
-       struct video_device vdev;
-       unsigned short width;
-       unsigned short height;
-       unsigned char brightness;
-       signed char contrast;
-       signed char color;
-       signed char hue;
-       struct mutex lock;
-};
-
-/*
- *     Module specific properties
- */
-
-MODULE_AUTHOR("Jakob Kemi <jakob.kemi@post.utfors.se>");
-MODULE_DESCRIPTION("Winbond w9966cf WebCam driver (0.32)");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.33.1");
-
-#ifdef MODULE
-static char *pardev[] = {[0 ... W9966_MAXCAMS] = ""};
-#else
-static char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"};
-#endif
-module_param_array(pardev, charp, NULL, 0);
-MODULE_PARM_DESC(pardev, "pardev: where to search for\n"
-               "\teach camera. 'aggressive' means brute-force search.\n"
-               "\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n"
-               "\tcam 1 to parport3 and search every parport for cam 2 etc...");
-
-static int parmode;
-module_param(parmode, int, 0);
-MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp");
-
-static int video_nr = -1;
-module_param(video_nr, int, 0);
-
-static struct w9966 w9966_cams[W9966_MAXCAMS];
-
-/*
- *     Private function defines
- */
-
-
-/* Set camera phase flags, so we know what to uninit when terminating */
-static inline void w9966_set_state(struct w9966 *cam, int mask, int val)
-{
-       cam->dev_state = (cam->dev_state & ~mask) ^ val;
-}
-
-/* Get camera phase flags */
-static inline int w9966_get_state(struct w9966 *cam, int mask, int val)
-{
-       return ((cam->dev_state & mask) == val);
-}
-
-/* Claim parport for ourself */
-static void w9966_pdev_claim(struct w9966 *cam)
-{
-       if (w9966_get_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED))
-               return;
-       parport_claim_or_block(cam->pdev);
-       w9966_set_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED);
-}
-
-/* Release parport for others to use */
-static void w9966_pdev_release(struct w9966 *cam)
-{
-       if (w9966_get_state(cam, W9966_STATE_CLAIMED, 0))
-               return;
-       parport_release(cam->pdev);
-       w9966_set_state(cam, W9966_STATE_CLAIMED, 0);
-}
-
-/* Read register from W9966 interface-chip
-   Expects a claimed pdev
-   -1 on error, else register data (byte) */
-static int w9966_read_reg(struct w9966 *cam, int reg)
-{
-       /* ECP, read, regtransfer, REG, REG, REG, REG, REG */
-       const unsigned char addr = 0x80 | (reg & 0x1f);
-       unsigned char val;
-
-       if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
-               return -1;
-       if (parport_write(cam->pport, &addr, 1) != 1)
-               return -1;
-       if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
-               return -1;
-       if (parport_read(cam->pport, &val, 1) != 1)
-               return -1;
-
-       return val;
-}
-
-/* Write register to W9966 interface-chip
-   Expects a claimed pdev
-   -1 on error */
-static int w9966_write_reg(struct w9966 *cam, int reg, int data)
-{
-       /* ECP, write, regtransfer, REG, REG, REG, REG, REG */
-       const unsigned char addr = 0xc0 | (reg & 0x1f);
-       const unsigned char val = data;
-
-       if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
-               return -1;
-       if (parport_write(cam->pport, &addr, 1) != 1)
-               return -1;
-       if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
-               return -1;
-       if (parport_write(cam->pport, &val, 1) != 1)
-               return -1;
-
-       return 0;
-}
-
-/*
- *     Ugly and primitive i2c protocol functions
- */
-
-/* Sets the data line on the i2c bus.
-   Expects a claimed pdev. */
-static void w9966_i2c_setsda(struct w9966 *cam, int state)
-{
-       if (state)
-               cam->i2c_state |= W9966_I2C_W_DATA;
-       else
-               cam->i2c_state &= ~W9966_I2C_W_DATA;
-
-       w9966_write_reg(cam, 0x18, cam->i2c_state);
-       udelay(5);
-}
-
-/* Get peripheral clock line
-   Expects a claimed pdev. */
-static int w9966_i2c_getscl(struct w9966 *cam)
-{
-       const unsigned char state = w9966_read_reg(cam, 0x18);
-       return ((state & W9966_I2C_R_CLOCK) > 0);
-}
-
-/* Sets the clock line on the i2c bus.
-   Expects a claimed pdev. -1 on error */
-static int w9966_i2c_setscl(struct w9966 *cam, int state)
-{
-       unsigned long timeout;
-
-       if (state)
-               cam->i2c_state |= W9966_I2C_W_CLOCK;
-       else
-               cam->i2c_state &= ~W9966_I2C_W_CLOCK;
-
-       w9966_write_reg(cam, 0x18, cam->i2c_state);
-       udelay(5);
-
-       /* we go to high, we also expect the peripheral to ack. */
-       if (state) {
-               timeout = jiffies + 100;
-               while (!w9966_i2c_getscl(cam)) {
-                       if (time_after(jiffies, timeout))
-                               return -1;
-               }
-       }
-       return 0;
-}
-
-#if 0
-/* Get peripheral data line
-   Expects a claimed pdev. */
-static int w9966_i2c_getsda(struct w9966 *cam)
-{
-       const unsigned char state = w9966_read_reg(cam, 0x18);
-       return ((state & W9966_I2C_R_DATA) > 0);
-}
-#endif
-
-/* Write a byte with ack to the i2c bus.
-   Expects a claimed pdev. -1 on error */
-static int w9966_i2c_wbyte(struct w9966 *cam, int data)
-{
-       int i;
-
-       for (i = 7; i >= 0; i--) {
-               w9966_i2c_setsda(cam, (data >> i) & 0x01);
-
-               if (w9966_i2c_setscl(cam, 1) == -1)
-                       return -1;
-               w9966_i2c_setscl(cam, 0);
-       }
-
-       w9966_i2c_setsda(cam, 1);
-
-       if (w9966_i2c_setscl(cam, 1) == -1)
-               return -1;
-       w9966_i2c_setscl(cam, 0);
-
-       return 0;
-}
-
-/* Read a data byte with ack from the i2c-bus
-   Expects a claimed pdev. -1 on error */
-#if 0
-static int w9966_i2c_rbyte(struct w9966 *cam)
-{
-       unsigned char data = 0x00;
-       int i;
-
-       w9966_i2c_setsda(cam, 1);
-
-       for (i = 0; i < 8; i++) {
-               if (w9966_i2c_setscl(cam, 1) == -1)
-                       return -1;
-               data = data << 1;
-               if (w9966_i2c_getsda(cam))
-                       data |= 0x01;
-
-               w9966_i2c_setscl(cam, 0);
-       }
-       return data;
-}
-#endif
-
-/* Read a register from the i2c device.
-   Expects claimed pdev. -1 on error */
-#if 0
-static int w9966_read_reg_i2c(struct w9966 *cam, int reg)
-{
-       int data;
-
-       w9966_i2c_setsda(cam, 0);
-       w9966_i2c_setscl(cam, 0);
-
-       if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
-           w9966_i2c_wbyte(cam, reg) == -1)
-               return -1;
-
-       w9966_i2c_setsda(cam, 1);
-       if (w9966_i2c_setscl(cam, 1) == -1)
-               return -1;
-       w9966_i2c_setsda(cam, 0);
-       w9966_i2c_setscl(cam, 0);
-
-       if (w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1)
-               return -1;
-       data = w9966_i2c_rbyte(cam);
-       if (data == -1)
-               return -1;
-
-       w9966_i2c_setsda(cam, 0);
-
-       if (w9966_i2c_setscl(cam, 1) == -1)
-               return -1;
-       w9966_i2c_setsda(cam, 1);
-
-       return data;
-}
-#endif
-
-/* Write a register to the i2c device.
-   Expects claimed pdev. -1 on error */
-static int w9966_write_reg_i2c(struct w9966 *cam, int reg, int data)
-{
-       w9966_i2c_setsda(cam, 0);
-       w9966_i2c_setscl(cam, 0);
-
-       if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
-                       w9966_i2c_wbyte(cam, reg) == -1 ||
-                       w9966_i2c_wbyte(cam, data) == -1)
-               return -1;
-
-       w9966_i2c_setsda(cam, 0);
-       if (w9966_i2c_setscl(cam, 1) == -1)
-               return -1;
-
-       w9966_i2c_setsda(cam, 1);
-
-       return 0;
-}
-
-/* Find a good length for capture window (used both for W and H)
-   A bit ugly but pretty functional. The capture length
-   have to match the downscale */
-static int w9966_findlen(int near, int size, int maxlen)
-{
-       int bestlen = size;
-       int besterr = abs(near - bestlen);
-       int len;
-
-       for (len = size + 1; len < maxlen; len++) {
-               int err;
-               if (((64 * size) % len) != 0)
-                       continue;
-
-               err = abs(near - len);
-
-               /* Only continue as long as we keep getting better values */
-               if (err > besterr)
-                       break;
-
-               besterr = err;
-               bestlen = len;
-       }
-
-       return bestlen;
-}
-
-/* Modify capture window (if necessary)
-   and calculate downscaling
-   Return -1 on error */
-static int w9966_calcscale(int size, int min, int max, int *beg, int *end, unsigned char *factor)
-{
-       int maxlen = max - min;
-       int len = *end - *beg + 1;
-       int newlen = w9966_findlen(len, size, maxlen);
-       int err = newlen - len;
-
-       /* Check for bad format */
-       if (newlen > maxlen || newlen < size)
-               return -1;
-
-       /* Set factor (6 bit fixed) */
-       *factor = (64 * size) / newlen;
-       if (*factor == 64)
-               *factor = 0x00; /* downscale is disabled */
-       else
-               *factor |= 0x80; /* set downscale-enable bit */
-
-       /* Modify old beginning and end */
-       *beg -= err / 2;
-       *end += err - (err / 2);
-
-       /* Move window if outside borders */
-       if (*beg < min) {
-               *end += min - *beg;
-               *beg += min - *beg;
-       }
-       if (*end > max) {
-               *beg -= *end - max;
-               *end -= *end - max;
-       }
-
-       return 0;
-}
-
-/* Setup the cameras capture window etc.
-   Expects a claimed pdev
-   return -1 on error */
-static int w9966_setup(struct w9966 *cam, int x1, int y1, int x2, int y2, int w, int h)
-{
-       unsigned int i;
-       unsigned int enh_s, enh_e;
-       unsigned char scale_x, scale_y;
-       unsigned char regs[0x1c];
-       unsigned char saa7111_regs[] = {
-               0x21, 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00,
-               0x88, 0x10, 0x80, 0x40, 0x40, 0x00, 0x01, 0x00,
-               0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x00, 0x00, 0x00, 0x71, 0xe7, 0x00, 0x00, 0xc0
-       };
-
-
-       if (w * h * 2 > W9966_SRAMSIZE) {
-               DPRINTF("capture window exceeds SRAM size!.\n");
-               w = 200; h = 160;       /* Pick default values */
-       }
-
-       w &= ~0x1;
-       if (w < 2)
-               w = 2;
-       if (h < 1)
-               h = 1;
-       if (w > W9966_WND_MAX_W)
-               w = W9966_WND_MAX_W;
-       if (h > W9966_WND_MAX_H)
-               h = W9966_WND_MAX_H;
-
-       cam->width = w;
-       cam->height = h;
-
-       enh_s = 0;
-       enh_e = w * h * 2;
-
-       /* Modify capture window if necessary and calculate downscaling */
-       if (w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 ||
-                       w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0)
-               return -1;
-
-       DPRINTF("%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n",
-                       w, h, x1, x2, y1, y2, scale_x & ~0x80, scale_y & ~0x80);
-
-       /* Setup registers */
-       regs[0x00] = 0x00;                      /* Set normal operation */
-       regs[0x01] = 0x18;                      /* Capture mode */
-       regs[0x02] = scale_y;                   /* V-scaling */
-       regs[0x03] = scale_x;                   /* H-scaling */
-
-       /* Capture window */
-       regs[0x04] = (x1 & 0x0ff);              /* X-start (8 low bits) */
-       regs[0x05] = (x1 & 0x300)>>8;           /* X-start (2 high bits) */
-       regs[0x06] = (y1 & 0x0ff);              /* Y-start (8 low bits) */
-       regs[0x07] = (y1 & 0x300)>>8;           /* Y-start (2 high bits) */
-       regs[0x08] = (x2 & 0x0ff);              /* X-end (8 low bits) */
-       regs[0x09] = (x2 & 0x300)>>8;           /* X-end (2 high bits) */
-       regs[0x0a] = (y2 & 0x0ff);              /* Y-end (8 low bits) */
-
-       regs[0x0c] = W9966_SRAMID;              /* SRAM-banks (1x 128kb) */
-
-       /* Enhancement layer */
-       regs[0x0d] = (enh_s & 0x000ff);         /* Enh. start (0-7) */
-       regs[0x0e] = (enh_s & 0x0ff00) >> 8;    /* Enh. start (8-15) */
-       regs[0x0f] = (enh_s & 0x70000) >> 16;   /* Enh. start (16-17/18??) */
-       regs[0x10] = (enh_e & 0x000ff);         /* Enh. end (0-7) */
-       regs[0x11] = (enh_e & 0x0ff00) >> 8;    /* Enh. end (8-15) */
-       regs[0x12] = (enh_e & 0x70000) >> 16;   /* Enh. end (16-17/18??) */
-
-       /* Misc */
-       regs[0x13] = 0x40;                      /* VEE control (raw 4:2:2) */
-       regs[0x17] = 0x00;                      /* ??? */
-       regs[0x18] = cam->i2c_state = 0x00;     /* Serial bus */
-       regs[0x19] = 0xff;                      /* I/O port direction control */
-       regs[0x1a] = 0xff;                      /* I/O port data register */
-       regs[0x1b] = 0x10;                      /* ??? */
-
-       /* SAA7111 chip settings */
-       saa7111_regs[0x0a] = cam->brightness;
-       saa7111_regs[0x0b] = cam->contrast;
-       saa7111_regs[0x0c] = cam->color;
-       saa7111_regs[0x0d] = cam->hue;
-
-       /* Reset (ECP-fifo & serial-bus) */
-       if (w9966_write_reg(cam, 0x00, 0x03) == -1)
-               return -1;
-
-       /* Write regs to w9966cf chip */
-       for (i = 0; i < 0x1c; i++)
-               if (w9966_write_reg(cam, i, regs[i]) == -1)
-                       return -1;
-
-       /* Write regs to saa7111 chip */
-       for (i = 0; i < 0x20; i++)
-               if (w9966_write_reg_i2c(cam, i, saa7111_regs[i]) == -1)
-                       return -1;
-
-       return 0;
-}
-
-/*
- *     Video4linux interfacing
- */
-
-static int cam_querycap(struct file *file, void  *priv,
-                                       struct v4l2_capability *vcap)
-{
-       struct w9966 *cam = video_drvdata(file);
-
-       strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver));
-       strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card));
-       strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
-       vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
-       vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
-       return 0;
-}
-
-static int cam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
-{
-       if (vin->index > 0)
-               return -EINVAL;
-       strlcpy(vin->name, "Camera", sizeof(vin->name));
-       vin->type = V4L2_INPUT_TYPE_CAMERA;
-       vin->audioset = 0;
-       vin->tuner = 0;
-       vin->std = 0;
-       vin->status = 0;
-       return 0;
-}
-
-static int cam_g_input(struct file *file, void *fh, unsigned int *inp)
-{
-       *inp = 0;
-       return 0;
-}
-
-static int cam_s_input(struct file *file, void *fh, unsigned int inp)
-{
-       return (inp > 0) ? -EINVAL : 0;
-}
-
-static int cam_s_ctrl(struct v4l2_ctrl *ctrl)
-{
-       struct w9966 *cam =
-               container_of(ctrl->handler, struct w9966, hdl);
-       int ret = 0;
-
-       mutex_lock(&cam->lock);
-       switch (ctrl->id) {
-       case V4L2_CID_BRIGHTNESS:
-               cam->brightness = ctrl->val;
-               break;
-       case V4L2_CID_CONTRAST:
-               cam->contrast = ctrl->val;
-               break;
-       case V4L2_CID_SATURATION:
-               cam->color = ctrl->val;
-               break;
-       case V4L2_CID_HUE:
-               cam->hue = ctrl->val;
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       if (ret == 0) {
-               w9966_pdev_claim(cam);
-
-               if (w9966_write_reg_i2c(cam, 0x0a, cam->brightness) == -1 ||
-                   w9966_write_reg_i2c(cam, 0x0b, cam->contrast) == -1 ||
-                   w9966_write_reg_i2c(cam, 0x0c, cam->color) == -1 ||
-                   w9966_write_reg_i2c(cam, 0x0d, cam->hue) == -1) {
-                       ret = -EIO;
-               }
-
-               w9966_pdev_release(cam);
-       }
-       mutex_unlock(&cam->lock);
-       return ret;
-}
-
-static int cam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
-       struct w9966 *cam = video_drvdata(file);
-       struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
-       pix->width = cam->width;
-       pix->height = cam->height;
-       pix->pixelformat = V4L2_PIX_FMT_YUYV;
-       pix->field = V4L2_FIELD_NONE;
-       pix->bytesperline = 2 * cam->width;
-       pix->sizeimage = 2 * cam->width * cam->height;
-       /* Just a guess */
-       pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
-       return 0;
-}
-
-static int cam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
-       struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
-       if (pix->width < 2)
-               pix->width = 2;
-       if (pix->height < 1)
-               pix->height = 1;
-       if (pix->width > W9966_WND_MAX_W)
-               pix->width = W9966_WND_MAX_W;
-       if (pix->height > W9966_WND_MAX_H)
-               pix->height = W9966_WND_MAX_H;
-       pix->pixelformat = V4L2_PIX_FMT_YUYV;
-       pix->field = V4L2_FIELD_NONE;
-       pix->bytesperline = 2 * pix->width;
-       pix->sizeimage = 2 * pix->width * pix->height;
-       /* Just a guess */
-       pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
-       return 0;
-}
-
-static int cam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
-       struct w9966 *cam = video_drvdata(file);
-       struct v4l2_pix_format *pix = &fmt->fmt.pix;
-       int ret = cam_try_fmt_vid_cap(file, fh, fmt);
-
-       if (ret)
-               return ret;
-
-       mutex_lock(&cam->lock);
-       /* Update camera regs */
-       w9966_pdev_claim(cam);
-       ret = w9966_setup(cam, 0, 0, 1023, 1023, pix->width, pix->height);
-       w9966_pdev_release(cam);
-       mutex_unlock(&cam->lock);
-       return ret;
-}
-
-static int cam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
-{
-       static struct v4l2_fmtdesc formats[] = {
-               { 0, 0, 0,
-                 "YUV 4:2:2", V4L2_PIX_FMT_YUYV,
-                 { 0, 0, 0, 0 }
-               },
-       };
-       enum v4l2_buf_type type = fmt->type;
-
-       if (fmt->index > 0)
-               return -EINVAL;
-
-       *fmt = formats[fmt->index];
-       fmt->type = type;
-       return 0;
-}
-
-/* Capture data */
-static ssize_t w9966_v4l_read(struct file *file, char  __user *buf,
-               size_t count, loff_t *ppos)
-{
-       struct w9966 *cam = video_drvdata(file);
-       unsigned char addr = 0xa0;      /* ECP, read, CCD-transfer, 00000 */
-       unsigned char __user *dest = (unsigned char __user *)buf;
-       unsigned long dleft = count;
-       unsigned char *tbuf;
-
-       /* Why would anyone want more than this?? */
-       if (count > cam->width * cam->height * 2)
-               return -EINVAL;
-
-       mutex_lock(&cam->lock);
-       w9966_pdev_claim(cam);
-       w9966_write_reg(cam, 0x00, 0x02);       /* Reset ECP-FIFO buffer */
-       w9966_write_reg(cam, 0x00, 0x00);       /* Return to normal operation */
-       w9966_write_reg(cam, 0x01, 0x98);       /* Enable capture */
-
-       /* write special capture-addr and negotiate into data transfer */
-       if ((parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0) ||
-                       (parport_write(cam->pport, &addr, 1) != 1) ||
-                       (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0)) {
-               w9966_pdev_release(cam);
-               mutex_unlock(&cam->lock);
-               return -EFAULT;
-       }
-
-       tbuf = kmalloc(W9966_RBUFFER, GFP_KERNEL);
-       if (tbuf == NULL) {
-               count = -ENOMEM;
-               goto out;
-       }
-
-       while (dleft > 0) {
-               unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft;
-
-               if (parport_read(cam->pport, tbuf, tsize) < tsize) {
-                       count = -EFAULT;
-                       goto out;
-               }
-               if (copy_to_user(dest, tbuf, tsize) != 0) {
-                       count = -EFAULT;
-                       goto out;
-               }
-               dest += tsize;
-               dleft -= tsize;
-       }
-
-       w9966_write_reg(cam, 0x01, 0x18);       /* Disable capture */
-
-out:
-       kfree(tbuf);
-       w9966_pdev_release(cam);
-       mutex_unlock(&cam->lock);
-
-       return count;
-}
-
-static const struct v4l2_file_operations w9966_fops = {
-       .owner          = THIS_MODULE,
-       .open           = v4l2_fh_open,
-       .release        = v4l2_fh_release,
-       .poll           = v4l2_ctrl_poll,
-       .unlocked_ioctl = video_ioctl2,
-       .read           = w9966_v4l_read,
-};
-
-static const struct v4l2_ioctl_ops w9966_ioctl_ops = {
-       .vidioc_querycap                    = cam_querycap,
-       .vidioc_g_input                     = cam_g_input,
-       .vidioc_s_input                     = cam_s_input,
-       .vidioc_enum_input                  = cam_enum_input,
-       .vidioc_enum_fmt_vid_cap            = cam_enum_fmt_vid_cap,
-       .vidioc_g_fmt_vid_cap               = cam_g_fmt_vid_cap,
-       .vidioc_s_fmt_vid_cap               = cam_s_fmt_vid_cap,
-       .vidioc_try_fmt_vid_cap             = cam_try_fmt_vid_cap,
-       .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 cam_ctrl_ops = {
-       .s_ctrl = cam_s_ctrl,
-};
-
-
-/* Initialize camera device. Setup all internal flags, set a
-   default video mode, setup ccd-chip, register v4l device etc..
-   Also used for 'probing' of hardware.
-   -1 on error */
-static int w9966_init(struct w9966 *cam, struct parport *port)
-{
-       struct v4l2_device *v4l2_dev = &cam->v4l2_dev;
-
-       if (cam->dev_state != 0)
-               return -1;
-
-       strlcpy(v4l2_dev->name, "w9966", sizeof(v4l2_dev->name));
-
-       if (v4l2_device_register(NULL, v4l2_dev) < 0) {
-               v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
-               return -1;
-       }
-
-       v4l2_ctrl_handler_init(&cam->hdl, 4);
-       v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
-                         V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
-       v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
-                         V4L2_CID_CONTRAST, -64, 64, 1, 64);
-       v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
-                         V4L2_CID_SATURATION, -64, 64, 1, 64);
-       v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
-                         V4L2_CID_HUE, -128, 127, 1, 0);
-       if (cam->hdl.error) {
-               v4l2_err(v4l2_dev, "couldn't register controls\n");
-               return -1;
-       }
-       cam->pport = port;
-       cam->brightness = 128;
-       cam->contrast = 64;
-       cam->color = 64;
-       cam->hue = 0;
-
-       /* Select requested transfer mode */
-       switch (parmode) {
-       default:        /* Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) */
-       case 0:
-               if (port->modes & PARPORT_MODE_ECP)
-                       cam->ppmode = IEEE1284_MODE_ECP;
-               else if (port->modes & PARPORT_MODE_EPP)
-                       cam->ppmode = IEEE1284_MODE_EPP;
-               else
-                       cam->ppmode = IEEE1284_MODE_ECP;
-               break;
-       case 1:         /* hw- or sw-ecp */
-               cam->ppmode = IEEE1284_MODE_ECP;
-               break;
-       case 2:         /* hw- or sw-epp */
-               cam->ppmode = IEEE1284_MODE_EPP;
-               break;
-       }
-
-       /* Tell the parport driver that we exists */
-       cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL);
-       if (cam->pdev == NULL) {
-               DPRINTF("parport_register_device() failed\n");
-               return -1;
-       }
-       w9966_set_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV);
-
-       w9966_pdev_claim(cam);
-
-       /* Setup a default capture mode */
-       if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) {
-               DPRINTF("w9966_setup() failed.\n");
-               return -1;
-       }
-
-       w9966_pdev_release(cam);
-
-       /* Fill in the video_device struct and register us to v4l */
-       strlcpy(cam->vdev.name, W9966_DRIVERNAME, sizeof(cam->vdev.name));
-       cam->vdev.v4l2_dev = v4l2_dev;
-       cam->vdev.fops = &w9966_fops;
-       cam->vdev.ioctl_ops = &w9966_ioctl_ops;
-       cam->vdev.release = video_device_release_empty;
-       cam->vdev.ctrl_handler = &cam->hdl;
-       video_set_drvdata(&cam->vdev, cam);
-
-       mutex_init(&cam->lock);
-
-       if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0)
-               return -1;
-
-       w9966_set_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV);
-
-       /* All ok */
-       v4l2_info(v4l2_dev, "Found and initialized a webcam on %s.\n",
-                       cam->pport->name);
-       return 0;
-}
-
-
-/* Terminate everything gracefully */
-static void w9966_term(struct w9966 *cam)
-{
-       /* Unregister from v4l */
-       if (w9966_get_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) {
-               video_unregister_device(&cam->vdev);
-               w9966_set_state(cam, W9966_STATE_VDEV, 0);
-       }
-
-       v4l2_ctrl_handler_free(&cam->hdl);
-
-       /* Terminate from IEEE1284 mode and release pdev block */
-       if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
-               w9966_pdev_claim(cam);
-               parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT);
-               w9966_pdev_release(cam);
-       }
-
-       /* Unregister from parport */
-       if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
-               parport_unregister_device(cam->pdev);
-               w9966_set_state(cam, W9966_STATE_PDEV, 0);
-       }
-       memset(cam, 0, sizeof(*cam));
-}
-
-
-/* Called once for every parport on init */
-static void w9966_attach(struct parport *port)
-{
-       int i;
-
-       for (i = 0; i < W9966_MAXCAMS; i++) {
-               if (w9966_cams[i].dev_state != 0)       /* Cam is already assigned */
-                       continue;
-               if (strcmp(pardev[i], "aggressive") == 0 || strcmp(pardev[i], port->name) == 0) {
-                       if (w9966_init(&w9966_cams[i], port) != 0)
-                               w9966_term(&w9966_cams[i]);
-                       break;  /* return */
-               }
-       }
-}
-
-/* Called once for every parport on termination */
-static void w9966_detach(struct parport *port)
-{
-       int i;
-
-       for (i = 0; i < W9966_MAXCAMS; i++)
-               if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port)
-                       w9966_term(&w9966_cams[i]);
-}
-
-
-static struct parport_driver w9966_ppd = {
-       .name = W9966_DRIVERNAME,
-       .attach = w9966_attach,
-       .detach = w9966_detach,
-};
-
-/* Module entry point */
-static int __init w9966_mod_init(void)
-{
-       int i;
-
-       for (i = 0; i < W9966_MAXCAMS; i++)
-               w9966_cams[i].dev_state = 0;
-
-       return parport_register_driver(&w9966_ppd);
-}
-
-/* Module cleanup */
-static void __exit w9966_mod_term(void)
-{
-       parport_unregister_driver(&w9966_ppd);
-}
-
-module_init(w9966_mod_init);
-module_exit(w9966_mod_term);
index 4160ca4..d3c79d9 100644 (file)
@@ -647,6 +647,7 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
        dev->ts_packet_size  = 188 * 4;
        dev->ts_packet_count  = 32;
        sizes[0] = dev->ts_packet_size * dev->ts_packet_count;
+       alloc_ctxs[0] = dev->alloc_ctx;
        return 0;
 }
 
@@ -662,14 +663,11 @@ static void buffer_finish(struct vb2_buffer *vb)
 {
        struct cx8802_dev *dev = vb->vb2_queue->drv_priv;
        struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
-       struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
        struct cx88_riscmem *risc = &buf->risc;
 
        if (risc->cpu)
                pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
        memset(risc, 0, sizeof(*risc));
-
-       dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
 }
 
 static void buffer_queue(struct vb2_buffer *vb)
index c344bfd..5780e2f 100644 (file)
@@ -92,6 +92,7 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
        dev->ts_packet_size  = 188 * 4;
        dev->ts_packet_count = dvb_buf_tscnt;
        sizes[0] = dev->ts_packet_size * dev->ts_packet_count;
+       alloc_ctxs[0] = dev->alloc_ctx;
        *num_buffers = dvb_buf_tscnt;
        return 0;
 }
@@ -108,14 +109,11 @@ static void buffer_finish(struct vb2_buffer *vb)
 {
        struct cx8802_dev *dev = vb->vb2_queue->drv_priv;
        struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
-       struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
        struct cx88_riscmem *risc = &buf->risc;
 
        if (risc->cpu)
                pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
        memset(risc, 0, sizeof(*risc));
-
-       dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
 }
 
 static void buffer_queue(struct vb2_buffer *vb)
index f181a3a..1c1f69e 100644 (file)
@@ -235,10 +235,6 @@ int cx8802_buf_prepare(struct vb2_queue *q, struct cx8802_dev *dev,
                return -EINVAL;
        vb2_set_plane_payload(&buf->vb, 0, size);
 
-       rc = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
-       if (!rc)
-               return -EIO;
-
        rc = cx88_risc_databuffer(dev->pci, risc, sgt->sgl,
                             dev->ts_packet_size, dev->ts_packet_count, 0);
        if (rc) {
@@ -733,6 +729,11 @@ static int cx8802_probe(struct pci_dev *pci_dev,
        if (NULL == dev)
                goto fail_core;
        dev->pci = pci_dev;
+       dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
+       if (IS_ERR(dev->alloc_ctx)) {
+               err = PTR_ERR(dev->alloc_ctx);
+               goto fail_core;
+       }
        dev->core = core;
 
        /* Maintain a reference so cx88-video can query the 8802 device. */
@@ -752,6 +753,7 @@ static int cx8802_probe(struct pci_dev *pci_dev,
        return 0;
 
  fail_free:
+       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
        kfree(dev);
  fail_core:
        core->dvbdev = NULL;
@@ -798,6 +800,7 @@ static void cx8802_remove(struct pci_dev *pci_dev)
        /* common */
        cx8802_fini_common(dev);
        cx88_core_put(dev->core,dev->pci);
+       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
        kfree(dev);
 }
 
index 6ab6e27..32eb7fd 100644 (file)
@@ -120,6 +120,7 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
                sizes[0] = VBI_LINE_NTSC_COUNT * VBI_LINE_LENGTH * 2;
        else
                sizes[0] = VBI_LINE_PAL_COUNT * VBI_LINE_LENGTH * 2;
+       alloc_ctxs[0] = dev->alloc_ctx;
        return 0;
 }
 
@@ -131,7 +132,6 @@ static int buffer_prepare(struct vb2_buffer *vb)
        struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
        unsigned int lines;
        unsigned int size;
-       int rc;
 
        if (dev->core->tvnorm & V4L2_STD_525_60)
                lines = VBI_LINE_NTSC_COUNT;
@@ -142,10 +142,6 @@ static int buffer_prepare(struct vb2_buffer *vb)
                return -EINVAL;
        vb2_set_plane_payload(vb, 0, size);
 
-       rc = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
-       if (!rc)
-               return -EIO;
-
        cx88_risc_buffer(dev->pci, &buf->risc, sgt->sgl,
                         0, VBI_LINE_LENGTH * lines,
                         VBI_LINE_LENGTH, 0,
@@ -157,14 +153,11 @@ static void buffer_finish(struct vb2_buffer *vb)
 {
        struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
        struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
-       struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
        struct cx88_riscmem *risc = &buf->risc;
 
        if (risc->cpu)
                pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
        memset(risc, 0, sizeof(*risc));
-
-       dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
 }
 
 static void buffer_queue(struct vb2_buffer *vb)
index a64ae31..860c98f 100644 (file)
@@ -440,6 +440,7 @@ static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
 
        *num_planes = 1;
        sizes[0] = (dev->fmt->depth * core->width * core->height) >> 3;
+       alloc_ctxs[0] = dev->alloc_ctx;
        return 0;
 }
 
@@ -449,7 +450,6 @@ static int buffer_prepare(struct vb2_buffer *vb)
        struct cx88_core *core = dev->core;
        struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
        struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
-       int rc;
 
        buf->bpl = core->width * dev->fmt->depth >> 3;
 
@@ -457,10 +457,6 @@ static int buffer_prepare(struct vb2_buffer *vb)
                return -EINVAL;
        vb2_set_plane_payload(vb, 0, core->height * buf->bpl);
 
-       rc = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
-       if (!rc)
-               return -EIO;
-
        switch (core->field) {
        case V4L2_FIELD_TOP:
                cx88_risc_buffer(dev->pci, &buf->risc,
@@ -505,14 +501,11 @@ static void buffer_finish(struct vb2_buffer *vb)
 {
        struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
        struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
-       struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
        struct cx88_riscmem *risc = &buf->risc;
 
        if (risc->cpu)
                pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
        memset(risc, 0, sizeof(*risc));
-
-       dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
 }
 
 static void buffer_queue(struct vb2_buffer *vb)
@@ -530,7 +523,6 @@ static void buffer_queue(struct vb2_buffer *vb)
 
        if (list_empty(&q->active)) {
                list_add_tail(&buf->list, &q->active);
-               start_video_dma(dev, q, buf);
                buf->count    = q->count++;
                dprintk(2,"[%p/%d] buffer_queue - first active\n",
                        buf, buf->vb.v4l2_buf.index);
@@ -1345,6 +1337,12 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
                err = -EIO;
                goto fail_core;
        }
+       dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
+       if (IS_ERR(dev->alloc_ctx)) {
+               err = PTR_ERR(dev->alloc_ctx);
+               goto fail_core;
+       }
+
 
        /* initialize driver struct */
        spin_lock_init(&dev->slock);
@@ -1549,6 +1547,7 @@ fail_unreg:
        free_irq(pci_dev->irq, dev);
        mutex_unlock(&core->lock);
 fail_core:
+       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
        core->v4ldev = NULL;
        cx88_core_put(core,dev->pci);
 fail_free:
@@ -1582,6 +1581,7 @@ static void cx8800_finidev(struct pci_dev *pci_dev)
 
        /* free memory */
        cx88_core_put(core,dev->pci);
+       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
        kfree(dev);
 }
 
index 3b0ae75..7748ca9 100644 (file)
@@ -485,6 +485,7 @@ struct cx8800_dev {
        /* pci i/o */
        struct pci_dev             *pci;
        unsigned char              pci_rev,pci_lat;
+       void                       *alloc_ctx;
 
        const struct cx8800_fmt    *fmt;
 
@@ -548,6 +549,7 @@ struct cx8802_dev {
        /* pci i/o */
        struct pci_dev             *pci;
        unsigned char              pci_rev,pci_lat;
+       void                       *alloc_ctx;
 
        /* dma queues */
        struct cx88_dmaqueue       mpegq;
index 0c61155..765bffb 100644 (file)
@@ -65,14 +65,6 @@ config VIDEO_TIMBERDALE
        ---help---
          Add support for the Video In peripherial of the timberdale FPGA.
 
-config VIDEO_VINO
-       tristate "SGI Vino Video For Linux"
-       depends on I2C && SGI_IP22 && VIDEO_V4L2
-       select VIDEO_SAA7191 if MEDIA_SUBDRV_AUTOSELECT
-       help
-         Say Y here to build in support for the Vino video input system found
-         on SGI Indy machines.
-
 config VIDEO_M32R_AR
        tristate "AR devices"
        depends on VIDEO_V4L2
@@ -112,7 +104,7 @@ config VIDEO_OMAP3_DEBUG
 config VIDEO_S3C_CAMIF
        tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"
        depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
-       depends on PM_RUNTIME
+       depends on PM
        depends on ARCH_S3C64XX || PLAT_S3C24XX || COMPILE_TEST
        depends on HAS_DMA
        select VIDEOBUF2_DMA_CONTIG
index b818afb..a49936b 100644 (file)
@@ -2,9 +2,6 @@
 # Makefile for the video capture/playback device drivers.
 #
 
-obj-$(CONFIG_VIDEO_VINO) += indycam.o
-obj-$(CONFIG_VIDEO_VINO) += vino.o
-
 obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o
 obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
 
diff --git a/drivers/media/platform/indycam.c b/drivers/media/platform/indycam.c
deleted file mode 100644 (file)
index f1d192b..0000000
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- *  indycam.c - Silicon Graphics IndyCam digital camera driver
- *
- *  Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
- *  Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- */
-
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-
-/* IndyCam decodes stream of photons into digital image representation ;-) */
-#include <linux/videodev2.h>
-#include <linux/i2c.h>
-#include <media/v4l2-device.h>
-
-#include "indycam.h"
-
-#define INDYCAM_MODULE_VERSION "0.0.5"
-
-MODULE_DESCRIPTION("SGI IndyCam driver");
-MODULE_VERSION(INDYCAM_MODULE_VERSION);
-MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
-MODULE_LICENSE("GPL");
-
-
-// #define INDYCAM_DEBUG
-
-#ifdef INDYCAM_DEBUG
-#define dprintk(x...) printk("IndyCam: " x);
-#define indycam_regdump(client) indycam_regdump_debug(client)
-#else
-#define dprintk(x...)
-#define indycam_regdump(client)
-#endif
-
-struct indycam {
-       struct v4l2_subdev sd;
-       u8 version;
-};
-
-static inline struct indycam *to_indycam(struct v4l2_subdev *sd)
-{
-       return container_of(sd, struct indycam, sd);
-}
-
-static const u8 initseq[] = {
-       INDYCAM_CONTROL_AGCENA,         /* INDYCAM_CONTROL */
-       INDYCAM_SHUTTER_60,             /* INDYCAM_SHUTTER */
-       INDYCAM_GAIN_DEFAULT,           /* INDYCAM_GAIN */
-       0x00,                           /* INDYCAM_BRIGHTNESS (read-only) */
-       INDYCAM_RED_BALANCE_DEFAULT,    /* INDYCAM_RED_BALANCE */
-       INDYCAM_BLUE_BALANCE_DEFAULT,   /* INDYCAM_BLUE_BALANCE */
-       INDYCAM_RED_SATURATION_DEFAULT, /* INDYCAM_RED_SATURATION */
-       INDYCAM_BLUE_SATURATION_DEFAULT,/* INDYCAM_BLUE_SATURATION */
-};
-
-/* IndyCam register handling */
-
-static int indycam_read_reg(struct v4l2_subdev *sd, u8 reg, u8 *value)
-{
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-       int ret;
-
-       if (reg == INDYCAM_REG_RESET) {
-               dprintk("indycam_read_reg(): "
-                       "skipping write-only register %d\n", reg);
-               *value = 0;
-               return 0;
-       }
-
-       ret = i2c_smbus_read_byte_data(client, reg);
-
-       if (ret < 0) {
-               printk(KERN_ERR "IndyCam: indycam_read_reg(): read failed, "
-                      "register = 0x%02x\n", reg);
-               return ret;
-       }
-
-       *value = (u8)ret;
-
-       return 0;
-}
-
-static int indycam_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value)
-{
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-       int err;
-
-       if (reg == INDYCAM_REG_BRIGHTNESS || reg == INDYCAM_REG_VERSION) {
-               dprintk("indycam_write_reg(): "
-                       "skipping read-only register %d\n", reg);
-               return 0;
-       }
-
-       dprintk("Writing Reg %d = 0x%02x\n", reg, value);
-       err = i2c_smbus_write_byte_data(client, reg, value);
-
-       if (err) {
-               printk(KERN_ERR "IndyCam: indycam_write_reg(): write failed, "
-                      "register = 0x%02x, value = 0x%02x\n", reg, value);
-       }
-       return err;
-}
-
-static int indycam_write_block(struct v4l2_subdev *sd, u8 reg,
-                              u8 length, u8 *data)
-{
-       int i, err;
-
-       for (i = 0; i < length; i++) {
-               err = indycam_write_reg(sd, reg + i, data[i]);
-               if (err)
-                       return err;
-       }
-
-       return 0;
-}
-
-/* Helper functions */
-
-#ifdef INDYCAM_DEBUG
-static void indycam_regdump_debug(struct v4l2_subdev *sd)
-{
-       int i;
-       u8 val;
-
-       for (i = 0; i < 9; i++) {
-               indycam_read_reg(sd, i, &val);
-               dprintk("Reg %d = 0x%02x\n", i, val);
-       }
-}
-#endif
-
-static int indycam_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       struct indycam *camera = to_indycam(sd);
-       u8 reg;
-       int ret = 0;
-
-       switch (ctrl->id) {
-       case V4L2_CID_AUTOGAIN:
-       case V4L2_CID_AUTO_WHITE_BALANCE:
-               ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, &reg);
-               if (ret)
-                       return -EIO;
-               if (ctrl->id == V4L2_CID_AUTOGAIN)
-                       ctrl->value = (reg & INDYCAM_CONTROL_AGCENA)
-                               ? 1 : 0;
-               else
-                       ctrl->value = (reg & INDYCAM_CONTROL_AWBCTL)
-                               ? 1 : 0;
-               break;
-       case V4L2_CID_EXPOSURE:
-               ret = indycam_read_reg(sd, INDYCAM_REG_SHUTTER, &reg);
-               if (ret)
-                       return -EIO;
-               ctrl->value = ((s32)reg == 0x00) ? 0xff : ((s32)reg - 1);
-               break;
-       case V4L2_CID_GAIN:
-               ret = indycam_read_reg(sd, INDYCAM_REG_GAIN, &reg);
-               if (ret)
-                       return -EIO;
-               ctrl->value = (s32)reg;
-               break;
-       case V4L2_CID_RED_BALANCE:
-               ret = indycam_read_reg(sd, INDYCAM_REG_RED_BALANCE, &reg);
-               if (ret)
-                       return -EIO;
-               ctrl->value = (s32)reg;
-               break;
-       case V4L2_CID_BLUE_BALANCE:
-               ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_BALANCE, &reg);
-               if (ret)
-                       return -EIO;
-               ctrl->value = (s32)reg;
-               break;
-       case INDYCAM_CONTROL_RED_SATURATION:
-               ret = indycam_read_reg(sd,
-                                      INDYCAM_REG_RED_SATURATION, &reg);
-               if (ret)
-                       return -EIO;
-               ctrl->value = (s32)reg;
-               break;
-       case INDYCAM_CONTROL_BLUE_SATURATION:
-               ret = indycam_read_reg(sd,
-                                      INDYCAM_REG_BLUE_SATURATION, &reg);
-               if (ret)
-                       return -EIO;
-               ctrl->value = (s32)reg;
-               break;
-       case V4L2_CID_GAMMA:
-               if (camera->version == CAMERA_VERSION_MOOSE) {
-                       ret = indycam_read_reg(sd,
-                                              INDYCAM_REG_GAMMA, &reg);
-                       if (ret)
-                               return -EIO;
-                       ctrl->value = (s32)reg;
-               } else {
-                       ctrl->value = INDYCAM_GAMMA_DEFAULT;
-               }
-               break;
-       default:
-               ret = -EINVAL;
-       }
-
-       return ret;
-}
-
-static int indycam_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       struct indycam *camera = to_indycam(sd);
-       u8 reg;
-       int ret = 0;
-
-       switch (ctrl->id) {
-       case V4L2_CID_AUTOGAIN:
-       case V4L2_CID_AUTO_WHITE_BALANCE:
-               ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, &reg);
-               if (ret)
-                       break;
-
-               if (ctrl->id == V4L2_CID_AUTOGAIN) {
-                       if (ctrl->value)
-                               reg |= INDYCAM_CONTROL_AGCENA;
-                       else
-                               reg &= ~INDYCAM_CONTROL_AGCENA;
-               } else {
-                       if (ctrl->value)
-                               reg |= INDYCAM_CONTROL_AWBCTL;
-                       else
-                               reg &= ~INDYCAM_CONTROL_AWBCTL;
-               }
-
-               ret = indycam_write_reg(sd, INDYCAM_REG_CONTROL, reg);
-               break;
-       case V4L2_CID_EXPOSURE:
-               reg = (ctrl->value == 0xff) ? 0x00 : (ctrl->value + 1);
-               ret = indycam_write_reg(sd, INDYCAM_REG_SHUTTER, reg);
-               break;
-       case V4L2_CID_GAIN:
-               ret = indycam_write_reg(sd, INDYCAM_REG_GAIN, ctrl->value);
-               break;
-       case V4L2_CID_RED_BALANCE:
-               ret = indycam_write_reg(sd, INDYCAM_REG_RED_BALANCE,
-                                       ctrl->value);
-               break;
-       case V4L2_CID_BLUE_BALANCE:
-               ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_BALANCE,
-                                       ctrl->value);
-               break;
-       case INDYCAM_CONTROL_RED_SATURATION:
-               ret = indycam_write_reg(sd, INDYCAM_REG_RED_SATURATION,
-                                       ctrl->value);
-               break;
-       case INDYCAM_CONTROL_BLUE_SATURATION:
-               ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_SATURATION,
-                                       ctrl->value);
-               break;
-       case V4L2_CID_GAMMA:
-               if (camera->version == CAMERA_VERSION_MOOSE) {
-                       ret = indycam_write_reg(sd, INDYCAM_REG_GAMMA,
-                                               ctrl->value);
-               }
-               break;
-       default:
-               ret = -EINVAL;
-       }
-
-       return ret;
-}
-
-/* I2C-interface */
-
-/* ----------------------------------------------------------------------- */
-
-static const struct v4l2_subdev_core_ops indycam_core_ops = {
-       .g_ctrl = indycam_g_ctrl,
-       .s_ctrl = indycam_s_ctrl,
-};
-
-static const struct v4l2_subdev_ops indycam_ops = {
-       .core = &indycam_core_ops,
-};
-
-static int indycam_probe(struct i2c_client *client,
-                         const struct i2c_device_id *id)
-{
-       int err = 0;
-       struct indycam *camera;
-       struct v4l2_subdev *sd;
-
-       v4l_info(client, "chip found @ 0x%x (%s)\n",
-                       client->addr << 1, client->adapter->name);
-
-       camera = kzalloc(sizeof(struct indycam), GFP_KERNEL);
-       if (!camera)
-               return -ENOMEM;
-
-       sd = &camera->sd;
-       v4l2_i2c_subdev_init(sd, client, &indycam_ops);
-
-       camera->version = i2c_smbus_read_byte_data(client,
-                                                  INDYCAM_REG_VERSION);
-       if (camera->version != CAMERA_VERSION_INDY &&
-           camera->version != CAMERA_VERSION_MOOSE) {
-               kfree(camera);
-               return -ENODEV;
-       }
-
-       printk(KERN_INFO "IndyCam v%d.%d detected\n",
-              INDYCAM_VERSION_MAJOR(camera->version),
-              INDYCAM_VERSION_MINOR(camera->version));
-
-       indycam_regdump(sd);
-
-       // initialize
-       err = indycam_write_block(sd, 0, sizeof(initseq), (u8 *)&initseq);
-       if (err) {
-               printk(KERN_ERR "IndyCam initialization failed\n");
-               kfree(camera);
-               return -EIO;
-       }
-
-       indycam_regdump(sd);
-
-       // white balance
-       err = indycam_write_reg(sd, INDYCAM_REG_CONTROL,
-                         INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL);
-       if (err) {
-               printk(KERN_ERR "IndyCam: White balancing camera failed\n");
-               kfree(camera);
-               return -EIO;
-       }
-
-       indycam_regdump(sd);
-
-       printk(KERN_INFO "IndyCam initialized\n");
-
-       return 0;
-}
-
-static int indycam_remove(struct i2c_client *client)
-{
-       struct v4l2_subdev *sd = i2c_get_clientdata(client);
-
-       v4l2_device_unregister_subdev(sd);
-       kfree(to_indycam(sd));
-       return 0;
-}
-
-static const struct i2c_device_id indycam_id[] = {
-       { "indycam", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, indycam_id);
-
-static struct i2c_driver indycam_driver = {
-       .driver = {
-               .owner  = THIS_MODULE,
-               .name   = "indycam",
-       },
-       .probe          = indycam_probe,
-       .remove         = indycam_remove,
-       .id_table       = indycam_id,
-};
-
-module_i2c_driver(indycam_driver);
diff --git a/drivers/media/platform/indycam.h b/drivers/media/platform/indycam.h
deleted file mode 100644 (file)
index 881f21c..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- *  indycam.h - Silicon Graphics IndyCam digital camera driver
- *
- *  Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
- *  Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- */
-
-#ifndef _INDYCAM_H_
-#define _INDYCAM_H_
-
-/* I2C address for the Guinness Camera */
-#define INDYCAM_ADDR                   0x56
-
-/* Camera version */
-#define CAMERA_VERSION_INDY            0x10    /* v1.0 */
-#define CAMERA_VERSION_MOOSE           0x12    /* v1.2 */
-#define INDYCAM_VERSION_MAJOR(x)       (((x) & 0xf0) >> 4)
-#define INDYCAM_VERSION_MINOR(x)       ((x) & 0x0f)
-
-/* Register bus addresses */
-#define INDYCAM_REG_CONTROL            0x00
-#define INDYCAM_REG_SHUTTER            0x01
-#define INDYCAM_REG_GAIN               0x02
-#define INDYCAM_REG_BRIGHTNESS         0x03 /* read-only */
-#define INDYCAM_REG_RED_BALANCE                0x04
-#define INDYCAM_REG_BLUE_BALANCE       0x05
-#define INDYCAM_REG_RED_SATURATION     0x06
-#define INDYCAM_REG_BLUE_SATURATION    0x07
-#define INDYCAM_REG_GAMMA              0x08
-#define INDYCAM_REG_VERSION            0x0e /* read-only */
-#define INDYCAM_REG_RESET              0x0f /* write-only */
-
-#define INDYCAM_REG_LED                        0x46
-#define INDYCAM_REG_ORIENTATION                0x47
-#define INDYCAM_REG_BUTTON             0x48
-
-/* Field definitions of registers */
-#define INDYCAM_CONTROL_AGCENA         (1<<0) /* automatic gain control */
-#define INDYCAM_CONTROL_AWBCTL         (1<<1) /* automatic white balance */
-                                               /* 2-3 are reserved */
-#define INDYCAM_CONTROL_EVNFLD         (1<<4)  /* read-only */
-
-#define INDYCAM_SHUTTER_10000          0x02    /* 1/10000 second */
-#define INDYCAM_SHUTTER_4000           0x04    /* 1/4000 second */
-#define INDYCAM_SHUTTER_2000           0x08    /* 1/2000 second */
-#define INDYCAM_SHUTTER_1000           0x10    /* 1/1000 second */
-#define INDYCAM_SHUTTER_500            0x20    /* 1/500 second */
-#define INDYCAM_SHUTTER_250            0x3f    /* 1/250 second */
-#define INDYCAM_SHUTTER_125            0x7e    /* 1/125 second */
-#define INDYCAM_SHUTTER_100            0x9e    /* 1/100 second */
-#define INDYCAM_SHUTTER_60             0x00    /* 1/60 second */
-
-#define INDYCAM_LED_ACTIVE                     0x10
-#define INDYCAM_LED_INACTIVE                   0x30
-#define INDYCAM_ORIENTATION_BOTTOM_TO_TOP      0x40
-#define INDYCAM_BUTTON_RELEASED                        0x10
-
-/* Values for controls */
-#define INDYCAM_SHUTTER_MIN            0x00
-#define INDYCAM_SHUTTER_MAX            0xff
-#define INDYCAM_GAIN_MIN                0x00
-#define INDYCAM_GAIN_MAX                0xff
-#define INDYCAM_RED_BALANCE_MIN                0x00
-#define INDYCAM_RED_BALANCE_MAX                0xff
-#define INDYCAM_BLUE_BALANCE_MIN        0x00
-#define INDYCAM_BLUE_BALANCE_MAX        0xff
-#define INDYCAM_RED_SATURATION_MIN      0x00
-#define INDYCAM_RED_SATURATION_MAX      0xff
-#define INDYCAM_BLUE_SATURATION_MIN    0x00
-#define INDYCAM_BLUE_SATURATION_MAX    0xff
-#define INDYCAM_GAMMA_MIN              0x00
-#define INDYCAM_GAMMA_MAX              0xff
-
-#define INDYCAM_AGC_DEFAULT            1
-#define INDYCAM_AWB_DEFAULT            0
-#define INDYCAM_SHUTTER_DEFAULT                0xff
-#define INDYCAM_GAIN_DEFAULT           0x80
-#define INDYCAM_RED_BALANCE_DEFAULT    0x18
-#define INDYCAM_BLUE_BALANCE_DEFAULT   0xa4
-#define INDYCAM_RED_SATURATION_DEFAULT 0x80
-#define INDYCAM_BLUE_SATURATION_DEFAULT        0xc0
-#define INDYCAM_GAMMA_DEFAULT          0x80
-
-/* Driver interface definitions */
-
-#define INDYCAM_CONTROL_RED_SATURATION         (V4L2_CID_PRIVATE_BASE + 0)
-#define INDYCAM_CONTROL_BLUE_SATURATION                (V4L2_CID_PRIVATE_BASE + 1)
-
-#endif
index beb180e..5a1835d 100644 (file)
@@ -8,7 +8,7 @@
 
 config VIDEO_SAMSUNG_S5P_TV
        bool "Samsung TV driver for S5P platform"
-       depends on PM_RUNTIME
+       depends on PM
        depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
        default n
        ---help---
index 126ac7c..0c1f556 100644 (file)
 #define VNDMR_REG      0x58    /* Video n Data Mode Register */
 #define VNDMR2_REG     0x5C    /* Video n Data Mode Register 2 */
 #define VNUVAOF_REG    0x60    /* Video n UV Address Offset Register */
+#define VNC1A_REG      0x80    /* Video n Coefficient Set C1A Register */
+#define VNC1B_REG      0x84    /* Video n Coefficient Set C1B Register */
+#define VNC1C_REG      0x88    /* Video n Coefficient Set C1C Register */
+#define VNC2A_REG      0x90    /* Video n Coefficient Set C2A Register */
+#define VNC2B_REG      0x94    /* Video n Coefficient Set C2B Register */
+#define VNC2C_REG      0x98    /* Video n Coefficient Set C2C Register */
+#define VNC3A_REG      0xA0    /* Video n Coefficient Set C3A Register */
+#define VNC3B_REG      0xA4    /* Video n Coefficient Set C3B Register */
+#define VNC3C_REG      0xA8    /* Video n Coefficient Set C3C Register */
+#define VNC4A_REG      0xB0    /* Video n Coefficient Set C4A Register */
+#define VNC4B_REG      0xB4    /* Video n Coefficient Set C4B Register */
+#define VNC4C_REG      0xB8    /* Video n Coefficient Set C4C Register */
+#define VNC5A_REG      0xC0    /* Video n Coefficient Set C5A Register */
+#define VNC5B_REG      0xC4    /* Video n Coefficient Set C5B Register */
+#define VNC5C_REG      0xC8    /* Video n Coefficient Set C5C Register */
+#define VNC6A_REG      0xD0    /* Video n Coefficient Set C6A Register */
+#define VNC6B_REG      0xD4    /* Video n Coefficient Set C6B Register */
+#define VNC6C_REG      0xD8    /* Video n Coefficient Set C6C Register */
+#define VNC7A_REG      0xE0    /* Video n Coefficient Set C7A Register */
+#define VNC7B_REG      0xE4    /* Video n Coefficient Set C7B Register */
+#define VNC7C_REG      0xE8    /* Video n Coefficient Set C7C Register */
+#define VNC8A_REG      0xF0    /* Video n Coefficient Set C8A Register */
+#define VNC8B_REG      0xF4    /* Video n Coefficient Set C8B Register */
+#define VNC8C_REG      0xF8    /* Video n Coefficient Set C8C Register */
 
 /* Register bit fields for R-Car VIN */
 /* Video n Main Control Register bits */
 #define VNDMR2_VPS             (1 << 30)
 #define VNDMR2_HPS             (1 << 29)
 #define VNDMR2_FTEV            (1 << 17)
+#define VNDMR2_VLV(n)          ((n & 0xf) << 12)
 
 #define VIN_MAX_WIDTH          2048
 #define VIN_MAX_HEIGHT         2048
@@ -117,6 +142,324 @@ enum chip_id {
        RCAR_E1,
 };
 
+struct vin_coeff {
+       unsigned short xs_value;
+       u32 coeff_set[24];
+};
+
+static const struct vin_coeff vin_coeff_set[] = {
+       { 0x0000, {
+               0x00000000,             0x00000000,             0x00000000,
+               0x00000000,             0x00000000,             0x00000000,
+               0x00000000,             0x00000000,             0x00000000,
+               0x00000000,             0x00000000,             0x00000000,
+               0x00000000,             0x00000000,             0x00000000,
+               0x00000000,             0x00000000,             0x00000000,
+               0x00000000,             0x00000000,             0x00000000,
+               0x00000000,             0x00000000,             0x00000000 },
+       },
+       { 0x1000, {
+               0x000fa400,             0x000fa400,             0x09625902,
+               0x000003f8,             0x00000403,             0x3de0d9f0,
+               0x001fffed,             0x00000804,             0x3cc1f9c3,
+               0x001003de,             0x00000c01,             0x3cb34d7f,
+               0x002003d2,             0x00000c00,             0x3d24a92d,
+               0x00200bca,             0x00000bff,             0x3df600d2,
+               0x002013cc,             0x000007ff,             0x3ed70c7e,
+               0x00100fde,             0x00000000,             0x3f87c036 },
+       },
+       { 0x1200, {
+               0x002ffff1,             0x002ffff1,             0x02a0a9c8,
+               0x002003e7,             0x001ffffa,             0x000185bc,
+               0x002007dc,             0x000003ff,             0x3e52859c,
+               0x00200bd4,             0x00000002,             0x3d53996b,
+               0x00100fd0,             0x00000403,             0x3d04ad2d,
+               0x00000bd5,             0x00000403,             0x3d35ace7,
+               0x3ff003e4,             0x00000801,             0x3dc674a1,
+               0x3fffe800,             0x00000800,             0x3e76f461 },
+       },
+       { 0x1400, {
+               0x00100be3,             0x00100be3,             0x04d1359a,
+               0x00000fdb,             0x002003ed,             0x0211fd93,
+               0x00000fd6,             0x002003f4,             0x0002d97b,
+               0x000007d6,             0x002ffffb,             0x3e93b956,
+               0x3ff003da,             0x001003ff,             0x3db49926,
+               0x3fffefe9,             0x00100001,             0x3d655cee,
+               0x3fffd400,             0x00000003,             0x3d65f4b6,
+               0x000fb421,             0x00000402,             0x3dc6547e },
+       },
+       { 0x1600, {
+               0x00000bdd,             0x00000bdd,             0x06519578,
+               0x3ff007da,             0x00000be3,             0x03c24973,
+               0x3ff003d9,             0x00000be9,             0x01b30d5f,
+               0x3ffff7df,             0x001003f1,             0x0003c542,
+               0x000fdfec,             0x001003f7,             0x3ec4711d,
+               0x000fc400,             0x002ffffd,             0x3df504f1,
+               0x001fa81a,             0x002ffc00,             0x3d957cc2,
+               0x002f8c3c,             0x00100000,             0x3db5c891 },
+       },
+       { 0x1800, {
+               0x3ff003dc,             0x3ff003dc,             0x0791e558,
+               0x000ff7dd,             0x3ff007de,             0x05328554,
+               0x000fe7e3,             0x3ff00be2,             0x03232546,
+               0x000fd7ee,             0x000007e9,             0x0143bd30,
+               0x001fb800,             0x000007ee,             0x00044511,
+               0x002fa015,             0x000007f4,             0x3ef4bcee,
+               0x002f8832,             0x001003f9,             0x3e4514c7,
+               0x001f7853,             0x001003fd,             0x3de54c9f },
+       },
+       { 0x1a00, {
+               0x000fefe0,             0x000fefe0,             0x08721d3c,
+               0x001fdbe7,             0x000ffbde,             0x0652a139,
+               0x001fcbf0,             0x000003df,             0x0463292e,
+               0x002fb3ff,             0x3ff007e3,             0x0293a91d,
+               0x002f9c12,             0x3ff00be7,             0x01241905,
+               0x001f8c29,             0x000007ed,             0x3fe470eb,
+               0x000f7c46,             0x000007f2,             0x3f04b8ca,
+               0x3fef7865,             0x000007f6,             0x3e74e4a8 },
+       },
+       { 0x1c00, {
+               0x001fd3e9,             0x001fd3e9,             0x08f23d26,
+               0x002fbff3,             0x001fe3e4,             0x0712ad23,
+               0x002fa800,             0x000ff3e0,             0x05631d1b,
+               0x001f9810,             0x000ffbe1,             0x03b3890d,
+               0x000f8c23,             0x000003e3,             0x0233e8fa,
+               0x3fef843b,             0x000003e7,             0x00f430e4,
+               0x3fbf8456,             0x3ff00bea,             0x00046cc8,
+               0x3f8f8c72,             0x3ff00bef,             0x3f3490ac },
+       },
+       { 0x1e00, {
+               0x001fbbf4,             0x001fbbf4,             0x09425112,
+               0x001fa800,             0x002fc7ed,             0x0792b110,
+               0x000f980e,             0x001fdbe6,             0x0613110a,
+               0x3fff8c20,             0x001fe7e3,             0x04a368fd,
+               0x3fcf8c33,             0x000ff7e2,             0x0343b8ed,
+               0x3f9f8c4a,             0x000fffe3,             0x0203f8da,
+               0x3f5f9c61,             0x000003e6,             0x00e428c5,
+               0x3f1fb07b,             0x000003eb,             0x3fe440af },
+       },
+       { 0x2000, {
+               0x000fa400,             0x000fa400,             0x09625902,
+               0x3fff980c,             0x001fb7f5,             0x0812b0ff,
+               0x3fdf901c,             0x001fc7ed,             0x06b2fcfa,
+               0x3faf902d,             0x001fd3e8,             0x055348f1,
+               0x3f7f983f,             0x001fe3e5,             0x04038ce3,
+               0x3f3fa454,             0x001fefe3,             0x02e3c8d1,
+               0x3f0fb86a,             0x001ff7e4,             0x01c3e8c0,
+               0x3ecfd880,             0x000fffe6,             0x00c404ac },
+       },
+       { 0x2200, {
+               0x3fdf9c0b,             0x3fdf9c0b,             0x09725cf4,
+               0x3fbf9818,             0x3fffa400,             0x0842a8f1,
+               0x3f8f9827,             0x000fb3f7,             0x0702f0ec,
+               0x3f5fa037,             0x000fc3ef,             0x05d330e4,
+               0x3f2fac49,             0x001fcfea,             0x04a364d9,
+               0x3effc05c,             0x001fdbe7,             0x038394ca,
+               0x3ecfdc6f,             0x001fe7e6,             0x0273b0bb,
+               0x3ea00083,             0x001fefe6,             0x0183c0a9 },
+       },
+       { 0x2400, {
+               0x3f9fa014,             0x3f9fa014,             0x098260e6,
+               0x3f7f9c23,             0x3fcf9c0a,             0x08629ce5,
+               0x3f4fa431,             0x3fefa400,             0x0742d8e1,
+               0x3f1fb440,             0x3fffb3f8,             0x062310d9,
+               0x3eefc850,             0x000fbbf2,             0x050340d0,
+               0x3ecfe062,             0x000fcbec,             0x041364c2,
+               0x3ea00073,             0x001fd3ea,             0x03037cb5,
+               0x3e902086,             0x001fdfe8,             0x022388a5 },
+       },
+       { 0x2600, {
+               0x3f5fa81e,             0x3f5fa81e,             0x096258da,
+               0x3f3fac2b,             0x3f8fa412,             0x088290d8,
+               0x3f0fbc38,             0x3fafa408,             0x0772c8d5,
+               0x3eefcc47,             0x3fcfa800,             0x0672f4ce,
+               0x3ecfe456,             0x3fefaffa,             0x05531cc6,
+               0x3eb00066,             0x3fffbbf3,             0x047334bb,
+               0x3ea01c77,             0x000fc7ee,             0x039348ae,
+               0x3ea04486,             0x000fd3eb,             0x02b350a1 },
+       },
+       { 0x2800, {
+               0x3f2fb426,             0x3f2fb426,             0x094250ce,
+               0x3f0fc032,             0x3f4fac1b,             0x086284cd,
+               0x3eefd040,             0x3f7fa811,             0x0782acc9,
+               0x3ecfe84c,             0x3f9fa807,             0x06a2d8c4,
+               0x3eb0005b,             0x3fbfac00,             0x05b2f4bc,
+               0x3eb0186a,             0x3fdfb3fa,             0x04c308b4,
+               0x3eb04077,             0x3fefbbf4,             0x03f31ca8,
+               0x3ec06884,             0x000fbff2,             0x03031c9e },
+       },
+       { 0x2a00, {
+               0x3f0fc42d,             0x3f0fc42d,             0x090240c4,
+               0x3eefd439,             0x3f2fb822,             0x08526cc2,
+               0x3edfe845,             0x3f4fb018,             0x078294bf,
+               0x3ec00051,             0x3f6fac0f,             0x06b2b4bb,
+               0x3ec0185f,             0x3f8fac07,             0x05e2ccb4,
+               0x3ec0386b,             0x3fafac00,             0x0502e8ac,
+               0x3ed05c77,             0x3fcfb3fb,             0x0432f0a3,
+               0x3ef08482,             0x3fdfbbf6,             0x0372f898 },
+       },
+       { 0x2c00, {
+               0x3eefdc31,             0x3eefdc31,             0x08e238b8,
+               0x3edfec3d,             0x3f0fc828,             0x082258b9,
+               0x3ed00049,             0x3f1fc01e,             0x077278b6,
+               0x3ed01455,             0x3f3fb815,             0x06c294b2,
+               0x3ed03460,             0x3f5fb40d,             0x0602acac,
+               0x3ef0506c,             0x3f7fb006,             0x0542c0a4,
+               0x3f107476,             0x3f9fb400,             0x0472c89d,
+               0x3f309c80,             0x3fbfb7fc,             0x03b2cc94 },
+       },
+       { 0x2e00, {
+               0x3eefec37,             0x3eefec37,             0x088220b0,
+               0x3ee00041,             0x3effdc2d,             0x07f244ae,
+               0x3ee0144c,             0x3f0fd023,             0x07625cad,
+               0x3ef02c57,             0x3f1fc81a,             0x06c274a9,
+               0x3f004861,             0x3f3fbc13,             0x060288a6,
+               0x3f20686b,             0x3f5fb80c,             0x05529c9e,
+               0x3f408c74,             0x3f6fb805,             0x04b2ac96,
+               0x3f80ac7e,             0x3f8fb800,             0x0402ac8e },
+       },
+       { 0x3000, {
+               0x3ef0003a,             0x3ef0003a,             0x084210a6,
+               0x3ef01045,             0x3effec32,             0x07b228a7,
+               0x3f00284e,             0x3f0fdc29,             0x073244a4,
+               0x3f104058,             0x3f0fd420,             0x06a258a2,
+               0x3f305c62,             0x3f2fc818,             0x0612689d,
+               0x3f508069,             0x3f3fc011,             0x05728496,
+               0x3f80a072,             0x3f4fc00a,             0x04d28c90,
+               0x3fc0c07b,             0x3f6fbc04,             0x04429088 },
+       },
+       { 0x3200, {
+               0x3f00103e,             0x3f00103e,             0x07f1fc9e,
+               0x3f102447,             0x3f000035,             0x0782149d,
+               0x3f203c4f,             0x3f0ff02c,             0x07122c9c,
+               0x3f405458,             0x3f0fe424,             0x06924099,
+               0x3f607061,             0x3f1fd41d,             0x06024c97,
+               0x3f909068,             0x3f2fcc16,             0x05726490,
+               0x3fc0b070,             0x3f3fc80f,             0x04f26c8a,
+               0x0000d077,             0x3f4fc409,             0x04627484 },
+       },
+       { 0x3400, {
+               0x3f202040,             0x3f202040,             0x07a1e898,
+               0x3f303449,             0x3f100c38,             0x0741fc98,
+               0x3f504c50,             0x3f10002f,             0x06e21495,
+               0x3f706459,             0x3f1ff028,             0x06722492,
+               0x3fa08060,             0x3f1fe421,             0x05f2348f,
+               0x3fd09c67,             0x3f1fdc19,             0x05824c89,
+               0x0000bc6e,             0x3f2fd014,             0x04f25086,
+               0x0040dc74,             0x3f3fcc0d,             0x04825c7f },
+       },
+       { 0x3600, {
+               0x3f403042,             0x3f403042,             0x0761d890,
+               0x3f504848,             0x3f301c3b,             0x0701f090,
+               0x3f805c50,             0x3f200c33,             0x06a2008f,
+               0x3fa07458,             0x3f10002b,             0x06520c8d,
+               0x3fd0905e,             0x3f1ff424,             0x05e22089,
+               0x0000ac65,             0x3f1fe81d,             0x05823483,
+               0x0030cc6a,             0x3f2fdc18,             0x04f23c81,
+               0x0080e871,             0x3f2fd412,             0x0482407c },
+       },
+       { 0x3800, {
+               0x3f604043,             0x3f604043,             0x0721c88a,
+               0x3f80544a,             0x3f502c3c,             0x06d1d88a,
+               0x3fb06851,             0x3f301c35,             0x0681e889,
+               0x3fd08456,             0x3f30082f,             0x0611fc88,
+               0x00009c5d,             0x3f200027,             0x05d20884,
+               0x0030b863,             0x3f2ff421,             0x05621880,
+               0x0070d468,             0x3f2fe81b,             0x0502247c,
+               0x00c0ec6f,             0x3f2fe015,             0x04a22877 },
+       },
+       { 0x3a00, {
+               0x3f904c44,             0x3f904c44,             0x06e1b884,
+               0x3fb0604a,             0x3f70383e,             0x0691c885,
+               0x3fe07451,             0x3f502c36,             0x0661d483,
+               0x00009055,             0x3f401831,             0x0601ec81,
+               0x0030a85b,             0x3f300c2a,             0x05b1f480,
+               0x0070c061,             0x3f300024,             0x0562047a,
+               0x00b0d867,             0x3f3ff41e,             0x05020c77,
+               0x00f0f46b,             0x3f2fec19,             0x04a21474 },
+       },
+       { 0x3c00, {
+               0x3fb05c43,             0x3fb05c43,             0x06c1b07e,
+               0x3fe06c4b,             0x3f902c3f,             0x0681c081,
+               0x0000844f,             0x3f703838,             0x0631cc7d,
+               0x00309855,             0x3f602433,             0x05d1d47e,
+               0x0060b459,             0x3f50142e,             0x0581e47b,
+               0x00a0c85f,             0x3f400828,             0x0531f078,
+               0x00e0e064,             0x3f300021,             0x0501fc73,
+               0x00b0fc6a,             0x3f3ff41d,             0x04a20873 },
+       },
+       { 0x3e00, {
+               0x3fe06444,             0x3fe06444,             0x0681a07a,
+               0x00007849,             0x3fc0503f,             0x0641b07a,
+               0x0020904d,             0x3fa0403a,             0x05f1c07a,
+               0x0060a453,             0x3f803034,             0x05c1c878,
+               0x0090b858,             0x3f70202f,             0x0571d477,
+               0x00d0d05d,             0x3f501829,             0x0531e073,
+               0x0110e462,             0x3f500825,             0x04e1e471,
+               0x01510065,             0x3f40001f,             0x04a1f06d },
+       },
+       { 0x4000, {
+               0x00007044,             0x00007044,             0x06519476,
+               0x00208448,             0x3fe05c3f,             0x0621a476,
+               0x0050984d,             0x3fc04c3a,             0x05e1b075,
+               0x0080ac52,             0x3fa03c35,             0x05a1b875,
+               0x00c0c056,             0x3f803030,             0x0561c473,
+               0x0100d45b,             0x3f70202b,             0x0521d46f,
+               0x0140e860,             0x3f601427,             0x04d1d46e,
+               0x01810064,             0x3f500822,             0x0491dc6b },
+       },
+       { 0x5000, {
+               0x0110a442,             0x0110a442,             0x0551545e,
+               0x0140b045,             0x00e0983f,             0x0531585f,
+               0x0160c047,             0x00c08c3c,             0x0511645e,
+               0x0190cc4a,             0x00908039,             0x04f1685f,
+               0x01c0dc4c,             0x00707436,             0x04d1705e,
+               0x0200e850,             0x00506833,             0x04b1785b,
+               0x0230f453,             0x00305c30,             0x0491805a,
+               0x02710056,             0x0010542d,             0x04718059 },
+       },
+       { 0x6000, {
+               0x01c0bc40,             0x01c0bc40,             0x04c13052,
+               0x01e0c841,             0x01a0b43d,             0x04c13851,
+               0x0210cc44,             0x0180a83c,             0x04a13453,
+               0x0230d845,             0x0160a03a,             0x04913c52,
+               0x0260e047,             0x01409838,             0x04714052,
+               0x0280ec49,             0x01208c37,             0x04514c50,
+               0x02b0f44b,             0x01008435,             0x04414c50,
+               0x02d1004c,             0x00e07c33,             0x0431544f },
+       },
+       { 0x7000, {
+               0x0230c83e,             0x0230c83e,             0x04711c4c,
+               0x0250d03f,             0x0210c43c,             0x0471204b,
+               0x0270d840,             0x0200b83c,             0x0451244b,
+               0x0290dc42,             0x01e0b43a,             0x0441244c,
+               0x02b0e443,             0x01c0b038,             0x0441284b,
+               0x02d0ec44,             0x01b0a438,             0x0421304a,
+               0x02f0f445,             0x0190a036,             0x04213449,
+               0x0310f847,             0x01709c34,             0x04213848 },
+       },
+       { 0x8000, {
+               0x0280d03d,             0x0280d03d,             0x04310c48,
+               0x02a0d43e,             0x0270c83c,             0x04311047,
+               0x02b0dc3e,             0x0250c83a,             0x04311447,
+               0x02d0e040,             0x0240c03a,             0x04211446,
+               0x02e0e840,             0x0220bc39,             0x04111847,
+               0x0300e842,             0x0210b438,             0x04012445,
+               0x0310f043,             0x0200b037,             0x04012045,
+               0x0330f444,             0x01e0ac36,             0x03f12445 },
+       },
+       { 0xefff, {
+               0x0340dc3a,             0x0340dc3a,             0x03b0ec40,
+               0x0340e03a,             0x0330e039,             0x03c0f03e,
+               0x0350e03b,             0x0330dc39,             0x03c0ec3e,
+               0x0350e43a,             0x0320dc38,             0x03c0f43e,
+               0x0360e43b,             0x0320d839,             0x03b0f03e,
+               0x0360e83b,             0x0310d838,             0x03c0fc3b,
+               0x0370e83b,             0x0310d439,             0x03a0f83d,
+               0x0370e83c,             0x0300d438,             0x03b0fc3c },
+       }
+};
+
 enum rcar_vin_state {
        STOPPED = 0,
        RUNNING,
@@ -161,6 +504,9 @@ struct rcar_vin_cam {
        /* Client output, as seen by the VIN */
        unsigned int                    width;
        unsigned int                    height;
+       /* User window from S_FMT */
+       unsigned int out_width;
+       unsigned int out_height;
        /*
         * User window from S_CROP / G_CROP, produced by client cropping and
         * scaling, VIN scaling and VIN cropping, mapped back onto the client
@@ -332,7 +678,7 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
                vnmc |= VNMC_BPS;
 
        /* progressive or interlaced mode */
-       interrupts = progressive ? VNIE_FIE | VNIE_EFE : VNIE_EFE;
+       interrupts = progressive ? VNIE_FIE : VNIE_EFE;
 
        /* ack interrupts */
        iowrite32(interrupts, priv->base + VNINTS_REG);
@@ -667,6 +1013,60 @@ static void rcar_vin_clock_stop(struct soc_camera_host *ici)
        /* VIN does not have "mclk" */
 }
 
+static void set_coeff(struct rcar_vin_priv *priv, unsigned short xs)
+{
+       int i;
+       const struct vin_coeff *p_prev_set = NULL;
+       const struct vin_coeff *p_set = NULL;
+
+       /* Look for suitable coefficient values */
+       for (i = 0; i < ARRAY_SIZE(vin_coeff_set); i++) {
+               p_prev_set = p_set;
+               p_set = &vin_coeff_set[i];
+
+               if (xs < p_set->xs_value)
+                       break;
+       }
+
+       /* Use previous value if its XS value is closer */
+       if (p_prev_set && p_set &&
+           xs - p_prev_set->xs_value < p_set->xs_value - xs)
+               p_set = p_prev_set;
+
+       /* Set coefficient registers */
+       iowrite32(p_set->coeff_set[0], priv->base + VNC1A_REG);
+       iowrite32(p_set->coeff_set[1], priv->base + VNC1B_REG);
+       iowrite32(p_set->coeff_set[2], priv->base + VNC1C_REG);
+
+       iowrite32(p_set->coeff_set[3], priv->base + VNC2A_REG);
+       iowrite32(p_set->coeff_set[4], priv->base + VNC2B_REG);
+       iowrite32(p_set->coeff_set[5], priv->base + VNC2C_REG);
+
+       iowrite32(p_set->coeff_set[6], priv->base + VNC3A_REG);
+       iowrite32(p_set->coeff_set[7], priv->base + VNC3B_REG);
+       iowrite32(p_set->coeff_set[8], priv->base + VNC3C_REG);
+
+       iowrite32(p_set->coeff_set[9], priv->base + VNC4A_REG);
+       iowrite32(p_set->coeff_set[10], priv->base + VNC4B_REG);
+       iowrite32(p_set->coeff_set[11], priv->base + VNC4C_REG);
+
+       iowrite32(p_set->coeff_set[12], priv->base + VNC5A_REG);
+       iowrite32(p_set->coeff_set[13], priv->base + VNC5B_REG);
+       iowrite32(p_set->coeff_set[14], priv->base + VNC5C_REG);
+
+       iowrite32(p_set->coeff_set[15], priv->base + VNC6A_REG);
+       iowrite32(p_set->coeff_set[16], priv->base + VNC6B_REG);
+       iowrite32(p_set->coeff_set[17], priv->base + VNC6C_REG);
+
+       iowrite32(p_set->coeff_set[18], priv->base + VNC7A_REG);
+       iowrite32(p_set->coeff_set[19], priv->base + VNC7B_REG);
+       iowrite32(p_set->coeff_set[20], priv->base + VNC7C_REG);
+
+       iowrite32(p_set->coeff_set[21], priv->base + VNC8A_REG);
+       iowrite32(p_set->coeff_set[22], priv->base + VNC8B_REG);
+       iowrite32(p_set->coeff_set[23], priv->base + VNC8C_REG);
+}
+
 /* rect is guaranteed to not exceed the scaled camera rectangle */
 static int rcar_vin_set_rect(struct soc_camera_device *icd)
 {
@@ -676,6 +1076,7 @@ static int rcar_vin_set_rect(struct soc_camera_device *icd)
        unsigned int left_offset, top_offset;
        unsigned char dsize = 0;
        struct v4l2_rect *cam_subrect = &cam->subrect;
+       u32 value;
 
        dev_dbg(icd->parent, "Crop %ux%u@%u:%u\n",
                icd->user_width, icd->user_height, cam->vin_left, cam->vin_top);
@@ -695,40 +1096,64 @@ static int rcar_vin_set_rect(struct soc_camera_device *icd)
 
        /* Set Start/End Pixel/Line Pre-Clip */
        iowrite32(left_offset << dsize, priv->base + VNSPPRC_REG);
-       iowrite32((left_offset + cam->width - 1) << dsize,
+       iowrite32((left_offset + cam_subrect->width - 1) << dsize,
                  priv->base + VNEPPRC_REG);
        switch (priv->field) {
        case V4L2_FIELD_INTERLACED:
        case V4L2_FIELD_INTERLACED_TB:
        case V4L2_FIELD_INTERLACED_BT:
                iowrite32(top_offset / 2, priv->base + VNSLPRC_REG);
-               iowrite32((top_offset + cam->height) / 2 - 1,
+               iowrite32((top_offset + cam_subrect->height) / 2 - 1,
                          priv->base + VNELPRC_REG);
                break;
        default:
                iowrite32(top_offset, priv->base + VNSLPRC_REG);
-               iowrite32(top_offset + cam->height - 1,
+               iowrite32(top_offset + cam_subrect->height - 1,
                          priv->base + VNELPRC_REG);
                break;
        }
 
+       /* Set scaling coefficient */
+       value = 0;
+       if (cam_subrect->height != cam->out_height)
+               value = (4096 * cam_subrect->height) / cam->out_height;
+       dev_dbg(icd->parent, "YS Value: %x\n", value);
+       iowrite32(value, priv->base + VNYS_REG);
+
+       value = 0;
+       if (cam_subrect->width != cam->out_width)
+               value = (4096 * cam_subrect->width) / cam->out_width;
+
+       /* Horizontal upscaling is up to double size */
+       if (0 < value && value < 2048)
+               value = 2048;
+
+       dev_dbg(icd->parent, "XS Value: %x\n", value);
+       iowrite32(value, priv->base + VNXS_REG);
+
+       /* Horizontal upscaling is carried out by scaling down from double size */
+       if (value < 4096)
+               value *= 2;
+
+       set_coeff(priv, value);
+
        /* Set Start/End Pixel/Line Post-Clip */
        iowrite32(0, priv->base + VNSPPOC_REG);
        iowrite32(0, priv->base + VNSLPOC_REG);
-       iowrite32((cam_subrect->width - 1) << dsize, priv->base + VNEPPOC_REG);
+       iowrite32((cam->out_width - 1) << dsize, priv->base + VNEPPOC_REG);
        switch (priv->field) {
        case V4L2_FIELD_INTERLACED:
        case V4L2_FIELD_INTERLACED_TB:
        case V4L2_FIELD_INTERLACED_BT:
-               iowrite32(cam_subrect->height / 2 - 1,
+               iowrite32(cam->out_height / 2 - 1,
                          priv->base + VNELPOC_REG);
                break;
        default:
-               iowrite32(cam_subrect->height - 1, priv->base + VNELPOC_REG);
+               iowrite32(cam->out_height - 1, priv->base + VNELPOC_REG);
                break;
        }
 
-       iowrite32(ALIGN(cam->width, 0x10), priv->base + VNIS_REG);
+       iowrite32(ALIGN(cam->out_width, 0x10), priv->base + VNIS_REG);
 
        return 0;
 }
@@ -819,7 +1244,7 @@ static int rcar_vin_set_bus_param(struct soc_camera_device *icd)
        if (ret < 0 && ret != -ENOIOCTLCMD)
                return ret;
 
-       val = priv->field == V4L2_FIELD_NONE ? VNDMR2_FTEV : 0;
+       val = VNDMR2_FTEV | VNDMR2_VLV(1);
        if (!(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
                val |= VNDMR2_VPS;
        if (!(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
@@ -879,6 +1304,14 @@ static const struct soc_mbus_pixelfmt rcar_vin_formats[] = {
                .order                  = SOC_MBUS_ORDER_LE,
                .layout                 = SOC_MBUS_LAYOUT_PLANAR_Y_C,
        },
+       {
+               .fourcc                 = V4L2_PIX_FMT_YUYV,
+               .name                   = "YUYV",
+               .bits_per_sample        = 16,
+               .packing                = SOC_MBUS_PACKING_NONE,
+               .order                  = SOC_MBUS_ORDER_LE,
+               .layout                 = SOC_MBUS_LAYOUT_PACKED,
+       },
        {
                .fourcc                 = V4L2_PIX_FMT_UYVY,
                .name                   = "UYVY",
@@ -999,6 +1432,8 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx,
                cam->subrect = rect;
                cam->width = mf.width;
                cam->height = mf.height;
+               cam->out_width  = mf.width;
+               cam->out_height = mf.height;
 
                icd->host_priv = cam;
        } else {
@@ -1259,6 +1694,9 @@ static int rcar_vin_set_fmt(struct soc_camera_device *icd,
        dev_dbg(dev, "W: %u : %u, H: %u : %u\n",
                vin_sub_width, pix->width, vin_sub_height, pix->height);
 
+       cam->out_width = pix->width;
+       cam->out_height = pix->height;
+
        icd->current_fmt = xlate;
 
        priv->field = field;
@@ -1310,8 +1748,12 @@ static int rcar_vin_try_fmt(struct soc_camera_device *icd,
        if (ret < 0)
                return ret;
 
-       pix->width = mf.width;
-       pix->height = mf.height;
+       /* Adjust only if VIN cannot scale */
+       if (pix->width > mf.width * 2)
+               pix->width = mf.width * 2;
+       if (pix->height > mf.height * 3)
+               pix->height = mf.height * 3;
+
        pix->field = mf.field;
        pix->colorspace = mf.colorspace;
 
@@ -1395,6 +1837,8 @@ static struct soc_camera_host_ops rcar_vin_host_ops = {
 
 #ifdef CONFIG_OF
 static struct of_device_id rcar_vin_of_table[] = {
+       { .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 },
+       { .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 },
        { .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 },
        { .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 },
        { .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 },
diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c
deleted file mode 100644 (file)
index 2c85357..0000000
+++ /dev/null
@@ -1,4345 +0,0 @@
-/*
- * Driver for the VINO (Video In No Out) system found in SGI Indys.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
- *
- * Based on the previous version of the driver for 2.4 kernels by:
- * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
- *
- * v4l2_device/v4l2_subdev conversion by:
- * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
- *
- * Note: this conversion is untested! Please contact the linux-media
- * mailinglist if you can test this, together with the test results.
- */
-
-/*
- * TODO:
- * - remove "mark pages reserved-hacks" from memory allocation code
- *   and implement fault()
- * - check decimation, calculating and reporting image size when
- *   using decimation
- * - implement read(), user mode buffers and overlay (?)
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/time.h>
-#include <linux/kmod.h>
-
-#include <linux/i2c.h>
-
-#include <linux/videodev2.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <linux/mutex.h>
-
-#include <asm/paccess.h>
-#include <asm/io.h>
-#include <asm/sgi/ip22.h>
-#include <asm/sgi/mc.h>
-
-#include "vino.h"
-#include "saa7191.h"
-#include "indycam.h"
-
-/* Uncomment the following line to get lots and lots of (mostly useless)
- * debug info.
- * Note that the debug output also slows down the driver significantly */
-// #define VINO_DEBUG
-// #define VINO_DEBUG_INT
-
-#define VINO_MODULE_VERSION "0.0.7"
-
-MODULE_DESCRIPTION("SGI VINO Video4Linux2 driver");
-MODULE_VERSION(VINO_MODULE_VERSION);
-MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
-MODULE_LICENSE("GPL");
-
-#ifdef VINO_DEBUG
-#define dprintk(x...) printk("VINO: " x);
-#else
-#define dprintk(x...)
-#endif
-
-#define VINO_NO_CHANNEL                        0
-#define VINO_CHANNEL_A                 1
-#define VINO_CHANNEL_B                 2
-
-#define VINO_PAL_WIDTH                 768
-#define VINO_PAL_HEIGHT                        576
-#define VINO_NTSC_WIDTH                        640
-#define VINO_NTSC_HEIGHT               480
-
-#define VINO_MIN_WIDTH                 32
-#define VINO_MIN_HEIGHT                        32
-
-#define VINO_CLIPPING_START_ODD_D1     1
-#define VINO_CLIPPING_START_ODD_PAL    15
-#define VINO_CLIPPING_START_ODD_NTSC   12
-
-#define VINO_CLIPPING_START_EVEN_D1    2
-#define VINO_CLIPPING_START_EVEN_PAL   15
-#define VINO_CLIPPING_START_EVEN_NTSC  12
-
-#define VINO_INPUT_CHANNEL_COUNT       3
-
-/* the number is the index for vino_inputs */
-#define VINO_INPUT_NONE                        -1
-#define VINO_INPUT_COMPOSITE           0
-#define VINO_INPUT_SVIDEO              1
-#define VINO_INPUT_D1                  2
-
-#define VINO_PAGE_RATIO                        (PAGE_SIZE / VINO_PAGE_SIZE)
-
-#define VINO_FIFO_THRESHOLD_DEFAULT    16
-
-#define VINO_FRAMEBUFFER_SIZE          ((VINO_PAL_WIDTH \
-                                         * VINO_PAL_HEIGHT * 4 \
-                                         + 3 * PAGE_SIZE) & ~(PAGE_SIZE - 1))
-
-#define VINO_FRAMEBUFFER_COUNT_MAX     8
-
-#define VINO_FRAMEBUFFER_UNUSED                0
-#define VINO_FRAMEBUFFER_IN_USE                1
-#define VINO_FRAMEBUFFER_READY         2
-
-#define VINO_QUEUE_ERROR               -1
-#define VINO_QUEUE_MAGIC               0x20050125
-
-#define VINO_MEMORY_NONE               0
-#define VINO_MEMORY_MMAP               1
-#define VINO_MEMORY_USERPTR            2
-
-#define VINO_DUMMY_DESC_COUNT          4
-#define VINO_DESC_FETCH_DELAY          5       /* microseconds */
-
-#define VINO_MAX_FRAME_SKIP_COUNT      128
-
-/* the number is the index for vino_data_formats */
-#define VINO_DATA_FMT_NONE             -1
-#define VINO_DATA_FMT_GREY             0
-#define VINO_DATA_FMT_RGB332           1
-#define VINO_DATA_FMT_RGB32            2
-#define VINO_DATA_FMT_YUV              3
-
-#define VINO_DATA_FMT_COUNT            4
-
-/* the number is the index for vino_data_norms */
-#define VINO_DATA_NORM_NONE            -1
-#define VINO_DATA_NORM_NTSC            0
-#define VINO_DATA_NORM_PAL             1
-#define VINO_DATA_NORM_SECAM           2
-#define VINO_DATA_NORM_D1              3
-
-#define VINO_DATA_NORM_COUNT           4
-
-/* I2C controller flags */
-#define SGI_I2C_FORCE_IDLE             (0 << 0)
-#define SGI_I2C_NOT_IDLE               (1 << 0)
-#define SGI_I2C_WRITE                  (0 << 1)
-#define SGI_I2C_READ                   (1 << 1)
-#define SGI_I2C_RELEASE_BUS            (0 << 2)
-#define SGI_I2C_HOLD_BUS               (1 << 2)
-#define SGI_I2C_XFER_DONE              (0 << 4)
-#define SGI_I2C_XFER_BUSY              (1 << 4)
-#define SGI_I2C_ACK                    (0 << 5)
-#define SGI_I2C_NACK                   (1 << 5)
-#define SGI_I2C_BUS_OK                 (0 << 7)
-#define SGI_I2C_BUS_ERR                        (1 << 7)
-
-/* Internal data structure definitions */
-
-struct vino_input {
-       char *name;
-       v4l2_std_id std;
-};
-
-struct vino_clipping {
-       unsigned int left, right, top, bottom;
-};
-
-struct vino_data_format {
-       /* the description */
-       char *description;
-       /* bytes per pixel */
-       unsigned int bpp;
-       /* V4L2 fourcc code */
-       __u32 pixelformat;
-       /* V4L2 colorspace (duh!) */
-       enum v4l2_colorspace colorspace;
-};
-
-struct vino_data_norm {
-       char *description;
-       unsigned int width, height;
-       struct vino_clipping odd;
-       struct vino_clipping even;
-
-       v4l2_std_id std;
-       unsigned int fps_min, fps_max;
-       __u32 framelines;
-};
-
-struct vino_descriptor_table {
-       /* the number of PAGE_SIZE sized pages in the buffer */
-       unsigned int page_count;
-       /* virtual (kmalloc'd) pointers to the actual data
-        * (in PAGE_SIZE chunks, used with mmap streaming) */
-       unsigned long *virtual;
-
-       /* cpu address for the VINO descriptor table
-        * (contains DMA addresses, VINO_PAGE_SIZE chunks) */
-       unsigned long *dma_cpu;
-       /* dma address for the VINO descriptor table
-        * (contains DMA addresses, VINO_PAGE_SIZE chunks) */
-       dma_addr_t dma;
-};
-
-struct vino_framebuffer {
-       /* identifier nubmer */
-       unsigned int id;
-       /* the length of the whole buffer */
-       unsigned int size;
-       /* the length of actual data in buffer */
-       unsigned int data_size;
-       /* the data format */
-       unsigned int data_format;
-       /* the state of buffer data */
-       unsigned int state;
-       /* is the buffer mapped in user space? */
-       unsigned int map_count;
-       /* memory offset for mmap() */
-       unsigned int offset;
-       /* frame counter */
-       unsigned int frame_counter;
-       /* timestamp (written when image capture finishes) */
-       struct timeval timestamp;
-
-       struct vino_descriptor_table desc_table;
-
-       spinlock_t state_lock;
-};
-
-struct vino_framebuffer_fifo {
-       unsigned int length;
-
-       unsigned int used;
-       unsigned int head;
-       unsigned int tail;
-
-       unsigned int data[VINO_FRAMEBUFFER_COUNT_MAX];
-};
-
-struct vino_framebuffer_queue {
-       unsigned int magic;
-
-       /* VINO_MEMORY_NONE, VINO_MEMORY_MMAP or VINO_MEMORY_USERPTR */
-       unsigned int type;
-       unsigned int length;
-
-       /* data field of in and out contain index numbers for buffer */
-       struct vino_framebuffer_fifo in;
-       struct vino_framebuffer_fifo out;
-
-       struct vino_framebuffer *buffer[VINO_FRAMEBUFFER_COUNT_MAX];
-
-       spinlock_t queue_lock;
-       struct mutex queue_mutex;
-       wait_queue_head_t frame_wait_queue;
-};
-
-struct vino_interrupt_data {
-       struct timeval timestamp;
-       unsigned int frame_counter;
-       unsigned int skip_count;
-       unsigned int skip;
-};
-
-struct vino_channel_settings {
-       unsigned int channel;
-
-       int input;
-       unsigned int data_format;
-       unsigned int data_norm;
-       struct vino_clipping clipping;
-       unsigned int decimation;
-       unsigned int line_size;
-       unsigned int alpha;
-       unsigned int fps;
-       unsigned int framert_reg;
-
-       unsigned int fifo_threshold;
-
-       struct vino_framebuffer_queue fb_queue;
-
-       /* number of the current field */
-       unsigned int field;
-
-       /* read in progress */
-       int reading;
-       /* streaming is active */
-       int streaming;
-       /* the driver is currently processing the queue */
-       int capturing;
-
-       struct mutex mutex;
-       spinlock_t capture_lock;
-
-       unsigned int users;
-
-       struct vino_interrupt_data int_data;
-
-       /* V4L support */
-       struct video_device *vdev;
-};
-
-struct vino_settings {
-       struct v4l2_device v4l2_dev;
-       struct vino_channel_settings a;
-       struct vino_channel_settings b;
-
-       /* the channel which owns this client:
-        * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */
-       unsigned int decoder_owner;
-       struct v4l2_subdev *decoder;
-       unsigned int camera_owner;
-       struct v4l2_subdev *camera;
-
-       /* a lock for vino register access */
-       spinlock_t vino_lock;
-       /* a lock for channel input changes */
-       spinlock_t input_lock;
-
-       unsigned long dummy_page;
-       struct vino_descriptor_table dummy_desc_table;
-};
-
-/* Module parameters */
-
-/*
- * Using vino_pixel_conversion the ABGR32-format pixels supplied
- * by the VINO chip can be converted to more common formats
- * like RGBA32 (or probably RGB24 in the future). This way we
- * can give out data that can be specified correctly with
- * the V4L2-definitions.
- *
- * The pixel format is specified as RGBA32 when no conversion
- * is used.
- *
- * Note that this only affects the 32-bit bit depth.
- *
- * Use non-zero value to enable conversion.
- */
-static int vino_pixel_conversion;
-
-module_param_named(pixelconv, vino_pixel_conversion, int, 0);
-
-MODULE_PARM_DESC(pixelconv,
-                "enable pixel conversion (non-zero value enables)");
-
-/* Internal data structures */
-
-static struct sgi_vino *vino;
-
-static struct vino_settings *vino_drvdata;
-
-#define camera_call(o, f, args...) \
-       v4l2_subdev_call(vino_drvdata->camera, o, f, ##args)
-#define decoder_call(o, f, args...) \
-       v4l2_subdev_call(vino_drvdata->decoder, o, f, ##args)
-
-static const char *vino_driver_name = "vino";
-static const char *vino_driver_description = "SGI VINO";
-static const char *vino_bus_name = "GIO64 bus";
-static const char *vino_vdev_name_a = "SGI VINO Channel A";
-static const char *vino_vdev_name_b = "SGI VINO Channel B";
-
-static void vino_capture_tasklet(unsigned long channel);
-
-DECLARE_TASKLET(vino_tasklet_a, vino_capture_tasklet, VINO_CHANNEL_A);
-DECLARE_TASKLET(vino_tasklet_b, vino_capture_tasklet, VINO_CHANNEL_B);
-
-static const struct vino_input vino_inputs[] = {
-       {
-               .name           = "Composite",
-               .std            = V4L2_STD_NTSC | V4L2_STD_PAL
-               | V4L2_STD_SECAM,
-       }, {
-               .name           = "S-Video",
-               .std            = V4L2_STD_NTSC | V4L2_STD_PAL
-               | V4L2_STD_SECAM,
-       }, {
-               .name           = "D1/IndyCam",
-               .std            = V4L2_STD_NTSC,
-       }
-};
-
-static const struct vino_data_format vino_data_formats[] = {
-       {
-               .description    = "8-bit greyscale",
-               .bpp            = 1,
-               .pixelformat    = V4L2_PIX_FMT_GREY,
-               .colorspace     = V4L2_COLORSPACE_SMPTE170M,
-       }, {
-               .description    = "8-bit dithered RGB 3-3-2",
-               .bpp            = 1,
-               .pixelformat    = V4L2_PIX_FMT_RGB332,
-               .colorspace     = V4L2_COLORSPACE_SRGB,
-       }, {
-               .description    = "32-bit RGB",
-               .bpp            = 4,
-               .pixelformat    = V4L2_PIX_FMT_RGB32,
-               .colorspace     = V4L2_COLORSPACE_SRGB,
-       }, {
-               .description    = "YUV 4:2:2",
-               .bpp            = 2,
-               .pixelformat    = V4L2_PIX_FMT_YUYV, // XXX: swapped?
-               .colorspace     = V4L2_COLORSPACE_SMPTE170M,
-       }
-};
-
-static const struct vino_data_norm vino_data_norms[] = {
-       {
-               .description    = "NTSC",
-               .std            = V4L2_STD_NTSC,
-               .fps_min        = 6,
-               .fps_max        = 30,
-               .framelines     = 525,
-               .width          = VINO_NTSC_WIDTH,
-               .height         = VINO_NTSC_HEIGHT,
-               .odd            = {
-                       .top    = VINO_CLIPPING_START_ODD_NTSC,
-                       .left   = 0,
-                       .bottom = VINO_CLIPPING_START_ODD_NTSC
-                       + VINO_NTSC_HEIGHT / 2 - 1,
-                       .right  = VINO_NTSC_WIDTH,
-               },
-               .even           = {
-                       .top    = VINO_CLIPPING_START_EVEN_NTSC,
-                       .left   = 0,
-                       .bottom = VINO_CLIPPING_START_EVEN_NTSC
-                       + VINO_NTSC_HEIGHT / 2 - 1,
-                       .right  = VINO_NTSC_WIDTH,
-               },
-       }, {
-               .description    = "PAL",
-               .std            = V4L2_STD_PAL,
-               .fps_min        = 5,
-               .fps_max        = 25,
-               .framelines     = 625,
-               .width          = VINO_PAL_WIDTH,
-               .height         = VINO_PAL_HEIGHT,
-               .odd            = {
-                       .top    = VINO_CLIPPING_START_ODD_PAL,
-                       .left   = 0,
-                       .bottom = VINO_CLIPPING_START_ODD_PAL
-                       + VINO_PAL_HEIGHT / 2 - 1,
-                       .right  = VINO_PAL_WIDTH,
-               },
-               .even           = {
-                       .top    = VINO_CLIPPING_START_EVEN_PAL,
-                       .left   = 0,
-                       .bottom = VINO_CLIPPING_START_EVEN_PAL
-                       + VINO_PAL_HEIGHT / 2 - 1,
-                       .right  = VINO_PAL_WIDTH,
-               },
-       }, {
-               .description    = "SECAM",
-               .std            = V4L2_STD_SECAM,
-               .fps_min        = 5,
-               .fps_max        = 25,
-               .framelines     = 625,
-               .width          = VINO_PAL_WIDTH,
-               .height         = VINO_PAL_HEIGHT,
-               .odd            = {
-                       .top    = VINO_CLIPPING_START_ODD_PAL,
-                       .left   = 0,
-                       .bottom = VINO_CLIPPING_START_ODD_PAL
-                       + VINO_PAL_HEIGHT / 2 - 1,
-                       .right  = VINO_PAL_WIDTH,
-               },
-               .even           = {
-                       .top    = VINO_CLIPPING_START_EVEN_PAL,
-                       .left   = 0,
-                       .bottom = VINO_CLIPPING_START_EVEN_PAL
-                       + VINO_PAL_HEIGHT / 2 - 1,
-                       .right  = VINO_PAL_WIDTH,
-               },
-       }, {
-               .description    = "NTSC/D1",
-               .std            = V4L2_STD_NTSC,
-               .fps_min        = 6,
-               .fps_max        = 30,
-               .framelines     = 525,
-               .width          = VINO_NTSC_WIDTH,
-               .height         = VINO_NTSC_HEIGHT,
-               .odd            = {
-                       .top    = VINO_CLIPPING_START_ODD_D1,
-                       .left   = 0,
-                       .bottom = VINO_CLIPPING_START_ODD_D1
-                       + VINO_NTSC_HEIGHT / 2 - 1,
-                       .right  = VINO_NTSC_WIDTH,
-               },
-               .even           = {
-                       .top    = VINO_CLIPPING_START_EVEN_D1,
-                       .left   = 0,
-                       .bottom = VINO_CLIPPING_START_EVEN_D1
-                       + VINO_NTSC_HEIGHT / 2 - 1,
-                       .right  = VINO_NTSC_WIDTH,
-               },
-       }
-};
-
-#define VINO_INDYCAM_V4L2_CONTROL_COUNT                9
-
-struct v4l2_queryctrl vino_indycam_v4l2_controls[] = {
-       {
-               .id = V4L2_CID_AUTOGAIN,
-               .type = V4L2_CTRL_TYPE_BOOLEAN,
-               .name = "Automatic Gain Control",
-               .minimum = 0,
-               .maximum = 1,
-               .step = 1,
-               .default_value = INDYCAM_AGC_DEFAULT,
-       }, {
-               .id = V4L2_CID_AUTO_WHITE_BALANCE,
-               .type = V4L2_CTRL_TYPE_BOOLEAN,
-               .name = "Automatic White Balance",
-               .minimum = 0,
-               .maximum = 1,
-               .step = 1,
-               .default_value = INDYCAM_AWB_DEFAULT,
-       }, {
-               .id = V4L2_CID_GAIN,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Gain",
-               .minimum = INDYCAM_GAIN_MIN,
-               .maximum = INDYCAM_GAIN_MAX,
-               .step = 1,
-               .default_value = INDYCAM_GAIN_DEFAULT,
-       }, {
-               .id = INDYCAM_CONTROL_RED_SATURATION,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Red Saturation",
-               .minimum = INDYCAM_RED_SATURATION_MIN,
-               .maximum = INDYCAM_RED_SATURATION_MAX,
-               .step = 1,
-               .default_value = INDYCAM_RED_SATURATION_DEFAULT,
-       }, {
-               .id = INDYCAM_CONTROL_BLUE_SATURATION,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Blue Saturation",
-               .minimum = INDYCAM_BLUE_SATURATION_MIN,
-               .maximum = INDYCAM_BLUE_SATURATION_MAX,
-               .step = 1,
-               .default_value = INDYCAM_BLUE_SATURATION_DEFAULT,
-       }, {
-               .id = V4L2_CID_RED_BALANCE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Red Balance",
-               .minimum = INDYCAM_RED_BALANCE_MIN,
-               .maximum = INDYCAM_RED_BALANCE_MAX,
-               .step = 1,
-               .default_value = INDYCAM_RED_BALANCE_DEFAULT,
-       }, {
-               .id = V4L2_CID_BLUE_BALANCE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Blue Balance",
-               .minimum = INDYCAM_BLUE_BALANCE_MIN,
-               .maximum = INDYCAM_BLUE_BALANCE_MAX,
-               .step = 1,
-               .default_value = INDYCAM_BLUE_BALANCE_DEFAULT,
-       }, {
-               .id = V4L2_CID_EXPOSURE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Shutter Control",
-               .minimum = INDYCAM_SHUTTER_MIN,
-               .maximum = INDYCAM_SHUTTER_MAX,
-               .step = 1,
-               .default_value = INDYCAM_SHUTTER_DEFAULT,
-       }, {
-               .id = V4L2_CID_GAMMA,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Gamma",
-               .minimum = INDYCAM_GAMMA_MIN,
-               .maximum = INDYCAM_GAMMA_MAX,
-               .step = 1,
-               .default_value = INDYCAM_GAMMA_DEFAULT,
-       }
-};
-
-#define VINO_SAA7191_V4L2_CONTROL_COUNT                9
-
-struct v4l2_queryctrl vino_saa7191_v4l2_controls[] = {
-       {
-               .id = V4L2_CID_HUE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Hue",
-               .minimum = SAA7191_HUE_MIN,
-               .maximum = SAA7191_HUE_MAX,
-               .step = 1,
-               .default_value = SAA7191_HUE_DEFAULT,
-       }, {
-               .id = SAA7191_CONTROL_BANDPASS,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Luminance Bandpass",
-               .minimum = SAA7191_BANDPASS_MIN,
-               .maximum = SAA7191_BANDPASS_MAX,
-               .step = 1,
-               .default_value = SAA7191_BANDPASS_DEFAULT,
-       }, {
-               .id = SAA7191_CONTROL_BANDPASS_WEIGHT,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Luminance Bandpass Weight",
-               .minimum = SAA7191_BANDPASS_WEIGHT_MIN,
-               .maximum = SAA7191_BANDPASS_WEIGHT_MAX,
-               .step = 1,
-               .default_value = SAA7191_BANDPASS_WEIGHT_DEFAULT,
-       }, {
-               .id = SAA7191_CONTROL_CORING,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "HF Luminance Coring",
-               .minimum = SAA7191_CORING_MIN,
-               .maximum = SAA7191_CORING_MAX,
-               .step = 1,
-               .default_value = SAA7191_CORING_DEFAULT,
-       }, {
-               .id = SAA7191_CONTROL_FORCE_COLOUR,
-               .type = V4L2_CTRL_TYPE_BOOLEAN,
-               .name = "Force Colour",
-               .minimum = SAA7191_FORCE_COLOUR_MIN,
-               .maximum = SAA7191_FORCE_COLOUR_MAX,
-               .step = 1,
-               .default_value = SAA7191_FORCE_COLOUR_DEFAULT,
-       }, {
-               .id = SAA7191_CONTROL_CHROMA_GAIN,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Chrominance Gain Control",
-               .minimum = SAA7191_CHROMA_GAIN_MIN,
-               .maximum = SAA7191_CHROMA_GAIN_MAX,
-               .step = 1,
-               .default_value = SAA7191_CHROMA_GAIN_DEFAULT,
-       }, {
-               .id = SAA7191_CONTROL_VTRC,
-               .type = V4L2_CTRL_TYPE_BOOLEAN,
-               .name = "VTR Time Constant",
-               .minimum = SAA7191_VTRC_MIN,
-               .maximum = SAA7191_VTRC_MAX,
-               .step = 1,
-               .default_value = SAA7191_VTRC_DEFAULT,
-       }, {
-               .id = SAA7191_CONTROL_LUMA_DELAY,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Luminance Delay Compensation",
-               .minimum = SAA7191_LUMA_DELAY_MIN,
-               .maximum = SAA7191_LUMA_DELAY_MAX,
-               .step = 1,
-               .default_value = SAA7191_LUMA_DELAY_DEFAULT,
-       }, {
-               .id = SAA7191_CONTROL_VNR,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Vertical Noise Reduction",
-               .minimum = SAA7191_VNR_MIN,
-               .maximum = SAA7191_VNR_MAX,
-               .step = 1,
-               .default_value = SAA7191_VNR_DEFAULT,
-       }
-};
-
-/* VINO framebuffer/DMA descriptor management */
-
-static void vino_free_buffer_with_count(struct vino_framebuffer *fb,
-                                              unsigned int count)
-{
-       unsigned int i;
-
-       dprintk("vino_free_buffer_with_count(): count = %d\n", count);
-
-       for (i = 0; i < count; i++) {
-               ClearPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
-               dma_unmap_single(NULL,
-                                fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
-                                PAGE_SIZE, DMA_FROM_DEVICE);
-               free_page(fb->desc_table.virtual[i]);
-       }
-
-       dma_free_coherent(NULL,
-                         VINO_PAGE_RATIO * (fb->desc_table.page_count + 4) *
-                         sizeof(dma_addr_t), (void *)fb->desc_table.dma_cpu,
-                         fb->desc_table.dma);
-       kfree(fb->desc_table.virtual);
-
-       memset(fb, 0, sizeof(struct vino_framebuffer));
-}
-
-static void vino_free_buffer(struct vino_framebuffer *fb)
-{
-       vino_free_buffer_with_count(fb, fb->desc_table.page_count);
-}
-
-static int vino_allocate_buffer(struct vino_framebuffer *fb,
-                               unsigned int size)
-{
-       unsigned int count, i, j;
-       int ret = 0;
-
-       dprintk("vino_allocate_buffer():\n");
-
-       if (size < 1)
-               return -EINVAL;
-
-       memset(fb, 0, sizeof(struct vino_framebuffer));
-
-       count = ((size / PAGE_SIZE) + 4) & ~3;
-
-       dprintk("vino_allocate_buffer(): size = %d, count = %d\n",
-               size, count);
-
-       /* allocate memory for table with virtual (page) addresses */
-       fb->desc_table.virtual =
-               kmalloc(count * sizeof(unsigned long), GFP_KERNEL);
-       if (!fb->desc_table.virtual)
-               return -ENOMEM;
-
-       /* allocate memory for table with dma addresses
-        * (has space for four extra descriptors) */
-       fb->desc_table.dma_cpu =
-               dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) *
-                                  sizeof(dma_addr_t), &fb->desc_table.dma,
-                                  GFP_KERNEL | GFP_DMA);
-       if (!fb->desc_table.dma_cpu) {
-               ret = -ENOMEM;
-               goto out_free_virtual;
-       }
-
-       /* allocate pages for the buffer and acquire the according
-        * dma addresses */
-       for (i = 0; i < count; i++) {
-               dma_addr_t dma_data_addr;
-
-               fb->desc_table.virtual[i] =
-                       get_zeroed_page(GFP_KERNEL | GFP_DMA);
-               if (!fb->desc_table.virtual[i]) {
-                       ret = -ENOBUFS;
-                       break;
-               }
-
-               dma_data_addr =
-                       dma_map_single(NULL,
-                                      (void *)fb->desc_table.virtual[i],
-                                      PAGE_SIZE, DMA_FROM_DEVICE);
-
-               for (j = 0; j < VINO_PAGE_RATIO; j++) {
-                       fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] =
-                               dma_data_addr + VINO_PAGE_SIZE * j;
-               }
-
-               SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
-       }
-
-       /* page_count needs to be set anyway, because the descriptor table has
-        * been allocated according to this number */
-       fb->desc_table.page_count = count;
-
-       if (ret) {
-               /* the descriptor with index i doesn't contain
-                * a valid address yet */
-               vino_free_buffer_with_count(fb, i);
-               return ret;
-       }
-
-       //fb->size = size;
-       fb->size = count * PAGE_SIZE;
-       fb->data_format = VINO_DATA_FMT_NONE;
-
-       /* set the dma stop-bit for the last (count+1)th descriptor */
-       fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP;
-       return 0;
-
- out_free_virtual:
-       kfree(fb->desc_table.virtual);
-       return ret;
-}
-
-#if 0
-/* user buffers not fully implemented yet */
-static int vino_prepare_user_buffer(struct vino_framebuffer *fb,
-                                    void *user,
-                                    unsigned int size)
-{
-       unsigned int count, i, j;
-       int ret = 0;
-
-       dprintk("vino_prepare_user_buffer():\n");
-
-       if (size < 1)
-               return -EINVAL;
-
-       memset(fb, 0, sizeof(struct vino_framebuffer));
-
-       count = ((size / PAGE_SIZE)) & ~3;
-
-       dprintk("vino_prepare_user_buffer(): size = %d, count = %d\n",
-               size, count);
-
-       /* allocate memory for table with virtual (page) addresses */
-       fb->desc_table.virtual = (unsigned long *)
-               kmalloc(count * sizeof(unsigned long), GFP_KERNEL);
-       if (!fb->desc_table.virtual)
-               return -ENOMEM;
-
-       /* allocate memory for table with dma addresses
-        * (has space for four extra descriptors) */
-       fb->desc_table.dma_cpu =
-               dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) *
-                                  sizeof(dma_addr_t), &fb->desc_table.dma,
-                                  GFP_KERNEL | GFP_DMA);
-       if (!fb->desc_table.dma_cpu) {
-               ret = -ENOMEM;
-               goto out_free_virtual;
-       }
-
-       /* allocate pages for the buffer and acquire the according
-        * dma addresses */
-       for (i = 0; i < count; i++) {
-               dma_addr_t dma_data_addr;
-
-               fb->desc_table.virtual[i] =
-                       get_zeroed_page(GFP_KERNEL | GFP_DMA);
-               if (!fb->desc_table.virtual[i]) {
-                       ret = -ENOBUFS;
-                       break;
-               }
-
-               dma_data_addr =
-                       dma_map_single(NULL,
-                                      (void *)fb->desc_table.virtual[i],
-                                      PAGE_SIZE, DMA_FROM_DEVICE);
-
-               for (j = 0; j < VINO_PAGE_RATIO; j++) {
-                       fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] =
-                               dma_data_addr + VINO_PAGE_SIZE * j;
-               }
-
-               SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
-       }
-
-       /* page_count needs to be set anyway, because the descriptor table has
-        * been allocated according to this number */
-       fb->desc_table.page_count = count;
-
-       if (ret) {
-               /* the descriptor with index i doesn't contain
-                * a valid address yet */
-               vino_free_buffer_with_count(fb, i);
-               return ret;
-       }
-
-       //fb->size = size;
-       fb->size = count * PAGE_SIZE;
-
-       /* set the dma stop-bit for the last (count+1)th descriptor */
-       fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP;
-       return 0;
-
- out_free_virtual:
-       kfree(fb->desc_table.virtual);
-       return ret;
-}
-#endif
-
-static void vino_sync_buffer(struct vino_framebuffer *fb)
-{
-       int i;
-
-       dprintk("vino_sync_buffer():\n");
-
-       for (i = 0; i < fb->desc_table.page_count; i++)
-               dma_sync_single_for_cpu(NULL,
-                                       fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
-                                       PAGE_SIZE, DMA_FROM_DEVICE);
-}
-
-/* Framebuffer fifo functions (need to be locked externally) */
-
-static inline void vino_fifo_init(struct vino_framebuffer_fifo *f,
-                          unsigned int length)
-{
-       f->length = 0;
-       f->used = 0;
-       f->head = 0;
-       f->tail = 0;
-
-       if (length > VINO_FRAMEBUFFER_COUNT_MAX)
-               length = VINO_FRAMEBUFFER_COUNT_MAX;
-
-       f->length = length;
-}
-
-/* returns true/false */
-static inline int vino_fifo_has_id(struct vino_framebuffer_fifo *f,
-                                  unsigned int id)
-{
-       unsigned int i;
-
-       for (i = f->head; i == (f->tail - 1); i = (i + 1) % f->length) {
-               if (f->data[i] == id)
-                       return 1;
-       }
-
-       return 0;
-}
-
-#if 0
-/* returns true/false */
-static inline int vino_fifo_full(struct vino_framebuffer_fifo *f)
-{
-       return (f->used == f->length);
-}
-#endif
-
-static inline unsigned int vino_fifo_get_used(struct vino_framebuffer_fifo *f)
-{
-       return f->used;
-}
-
-static int vino_fifo_enqueue(struct vino_framebuffer_fifo *f, unsigned int id)
-{
-       if (id >= f->length) {
-               return VINO_QUEUE_ERROR;
-       }
-
-       if (vino_fifo_has_id(f, id)) {
-               return VINO_QUEUE_ERROR;
-       }
-
-       if (f->used < f->length) {
-               f->data[f->tail] = id;
-               f->tail = (f->tail + 1) % f->length;
-               f->used++;
-       } else {
-               return VINO_QUEUE_ERROR;
-       }
-
-       return 0;
-}
-
-static int vino_fifo_peek(struct vino_framebuffer_fifo *f, unsigned int *id)
-{
-       if (f->used > 0) {
-               *id = f->data[f->head];
-       } else {
-               return VINO_QUEUE_ERROR;
-       }
-
-       return 0;
-}
-
-static int vino_fifo_dequeue(struct vino_framebuffer_fifo *f, unsigned int *id)
-{
-       if (f->used > 0) {
-               *id = f->data[f->head];
-               f->head = (f->head + 1) % f->length;
-               f->used--;
-       } else {
-               return VINO_QUEUE_ERROR;
-       }
-
-       return 0;
-}
-
-/* Framebuffer queue functions */
-
-/* execute with queue_lock locked */
-static void vino_queue_free_with_count(struct vino_framebuffer_queue *q,
-                                      unsigned int length)
-{
-       unsigned int i;
-
-       q->length = 0;
-       memset(&q->in, 0, sizeof(struct vino_framebuffer_fifo));
-       memset(&q->out, 0, sizeof(struct vino_framebuffer_fifo));
-       for (i = 0; i < length; i++) {
-               dprintk("vino_queue_free_with_count(): freeing buffer %d\n",
-                       i);
-               vino_free_buffer(q->buffer[i]);
-               kfree(q->buffer[i]);
-       }
-
-       q->type = VINO_MEMORY_NONE;
-       q->magic = 0;
-}
-
-static void vino_queue_free(struct vino_framebuffer_queue *q)
-{
-       dprintk("vino_queue_free():\n");
-
-       if (q->magic != VINO_QUEUE_MAGIC)
-               return;
-       if (q->type != VINO_MEMORY_MMAP)
-               return;
-
-       mutex_lock(&q->queue_mutex);
-
-       vino_queue_free_with_count(q, q->length);
-
-       mutex_unlock(&q->queue_mutex);
-}
-
-static int vino_queue_init(struct vino_framebuffer_queue *q,
-                          unsigned int *length)
-{
-       unsigned int i;
-       int ret = 0;
-
-       dprintk("vino_queue_init(): length = %d\n", *length);
-
-       if (q->magic == VINO_QUEUE_MAGIC) {
-               dprintk("vino_queue_init(): queue already initialized!\n");
-               return -EINVAL;
-       }
-
-       if (q->type != VINO_MEMORY_NONE) {
-               dprintk("vino_queue_init(): queue already initialized!\n");
-               return -EINVAL;
-       }
-
-       if (*length < 1)
-               return -EINVAL;
-
-       mutex_lock(&q->queue_mutex);
-
-       if (*length > VINO_FRAMEBUFFER_COUNT_MAX)
-               *length = VINO_FRAMEBUFFER_COUNT_MAX;
-
-       q->length = 0;
-
-       for (i = 0; i < *length; i++) {
-               dprintk("vino_queue_init(): allocating buffer %d\n", i);
-               q->buffer[i] = kmalloc(sizeof(struct vino_framebuffer),
-                                      GFP_KERNEL);
-               if (!q->buffer[i]) {
-                       dprintk("vino_queue_init(): kmalloc() failed\n");
-                       ret = -ENOMEM;
-                       break;
-               }
-
-               ret = vino_allocate_buffer(q->buffer[i],
-                                          VINO_FRAMEBUFFER_SIZE);
-               if (ret) {
-                       kfree(q->buffer[i]);
-                       dprintk("vino_queue_init(): "
-                               "vino_allocate_buffer() failed\n");
-                       break;
-               }
-
-               q->buffer[i]->id = i;
-               if (i > 0) {
-                       q->buffer[i]->offset = q->buffer[i - 1]->offset +
-                               q->buffer[i - 1]->size;
-               } else {
-                       q->buffer[i]->offset = 0;
-               }
-
-               spin_lock_init(&q->buffer[i]->state_lock);
-
-               dprintk("vino_queue_init(): buffer = %d, offset = %d, "
-                       "size = %d\n", i, q->buffer[i]->offset,
-                       q->buffer[i]->size);
-       }
-
-       if (ret) {
-               vino_queue_free_with_count(q, i);
-               *length = 0;
-       } else {
-               q->length = *length;
-               vino_fifo_init(&q->in, q->length);
-               vino_fifo_init(&q->out, q->length);
-               q->type = VINO_MEMORY_MMAP;
-               q->magic = VINO_QUEUE_MAGIC;
-       }
-
-       mutex_unlock(&q->queue_mutex);
-
-       return ret;
-}
-
-static struct vino_framebuffer *vino_queue_add(struct
-                                              vino_framebuffer_queue *q,
-                                              unsigned int id)
-{
-       struct vino_framebuffer *ret = NULL;
-       unsigned int total;
-       unsigned long flags;
-
-       dprintk("vino_queue_add(): id = %d\n", id);
-
-       if (q->magic != VINO_QUEUE_MAGIC) {
-               return ret;
-       }
-
-       spin_lock_irqsave(&q->queue_lock, flags);
-
-       if (q->length == 0)
-               goto out;
-
-       if (id >= q->length)
-               goto out;
-
-       /* not needed?: if (vino_fifo_full(&q->out)) {
-               goto out;
-               }*/
-       /* check that outgoing queue isn't already full
-        * (or that it won't become full) */
-       total = vino_fifo_get_used(&q->in) +
-               vino_fifo_get_used(&q->out);
-       if (total >= q->length)
-               goto out;
-
-       if (vino_fifo_enqueue(&q->in, id))
-               goto out;
-
-       ret = q->buffer[id];
-
-out:
-       spin_unlock_irqrestore(&q->queue_lock, flags);
-
-       return ret;
-}
-
-static struct vino_framebuffer *vino_queue_transfer(struct
-                                                   vino_framebuffer_queue *q)
-{
-       struct vino_framebuffer *ret = NULL;
-       struct vino_framebuffer *fb;
-       int id;
-       unsigned long flags;
-
-       dprintk("vino_queue_transfer():\n");
-
-       if (q->magic != VINO_QUEUE_MAGIC) {
-               return ret;
-       }
-
-       spin_lock_irqsave(&q->queue_lock, flags);
-
-       if (q->length == 0)
-               goto out;
-
-       // now this actually removes an entry from the incoming queue
-       if (vino_fifo_dequeue(&q->in, &id)) {
-               goto out;
-       }
-
-       dprintk("vino_queue_transfer(): id = %d\n", id);
-       fb = q->buffer[id];
-
-       // we have already checked that the outgoing queue is not full, but...
-       if (vino_fifo_enqueue(&q->out, id)) {
-               printk(KERN_ERR "vino_queue_transfer(): "
-                      "outgoing queue is full, this shouldn't happen!\n");
-               goto out;
-       }
-
-       ret = fb;
-out:
-       spin_unlock_irqrestore(&q->queue_lock, flags);
-
-       return ret;
-}
-
-/* returns true/false */
-static int vino_queue_incoming_contains(struct vino_framebuffer_queue *q,
-                                       unsigned int id)
-{
-       int ret = 0;
-       unsigned long flags;
-
-       if (q->magic != VINO_QUEUE_MAGIC) {
-               return ret;
-       }
-
-       spin_lock_irqsave(&q->queue_lock, flags);
-
-       if (q->length == 0)
-               goto out;
-
-       ret = vino_fifo_has_id(&q->in, id);
-
-out:
-       spin_unlock_irqrestore(&q->queue_lock, flags);
-
-       return ret;
-}
-
-/* returns true/false */
-static int vino_queue_outgoing_contains(struct vino_framebuffer_queue *q,
-                                       unsigned int id)
-{
-       int ret = 0;
-       unsigned long flags;
-
-       if (q->magic != VINO_QUEUE_MAGIC) {
-               return ret;
-       }
-
-       spin_lock_irqsave(&q->queue_lock, flags);
-
-       if (q->length == 0)
-               goto out;
-
-       ret = vino_fifo_has_id(&q->out, id);
-
-out:
-       spin_unlock_irqrestore(&q->queue_lock, flags);
-
-       return ret;
-}
-
-static int vino_queue_get_incoming(struct vino_framebuffer_queue *q,
-                                  unsigned int *used)
-{
-       int ret = 0;
-       unsigned long flags;
-
-       if (q->magic != VINO_QUEUE_MAGIC) {
-               return VINO_QUEUE_ERROR;
-       }
-
-       spin_lock_irqsave(&q->queue_lock, flags);
-
-       if (q->length == 0) {
-               ret = VINO_QUEUE_ERROR;
-               goto out;
-       }
-
-       *used = vino_fifo_get_used(&q->in);
-
-out:
-       spin_unlock_irqrestore(&q->queue_lock, flags);
-
-       return ret;
-}
-
-static int vino_queue_get_outgoing(struct vino_framebuffer_queue *q,
-                                  unsigned int *used)
-{
-       int ret = 0;
-       unsigned long flags;
-
-       if (q->magic != VINO_QUEUE_MAGIC) {
-               return VINO_QUEUE_ERROR;
-       }
-
-       spin_lock_irqsave(&q->queue_lock, flags);
-
-       if (q->length == 0) {
-               ret = VINO_QUEUE_ERROR;
-               goto out;
-       }
-
-       *used = vino_fifo_get_used(&q->out);
-
-out:
-       spin_unlock_irqrestore(&q->queue_lock, flags);
-
-       return ret;
-}
-
-#if 0
-static int vino_queue_get_total(struct vino_framebuffer_queue *q,
-                               unsigned int *total)
-{
-       int ret = 0;
-       unsigned long flags;
-
-       if (q->magic != VINO_QUEUE_MAGIC) {
-               return VINO_QUEUE_ERROR;
-       }
-
-       spin_lock_irqsave(&q->queue_lock, flags);
-
-       if (q->length == 0) {
-               ret = VINO_QUEUE_ERROR;
-               goto out;
-       }
-
-       *total = vino_fifo_get_used(&q->in) +
-               vino_fifo_get_used(&q->out);
-
-out:
-       spin_unlock_irqrestore(&q->queue_lock, flags);
-
-       return ret;
-}
-#endif
-
-static struct vino_framebuffer *vino_queue_peek(struct
-                                               vino_framebuffer_queue *q,
-                                               unsigned int *id)
-{
-       struct vino_framebuffer *ret = NULL;
-       unsigned long flags;
-
-       if (q->magic != VINO_QUEUE_MAGIC) {
-               return ret;
-       }
-
-       spin_lock_irqsave(&q->queue_lock, flags);
-
-       if (q->length == 0)
-               goto out;
-
-       if (vino_fifo_peek(&q->in, id)) {
-               goto out;
-       }
-
-       ret = q->buffer[*id];
-out:
-       spin_unlock_irqrestore(&q->queue_lock, flags);
-
-       return ret;
-}
-
-static struct vino_framebuffer *vino_queue_remove(struct
-                                                 vino_framebuffer_queue *q,
-                                                 unsigned int *id)
-{
-       struct vino_framebuffer *ret = NULL;
-       unsigned long flags;
-       dprintk("vino_queue_remove():\n");
-
-       if (q->magic != VINO_QUEUE_MAGIC) {
-               return ret;
-       }
-
-       spin_lock_irqsave(&q->queue_lock, flags);
-
-       if (q->length == 0)
-               goto out;
-
-       if (vino_fifo_dequeue(&q->out, id)) {
-               goto out;
-       }
-
-       dprintk("vino_queue_remove(): id = %d\n", *id);
-       ret = q->buffer[*id];
-out:
-       spin_unlock_irqrestore(&q->queue_lock, flags);
-
-       return ret;
-}
-
-static struct
-vino_framebuffer *vino_queue_get_buffer(struct vino_framebuffer_queue *q,
-                                       unsigned int id)
-{
-       struct vino_framebuffer *ret = NULL;
-       unsigned long flags;
-
-       if (q->magic != VINO_QUEUE_MAGIC) {
-               return ret;
-       }
-
-       spin_lock_irqsave(&q->queue_lock, flags);
-
-       if (q->length == 0)
-               goto out;
-
-       if (id >= q->length)
-               goto out;
-
-       ret = q->buffer[id];
- out:
-       spin_unlock_irqrestore(&q->queue_lock, flags);
-
-       return ret;
-}
-
-static unsigned int vino_queue_get_length(struct vino_framebuffer_queue *q)
-{
-       unsigned int length = 0;
-       unsigned long flags;
-
-       if (q->magic != VINO_QUEUE_MAGIC) {
-               return length;
-       }
-
-       spin_lock_irqsave(&q->queue_lock, flags);
-       length = q->length;
-       spin_unlock_irqrestore(&q->queue_lock, flags);
-
-       return length;
-}
-
-static int vino_queue_has_mapped_buffers(struct vino_framebuffer_queue *q)
-{
-       unsigned int i;
-       int ret = 0;
-       unsigned long flags;
-
-       if (q->magic != VINO_QUEUE_MAGIC) {
-               return ret;
-       }
-
-       spin_lock_irqsave(&q->queue_lock, flags);
-       for (i = 0; i < q->length; i++) {
-               if (q->buffer[i]->map_count > 0) {
-                       ret = 1;
-                       break;
-               }
-       }
-       spin_unlock_irqrestore(&q->queue_lock, flags);
-
-       return ret;
-}
-
-/* VINO functions */
-
-/* execute with input_lock locked */
-static void vino_update_line_size(struct vino_channel_settings *vcs)
-{
-       unsigned int w = vcs->clipping.right - vcs->clipping.left;
-       unsigned int d = vcs->decimation;
-       unsigned int bpp = vino_data_formats[vcs->data_format].bpp;
-       unsigned int lsize;
-
-       dprintk("update_line_size(): before: w = %d, d = %d, "
-               "line_size = %d\n", w, d, vcs->line_size);
-
-       /* line size must be multiple of 8 bytes */
-       lsize = (bpp * (w / d)) & ~7;
-       w = (lsize / bpp) * d;
-
-       vcs->clipping.right = vcs->clipping.left + w;
-       vcs->line_size = lsize;
-
-       dprintk("update_line_size(): after: w = %d, d = %d, "
-               "line_size = %d\n", w, d, vcs->line_size);
-}
-
-/* execute with input_lock locked */
-static void vino_set_clipping(struct vino_channel_settings *vcs,
-                             unsigned int x, unsigned int y,
-                             unsigned int w, unsigned int h)
-{
-       unsigned int maxwidth, maxheight;
-       unsigned int d;
-
-       maxwidth = vino_data_norms[vcs->data_norm].width;
-       maxheight = vino_data_norms[vcs->data_norm].height;
-       d = vcs->decimation;
-
-       y &= ~1;        /* odd/even fields */
-
-       if (x > maxwidth) {
-               x = 0;
-       }
-       if (y > maxheight) {
-               y = 0;
-       }
-
-       if (((w / d) < VINO_MIN_WIDTH)
-           || ((h / d) < VINO_MIN_HEIGHT)) {
-               w = VINO_MIN_WIDTH * d;
-               h = VINO_MIN_HEIGHT * d;
-       }
-
-       if ((x + w) > maxwidth) {
-               w = maxwidth - x;
-               if ((w / d) < VINO_MIN_WIDTH)
-                       x = maxwidth - VINO_MIN_WIDTH * d;
-       }
-       if ((y + h) > maxheight) {
-               h = maxheight - y;
-               if ((h / d) < VINO_MIN_HEIGHT)
-                       y = maxheight - VINO_MIN_HEIGHT * d;
-       }
-
-       vcs->clipping.left = x;
-       vcs->clipping.top = y;
-       vcs->clipping.right = x + w;
-       vcs->clipping.bottom = y + h;
-
-       vino_update_line_size(vcs);
-
-       dprintk("clipping %d, %d, %d, %d / %d - %d\n",
-               vcs->clipping.left, vcs->clipping.top, vcs->clipping.right,
-               vcs->clipping.bottom, vcs->decimation, vcs->line_size);
-}
-
-/* execute with input_lock locked */
-static inline void vino_set_default_clipping(struct vino_channel_settings *vcs)
-{
-       vino_set_clipping(vcs, 0, 0, vino_data_norms[vcs->data_norm].width,
-                         vino_data_norms[vcs->data_norm].height);
-}
-
-/* execute with input_lock locked */
-static void vino_set_scaling(struct vino_channel_settings *vcs,
-                            unsigned int w, unsigned int h)
-{
-       unsigned int x, y, curw, curh, d;
-
-       x = vcs->clipping.left;
-       y = vcs->clipping.top;
-       curw = vcs->clipping.right - vcs->clipping.left;
-       curh = vcs->clipping.bottom - vcs->clipping.top;
-
-       d = max(curw / w, curh / h);
-
-       dprintk("scaling w: %d, h: %d, curw: %d, curh: %d, d: %d\n",
-               w, h, curw, curh, d);
-
-       if (d < 1) {
-               d = 1;
-       } else if (d > 8) {
-               d = 8;
-       }
-
-       vcs->decimation = d;
-       vino_set_clipping(vcs, x, y, w * d, h * d);
-
-       dprintk("scaling %d, %d, %d, %d / %d - %d\n", vcs->clipping.left,
-               vcs->clipping.top, vcs->clipping.right, vcs->clipping.bottom,
-               vcs->decimation, vcs->line_size);
-}
-
-/* execute with input_lock locked */
-static inline void vino_set_default_scaling(struct vino_channel_settings *vcs)
-{
-       vino_set_scaling(vcs, vcs->clipping.right - vcs->clipping.left,
-                        vcs->clipping.bottom - vcs->clipping.top);
-}
-
-/* execute with input_lock locked */
-static void vino_set_framerate(struct vino_channel_settings *vcs,
-                              unsigned int fps)
-{
-       unsigned int mask;
-
-       switch (vcs->data_norm) {
-       case VINO_DATA_NORM_NTSC:
-       case VINO_DATA_NORM_D1:
-               fps = (unsigned int)(fps / 6) * 6; // FIXME: round!
-
-               if (fps < vino_data_norms[vcs->data_norm].fps_min)
-                       fps = vino_data_norms[vcs->data_norm].fps_min;
-               if (fps > vino_data_norms[vcs->data_norm].fps_max)
-                       fps = vino_data_norms[vcs->data_norm].fps_max;
-
-               switch (fps) {
-               case 6:
-                       mask = 0x003;
-                       break;
-               case 12:
-                       mask = 0x0c3;
-                       break;
-               case 18:
-                       mask = 0x333;
-                       break;
-               case 24:
-                       mask = 0x3ff;
-                       break;
-               case 30:
-                       mask = 0xfff;
-                       break;
-               default:
-                       mask = VINO_FRAMERT_FULL;
-               }
-               vcs->framert_reg = VINO_FRAMERT_RT(mask);
-               break;
-       case VINO_DATA_NORM_PAL:
-       case VINO_DATA_NORM_SECAM:
-               fps = (unsigned int)(fps / 5) * 5; // FIXME: round!
-
-               if (fps < vino_data_norms[vcs->data_norm].fps_min)
-                       fps = vino_data_norms[vcs->data_norm].fps_min;
-               if (fps > vino_data_norms[vcs->data_norm].fps_max)
-                       fps = vino_data_norms[vcs->data_norm].fps_max;
-
-               switch (fps) {
-               case 5:
-                       mask = 0x003;
-                       break;
-               case 10:
-                       mask = 0x0c3;
-                       break;
-               case 15:
-                       mask = 0x333;
-                       break;
-               case 20:
-                       mask = 0x0ff;
-                       break;
-               case 25:
-                       mask = 0x3ff;
-                       break;
-               default:
-                       mask = VINO_FRAMERT_FULL;
-               }
-               vcs->framert_reg = VINO_FRAMERT_RT(mask) | VINO_FRAMERT_PAL;
-               break;
-       }
-
-       vcs->fps = fps;
-}
-
-/* execute with input_lock locked */
-static inline void vino_set_default_framerate(struct
-                                             vino_channel_settings *vcs)
-{
-       vino_set_framerate(vcs, vino_data_norms[vcs->data_norm].fps_max);
-}
-
-/* VINO I2C bus functions */
-
-struct i2c_algo_sgi_data {
-       void *data;     /* private data for lowlevel routines */
-       unsigned (*getctrl)(void *data);
-       void (*setctrl)(void *data, unsigned val);
-       unsigned (*rdata)(void *data);
-       void (*wdata)(void *data, unsigned val);
-
-       int xfer_timeout;
-       int ack_timeout;
-};
-
-static int wait_xfer_done(struct i2c_algo_sgi_data *adap)
-{
-       int i;
-
-       for (i = 0; i < adap->xfer_timeout; i++) {
-               if ((adap->getctrl(adap->data) & SGI_I2C_XFER_BUSY) == 0)
-                       return 0;
-               udelay(1);
-       }
-
-       return -ETIMEDOUT;
-}
-
-static int wait_ack(struct i2c_algo_sgi_data *adap)
-{
-       int i;
-
-       if (wait_xfer_done(adap))
-               return -ETIMEDOUT;
-       for (i = 0; i < adap->ack_timeout; i++) {
-               if ((adap->getctrl(adap->data) & SGI_I2C_NACK) == 0)
-                       return 0;
-               udelay(1);
-       }
-
-       return -ETIMEDOUT;
-}
-
-static int force_idle(struct i2c_algo_sgi_data *adap)
-{
-       int i;
-
-       adap->setctrl(adap->data, SGI_I2C_FORCE_IDLE);
-       for (i = 0; i < adap->xfer_timeout; i++) {
-               if ((adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE) == 0)
-                       goto out;
-               udelay(1);
-       }
-       return -ETIMEDOUT;
-out:
-       if (adap->getctrl(adap->data) & SGI_I2C_BUS_ERR)
-               return -EIO;
-       return 0;
-}
-
-static int do_address(struct i2c_algo_sgi_data *adap, unsigned int addr,
-                     int rd)
-{
-       if (rd)
-               adap->setctrl(adap->data, SGI_I2C_NOT_IDLE);
-       /* Check if bus is idle, eventually force it to do so */
-       if (adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE)
-               if (force_idle(adap))
-                       return -EIO;
-       /* Write out the i2c chip address and specify operation */
-       adap->setctrl(adap->data,
-                     SGI_I2C_HOLD_BUS | SGI_I2C_WRITE | SGI_I2C_NOT_IDLE);
-       if (rd)
-               addr |= 1;
-       adap->wdata(adap->data, addr);
-       if (wait_ack(adap))
-               return -EIO;
-       return 0;
-}
-
-static int i2c_read(struct i2c_algo_sgi_data *adap, unsigned char *buf,
-                   unsigned int len)
-{
-       int i;
-
-       adap->setctrl(adap->data,
-                     SGI_I2C_HOLD_BUS | SGI_I2C_READ | SGI_I2C_NOT_IDLE);
-       for (i = 0; i < len; i++) {
-               if (wait_xfer_done(adap))
-                       return -EIO;
-               buf[i] = adap->rdata(adap->data);
-       }
-       adap->setctrl(adap->data, SGI_I2C_RELEASE_BUS | SGI_I2C_FORCE_IDLE);
-
-       return 0;
-
-}
-
-static int i2c_write(struct i2c_algo_sgi_data *adap, unsigned char *buf,
-                    unsigned int len)
-{
-       int i;
-
-       /* We are already in write state */
-       for (i = 0; i < len; i++) {
-               adap->wdata(adap->data, buf[i]);
-               if (wait_ack(adap))
-                       return -EIO;
-       }
-       return 0;
-}
-
-static int sgi_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
-                   int num)
-{
-       struct i2c_algo_sgi_data *adap = i2c_adap->algo_data;
-       struct i2c_msg *p;
-       int i, err = 0;
-
-       for (i = 0; !err && i < num; i++) {
-               p = &msgs[i];
-               err = do_address(adap, p->addr, p->flags & I2C_M_RD);
-               if (err || !p->len)
-                       continue;
-               if (p->flags & I2C_M_RD)
-                       err = i2c_read(adap, p->buf, p->len);
-               else
-                       err = i2c_write(adap, p->buf, p->len);
-       }
-
-       return (err < 0) ? err : i;
-}
-
-static u32 sgi_func(struct i2c_adapter *adap)
-{
-       return I2C_FUNC_SMBUS_EMUL;
-}
-
-static const struct i2c_algorithm sgi_algo = {
-       .master_xfer    = sgi_xfer,
-       .functionality  = sgi_func,
-};
-
-static unsigned i2c_vino_getctrl(void *data)
-{
-       return vino->i2c_control;
-}
-
-static void i2c_vino_setctrl(void *data, unsigned val)
-{
-       vino->i2c_control = val;
-}
-
-static unsigned i2c_vino_rdata(void *data)
-{
-       return vino->i2c_data;
-}
-
-static void i2c_vino_wdata(void *data, unsigned val)
-{
-       vino->i2c_data = val;
-}
-
-static struct i2c_algo_sgi_data i2c_sgi_vino_data = {
-       .getctrl = &i2c_vino_getctrl,
-       .setctrl = &i2c_vino_setctrl,
-       .rdata   = &i2c_vino_rdata,
-       .wdata   = &i2c_vino_wdata,
-       .xfer_timeout = 200,
-       .ack_timeout  = 1000,
-};
-
-static struct i2c_adapter vino_i2c_adapter = {
-       .name                   = "VINO I2C bus",
-       .algo                   = &sgi_algo,
-       .algo_data              = &i2c_sgi_vino_data,
-       .owner                  = THIS_MODULE,
-};
-
-/*
- * Prepare VINO for DMA transfer...
- * (execute only with vino_lock and input_lock locked)
- */
-static int vino_dma_setup(struct vino_channel_settings *vcs,
-                         struct vino_framebuffer *fb)
-{
-       u32 ctrl, intr;
-       struct sgi_vino_channel *ch;
-       const struct vino_data_norm *norm;
-
-       dprintk("vino_dma_setup():\n");
-
-       vcs->field = 0;
-       fb->frame_counter = 0;
-
-       ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b;
-       norm = &vino_data_norms[vcs->data_norm];
-
-       ch->page_index = 0;
-       ch->line_count = 0;
-
-       /* VINO line size register is set 8 bytes less than actual */
-       ch->line_size = vcs->line_size - 8;
-
-       /* let VINO know where to transfer data */
-       ch->start_desc_tbl = fb->desc_table.dma;
-       ch->next_4_desc = fb->desc_table.dma;
-
-       /* give vino time to fetch the first four descriptors, 5 usec
-        * should be more than enough time */
-       udelay(VINO_DESC_FETCH_DELAY);
-
-       dprintk("vino_dma_setup(): start desc = %08x, next 4 desc = %08x\n",
-               ch->start_desc_tbl, ch->next_4_desc);
-
-       /* set the alpha register */
-       ch->alpha = vcs->alpha;
-
-       /* set clipping registers */
-       ch->clip_start = VINO_CLIP_ODD(norm->odd.top + vcs->clipping.top / 2) |
-               VINO_CLIP_EVEN(norm->even.top +
-                              vcs->clipping.top / 2) |
-               VINO_CLIP_X(vcs->clipping.left);
-       ch->clip_end = VINO_CLIP_ODD(norm->odd.top +
-                                    vcs->clipping.bottom / 2 - 1) |
-               VINO_CLIP_EVEN(norm->even.top +
-                              vcs->clipping.bottom / 2 - 1) |
-               VINO_CLIP_X(vcs->clipping.right);
-
-       /* set the size of actual content in the buffer (DECIMATION !) */
-       fb->data_size = ((vcs->clipping.right - vcs->clipping.left) /
-                        vcs->decimation) *
-               ((vcs->clipping.bottom - vcs->clipping.top) /
-                vcs->decimation) *
-               vino_data_formats[vcs->data_format].bpp;
-
-       ch->frame_rate = vcs->framert_reg;
-
-       ctrl = vino->control;
-       intr = vino->intr_status;
-
-       if (vcs->channel == VINO_CHANNEL_A) {
-               /* All interrupt conditions for this channel was cleared
-                * so clear the interrupt status register and enable
-                * interrupts */
-               intr &= ~VINO_INTSTAT_A;
-               ctrl |= VINO_CTRL_A_INT;
-
-               /* enable synchronization */
-               ctrl |= VINO_CTRL_A_SYNC_ENBL;
-
-               /* enable frame assembly */
-               ctrl |= VINO_CTRL_A_INTERLEAVE_ENBL;
-
-               /* set decimation used */
-               if (vcs->decimation < 2)
-                       ctrl &= ~VINO_CTRL_A_DEC_ENBL;
-               else {
-                       ctrl |= VINO_CTRL_A_DEC_ENBL;
-                       ctrl &= ~VINO_CTRL_A_DEC_SCALE_MASK;
-                       ctrl |= (vcs->decimation - 1) <<
-                               VINO_CTRL_A_DEC_SCALE_SHIFT;
-               }
-
-               /* select input interface */
-               if (vcs->input == VINO_INPUT_D1)
-                       ctrl |= VINO_CTRL_A_SELECT;
-               else
-                       ctrl &= ~VINO_CTRL_A_SELECT;
-
-               /* palette */
-               ctrl &= ~(VINO_CTRL_A_LUMA_ONLY | VINO_CTRL_A_RGB |
-                         VINO_CTRL_A_DITHER);
-       } else {
-               intr &= ~VINO_INTSTAT_B;
-               ctrl |= VINO_CTRL_B_INT;
-
-               ctrl |= VINO_CTRL_B_SYNC_ENBL;
-               ctrl |= VINO_CTRL_B_INTERLEAVE_ENBL;
-
-               if (vcs->decimation < 2)
-                       ctrl &= ~VINO_CTRL_B_DEC_ENBL;
-               else {
-                       ctrl |= VINO_CTRL_B_DEC_ENBL;
-                       ctrl &= ~VINO_CTRL_B_DEC_SCALE_MASK;
-                       ctrl |= (vcs->decimation - 1) <<
-                               VINO_CTRL_B_DEC_SCALE_SHIFT;
-
-               }
-               if (vcs->input == VINO_INPUT_D1)
-                       ctrl |= VINO_CTRL_B_SELECT;
-               else
-                       ctrl &= ~VINO_CTRL_B_SELECT;
-
-               ctrl &= ~(VINO_CTRL_B_LUMA_ONLY | VINO_CTRL_B_RGB |
-                         VINO_CTRL_B_DITHER);
-       }
-
-       /* set palette */
-       fb->data_format = vcs->data_format;
-
-       switch (vcs->data_format) {
-               case VINO_DATA_FMT_GREY:
-                       ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
-                               VINO_CTRL_A_LUMA_ONLY : VINO_CTRL_B_LUMA_ONLY;
-                       break;
-               case VINO_DATA_FMT_RGB32:
-                       ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
-                               VINO_CTRL_A_RGB : VINO_CTRL_B_RGB;
-                       break;
-               case VINO_DATA_FMT_YUV:
-                       /* nothing needs to be done */
-                       break;
-               case VINO_DATA_FMT_RGB332:
-                       ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
-                               VINO_CTRL_A_RGB | VINO_CTRL_A_DITHER :
-                               VINO_CTRL_B_RGB | VINO_CTRL_B_DITHER;
-                       break;
-       }
-
-       vino->intr_status = intr;
-       vino->control = ctrl;
-
-       return 0;
-}
-
-/* (execute only with vino_lock locked) */
-static inline void vino_dma_start(struct vino_channel_settings *vcs)
-{
-       u32 ctrl = vino->control;
-
-       dprintk("vino_dma_start():\n");
-       ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
-               VINO_CTRL_A_DMA_ENBL : VINO_CTRL_B_DMA_ENBL;
-       vino->control = ctrl;
-}
-
-/* (execute only with vino_lock locked) */
-static inline void vino_dma_stop(struct vino_channel_settings *vcs)
-{
-       u32 ctrl = vino->control;
-
-       ctrl &= (vcs->channel == VINO_CHANNEL_A) ?
-               ~VINO_CTRL_A_DMA_ENBL : ~VINO_CTRL_B_DMA_ENBL;
-       ctrl &= (vcs->channel == VINO_CHANNEL_A) ?
-               ~VINO_CTRL_A_INT : ~VINO_CTRL_B_INT;
-       vino->control = ctrl;
-       dprintk("vino_dma_stop():\n");
-}
-
-/*
- * Load dummy page to descriptor registers. This prevents generating of
- * spurious interrupts. (execute only with vino_lock locked)
- */
-static void vino_clear_interrupt(struct vino_channel_settings *vcs)
-{
-       struct sgi_vino_channel *ch;
-
-       ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b;
-
-       ch->page_index = 0;
-       ch->line_count = 0;
-
-       ch->start_desc_tbl = vino_drvdata->dummy_desc_table.dma;
-       ch->next_4_desc = vino_drvdata->dummy_desc_table.dma;
-
-       udelay(VINO_DESC_FETCH_DELAY);
-       dprintk("channel %c clear interrupt condition\n",
-              (vcs->channel == VINO_CHANNEL_A) ? 'A':'B');
-}
-
-static int vino_capture(struct vino_channel_settings *vcs,
-                       struct vino_framebuffer *fb)
-{
-       int err = 0;
-       unsigned long flags, flags2;
-
-       spin_lock_irqsave(&fb->state_lock, flags);
-
-       if (fb->state == VINO_FRAMEBUFFER_IN_USE)
-               err = -EBUSY;
-       fb->state = VINO_FRAMEBUFFER_IN_USE;
-
-       spin_unlock_irqrestore(&fb->state_lock, flags);
-
-       if (err)
-               return err;
-
-       spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags2);
-
-       vino_dma_setup(vcs, fb);
-       vino_dma_start(vcs);
-
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags2);
-       spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
-
-       return err;
-}
-
-static
-struct vino_framebuffer *vino_capture_enqueue(struct
-                                             vino_channel_settings *vcs,
-                                             unsigned int index)
-{
-       struct vino_framebuffer *fb;
-       unsigned long flags;
-
-       dprintk("vino_capture_enqueue():\n");
-
-       spin_lock_irqsave(&vcs->capture_lock, flags);
-
-       fb = vino_queue_add(&vcs->fb_queue, index);
-       if (fb == NULL) {
-               dprintk("vino_capture_enqueue(): vino_queue_add() failed, "
-                       "queue full?\n");
-               goto out;
-       }
-out:
-       spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
-       return fb;
-}
-
-static int vino_capture_next(struct vino_channel_settings *vcs, int start)
-{
-       struct vino_framebuffer *fb;
-       unsigned int incoming, id;
-       int err = 0;
-       unsigned long flags;
-
-       dprintk("vino_capture_next():\n");
-
-       spin_lock_irqsave(&vcs->capture_lock, flags);
-
-       if (start) {
-               /* start capture only if capture isn't in progress already */
-               if (vcs->capturing) {
-                       spin_unlock_irqrestore(&vcs->capture_lock, flags);
-                       return 0;
-               }
-
-       } else {
-               /* capture next frame:
-                * stop capture if capturing is not set */
-               if (!vcs->capturing) {
-                       spin_unlock_irqrestore(&vcs->capture_lock, flags);
-                       return 0;
-               }
-       }
-
-       err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
-       if (err) {
-               dprintk("vino_capture_next(): vino_queue_get_incoming() "
-                       "failed\n");
-               err = -EINVAL;
-               goto out;
-       }
-       if (incoming == 0) {
-               dprintk("vino_capture_next(): no buffers available\n");
-               goto out;
-       }
-
-       fb = vino_queue_peek(&vcs->fb_queue, &id);
-       if (fb == NULL) {
-               dprintk("vino_capture_next(): vino_queue_peek() failed\n");
-               err = -EINVAL;
-               goto out;
-       }
-
-       if (start) {
-               vcs->capturing = 1;
-       }
-
-       spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
-       err = vino_capture(vcs, fb);
-
-       return err;
-
-out:
-       vcs->capturing = 0;
-       spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
-       return err;
-}
-
-static inline int vino_is_capturing(struct vino_channel_settings *vcs)
-{
-       int ret;
-       unsigned long flags;
-
-       spin_lock_irqsave(&vcs->capture_lock, flags);
-
-       ret = vcs->capturing;
-
-       spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
-       return ret;
-}
-
-/* waits until a frame is captured */
-static int vino_wait_for_frame(struct vino_channel_settings *vcs)
-{
-       wait_queue_t wait;
-       int err = 0;
-
-       dprintk("vino_wait_for_frame():\n");
-
-       init_waitqueue_entry(&wait, current);
-       /* add ourselves into wait queue */
-       add_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait);
-
-       /* to ensure that schedule_timeout will return immediately
-        * if VINO interrupt was triggered meanwhile */
-       schedule_timeout_interruptible(msecs_to_jiffies(100));
-
-       if (signal_pending(current))
-               err = -EINTR;
-
-       remove_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait);
-
-       dprintk("vino_wait_for_frame(): waiting for frame %s\n",
-               err ? "failed" : "ok");
-
-       return err;
-}
-
-/* the function assumes that PAGE_SIZE % 4 == 0 */
-static void vino_convert_to_rgba(struct vino_framebuffer *fb) {
-       unsigned char *pageptr;
-       unsigned int page, i;
-       unsigned char a;
-
-       for (page = 0; page < fb->desc_table.page_count; page++) {
-               pageptr = (unsigned char *)fb->desc_table.virtual[page];
-
-               for (i = 0; i < PAGE_SIZE; i += 4) {
-                       a = pageptr[0];
-                       pageptr[0] = pageptr[3];
-                       pageptr[1] = pageptr[2];
-                       pageptr[2] = pageptr[1];
-                       pageptr[3] = a;
-                       pageptr += 4;
-               }
-       }
-}
-
-/* checks if the buffer is in correct state and syncs data */
-static int vino_check_buffer(struct vino_channel_settings *vcs,
-                            struct vino_framebuffer *fb)
-{
-       int err = 0;
-       unsigned long flags;
-
-       dprintk("vino_check_buffer():\n");
-
-       spin_lock_irqsave(&fb->state_lock, flags);
-       switch (fb->state) {
-       case VINO_FRAMEBUFFER_IN_USE:
-               err = -EIO;
-               break;
-       case VINO_FRAMEBUFFER_READY:
-               vino_sync_buffer(fb);
-               fb->state = VINO_FRAMEBUFFER_UNUSED;
-               break;
-       default:
-               err = -EINVAL;
-       }
-       spin_unlock_irqrestore(&fb->state_lock, flags);
-
-       if (!err) {
-               if (vino_pixel_conversion
-                   && (fb->data_format == VINO_DATA_FMT_RGB32)) {
-                       vino_convert_to_rgba(fb);
-               }
-       } else if (err && (err != -EINVAL)) {
-               dprintk("vino_check_buffer(): buffer not ready\n");
-
-               spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
-               vino_dma_stop(vcs);
-               vino_clear_interrupt(vcs);
-               spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
-       }
-
-       return err;
-}
-
-/* forcefully terminates capture */
-static void vino_capture_stop(struct vino_channel_settings *vcs)
-{
-       unsigned int incoming = 0, outgoing = 0, id;
-       unsigned long flags, flags2;
-
-       dprintk("vino_capture_stop():\n");
-
-       spin_lock_irqsave(&vcs->capture_lock, flags);
-
-       /* unset capturing to stop queue processing */
-       vcs->capturing = 0;
-
-       spin_lock_irqsave(&vino_drvdata->vino_lock, flags2);
-
-       vino_dma_stop(vcs);
-       vino_clear_interrupt(vcs);
-
-       spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags2);
-
-       /* remove all items from the queue */
-       if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) {
-               dprintk("vino_capture_stop(): "
-                       "vino_queue_get_incoming() failed\n");
-               goto out;
-       }
-       while (incoming > 0) {
-               vino_queue_transfer(&vcs->fb_queue);
-
-               if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) {
-                       dprintk("vino_capture_stop(): "
-                               "vino_queue_get_incoming() failed\n");
-                       goto out;
-               }
-       }
-
-       if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
-               dprintk("vino_capture_stop(): "
-                       "vino_queue_get_outgoing() failed\n");
-               goto out;
-       }
-       while (outgoing > 0) {
-               vino_queue_remove(&vcs->fb_queue, &id);
-
-               if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
-                       dprintk("vino_capture_stop(): "
-                               "vino_queue_get_outgoing() failed\n");
-                       goto out;
-               }
-       }
-
-out:
-       spin_unlock_irqrestore(&vcs->capture_lock, flags);
-}
-
-#if 0
-static int vino_capture_failed(struct vino_channel_settings *vcs)
-{
-       struct vino_framebuffer *fb;
-       unsigned long flags;
-       unsigned int i;
-       int ret;
-
-       dprintk("vino_capture_failed():\n");
-
-       spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
-
-       vino_dma_stop(vcs);
-       vino_clear_interrupt(vcs);
-
-       spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
-
-       ret = vino_queue_get_incoming(&vcs->fb_queue, &i);
-       if (ret == VINO_QUEUE_ERROR) {
-               dprintk("vino_queue_get_incoming() failed\n");
-               return -EINVAL;
-       }
-       if (i == 0) {
-               /* no buffers to process */
-               return 0;
-       }
-
-       fb = vino_queue_peek(&vcs->fb_queue, &i);
-       if (fb == NULL) {
-               dprintk("vino_queue_peek() failed\n");
-               return -EINVAL;
-       }
-
-       spin_lock_irqsave(&fb->state_lock, flags);
-       if (fb->state == VINO_FRAMEBUFFER_IN_USE) {
-               fb->state = VINO_FRAMEBUFFER_UNUSED;
-               vino_queue_transfer(&vcs->fb_queue);
-               vino_queue_remove(&vcs->fb_queue, &i);
-               /* we should actually discard the newest frame,
-                * but who cares ... */
-       }
-       spin_unlock_irqrestore(&fb->state_lock, flags);
-
-       return 0;
-}
-#endif
-
-static void vino_skip_frame(struct vino_channel_settings *vcs)
-{
-       struct vino_framebuffer *fb;
-       unsigned long flags;
-       unsigned int id;
-
-       spin_lock_irqsave(&vcs->capture_lock, flags);
-       fb = vino_queue_peek(&vcs->fb_queue, &id);
-       if (!fb) {
-               spin_unlock_irqrestore(&vcs->capture_lock, flags);
-               dprintk("vino_skip_frame(): vino_queue_peek() failed!\n");
-               return;
-       }
-       spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
-       spin_lock_irqsave(&fb->state_lock, flags);
-       fb->state = VINO_FRAMEBUFFER_UNUSED;
-       spin_unlock_irqrestore(&fb->state_lock, flags);
-
-       vino_capture_next(vcs, 0);
-}
-
-static void vino_frame_done(struct vino_channel_settings *vcs)
-{
-       struct vino_framebuffer *fb;
-       unsigned long flags;
-
-       spin_lock_irqsave(&vcs->capture_lock, flags);
-       fb = vino_queue_transfer(&vcs->fb_queue);
-       if (!fb) {
-               spin_unlock_irqrestore(&vcs->capture_lock, flags);
-               dprintk("vino_frame_done(): vino_queue_transfer() failed!\n");
-               return;
-       }
-       spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
-       fb->frame_counter = vcs->int_data.frame_counter;
-       memcpy(&fb->timestamp, &vcs->int_data.timestamp,
-              sizeof(struct timeval));
-
-       spin_lock_irqsave(&fb->state_lock, flags);
-       if (fb->state == VINO_FRAMEBUFFER_IN_USE)
-               fb->state = VINO_FRAMEBUFFER_READY;
-       spin_unlock_irqrestore(&fb->state_lock, flags);
-
-       wake_up(&vcs->fb_queue.frame_wait_queue);
-
-       vino_capture_next(vcs, 0);
-}
-
-static void vino_capture_tasklet(unsigned long channel) {
-       struct vino_channel_settings *vcs;
-
-       vcs = (channel == VINO_CHANNEL_A)
-               ? &vino_drvdata->a : &vino_drvdata->b;
-
-       if (vcs->int_data.skip)
-               vcs->int_data.skip_count++;
-
-       if (vcs->int_data.skip && (vcs->int_data.skip_count
-                                  <= VINO_MAX_FRAME_SKIP_COUNT)) {
-               vino_skip_frame(vcs);
-       } else {
-               vcs->int_data.skip_count = 0;
-               vino_frame_done(vcs);
-       }
-}
-
-static irqreturn_t vino_interrupt(int irq, void *dev_id)
-{
-       u32 ctrl, intr;
-       unsigned int fc_a, fc_b;
-       int handled_a = 0, skip_a = 0, done_a = 0;
-       int handled_b = 0, skip_b = 0, done_b = 0;
-
-#ifdef VINO_DEBUG_INT
-       int loop = 0;
-       unsigned int line_count = vino->a.line_count,
-               page_index = vino->a.page_index,
-               field_counter = vino->a.field_counter,
-               start_desc_tbl = vino->a.start_desc_tbl,
-               next_4_desc = vino->a.next_4_desc;
-       unsigned int line_count_2,
-               page_index_2,
-               field_counter_2,
-               start_desc_tbl_2,
-               next_4_desc_2;
-#endif
-
-       spin_lock(&vino_drvdata->vino_lock);
-
-       while ((intr = vino->intr_status)) {
-               fc_a = vino->a.field_counter >> 1;
-               fc_b = vino->b.field_counter >> 1;
-
-               /* handle error-interrupts in some special way ?
-                * --> skips frames */
-               if (intr & VINO_INTSTAT_A) {
-                       if (intr & VINO_INTSTAT_A_EOF) {
-                               vino_drvdata->a.field++;
-                               if (vino_drvdata->a.field > 1) {
-                                       vino_dma_stop(&vino_drvdata->a);
-                                       vino_clear_interrupt(&vino_drvdata->a);
-                                       vino_drvdata->a.field = 0;
-                                       done_a = 1;
-                               } else {
-                                       if (vino->a.page_index
-                                           != vino_drvdata->a.line_size) {
-                                               vino->a.line_count = 0;
-                                               vino->a.page_index =
-                                                       vino_drvdata->
-                                                       a.line_size;
-                                               vino->a.next_4_desc =
-                                                       vino->a.start_desc_tbl;
-                                       }
-                               }
-                               dprintk("channel A end-of-field "
-                                       "interrupt: %04x\n", intr);
-                       } else {
-                               vino_dma_stop(&vino_drvdata->a);
-                               vino_clear_interrupt(&vino_drvdata->a);
-                               vino_drvdata->a.field = 0;
-                               skip_a = 1;
-                               dprintk("channel A error interrupt: %04x\n",
-                                       intr);
-                       }
-
-#ifdef VINO_DEBUG_INT
-                       line_count_2 = vino->a.line_count;
-                       page_index_2 = vino->a.page_index;
-                       field_counter_2 = vino->a.field_counter;
-                       start_desc_tbl_2 = vino->a.start_desc_tbl;
-                       next_4_desc_2 = vino->a.next_4_desc;
-
-                       printk("intr = %04x, loop = %d, field = %d\n",
-                              intr, loop, vino_drvdata->a.field);
-                       printk("1- line count = %04d, page index = %04d, "
-                              "start = %08x, next = %08x\n"
-                              "   fieldc = %d, framec = %d\n",
-                              line_count, page_index, start_desc_tbl,
-                              next_4_desc, field_counter, fc_a);
-                       printk("12-line count = %04d, page index = %04d, "
-                              "   start = %08x, next = %08x\n",
-                              line_count_2, page_index_2, start_desc_tbl_2,
-                              next_4_desc_2);
-
-                       if (done_a)
-                               printk("\n");
-#endif
-               }
-
-               if (intr & VINO_INTSTAT_B) {
-                       if (intr & VINO_INTSTAT_B_EOF) {
-                               vino_drvdata->b.field++;
-                               if (vino_drvdata->b.field > 1) {
-                                       vino_dma_stop(&vino_drvdata->b);
-                                       vino_clear_interrupt(&vino_drvdata->b);
-                                       vino_drvdata->b.field = 0;
-                                       done_b = 1;
-                               }
-                               dprintk("channel B end-of-field "
-                                       "interrupt: %04x\n", intr);
-                       } else {
-                               vino_dma_stop(&vino_drvdata->b);
-                               vino_clear_interrupt(&vino_drvdata->b);
-                               vino_drvdata->b.field = 0;
-                               skip_b = 1;
-                               dprintk("channel B error interrupt: %04x\n",
-                                       intr);
-                       }
-               }
-
-               /* Always remember to clear interrupt status.
-                * Disable VINO interrupts while we do this. */
-               ctrl = vino->control;
-               vino->control = ctrl & ~(VINO_CTRL_A_INT | VINO_CTRL_B_INT);
-               vino->intr_status = ~intr;
-               vino->control = ctrl;
-
-               spin_unlock(&vino_drvdata->vino_lock);
-
-               if ((!handled_a) && (done_a || skip_a)) {
-                       if (!skip_a) {
-                               v4l2_get_timestamp(
-                                       &vino_drvdata->a.int_data.timestamp);
-                               vino_drvdata->a.int_data.frame_counter = fc_a;
-                       }
-                       vino_drvdata->a.int_data.skip = skip_a;
-
-                       dprintk("channel A %s, interrupt: %d\n",
-                               skip_a ? "skipping frame" : "frame done",
-                               intr);
-                       tasklet_hi_schedule(&vino_tasklet_a);
-                       handled_a = 1;
-               }
-
-               if ((!handled_b) && (done_b || skip_b)) {
-                       if (!skip_b) {
-                               v4l2_get_timestamp(
-                                       &vino_drvdata->b.int_data.timestamp);
-                               vino_drvdata->b.int_data.frame_counter = fc_b;
-                       }
-                       vino_drvdata->b.int_data.skip = skip_b;
-
-                       dprintk("channel B %s, interrupt: %d\n",
-                               skip_b ? "skipping frame" : "frame done",
-                               intr);
-                       tasklet_hi_schedule(&vino_tasklet_b);
-                       handled_b = 1;
-               }
-
-#ifdef VINO_DEBUG_INT
-               loop++;
-#endif
-               spin_lock(&vino_drvdata->vino_lock);
-       }
-
-       spin_unlock(&vino_drvdata->vino_lock);
-
-       return IRQ_HANDLED;
-}
-
-/* VINO video input management */
-
-static int vino_get_saa7191_input(int input)
-{
-       switch (input) {
-       case VINO_INPUT_COMPOSITE:
-               return SAA7191_INPUT_COMPOSITE;
-       case VINO_INPUT_SVIDEO:
-               return SAA7191_INPUT_SVIDEO;
-       default:
-               printk(KERN_ERR "VINO: vino_get_saa7191_input(): "
-                      "invalid input!\n");
-               return -1;
-       }
-}
-
-/* execute with input_lock locked */
-static int vino_is_input_owner(struct vino_channel_settings *vcs)
-{
-       switch(vcs->input) {
-       case VINO_INPUT_COMPOSITE:
-       case VINO_INPUT_SVIDEO:
-               return vino_drvdata->decoder_owner == vcs->channel;
-       case VINO_INPUT_D1:
-               return vino_drvdata->camera_owner == vcs->channel;
-       default:
-               return 0;
-       }
-}
-
-static int vino_acquire_input(struct vino_channel_settings *vcs)
-{
-       unsigned long flags;
-       int ret = 0;
-
-       dprintk("vino_acquire_input():\n");
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-       /* First try D1 and then SAA7191 */
-       if (vino_drvdata->camera
-           && (vino_drvdata->camera_owner == VINO_NO_CHANNEL)) {
-               vino_drvdata->camera_owner = vcs->channel;
-               vcs->input = VINO_INPUT_D1;
-               vcs->data_norm = VINO_DATA_NORM_D1;
-       } else if (vino_drvdata->decoder
-                  && (vino_drvdata->decoder_owner == VINO_NO_CHANNEL)) {
-               int input;
-               int data_norm = 0;
-               v4l2_std_id norm;
-
-               input = VINO_INPUT_COMPOSITE;
-
-               ret = decoder_call(video, s_routing,
-                               vino_get_saa7191_input(input), 0, 0);
-               if (ret) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-
-               spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-               /* Don't hold spinlocks while auto-detecting norm
-                * as it may take a while... */
-
-               ret = decoder_call(video, querystd, &norm);
-               if (!ret) {
-                       for (data_norm = 0; data_norm < 3; data_norm++) {
-                               if (vino_data_norms[data_norm].std & norm)
-                                       break;
-                       }
-                       if (data_norm == 3)
-                               data_norm = VINO_DATA_NORM_PAL;
-                       ret = decoder_call(video, s_std, norm);
-               }
-
-               spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-               if (ret) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-
-               vino_drvdata->decoder_owner = vcs->channel;
-
-               vcs->input = input;
-               vcs->data_norm = data_norm;
-       } else {
-               vcs->input = (vcs->channel == VINO_CHANNEL_A) ?
-                       vino_drvdata->b.input : vino_drvdata->a.input;
-               vcs->data_norm = (vcs->channel == VINO_CHANNEL_A) ?
-                       vino_drvdata->b.data_norm : vino_drvdata->a.data_norm;
-       }
-
-       if (vcs->input == VINO_INPUT_NONE) {
-               ret = -ENODEV;
-               goto out;
-       }
-
-       vino_set_default_clipping(vcs);
-       vino_set_default_scaling(vcs);
-       vino_set_default_framerate(vcs);
-
-       dprintk("vino_acquire_input(): %s\n", vino_inputs[vcs->input].name);
-
-out:
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-       return ret;
-}
-
-static int vino_set_input(struct vino_channel_settings *vcs, int input)
-{
-       struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ?
-               &vino_drvdata->b : &vino_drvdata->a;
-       unsigned long flags;
-       int ret = 0;
-
-       dprintk("vino_set_input():\n");
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-       if (vcs->input == input)
-               goto out;
-
-       switch (input) {
-       case VINO_INPUT_COMPOSITE:
-       case VINO_INPUT_SVIDEO:
-               if (!vino_drvdata->decoder) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-
-               if (vino_drvdata->decoder_owner == VINO_NO_CHANNEL) {
-                       vino_drvdata->decoder_owner = vcs->channel;
-               }
-
-               if (vino_drvdata->decoder_owner == vcs->channel) {
-                       int data_norm = 0;
-                       v4l2_std_id norm;
-
-                       ret = decoder_call(video, s_routing,
-                                       vino_get_saa7191_input(input), 0, 0);
-                       if (ret) {
-                               vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
-                               ret = -EINVAL;
-                               goto out;
-                       }
-
-                       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-                       /* Don't hold spinlocks while auto-detecting norm
-                        * as it may take a while... */
-
-                       ret = decoder_call(video, querystd, &norm);
-                       if (!ret) {
-                               for (data_norm = 0; data_norm < 3; data_norm++) {
-                                       if (vino_data_norms[data_norm].std & norm)
-                                               break;
-                               }
-                               if (data_norm == 3)
-                                       data_norm = VINO_DATA_NORM_PAL;
-                               ret = decoder_call(video, s_std, norm);
-                       }
-
-                       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-                       if (ret) {
-                               vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
-                               ret = -EINVAL;
-                               goto out;
-                       }
-
-                       vcs->input = input;
-                       vcs->data_norm = data_norm;
-               } else {
-                       if (input != vcs2->input) {
-                               ret = -EBUSY;
-                               goto out;
-                       }
-
-                       vcs->input = input;
-                       vcs->data_norm = vcs2->data_norm;
-               }
-
-               if (vino_drvdata->camera_owner == vcs->channel) {
-                       /* Transfer the ownership or release the input */
-                       if (vcs2->input == VINO_INPUT_D1) {
-                               vino_drvdata->camera_owner = vcs2->channel;
-                       } else {
-                               vino_drvdata->camera_owner = VINO_NO_CHANNEL;
-                       }
-               }
-               break;
-       case VINO_INPUT_D1:
-               if (!vino_drvdata->camera) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-
-               if (vino_drvdata->camera_owner == VINO_NO_CHANNEL)
-                       vino_drvdata->camera_owner = vcs->channel;
-
-               if (vino_drvdata->decoder_owner == vcs->channel) {
-                       /* Transfer the ownership or release the input */
-                       if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
-                                (vcs2->input == VINO_INPUT_SVIDEO)) {
-                               vino_drvdata->decoder_owner = vcs2->channel;
-                       } else {
-                               vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
-                       }
-               }
-
-               vcs->input = input;
-               vcs->data_norm = VINO_DATA_NORM_D1;
-               break;
-       default:
-               ret = -EINVAL;
-               goto out;
-       }
-
-       vino_set_default_clipping(vcs);
-       vino_set_default_scaling(vcs);
-       vino_set_default_framerate(vcs);
-
-       dprintk("vino_set_input(): %s\n", vino_inputs[vcs->input].name);
-
-out:
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-       return ret;
-}
-
-static void vino_release_input(struct vino_channel_settings *vcs)
-{
-       struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ?
-               &vino_drvdata->b : &vino_drvdata->a;
-       unsigned long flags;
-
-       dprintk("vino_release_input():\n");
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-       /* Release ownership of the channel
-        * and if the other channel takes input from
-        * the same source, transfer the ownership */
-       if (vino_drvdata->camera_owner == vcs->channel) {
-               if (vcs2->input == VINO_INPUT_D1) {
-                       vino_drvdata->camera_owner = vcs2->channel;
-               } else {
-                       vino_drvdata->camera_owner = VINO_NO_CHANNEL;
-               }
-       } else if (vino_drvdata->decoder_owner == vcs->channel) {
-               if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
-                        (vcs2->input == VINO_INPUT_SVIDEO)) {
-                       vino_drvdata->decoder_owner = vcs2->channel;
-               } else {
-                       vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
-               }
-       }
-       vcs->input = VINO_INPUT_NONE;
-
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-}
-
-/* execute with input_lock locked */
-static int vino_set_data_norm(struct vino_channel_settings *vcs,
-                             unsigned int data_norm,
-                             unsigned long *flags)
-{
-       int err = 0;
-
-       if (data_norm == vcs->data_norm)
-               return 0;
-
-       switch (vcs->input) {
-       case VINO_INPUT_D1:
-               /* only one "norm" supported */
-               if (data_norm != VINO_DATA_NORM_D1)
-                       return -EINVAL;
-               break;
-       case VINO_INPUT_COMPOSITE:
-       case VINO_INPUT_SVIDEO: {
-               v4l2_std_id norm;
-
-               if ((data_norm != VINO_DATA_NORM_PAL)
-                   && (data_norm != VINO_DATA_NORM_NTSC)
-                   && (data_norm != VINO_DATA_NORM_SECAM))
-                       return -EINVAL;
-
-               spin_unlock_irqrestore(&vino_drvdata->input_lock, *flags);
-
-               /* Don't hold spinlocks while setting norm
-                * as it may take a while... */
-
-               norm = vino_data_norms[data_norm].std;
-               err = decoder_call(video, s_std, norm);
-
-               spin_lock_irqsave(&vino_drvdata->input_lock, *flags);
-
-               if (err)
-                       goto out;
-
-               vcs->data_norm = data_norm;
-
-               vino_set_default_clipping(vcs);
-               vino_set_default_scaling(vcs);
-               vino_set_default_framerate(vcs);
-               break;
-       }
-       default:
-               return -EINVAL;
-       }
-
-out:
-       return err;
-}
-
-/* V4L2 helper functions */
-
-static int vino_find_data_format(__u32 pixelformat)
-{
-       int i;
-
-       for (i = 0; i < VINO_DATA_FMT_COUNT; i++) {
-               if (vino_data_formats[i].pixelformat == pixelformat)
-                       return i;
-       }
-
-       return VINO_DATA_FMT_NONE;
-}
-
-static int vino_int_enum_input(struct vino_channel_settings *vcs, __u32 index)
-{
-       int input = VINO_INPUT_NONE;
-       unsigned long flags;
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-       if (vino_drvdata->decoder && vino_drvdata->camera) {
-               switch (index) {
-               case 0:
-                       input = VINO_INPUT_COMPOSITE;
-                       break;
-               case 1:
-                       input = VINO_INPUT_SVIDEO;
-                       break;
-               case 2:
-                       input = VINO_INPUT_D1;
-                       break;
-               }
-       } else if (vino_drvdata->decoder) {
-               switch (index) {
-               case 0:
-                       input = VINO_INPUT_COMPOSITE;
-                       break;
-               case 1:
-                       input = VINO_INPUT_SVIDEO;
-                       break;
-               }
-       } else if (vino_drvdata->camera) {
-               switch (index) {
-               case 0:
-                       input = VINO_INPUT_D1;
-                       break;
-               }
-       }
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-       return input;
-}
-
-/* execute with input_lock locked */
-static __u32 vino_find_input_index(struct vino_channel_settings *vcs)
-{
-       __u32 index = 0;
-       // FIXME: detect when no inputs available
-
-       if (vino_drvdata->decoder && vino_drvdata->camera) {
-               switch (vcs->input) {
-               case VINO_INPUT_COMPOSITE:
-                       index = 0;
-                       break;
-               case VINO_INPUT_SVIDEO:
-                       index = 1;
-                       break;
-               case VINO_INPUT_D1:
-                       index = 2;
-                       break;
-               }
-       } else if (vino_drvdata->decoder) {
-               switch (vcs->input) {
-               case VINO_INPUT_COMPOSITE:
-                       index = 0;
-                       break;
-               case VINO_INPUT_SVIDEO:
-                       index = 1;
-                       break;
-               }
-       } else if (vino_drvdata->camera) {
-               switch (vcs->input) {
-               case VINO_INPUT_D1:
-                       index = 0;
-                       break;
-               }
-       }
-
-       return index;
-}
-
-/* V4L2 ioctls */
-
-static int vino_querycap(struct file *file, void *__fh,
-               struct v4l2_capability *cap)
-{
-       memset(cap, 0, sizeof(struct v4l2_capability));
-
-       strcpy(cap->driver, vino_driver_name);
-       strcpy(cap->card, vino_driver_description);
-       strcpy(cap->bus_info, vino_bus_name);
-       cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
-       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-       return 0;
-}
-
-static int vino_enum_input(struct file *file, void *__fh,
-                              struct v4l2_input *i)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       __u32 index = i->index;
-       int input;
-       dprintk("requested index = %d\n", index);
-
-       input = vino_int_enum_input(vcs, index);
-       if (input == VINO_INPUT_NONE)
-               return -EINVAL;
-
-       i->type = V4L2_INPUT_TYPE_CAMERA;
-       i->std = vino_inputs[input].std;
-       strcpy(i->name, vino_inputs[input].name);
-
-       if (input == VINO_INPUT_COMPOSITE || input == VINO_INPUT_SVIDEO)
-               decoder_call(video, g_input_status, &i->status);
-       return 0;
-}
-
-static int vino_g_input(struct file *file, void *__fh,
-                            unsigned int *i)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       __u32 index;
-       int input;
-       unsigned long flags;
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-       input = vcs->input;
-       index = vino_find_input_index(vcs);
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-       dprintk("input = %d\n", input);
-
-       if (input == VINO_INPUT_NONE) {
-               return -EINVAL;
-       }
-
-       *i = index;
-
-       return 0;
-}
-
-static int vino_s_input(struct file *file, void *__fh,
-                            unsigned int i)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       int input;
-       dprintk("requested input = %d\n", i);
-
-       input = vino_int_enum_input(vcs, i);
-       if (input == VINO_INPUT_NONE)
-               return -EINVAL;
-
-       return vino_set_input(vcs, input);
-}
-
-static int vino_querystd(struct file *file, void *__fh,
-                             v4l2_std_id *std)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       unsigned long flags;
-       int err = 0;
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-       switch (vcs->input) {
-       case VINO_INPUT_D1:
-               *std = vino_inputs[vcs->input].std;
-               break;
-       case VINO_INPUT_COMPOSITE:
-       case VINO_INPUT_SVIDEO: {
-               decoder_call(video, querystd, std);
-               break;
-       }
-       default:
-               err = -EINVAL;
-       }
-
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-       return err;
-}
-
-static int vino_g_std(struct file *file, void *__fh,
-                          v4l2_std_id *std)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       unsigned long flags;
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-       *std = vino_data_norms[vcs->data_norm].std;
-       dprintk("current standard = %d\n", vcs->data_norm);
-
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-       return 0;
-}
-
-static int vino_s_std(struct file *file, void *__fh,
-                          v4l2_std_id std)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       unsigned long flags;
-       int ret = 0;
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-       if (!vino_is_input_owner(vcs)) {
-               ret = -EBUSY;
-               goto out;
-       }
-
-       /* check if the standard is valid for the current input */
-       if (std & vino_inputs[vcs->input].std) {
-               dprintk("standard accepted\n");
-
-               /* change the video norm for SAA7191
-                * and accept NTSC for D1 (do nothing) */
-
-               if (vcs->input == VINO_INPUT_D1)
-                       goto out;
-
-               if (std & V4L2_STD_PAL) {
-                       ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL,
-                                                &flags);
-               } else if (std & V4L2_STD_NTSC) {
-                       ret = vino_set_data_norm(vcs, VINO_DATA_NORM_NTSC,
-                                                &flags);
-               } else if (std & V4L2_STD_SECAM) {
-                       ret = vino_set_data_norm(vcs, VINO_DATA_NORM_SECAM,
-                                                &flags);
-               } else {
-                       ret = -EINVAL;
-               }
-
-               if (ret) {
-                       ret = -EINVAL;
-               }
-       } else {
-               ret = -EINVAL;
-       }
-
-out:
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-       return ret;
-}
-
-static int vino_enum_fmt_vid_cap(struct file *file, void *__fh,
-                             struct v4l2_fmtdesc *fd)
-{
-       dprintk("format index = %d\n", fd->index);
-
-       if (fd->index >= VINO_DATA_FMT_COUNT)
-               return -EINVAL;
-       dprintk("format name = %s\n", vino_data_formats[fd->index].description);
-
-       fd->pixelformat = vino_data_formats[fd->index].pixelformat;
-       strcpy(fd->description, vino_data_formats[fd->index].description);
-       return 0;
-}
-
-static int vino_try_fmt_vid_cap(struct file *file, void *__fh,
-                            struct v4l2_format *f)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       struct vino_channel_settings tempvcs;
-       unsigned long flags;
-       struct v4l2_pix_format *pf = &f->fmt.pix;
-
-       dprintk("requested: w = %d, h = %d\n",
-                       pf->width, pf->height);
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-       memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings));
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-       tempvcs.data_format = vino_find_data_format(pf->pixelformat);
-       if (tempvcs.data_format == VINO_DATA_FMT_NONE) {
-               tempvcs.data_format = VINO_DATA_FMT_GREY;
-               pf->pixelformat =
-                       vino_data_formats[tempvcs.data_format].
-                       pixelformat;
-       }
-
-       /* data format must be set before clipping/scaling */
-       vino_set_scaling(&tempvcs, pf->width, pf->height);
-
-       dprintk("data format = %s\n",
-                       vino_data_formats[tempvcs.data_format].description);
-
-       pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) /
-               tempvcs.decimation;
-       pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) /
-               tempvcs.decimation;
-
-       pf->field = V4L2_FIELD_INTERLACED;
-       pf->bytesperline = tempvcs.line_size;
-       pf->sizeimage = tempvcs.line_size *
-               (tempvcs.clipping.bottom - tempvcs.clipping.top) /
-               tempvcs.decimation;
-       pf->colorspace =
-               vino_data_formats[tempvcs.data_format].colorspace;
-
-       return 0;
-}
-
-static int vino_g_fmt_vid_cap(struct file *file, void *__fh,
-                          struct v4l2_format *f)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       unsigned long flags;
-       struct v4l2_pix_format *pf = &f->fmt.pix;
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-       pf->width = (vcs->clipping.right - vcs->clipping.left) /
-               vcs->decimation;
-       pf->height = (vcs->clipping.bottom - vcs->clipping.top) /
-               vcs->decimation;
-       pf->pixelformat =
-               vino_data_formats[vcs->data_format].pixelformat;
-
-       pf->field = V4L2_FIELD_INTERLACED;
-       pf->bytesperline = vcs->line_size;
-       pf->sizeimage = vcs->line_size *
-               (vcs->clipping.bottom - vcs->clipping.top) /
-               vcs->decimation;
-       pf->colorspace =
-               vino_data_formats[vcs->data_format].colorspace;
-
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-       return 0;
-}
-
-static int vino_s_fmt_vid_cap(struct file *file, void *__fh,
-                          struct v4l2_format *f)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       int data_format;
-       unsigned long flags;
-       struct v4l2_pix_format *pf = &f->fmt.pix;
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-       data_format = vino_find_data_format(pf->pixelformat);
-
-       if (data_format == VINO_DATA_FMT_NONE) {
-               vcs->data_format = VINO_DATA_FMT_GREY;
-               pf->pixelformat =
-                       vino_data_formats[vcs->data_format].
-                       pixelformat;
-       } else {
-               vcs->data_format = data_format;
-       }
-
-       /* data format must be set before clipping/scaling */
-       vino_set_scaling(vcs, pf->width, pf->height);
-
-       dprintk("data format = %s\n",
-              vino_data_formats[vcs->data_format].description);
-
-       pf->width = vcs->clipping.right - vcs->clipping.left;
-       pf->height = vcs->clipping.bottom - vcs->clipping.top;
-
-       pf->field = V4L2_FIELD_INTERLACED;
-       pf->bytesperline = vcs->line_size;
-       pf->sizeimage = vcs->line_size *
-               (vcs->clipping.bottom - vcs->clipping.top) /
-               vcs->decimation;
-       pf->colorspace =
-               vino_data_formats[vcs->data_format].colorspace;
-
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-       return 0;
-}
-
-static int vino_cropcap(struct file *file, void *__fh,
-                            struct v4l2_cropcap *ccap)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       const struct vino_data_norm *norm;
-       unsigned long flags;
-
-       switch (ccap->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-               norm = &vino_data_norms[vcs->data_norm];
-
-               spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-               ccap->bounds.left = 0;
-               ccap->bounds.top = 0;
-               ccap->bounds.width = norm->width;
-               ccap->bounds.height = norm->height;
-               memcpy(&ccap->defrect, &ccap->bounds,
-                      sizeof(struct v4l2_rect));
-
-               ccap->pixelaspect.numerator = 1;
-               ccap->pixelaspect.denominator = 1;
-               break;
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int vino_g_crop(struct file *file, void *__fh,
-                           struct v4l2_crop *c)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       unsigned long flags;
-
-       switch (c->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-               c->c.left = vcs->clipping.left;
-               c->c.top = vcs->clipping.top;
-               c->c.width = vcs->clipping.right - vcs->clipping.left;
-               c->c.height = vcs->clipping.bottom - vcs->clipping.top;
-
-               spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-               break;
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int vino_s_crop(struct file *file, void *__fh,
-                           const struct v4l2_crop *c)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       unsigned long flags;
-
-       switch (c->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-               vino_set_clipping(vcs, c->c.left, c->c.top,
-                                 c->c.width, c->c.height);
-
-               spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-               break;
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int vino_g_parm(struct file *file, void *__fh,
-                           struct v4l2_streamparm *sp)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       unsigned long flags;
-       struct v4l2_captureparm *cp = &sp->parm.capture;
-
-       cp->capability = V4L2_CAP_TIMEPERFRAME;
-       cp->timeperframe.numerator = 1;
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-       cp->timeperframe.denominator = vcs->fps;
-
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-       /* TODO: cp->readbuffers = xxx; */
-
-       return 0;
-}
-
-static int vino_s_parm(struct file *file, void *__fh,
-                           struct v4l2_streamparm *sp)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       unsigned long flags;
-       struct v4l2_captureparm *cp = &sp->parm.capture;
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-       if ((cp->timeperframe.numerator == 0) ||
-           (cp->timeperframe.denominator == 0)) {
-               /* reset framerate */
-               vino_set_default_framerate(vcs);
-       } else {
-               vino_set_framerate(vcs, cp->timeperframe.denominator /
-                                  cp->timeperframe.numerator);
-       }
-
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-       return 0;
-}
-
-static int vino_reqbufs(struct file *file, void *__fh,
-                            struct v4l2_requestbuffers *rb)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-
-       if (vcs->reading)
-               return -EBUSY;
-
-       /* TODO: check queue type */
-       if (rb->memory != V4L2_MEMORY_MMAP) {
-               dprintk("type not mmap\n");
-               return -EINVAL;
-       }
-
-       dprintk("count = %d\n", rb->count);
-       if (rb->count > 0) {
-               if (vino_is_capturing(vcs)) {
-                       dprintk("busy, capturing\n");
-                       return -EBUSY;
-               }
-
-               if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) {
-                       dprintk("busy, buffers still mapped\n");
-                       return -EBUSY;
-               } else {
-                       vcs->streaming = 0;
-                       vino_queue_free(&vcs->fb_queue);
-                       vino_queue_init(&vcs->fb_queue, &rb->count);
-               }
-       } else {
-               vcs->streaming = 0;
-               vino_capture_stop(vcs);
-               vino_queue_free(&vcs->fb_queue);
-       }
-
-       return 0;
-}
-
-static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs,
-                                       struct vino_framebuffer *fb,
-                                       struct v4l2_buffer *b)
-{
-       if (vino_queue_outgoing_contains(&vcs->fb_queue,
-                                        fb->id)) {
-               b->flags &= ~V4L2_BUF_FLAG_QUEUED;
-               b->flags |= V4L2_BUF_FLAG_DONE;
-       } else if (vino_queue_incoming_contains(&vcs->fb_queue,
-                                      fb->id)) {
-               b->flags &= ~V4L2_BUF_FLAG_DONE;
-               b->flags |= V4L2_BUF_FLAG_QUEUED;
-       } else {
-               b->flags &= ~(V4L2_BUF_FLAG_DONE |
-                             V4L2_BUF_FLAG_QUEUED);
-       }
-
-       b->flags &= ~(V4L2_BUF_FLAG_TIMECODE);
-
-       if (fb->map_count > 0)
-               b->flags |= V4L2_BUF_FLAG_MAPPED;
-
-       b->flags &= ~V4L2_BUF_FLAG_TIMESTAMP_MASK;
-       b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-
-       b->index = fb->id;
-       b->memory = (vcs->fb_queue.type == VINO_MEMORY_MMAP) ?
-               V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR;
-       b->m.offset = fb->offset;
-       b->bytesused = fb->data_size;
-       b->length = fb->size;
-       b->field = V4L2_FIELD_INTERLACED;
-       b->sequence = fb->frame_counter;
-       memcpy(&b->timestamp, &fb->timestamp,
-              sizeof(struct timeval));
-       // b->input ?
-
-       dprintk("buffer %d: length = %d, bytesused = %d, offset = %d\n",
-               fb->id, fb->size, fb->data_size, fb->offset);
-}
-
-static int vino_querybuf(struct file *file, void *__fh,
-                             struct v4l2_buffer *b)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       struct vino_framebuffer *fb;
-
-       if (vcs->reading)
-               return -EBUSY;
-
-       /* TODO: check queue type */
-       if (b->index >= vino_queue_get_length(&vcs->fb_queue)) {
-               dprintk("invalid index = %d\n",
-                      b->index);
-               return -EINVAL;
-       }
-
-       fb = vino_queue_get_buffer(&vcs->fb_queue,
-                                  b->index);
-       if (fb == NULL) {
-               dprintk("vino_queue_get_buffer() failed");
-               return -EINVAL;
-       }
-
-       vino_v4l2_get_buffer_status(vcs, fb, b);
-
-       return 0;
-}
-
-static int vino_qbuf(struct file *file, void *__fh,
-                         struct v4l2_buffer *b)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       struct vino_framebuffer *fb;
-       int ret;
-
-       if (vcs->reading)
-               return -EBUSY;
-
-       /* TODO: check queue type */
-       if (b->memory != V4L2_MEMORY_MMAP) {
-               dprintk("type not mmap\n");
-               return -EINVAL;
-       }
-
-       fb = vino_capture_enqueue(vcs, b->index);
-       if (fb == NULL)
-               return -EINVAL;
-
-       vino_v4l2_get_buffer_status(vcs, fb, b);
-
-       if (vcs->streaming) {
-               ret = vino_capture_next(vcs, 1);
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
-
-static int vino_dqbuf(struct file *file, void *__fh,
-                          struct v4l2_buffer *b)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       unsigned int nonblocking = file->f_flags & O_NONBLOCK;
-       struct vino_framebuffer *fb;
-       unsigned int incoming, outgoing;
-       int err;
-
-       if (vcs->reading)
-               return -EBUSY;
-
-       /* TODO: check queue type */
-
-       err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
-       if (err) {
-               dprintk("vino_queue_get_incoming() failed\n");
-               return -EINVAL;
-       }
-       err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing);
-       if (err) {
-               dprintk("vino_queue_get_outgoing() failed\n");
-               return -EINVAL;
-       }
-
-       dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing);
-
-       if (outgoing == 0) {
-               if (incoming == 0) {
-                       dprintk("no incoming or outgoing buffers\n");
-                       return -EINVAL;
-               }
-               if (nonblocking) {
-                       dprintk("non-blocking I/O was selected and "
-                               "there are no buffers to dequeue\n");
-                       return -EAGAIN;
-               }
-
-               err = vino_wait_for_frame(vcs);
-               if (err) {
-                       err = vino_wait_for_frame(vcs);
-                       if (err) {
-                               /* interrupted or no frames captured because of
-                                * frame skipping */
-                               /* vino_capture_failed(vcs); */
-                               return -EIO;
-                       }
-               }
-       }
-
-       fb = vino_queue_remove(&vcs->fb_queue, &b->index);
-       if (fb == NULL) {
-               dprintk("vino_queue_remove() failed\n");
-               return -EINVAL;
-       }
-
-       err = vino_check_buffer(vcs, fb);
-
-       vino_v4l2_get_buffer_status(vcs, fb, b);
-
-       if (err)
-               return -EIO;
-
-       return 0;
-}
-
-static int vino_streamon(struct file *file, void *__fh,
-               enum v4l2_buf_type i)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       unsigned int incoming;
-       int ret;
-       if (vcs->reading)
-               return -EBUSY;
-
-       if (vcs->streaming)
-               return 0;
-
-       // TODO: check queue type
-
-       if (vino_queue_get_length(&vcs->fb_queue) < 1) {
-               dprintk("no buffers allocated\n");
-               return -EINVAL;
-       }
-
-       ret = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
-       if (ret) {
-               dprintk("vino_queue_get_incoming() failed\n");
-               return -EINVAL;
-       }
-
-       vcs->streaming = 1;
-
-       if (incoming > 0) {
-               ret = vino_capture_next(vcs, 1);
-               if (ret) {
-                       vcs->streaming = 0;
-
-                       dprintk("couldn't start capture\n");
-                       return -EINVAL;
-               }
-       }
-
-       return 0;
-}
-
-static int vino_streamoff(struct file *file, void *__fh,
-               enum v4l2_buf_type i)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       if (vcs->reading)
-               return -EBUSY;
-
-       if (!vcs->streaming)
-               return 0;
-
-       vcs->streaming = 0;
-       vino_capture_stop(vcs);
-
-       return 0;
-}
-
-static int vino_queryctrl(struct file *file, void *__fh,
-                              struct v4l2_queryctrl *queryctrl)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       unsigned long flags;
-       int i;
-       int err = 0;
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-       switch (vcs->input) {
-       case VINO_INPUT_D1:
-               for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
-                       if (vino_indycam_v4l2_controls[i].id ==
-                           queryctrl->id) {
-                               memcpy(queryctrl,
-                                      &vino_indycam_v4l2_controls[i],
-                                      sizeof(struct v4l2_queryctrl));
-                               queryctrl->reserved[0] = 0;
-                               goto found;
-                       }
-               }
-
-               err =  -EINVAL;
-               break;
-       case VINO_INPUT_COMPOSITE:
-       case VINO_INPUT_SVIDEO:
-               for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
-                       if (vino_saa7191_v4l2_controls[i].id ==
-                           queryctrl->id) {
-                               memcpy(queryctrl,
-                                      &vino_saa7191_v4l2_controls[i],
-                                      sizeof(struct v4l2_queryctrl));
-                               queryctrl->reserved[0] = 0;
-                               goto found;
-                       }
-               }
-
-               err =  -EINVAL;
-               break;
-       default:
-               err =  -EINVAL;
-       }
-
- found:
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-       return err;
-}
-
-static int vino_g_ctrl(struct file *file, void *__fh,
-                           struct v4l2_control *control)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       unsigned long flags;
-       int i;
-       int err = 0;
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-       switch (vcs->input) {
-       case VINO_INPUT_D1: {
-               err = -EINVAL;
-               for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
-                       if (vino_indycam_v4l2_controls[i].id == control->id) {
-                               err = 0;
-                               break;
-                       }
-               }
-
-               if (err)
-                       goto out;
-
-               err = camera_call(core, g_ctrl, control);
-               if (err)
-                       err = -EINVAL;
-               break;
-       }
-       case VINO_INPUT_COMPOSITE:
-       case VINO_INPUT_SVIDEO: {
-               err = -EINVAL;
-               for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
-                       if (vino_saa7191_v4l2_controls[i].id == control->id) {
-                               err = 0;
-                               break;
-                       }
-               }
-
-               if (err)
-                       goto out;
-
-               err = decoder_call(core, g_ctrl, control);
-               if (err)
-                       err = -EINVAL;
-               break;
-       }
-       default:
-               err =  -EINVAL;
-       }
-
-out:
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-       return err;
-}
-
-static int vino_s_ctrl(struct file *file, void *__fh,
-                           struct v4l2_control *control)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       unsigned long flags;
-       int i;
-       int err = 0;
-
-       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
-       if (!vino_is_input_owner(vcs)) {
-               err = -EBUSY;
-               goto out;
-       }
-
-       switch (vcs->input) {
-       case VINO_INPUT_D1: {
-               err = -EINVAL;
-               for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
-                       if (vino_indycam_v4l2_controls[i].id == control->id) {
-                               err = 0;
-                               break;
-                       }
-               }
-               if (err)
-                       goto out;
-               if (control->value < vino_indycam_v4l2_controls[i].minimum ||
-                   control->value > vino_indycam_v4l2_controls[i].maximum) {
-                       err = -ERANGE;
-                       goto out;
-               }
-               err = camera_call(core, s_ctrl, control);
-               if (err)
-                       err = -EINVAL;
-               break;
-       }
-       case VINO_INPUT_COMPOSITE:
-       case VINO_INPUT_SVIDEO: {
-               err = -EINVAL;
-               for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
-                       if (vino_saa7191_v4l2_controls[i].id == control->id) {
-                               err = 0;
-                               break;
-                       }
-               }
-               if (err)
-                       goto out;
-               if (control->value < vino_saa7191_v4l2_controls[i].minimum ||
-                   control->value > vino_saa7191_v4l2_controls[i].maximum) {
-                       err = -ERANGE;
-                       goto out;
-               }
-
-               err = decoder_call(core, s_ctrl, control);
-               if (err)
-                       err = -EINVAL;
-               break;
-       }
-       default:
-               err =  -EINVAL;
-       }
-
-out:
-       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
-       return err;
-}
-
-/* File operations */
-
-static int vino_open(struct file *file)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       int ret = 0;
-       dprintk("open(): channel = %c\n",
-              (vcs->channel == VINO_CHANNEL_A) ? 'A' : 'B');
-
-       mutex_lock(&vcs->mutex);
-
-       if (vcs->users) {
-               dprintk("open(): driver busy\n");
-               ret = -EBUSY;
-               goto out;
-       }
-
-       ret = vino_acquire_input(vcs);
-       if (ret) {
-               dprintk("open(): vino_acquire_input() failed\n");
-               goto out;
-       }
-
-       vcs->users++;
-
- out:
-       mutex_unlock(&vcs->mutex);
-
-       dprintk("open(): %s!\n", ret ? "failed" : "complete");
-
-       return ret;
-}
-
-static int vino_close(struct file *file)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       dprintk("close():\n");
-
-       mutex_lock(&vcs->mutex);
-
-       vcs->users--;
-
-       if (!vcs->users) {
-               vino_release_input(vcs);
-
-               /* stop DMA and free buffers */
-               vino_capture_stop(vcs);
-               vino_queue_free(&vcs->fb_queue);
-       }
-
-       mutex_unlock(&vcs->mutex);
-
-       return 0;
-}
-
-static void vino_vm_open(struct vm_area_struct *vma)
-{
-       struct vino_framebuffer *fb = vma->vm_private_data;
-
-       fb->map_count++;
-       dprintk("vino_vm_open(): count = %d\n", fb->map_count);
-}
-
-static void vino_vm_close(struct vm_area_struct *vma)
-{
-       struct vino_framebuffer *fb = vma->vm_private_data;
-
-       fb->map_count--;
-       dprintk("vino_vm_close(): count = %d\n", fb->map_count);
-}
-
-static const struct vm_operations_struct vino_vm_ops = {
-       .open   = vino_vm_open,
-       .close  = vino_vm_close,
-};
-
-static int vino_mmap(struct file *file, struct vm_area_struct *vma)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-
-       unsigned long start = vma->vm_start;
-       unsigned long size = vma->vm_end - vma->vm_start;
-       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
-
-       struct vino_framebuffer *fb = NULL;
-       unsigned int i, length;
-       int ret = 0;
-
-       dprintk("mmap():\n");
-
-       // TODO: reject mmap if already mapped
-
-       if (mutex_lock_interruptible(&vcs->mutex))
-               return -EINTR;
-
-       if (vcs->reading) {
-               ret = -EBUSY;
-               goto out;
-       }
-
-       // TODO: check queue type
-
-       if (!(vma->vm_flags & VM_WRITE)) {
-               dprintk("mmap(): app bug: PROT_WRITE please\n");
-               ret = -EINVAL;
-               goto out;
-       }
-       if (!(vma->vm_flags & VM_SHARED)) {
-               dprintk("mmap(): app bug: MAP_SHARED please\n");
-               ret = -EINVAL;
-               goto out;
-       }
-
-       /* find the correct buffer using offset */
-       length = vino_queue_get_length(&vcs->fb_queue);
-       if (length == 0) {
-               dprintk("mmap(): queue not initialized\n");
-               ret = -EINVAL;
-               goto out;
-       }
-
-       for (i = 0; i < length; i++) {
-               fb = vino_queue_get_buffer(&vcs->fb_queue, i);
-               if (fb == NULL) {
-                       dprintk("mmap(): vino_queue_get_buffer() failed\n");
-                       ret = -EINVAL;
-                       goto out;
-               }
-
-               if (fb->offset == offset)
-                       goto found;
-       }
-
-       dprintk("mmap(): invalid offset = %lu\n", offset);
-       ret = -EINVAL;
-       goto out;
-
-found:
-       dprintk("mmap(): buffer = %d\n", i);
-
-       if (size > (fb->desc_table.page_count * PAGE_SIZE)) {
-               dprintk("mmap(): failed: size = %lu > %lu\n",
-                       size, fb->desc_table.page_count * PAGE_SIZE);
-               ret = -EINVAL;
-               goto out;
-       }
-
-       for (i = 0; i < fb->desc_table.page_count; i++) {
-               unsigned long pfn =
-                       virt_to_phys((void *)fb->desc_table.virtual[i]) >>
-                       PAGE_SHIFT;
-
-               if (size < PAGE_SIZE)
-                       break;
-
-               // protection was: PAGE_READONLY
-               if (remap_pfn_range(vma, start, pfn, PAGE_SIZE,
-                                   vma->vm_page_prot)) {
-                       dprintk("mmap(): remap_pfn_range() failed\n");
-                       ret = -EAGAIN;
-                       goto out;
-               }
-
-               start += PAGE_SIZE;
-               size -= PAGE_SIZE;
-       }
-
-       fb->map_count = 1;
-
-       vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
-       vma->vm_flags &= ~VM_IO;
-       vma->vm_private_data = fb;
-       vma->vm_file = file;
-       vma->vm_ops = &vino_vm_ops;
-
-out:
-       mutex_unlock(&vcs->mutex);
-
-       return ret;
-}
-
-static unsigned int vino_poll(struct file *file, poll_table *pt)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       unsigned int outgoing;
-       unsigned int ret = 0;
-
-       // lock mutex (?)
-       // TODO: this has to be corrected for different read modes
-
-       dprintk("poll():\n");
-
-       if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
-               dprintk("poll(): vino_queue_get_outgoing() failed\n");
-               ret = POLLERR;
-               goto error;
-       }
-       if (outgoing > 0)
-               goto over;
-
-       poll_wait(file, &vcs->fb_queue.frame_wait_queue, pt);
-
-       if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
-               dprintk("poll(): vino_queue_get_outgoing() failed\n");
-               ret = POLLERR;
-               goto error;
-       }
-
-over:
-       dprintk("poll(): data %savailable\n",
-               (outgoing > 0) ? "" : "not ");
-
-       if (outgoing > 0)
-               ret = POLLIN | POLLRDNORM;
-
-error:
-       return ret;
-}
-
-static long vino_ioctl(struct file *file,
-                     unsigned int cmd, unsigned long arg)
-{
-       struct vino_channel_settings *vcs = video_drvdata(file);
-       long ret;
-
-       if (mutex_lock_interruptible(&vcs->mutex))
-               return -EINTR;
-
-       ret = video_ioctl2(file, cmd, arg);
-
-       mutex_unlock(&vcs->mutex);
-
-       return ret;
-}
-
-/* Initialization and cleanup */
-
-/* __initdata */
-static int vino_init_stage;
-
-const struct v4l2_ioctl_ops vino_ioctl_ops = {
-       .vidioc_enum_fmt_vid_cap     = vino_enum_fmt_vid_cap,
-       .vidioc_g_fmt_vid_cap        = vino_g_fmt_vid_cap,
-       .vidioc_s_fmt_vid_cap        = vino_s_fmt_vid_cap,
-       .vidioc_try_fmt_vid_cap      = vino_try_fmt_vid_cap,
-       .vidioc_querycap             = vino_querycap,
-       .vidioc_enum_input           = vino_enum_input,
-       .vidioc_g_input              = vino_g_input,
-       .vidioc_s_input              = vino_s_input,
-       .vidioc_g_std                = vino_g_std,
-       .vidioc_s_std                = vino_s_std,
-       .vidioc_querystd             = vino_querystd,
-       .vidioc_cropcap              = vino_cropcap,
-       .vidioc_s_crop               = vino_s_crop,
-       .vidioc_g_crop               = vino_g_crop,
-       .vidioc_s_parm               = vino_s_parm,
-       .vidioc_g_parm               = vino_g_parm,
-       .vidioc_reqbufs              = vino_reqbufs,
-       .vidioc_querybuf             = vino_querybuf,
-       .vidioc_qbuf                 = vino_qbuf,
-       .vidioc_dqbuf                = vino_dqbuf,
-       .vidioc_streamon             = vino_streamon,
-       .vidioc_streamoff            = vino_streamoff,
-       .vidioc_queryctrl            = vino_queryctrl,
-       .vidioc_g_ctrl               = vino_g_ctrl,
-       .vidioc_s_ctrl               = vino_s_ctrl,
-};
-
-static const struct v4l2_file_operations vino_fops = {
-       .owner          = THIS_MODULE,
-       .open           = vino_open,
-       .release        = vino_close,
-       .unlocked_ioctl = vino_ioctl,
-       .mmap           = vino_mmap,
-       .poll           = vino_poll,
-};
-
-static struct video_device vdev_template = {
-       .name           = "NOT SET",
-       .fops           = &vino_fops,
-       .ioctl_ops      = &vino_ioctl_ops,
-       .tvnorms        = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
-};
-
-static void vino_module_cleanup(int stage)
-{
-       switch(stage) {
-       case 11:
-               video_unregister_device(vino_drvdata->b.vdev);
-               vino_drvdata->b.vdev = NULL;
-       case 10:
-               video_unregister_device(vino_drvdata->a.vdev);
-               vino_drvdata->a.vdev = NULL;
-       case 9:
-               i2c_del_adapter(&vino_i2c_adapter);
-       case 8:
-               free_irq(SGI_VINO_IRQ, NULL);
-       case 7:
-               if (vino_drvdata->b.vdev) {
-                       video_device_release(vino_drvdata->b.vdev);
-                       vino_drvdata->b.vdev = NULL;
-               }
-       case 6:
-               if (vino_drvdata->a.vdev) {
-                       video_device_release(vino_drvdata->a.vdev);
-                       vino_drvdata->a.vdev = NULL;
-               }
-       case 5:
-               /* all entries in dma_cpu dummy table have the same address */
-               dma_unmap_single(NULL,
-                                vino_drvdata->dummy_desc_table.dma_cpu[0],
-                                PAGE_SIZE, DMA_FROM_DEVICE);
-               dma_free_coherent(NULL, VINO_DUMMY_DESC_COUNT
-                                 * sizeof(dma_addr_t),
-                                 (void *)vino_drvdata->
-                                 dummy_desc_table.dma_cpu,
-                                 vino_drvdata->dummy_desc_table.dma);
-       case 4:
-               free_page(vino_drvdata->dummy_page);
-       case 3:
-               v4l2_device_unregister(&vino_drvdata->v4l2_dev);
-       case 2:
-               kfree(vino_drvdata);
-       case 1:
-               iounmap(vino);
-       case 0:
-               break;
-       default:
-               dprintk("vino_module_cleanup(): invalid cleanup stage = %d\n",
-                       stage);
-       }
-}
-
-static int vino_probe(void)
-{
-       unsigned long rev_id;
-
-       if (ip22_is_fullhouse()) {
-               printk(KERN_ERR "VINO doesn't exist in IP22 Fullhouse\n");
-               return -ENODEV;
-       }
-
-       if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) {
-               printk(KERN_ERR "VINO is not found (EISA BUS not present)\n");
-               return -ENODEV;
-       }
-
-       vino = (struct sgi_vino *)ioremap(VINO_BASE, sizeof(struct sgi_vino));
-       if (!vino) {
-               printk(KERN_ERR "VINO: ioremap() failed\n");
-               return -EIO;
-       }
-       vino_init_stage++;
-
-       if (get_dbe(rev_id, &(vino->rev_id))) {
-               printk(KERN_ERR "Failed to read VINO revision register\n");
-               vino_module_cleanup(vino_init_stage);
-               return -ENODEV;
-       }
-
-       if (VINO_ID_VALUE(rev_id) != VINO_CHIP_ID) {
-               printk(KERN_ERR "Unknown VINO chip ID (Rev/ID: 0x%02lx)\n",
-                      rev_id);
-               vino_module_cleanup(vino_init_stage);
-               return -ENODEV;
-       }
-
-       printk(KERN_INFO "VINO revision %ld found\n", VINO_REV_NUM(rev_id));
-
-       return 0;
-}
-
-static int vino_init(void)
-{
-       dma_addr_t dma_dummy_address;
-       int err;
-       int i;
-
-       vino_drvdata = kzalloc(sizeof(struct vino_settings), GFP_KERNEL);
-       if (!vino_drvdata) {
-               vino_module_cleanup(vino_init_stage);
-               return -ENOMEM;
-       }
-       vino_init_stage++;
-       strlcpy(vino_drvdata->v4l2_dev.name, "vino",
-                       sizeof(vino_drvdata->v4l2_dev.name));
-       err = v4l2_device_register(NULL, &vino_drvdata->v4l2_dev);
-       if (err)
-               return err;
-       vino_init_stage++;
-
-       /* create a dummy dma descriptor */
-       vino_drvdata->dummy_page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
-       if (!vino_drvdata->dummy_page) {
-               vino_module_cleanup(vino_init_stage);
-               return -ENOMEM;
-       }
-       vino_init_stage++;
-
-       // TODO: use page_count in dummy_desc_table
-
-       vino_drvdata->dummy_desc_table.dma_cpu =
-               dma_alloc_coherent(NULL,
-               VINO_DUMMY_DESC_COUNT * sizeof(dma_addr_t),
-               &vino_drvdata->dummy_desc_table.dma,
-               GFP_KERNEL | GFP_DMA);
-       if (!vino_drvdata->dummy_desc_table.dma_cpu) {
-               vino_module_cleanup(vino_init_stage);
-               return -ENOMEM;
-       }
-       vino_init_stage++;
-
-       dma_dummy_address = dma_map_single(NULL,
-                                          (void *)vino_drvdata->dummy_page,
-                                       PAGE_SIZE, DMA_FROM_DEVICE);
-       for (i = 0; i < VINO_DUMMY_DESC_COUNT; i++) {
-               vino_drvdata->dummy_desc_table.dma_cpu[i] = dma_dummy_address;
-       }
-
-       /* initialize VINO */
-
-       vino->control = 0;
-       vino->a.next_4_desc = vino_drvdata->dummy_desc_table.dma;
-       vino->b.next_4_desc = vino_drvdata->dummy_desc_table.dma;
-       udelay(VINO_DESC_FETCH_DELAY);
-
-       vino->intr_status = 0;
-
-       vino->a.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT;
-       vino->b.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT;
-
-       return 0;
-}
-
-static int vino_init_channel_settings(struct vino_channel_settings *vcs,
-                                unsigned int channel, const char *name)
-{
-       vcs->channel = channel;
-       vcs->input = VINO_INPUT_NONE;
-       vcs->alpha = 0;
-       vcs->users = 0;
-       vcs->data_format = VINO_DATA_FMT_GREY;
-       vcs->data_norm = VINO_DATA_NORM_NTSC;
-       vcs->decimation = 1;
-       vino_set_default_clipping(vcs);
-       vino_set_default_framerate(vcs);
-
-       vcs->capturing = 0;
-
-       mutex_init(&vcs->mutex);
-       spin_lock_init(&vcs->capture_lock);
-
-       mutex_init(&vcs->fb_queue.queue_mutex);
-       spin_lock_init(&vcs->fb_queue.queue_lock);
-       init_waitqueue_head(&vcs->fb_queue.frame_wait_queue);
-
-       vcs->vdev = video_device_alloc();
-       if (!vcs->vdev) {
-               vino_module_cleanup(vino_init_stage);
-               return -ENOMEM;
-       }
-       vino_init_stage++;
-
-       memcpy(vcs->vdev, &vdev_template,
-              sizeof(struct video_device));
-       strcpy(vcs->vdev->name, name);
-       vcs->vdev->release = video_device_release;
-       vcs->vdev->v4l2_dev = &vino_drvdata->v4l2_dev;
-
-       video_set_drvdata(vcs->vdev, vcs);
-
-       return 0;
-}
-
-static int __init vino_module_init(void)
-{
-       int ret;
-
-       printk(KERN_INFO "SGI VINO driver version %s\n",
-              VINO_MODULE_VERSION);
-
-       ret = vino_probe();
-       if (ret)
-               return ret;
-
-       ret = vino_init();
-       if (ret)
-               return ret;
-
-       /* initialize data structures */
-
-       spin_lock_init(&vino_drvdata->vino_lock);
-       spin_lock_init(&vino_drvdata->input_lock);
-
-       ret = vino_init_channel_settings(&vino_drvdata->a, VINO_CHANNEL_A,
-                                   vino_vdev_name_a);
-       if (ret)
-               return ret;
-
-       ret = vino_init_channel_settings(&vino_drvdata->b, VINO_CHANNEL_B,
-                                   vino_vdev_name_b);
-       if (ret)
-               return ret;
-
-       /* initialize hardware and register V4L devices */
-
-       ret = request_irq(SGI_VINO_IRQ, vino_interrupt, 0,
-               vino_driver_description, NULL);
-       if (ret) {
-               printk(KERN_ERR "VINO: requesting IRQ %02d failed\n",
-                      SGI_VINO_IRQ);
-               vino_module_cleanup(vino_init_stage);
-               return -EAGAIN;
-       }
-       vino_init_stage++;
-
-       ret = i2c_add_adapter(&vino_i2c_adapter);
-       if (ret) {
-               printk(KERN_ERR "VINO I2C bus registration failed\n");
-               vino_module_cleanup(vino_init_stage);
-               return ret;
-       }
-       i2c_set_adapdata(&vino_i2c_adapter, &vino_drvdata->v4l2_dev);
-       vino_init_stage++;
-
-       ret = video_register_device(vino_drvdata->a.vdev,
-                                   VFL_TYPE_GRABBER, -1);
-       if (ret < 0) {
-               printk(KERN_ERR "VINO channel A Video4Linux-device "
-                      "registration failed\n");
-               vino_module_cleanup(vino_init_stage);
-               return -EINVAL;
-       }
-       vino_init_stage++;
-
-       ret = video_register_device(vino_drvdata->b.vdev,
-                                   VFL_TYPE_GRABBER, -1);
-       if (ret < 0) {
-               printk(KERN_ERR "VINO channel B Video4Linux-device "
-                      "registration failed\n");
-               vino_module_cleanup(vino_init_stage);
-               return -EINVAL;
-       }
-       vino_init_stage++;
-
-       vino_drvdata->decoder =
-               v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter,
-                              "saa7191", 0, I2C_ADDRS(0x45));
-       vino_drvdata->camera =
-               v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter,
-                              "indycam", 0, I2C_ADDRS(0x2b));
-
-       dprintk("init complete!\n");
-
-       return 0;
-}
-
-static void __exit vino_module_exit(void)
-{
-       dprintk("exiting, stage = %d ...\n", vino_init_stage);
-       vino_module_cleanup(vino_init_stage);
-       dprintk("cleanup complete, exit!\n");
-}
-
-module_init(vino_module_init);
-module_exit(vino_module_exit);
diff --git a/drivers/media/platform/vino.h b/drivers/media/platform/vino.h
deleted file mode 100644 (file)
index de2d615..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Driver for the VINO (Video In No Out) system found in SGI Indys.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * Copyright (C) 1999 Ulf Karlsson <ulfc@bun.falkenberg.se>
- * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
- */
-
-#ifndef _VINO_H_
-#define _VINO_H_
-
-#define VINO_BASE      0x00080000      /* Vino is in the EISA address space,
-                                        * but it is not an EISA bus card */
-#define VINO_PAGE_SIZE 4096
-
-struct sgi_vino_channel {
-       u32 _pad_alpha;
-       volatile u32 alpha;
-
-#define VINO_CLIP_X(x)         ((x) & 0x3ff)           /* bits 0:9 */
-#define VINO_CLIP_ODD(x)       (((x) & 0x1ff) << 10)   /* bits 10:18 */
-#define VINO_CLIP_EVEN(x)      (((x) & 0x1ff) << 19)   /* bits 19:27 */
-       u32 _pad_clip_start;
-       volatile u32 clip_start;
-       u32 _pad_clip_end;
-       volatile u32 clip_end;
-
-#define VINO_FRAMERT_FULL      0xfff
-#define VINO_FRAMERT_PAL       (1<<0)                  /* 0=NTSC 1=PAL */
-#define VINO_FRAMERT_RT(x)     (((x) & 0xfff) << 1)    /* bits 1:12 */
-       u32 _pad_frame_rate;
-       volatile u32 frame_rate;
-
-       u32 _pad_field_counter;
-       volatile u32 field_counter;
-       u32 _pad_line_size;
-       volatile u32 line_size;
-       u32 _pad_line_count;
-       volatile u32 line_count;
-       u32 _pad_page_index;
-       volatile u32 page_index;
-       u32 _pad_next_4_desc;
-       volatile u32 next_4_desc;
-       u32 _pad_start_desc_tbl;
-       volatile u32 start_desc_tbl;
-
-#define VINO_DESC_JUMP         (1<<30)
-#define VINO_DESC_STOP         (1<<31)
-#define VINO_DESC_VALID                (1<<32)
-       u32 _pad_desc_0;
-       volatile u32 desc_0;
-       u32 _pad_desc_1;
-       volatile u32 desc_1;
-       u32 _pad_desc_2;
-       volatile u32 desc_2;
-       u32 _pad_Bdesc_3;
-       volatile u32 desc_3;
-
-       u32 _pad_fifo_thres;
-       volatile u32 fifo_thres;
-       u32 _pad_fifo_read;
-       volatile u32 fifo_read;
-       u32 _pad_fifo_write;
-       volatile u32 fifo_write;
-};
-
-struct sgi_vino {
-#define VINO_CHIP_ID           0xb
-#define VINO_REV_NUM(x)                ((x) & 0x0f)
-#define VINO_ID_VALUE(x)       (((x) & 0xf0) >> 4)
-       u32 _pad_rev_id;
-       volatile u32 rev_id;
-
-#define VINO_CTRL_LITTLE_ENDIAN                (1<<0)
-#define VINO_CTRL_A_EOF_INT            (1<<1)  /* Field transferred int */
-#define VINO_CTRL_A_FIFO_INT           (1<<2)  /* FIFO overflow int */
-#define VINO_CTRL_A_EOD_INT            (1<<3)  /* End of desc table int */
-#define VINO_CTRL_A_INT                        (VINO_CTRL_A_EOF_INT | \
-                                        VINO_CTRL_A_FIFO_INT | \
-                                        VINO_CTRL_A_EOD_INT)
-#define VINO_CTRL_B_EOF_INT            (1<<4)  /* Field transferred int */
-#define VINO_CTRL_B_FIFO_INT           (1<<5)  /* FIFO overflow int */
-#define VINO_CTRL_B_EOD_INT            (1<<6)  /* End of desc table int */
-#define VINO_CTRL_B_INT                        (VINO_CTRL_B_EOF_INT | \
-                                        VINO_CTRL_B_FIFO_INT | \
-                                        VINO_CTRL_B_EOD_INT)
-#define VINO_CTRL_A_DMA_ENBL           (1<<7)
-#define VINO_CTRL_A_INTERLEAVE_ENBL    (1<<8)
-#define VINO_CTRL_A_SYNC_ENBL          (1<<9)
-#define VINO_CTRL_A_SELECT             (1<<10) /* 1=D1 0=Philips */
-#define VINO_CTRL_A_RGB                        (1<<11) /* 1=RGB 0=YUV */
-#define VINO_CTRL_A_LUMA_ONLY          (1<<12)
-#define VINO_CTRL_A_DEC_ENBL           (1<<13) /* Decimation */
-#define VINO_CTRL_A_DEC_SCALE_MASK     0x1c000 /* bits 14:17 */
-#define VINO_CTRL_A_DEC_SCALE_SHIFT    (14)
-#define VINO_CTRL_A_DEC_HOR_ONLY       (1<<17) /* Horizontal only */
-#define VINO_CTRL_A_DITHER             (1<<18) /* 24 -> 8 bit dither */
-#define VINO_CTRL_B_DMA_ENBL           (1<<19)
-#define VINO_CTRL_B_INTERLEAVE_ENBL    (1<<20)
-#define VINO_CTRL_B_SYNC_ENBL          (1<<21)
-#define VINO_CTRL_B_SELECT             (1<<22) /* 1=D1 0=Philips */
-#define VINO_CTRL_B_RGB                        (1<<23) /* 1=RGB 0=YUV */
-#define VINO_CTRL_B_LUMA_ONLY          (1<<24)
-#define VINO_CTRL_B_DEC_ENBL           (1<<25) /* Decimation */
-#define VINO_CTRL_B_DEC_SCALE_MASK     0x1c000000      /* bits 26:28 */
-#define VINO_CTRL_B_DEC_SCALE_SHIFT    (26)
-#define VINO_CTRL_B_DEC_HOR_ONLY       (1<<29) /* Decimation horizontal only */
-#define VINO_CTRL_B_DITHER             (1<<30) /* ChanB 24 -> 8 bit dither */
-       u32 _pad_control;
-       volatile u32 control;
-
-#define VINO_INTSTAT_A_EOF             (1<<0)  /* Field transferred int */
-#define VINO_INTSTAT_A_FIFO            (1<<1)  /* FIFO overflow int */
-#define VINO_INTSTAT_A_EOD             (1<<2)  /* End of desc table int */
-#define VINO_INTSTAT_A                 (VINO_INTSTAT_A_EOF | \
-                                        VINO_INTSTAT_A_FIFO | \
-                                        VINO_INTSTAT_A_EOD)
-#define VINO_INTSTAT_B_EOF             (1<<3)  /* Field transferred int */
-#define VINO_INTSTAT_B_FIFO            (1<<4)  /* FIFO overflow int */
-#define VINO_INTSTAT_B_EOD             (1<<5)  /* End of desc table int */
-#define VINO_INTSTAT_B                 (VINO_INTSTAT_B_EOF | \
-                                        VINO_INTSTAT_B_FIFO | \
-                                        VINO_INTSTAT_B_EOD)
-       u32 _pad_intr_status;
-       volatile u32 intr_status;
-
-       u32 _pad_i2c_control;
-       volatile u32 i2c_control;
-       u32 _pad_i2c_data;
-       volatile u32 i2c_data;
-
-       struct sgi_vino_channel a;
-       struct sgi_vino_channel b;
-};
-
-#endif
index ee5c399..39ff79f 100644 (file)
@@ -625,7 +625,7 @@ int vivid_vid_out_g_selection(struct file *file, void *priv,
                sel->r = dev->fmt_out_rect;
                break;
        case V4L2_SEL_TGT_CROP_BOUNDS:
-               if (!dev->has_compose_out)
+               if (!dev->has_crop_out)
                        return -EINVAL;
                sel->r = vivid_max_rect;
                break;
index 056181f..7496f33 100644 (file)
@@ -24,7 +24,6 @@ if MEDIA_ANALOG_TV_SUPPORT
        comment "Analog TV USB devices"
 source "drivers/media/usb/pvrusb2/Kconfig"
 source "drivers/media/usb/hdpvr/Kconfig"
-source "drivers/media/usb/tlg2300/Kconfig"
 source "drivers/media/usb/usbvision/Kconfig"
 source "drivers/media/usb/stk1160/Kconfig"
 source "drivers/media/usb/go7007/Kconfig"
index 6f2eb7c..8874ba7 100644 (file)
@@ -16,7 +16,6 @@ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/
 obj-$(CONFIG_VIDEO_AU0828) += au0828/
 obj-$(CONFIG_VIDEO_HDPVR)      += hdpvr/
 obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
-obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/
 obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
 obj-$(CONFIG_VIDEO_STK1160) += stk1160/
 obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
diff --git a/drivers/media/usb/tlg2300/Kconfig b/drivers/media/usb/tlg2300/Kconfig
deleted file mode 100644 (file)
index 645d915..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-config VIDEO_TLG2300
-       tristate "Telegent TLG2300 USB video capture support"
-       depends on VIDEO_DEV && I2C && SND && DVB_CORE
-       select VIDEO_TUNER
-       select VIDEO_TVEEPROM
-       depends on RC_CORE
-       select VIDEOBUF_VMALLOC
-       select SND_PCM
-       select VIDEOBUF_DVB
-
-       ---help---
-         This is a video4linux driver for Telegent tlg2300 based TV cards.
-         The driver supports V4L2, DVB-T and radio.
-
-         To compile this driver as a module, choose M here: the
-         module will be called poseidon
diff --git a/drivers/media/usb/tlg2300/Makefile b/drivers/media/usb/tlg2300/Makefile
deleted file mode 100644 (file)
index 137f8e3..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o
-
-obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o
-
-ccflags-y += -Idrivers/media/i2c
-ccflags-y += -Idrivers/media/tuners
-ccflags-y += -Idrivers/media/dvb-core
-ccflags-y += -Idrivers/media/dvb-frontends
-
diff --git a/drivers/media/usb/tlg2300/pd-alsa.c b/drivers/media/usb/tlg2300/pd-alsa.c
deleted file mode 100644 (file)
index dd8fe10..0000000
+++ /dev/null
@@ -1,337 +0,0 @@
-#include <linux/kernel.h>
-#include <linux/usb.h>
-#include <linux/init.h>
-#include <linux/sound.h>
-#include <linux/spinlock.h>
-#include <linux/soundcard.h>
-#include <linux/vmalloc.h>
-#include <linux/proc_fs.h>
-#include <linux/module.h>
-#include <linux/gfp.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/info.h>
-#include <sound/initval.h>
-#include <sound/control.h>
-#include <media/v4l2-common.h>
-#include "pd-common.h"
-#include "vendorcmds.h"
-
-static void complete_handler_audio(struct urb *urb);
-#define AUDIO_EP       (0x83)
-#define AUDIO_BUF_SIZE (512)
-#define PERIOD_SIZE    (1024 * 8)
-#define PERIOD_MIN     (4)
-#define PERIOD_MAX     PERIOD_MIN
-
-static struct snd_pcm_hardware snd_pd_hw_capture = {
-       .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
-               SNDRV_PCM_INFO_MMAP           |
-               SNDRV_PCM_INFO_INTERLEAVED |
-               SNDRV_PCM_INFO_MMAP_VALID,
-
-       .formats = SNDRV_PCM_FMTBIT_S16_LE,
-       .rates = SNDRV_PCM_RATE_48000,
-
-       .rate_min = 48000,
-       .rate_max = 48000,
-       .channels_min = 2,
-       .channels_max = 2,
-       .buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN,
-       .period_bytes_min = PERIOD_SIZE,
-       .period_bytes_max = PERIOD_SIZE,
-       .periods_min = PERIOD_MIN,
-       .periods_max = PERIOD_MAX,
-       /*
-       .buffer_bytes_max = 62720 * 8,
-       .period_bytes_min = 64,
-       .period_bytes_max = 12544,
-       .periods_min = 2,
-       .periods_max = 98
-       */
-};
-
-static int snd_pd_capture_open(struct snd_pcm_substream *substream)
-{
-       struct poseidon *p = snd_pcm_substream_chip(substream);
-       struct poseidon_audio *pa = &p->audio;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-
-       if (!p)
-               return -ENODEV;
-       pa->users++;
-       pa->card_close          = 0;
-       pa->capture_pcm_substream       = substream;
-       runtime->private_data           = p;
-
-       runtime->hw = snd_pd_hw_capture;
-       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
-       usb_autopm_get_interface(p->interface);
-       kref_get(&p->kref);
-       return 0;
-}
-
-static int snd_pd_pcm_close(struct snd_pcm_substream *substream)
-{
-       struct poseidon *p = snd_pcm_substream_chip(substream);
-       struct poseidon_audio *pa = &p->audio;
-
-       pa->users--;
-       pa->card_close          = 1;
-       usb_autopm_put_interface(p->interface);
-       kref_put(&p->kref, poseidon_delete);
-       return 0;
-}
-
-static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream,
-                                       struct snd_pcm_hw_params *hw_params)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       unsigned int size;
-
-       size = params_buffer_bytes(hw_params);
-       if (runtime->dma_area) {
-               if (runtime->dma_bytes > size)
-                       return 0;
-               vfree(runtime->dma_area);
-       }
-       runtime->dma_area = vmalloc(size);
-       if (!runtime->dma_area)
-               return -ENOMEM;
-       else
-               runtime->dma_bytes = size;
-       return 0;
-}
-
-static int audio_buf_free(struct poseidon *p)
-{
-       struct poseidon_audio *pa = &p->audio;
-       int i;
-
-       for (i = 0; i < AUDIO_BUFS; i++)
-               if (pa->urb_array[i])
-                       usb_kill_urb(pa->urb_array[i]);
-       free_all_urb_generic(pa->urb_array, AUDIO_BUFS);
-       logpm();
-       return 0;
-}
-
-static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream)
-{
-       struct poseidon *p = snd_pcm_substream_chip(substream);
-
-       logpm();
-       audio_buf_free(p);
-       return 0;
-}
-
-static int snd_pd_prepare(struct snd_pcm_substream *substream)
-{
-       return 0;
-}
-
-#define AUDIO_TRAILER_SIZE     (16)
-static inline void handle_audio_data(struct urb *urb, int *period_elapsed)
-{
-       struct poseidon_audio *pa = urb->context;
-       struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime;
-
-       int stride      = runtime->frame_bits >> 3;
-       int len         = urb->actual_length / stride;
-       unsigned char *cp       = urb->transfer_buffer;
-       unsigned int oldptr     = pa->rcv_position;
-
-       if (urb->actual_length == AUDIO_BUF_SIZE - 4)
-               len -= (AUDIO_TRAILER_SIZE / stride);
-
-       /* do the copy */
-       if (oldptr + len >= runtime->buffer_size) {
-               unsigned int cnt = runtime->buffer_size - oldptr;
-
-               memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride);
-               memcpy(runtime->dma_area, (cp + cnt * stride),
-                                       (len * stride - cnt * stride));
-       } else
-               memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
-
-       /* update the statas */
-       snd_pcm_stream_lock(pa->capture_pcm_substream);
-       pa->rcv_position        += len;
-       if (pa->rcv_position >= runtime->buffer_size)
-               pa->rcv_position -= runtime->buffer_size;
-
-       pa->copied_position += (len);
-       if (pa->copied_position >= runtime->period_size) {
-               pa->copied_position -= runtime->period_size;
-               *period_elapsed = 1;
-       }
-       snd_pcm_stream_unlock(pa->capture_pcm_substream);
-}
-
-static void complete_handler_audio(struct urb *urb)
-{
-       struct poseidon_audio *pa = urb->context;
-       struct snd_pcm_substream *substream = pa->capture_pcm_substream;
-       int    period_elapsed = 0;
-       int    ret;
-
-       if (1 == pa->card_close || pa->capture_stream != STREAM_ON)
-               return;
-
-       if (urb->status != 0) {
-               /*if (urb->status == -ESHUTDOWN)*/
-                       return;
-       }
-
-       if (substream) {
-               if (urb->actual_length) {
-                       handle_audio_data(urb, &period_elapsed);
-                       if (period_elapsed)
-                               snd_pcm_period_elapsed(substream);
-               }
-       }
-
-       ret = usb_submit_urb(urb, GFP_ATOMIC);
-       if (ret < 0)
-               log("audio urb failed (errcod = %i)", ret);
-       return;
-}
-
-static int fire_audio_urb(struct poseidon *p)
-{
-       int i, ret = 0;
-       struct poseidon_audio *pa = &p->audio;
-
-       alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS,
-                       p->udev, AUDIO_EP,
-                       AUDIO_BUF_SIZE, GFP_ATOMIC,
-                       complete_handler_audio, pa);
-
-       for (i = 0; i < AUDIO_BUFS; i++) {
-               ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL);
-               if (ret)
-                       log("urb err : %d", ret);
-       }
-       log();
-       return ret;
-}
-
-static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-       struct poseidon *p = snd_pcm_substream_chip(substream);
-       struct poseidon_audio *pa = &p->audio;
-
-       if (debug_mode)
-               log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream);
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_RESUME:
-       case SNDRV_PCM_TRIGGER_START:
-               if (pa->capture_stream == STREAM_ON)
-                       return 0;
-
-               pa->rcv_position = pa->copied_position = 0;
-               pa->capture_stream = STREAM_ON;
-
-               if (in_hibernation(p))
-                       return 0;
-               fire_audio_urb(p);
-               return 0;
-
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-               pa->capture_stream = STREAM_SUSPEND;
-               return 0;
-       case SNDRV_PCM_TRIGGER_STOP:
-               pa->capture_stream = STREAM_OFF;
-               return 0;
-       default:
-               return -EINVAL;
-       }
-}
-
-static snd_pcm_uframes_t
-snd_pd_capture_pointer(struct snd_pcm_substream *substream)
-{
-       struct poseidon *p = snd_pcm_substream_chip(substream);
-       struct poseidon_audio *pa = &p->audio;
-       return pa->rcv_position;
-}
-
-static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs,
-                                            unsigned long offset)
-{
-       void *pageptr = subs->runtime->dma_area + offset;
-       return vmalloc_to_page(pageptr);
-}
-
-static struct snd_pcm_ops pcm_capture_ops = {
-       .open      = snd_pd_capture_open,
-       .close     = snd_pd_pcm_close,
-       .ioctl     = snd_pcm_lib_ioctl,
-       .hw_params = snd_pd_hw_capture_params,
-       .hw_free   = snd_pd_hw_capture_free,
-       .prepare   = snd_pd_prepare,
-       .trigger   = snd_pd_capture_trigger,
-       .pointer   = snd_pd_capture_pointer,
-       .page      = snd_pcm_pd_get_page,
-};
-
-#ifdef CONFIG_PM
-int pm_alsa_suspend(struct poseidon *p)
-{
-       logpm(p);
-       audio_buf_free(p);
-       return 0;
-}
-
-int pm_alsa_resume(struct poseidon *p)
-{
-       logpm(p);
-       fire_audio_urb(p);
-       return 0;
-}
-#endif
-
-int poseidon_audio_init(struct poseidon *p)
-{
-       struct poseidon_audio *pa = &p->audio;
-       struct snd_card *card;
-       struct snd_pcm *pcm;
-       int ret;
-
-       ret = snd_card_new(&p->interface->dev, -1, "Telegent",
-                          THIS_MODULE, 0, &card);
-       if (ret != 0)
-               return ret;
-
-       ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm);
-       if (ret < 0) {
-               snd_card_free(card);
-               return ret;
-       }
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
-       pcm->info_flags   = 0;
-       pcm->private_data = p;
-       strcpy(pcm->name, "poseidon audio capture");
-
-       strcpy(card->driver, "ALSA driver");
-       strcpy(card->shortname, "poseidon Audio");
-       strcpy(card->longname, "poseidon ALSA Audio");
-
-       if (snd_card_register(card)) {
-               snd_card_free(card);
-               return -ENOMEM;
-       }
-       pa->card = card;
-       return 0;
-}
-
-int poseidon_audio_free(struct poseidon *p)
-{
-       struct poseidon_audio *pa = &p->audio;
-
-       if (pa->card)
-               snd_card_free(pa->card);
-       return 0;
-}
diff --git a/drivers/media/usb/tlg2300/pd-common.h b/drivers/media/usb/tlg2300/pd-common.h
deleted file mode 100644 (file)
index 9e23ad3..0000000
+++ /dev/null
@@ -1,271 +0,0 @@
-#ifndef PD_COMMON_H
-#define PD_COMMON_H
-
-#include <linux/fs.h>
-#include <linux/wait.h>
-#include <linux/list.h>
-#include <linux/videodev2.h>
-#include <linux/semaphore.h>
-#include <linux/usb.h>
-#include <linux/poll.h>
-#include <media/videobuf-vmalloc.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ctrls.h>
-
-#include "dvb_frontend.h"
-#include "dvbdev.h"
-#include "dvb_demux.h"
-#include "dmxdev.h"
-
-#define SBUF_NUM       8
-#define MAX_BUFFER_NUM 6
-#define PK_PER_URB     32
-#define ISO_PKT_SIZE   3072
-
-#define POSEIDON_STATE_NONE            (0x0000)
-#define POSEIDON_STATE_ANALOG          (0x0001)
-#define POSEIDON_STATE_FM              (0x0002)
-#define POSEIDON_STATE_DVBT            (0x0004)
-#define POSEIDON_STATE_DISCONNECT      (0x0080)
-
-#define PM_SUSPEND_DELAY       3
-
-#define V4L_PAL_VBI_LINES      18
-#define V4L_NTSC_VBI_LINES     12
-#define V4L_PAL_VBI_FRAMESIZE  (V4L_PAL_VBI_LINES * 1440 * 2)
-#define V4L_NTSC_VBI_FRAMESIZE (V4L_NTSC_VBI_LINES * 1440 * 2)
-
-#define TUNER_FREQ_MIN         (45000000U)
-#define TUNER_FREQ_MAX         (862000000U)
-
-struct vbi_data {
-       struct video_device     v_dev;
-       struct video_data       *video;
-       struct front_face       *front;
-
-       unsigned int            copied;
-       unsigned int            vbi_size; /* the whole size of two fields */
-       int                     users;
-};
-
-/*
- * This is the running context of the video, it is useful for
- * resume()
- */
-struct running_context {
-       u32             freq;           /* VIDIOC_S_FREQUENCY */
-       int             audio_idx;      /* VIDIOC_S_TUNER    */
-       v4l2_std_id     tvnormid;       /* VIDIOC_S_STD     */
-       int             sig_index;      /* VIDIOC_S_INPUT  */
-       struct v4l2_pix_format pix;     /* VIDIOC_S_FMT   */
-};
-
-struct video_data {
-       /* v4l2 video device */
-       struct video_device     v_dev;
-       struct v4l2_ctrl_handler ctrl_handler;
-
-       /* the working context */
-       struct running_context  context;
-
-       /* for data copy */
-       int             field_count;
-
-       char            *dst;
-       int             lines_copied;
-       int             prev_left;
-
-       int             lines_per_field;
-       int             lines_size;
-
-       /* for communication */
-       u8                      endpoint_addr;
-       struct urb              *urb_array[SBUF_NUM];
-       struct vbi_data         *vbi;
-       struct poseidon         *pd;
-       struct front_face       *front;
-
-       int                     is_streaming;
-       int                     users;
-
-       /* for bubble handler */
-       struct work_struct      bubble_work;
-};
-
-enum pcm_stream_state {
-       STREAM_OFF,
-       STREAM_ON,
-       STREAM_SUSPEND,
-};
-
-#define AUDIO_BUFS (3)
-#define CAPTURE_STREAM_EN 1
-struct poseidon_audio {
-       struct urb              *urb_array[AUDIO_BUFS];
-       unsigned int            copied_position;
-       struct snd_pcm_substream   *capture_pcm_substream;
-
-       unsigned int            rcv_position;
-       struct  snd_card        *card;
-       int                     card_close;
-
-       int                     users;
-       int                     pm_state;
-       enum pcm_stream_state   capture_stream;
-};
-
-struct radio_data {
-       __u32           fm_freq;
-       unsigned int    is_radio_streaming;
-       int             pre_emphasis;
-       struct video_device fm_dev;
-       struct v4l2_ctrl_handler ctrl_handler;
-};
-
-#define DVB_SBUF_NUM           4
-#define DVB_URB_BUF_SIZE       0x2000
-struct pd_dvb_adapter {
-       struct dvb_adapter      dvb_adap;
-       struct dvb_frontend     dvb_fe;
-       struct dmxdev           dmxdev;
-       struct dvb_demux        demux;
-
-       atomic_t                users;
-       atomic_t                active_feed;
-
-       /* data transfer */
-       s32                     is_streaming;
-       struct urb              *urb_array[DVB_SBUF_NUM];
-       struct poseidon         *pd_device;
-       u8                      ep_addr;
-       u8                      reserved[3];
-
-       /* data for power resume*/
-       struct dtv_frontend_properties fe_param;
-
-       /* for channel scanning */
-       int             prev_freq;
-       int             bandwidth;
-       unsigned long   last_jiffies;
-};
-
-struct front_face {
-       /* use this field to distinguish VIDEO and VBI */
-       enum v4l2_buf_type      type;
-
-       /* for host */
-       struct videobuf_queue   q;
-
-       /* the bridge for host and device */
-       struct videobuf_buffer  *curr_frame;
-
-       /* for device */
-       spinlock_t              queue_lock;
-       struct list_head        active;
-       struct poseidon         *pd;
-};
-
-struct poseidon {
-       struct list_head        device_list;
-
-       struct mutex            lock;
-       struct kref             kref;
-
-       /* for V4L2 */
-       struct v4l2_device      v4l2_dev;
-
-       /* hardware info */
-       struct usb_device       *udev;
-       struct usb_interface    *interface;
-       int                     cur_transfer_mode;
-
-       struct video_data       video_data;     /* video */
-       struct vbi_data         vbi_data;       /* vbi   */
-       struct poseidon_audio   audio;          /* audio (alsa) */
-       struct radio_data       radio_data;     /* FM    */
-       struct pd_dvb_adapter   dvb_data;       /* DVB   */
-
-       u32                     state;
-       struct file             *file_for_stream; /* the active stream*/
-
-#ifdef CONFIG_PM
-       int (*pm_suspend)(struct poseidon *);
-       int (*pm_resume)(struct poseidon *);
-       pm_message_t            msg;
-
-       struct work_struct      pm_work;
-       u8                      portnum;
-#endif
-};
-
-struct poseidon_format {
-       char    *name;
-       int     fourcc;          /* video4linux 2         */
-       int     depth;           /* bit/pixel             */
-       int     flags;
-};
-
-struct poseidon_tvnorm {
-       v4l2_std_id     v4l2_id;
-       char            name[12];
-       u32             tlg_tvnorm;
-};
-
-/* video */
-int pd_video_init(struct poseidon *);
-void pd_video_exit(struct poseidon *);
-int stop_all_video_stream(struct poseidon *);
-
-/* alsa audio */
-int poseidon_audio_init(struct poseidon *);
-int poseidon_audio_free(struct poseidon *);
-#ifdef CONFIG_PM
-int pm_alsa_suspend(struct poseidon *);
-int pm_alsa_resume(struct poseidon *);
-#endif
-
-/* dvb */
-int pd_dvb_usb_device_init(struct poseidon *);
-void pd_dvb_usb_device_exit(struct poseidon *);
-void pd_dvb_usb_device_cleanup(struct poseidon *);
-int pd_dvb_get_adapter_num(struct pd_dvb_adapter *);
-void dvb_stop_streaming(struct pd_dvb_adapter *);
-
-/* FM */
-int poseidon_fm_init(struct poseidon *);
-int poseidon_fm_exit(struct poseidon *);
-
-/* vendor command ops */
-int send_set_req(struct poseidon*, u8, s32, s32*);
-int send_get_req(struct poseidon*, u8, s32, void*, s32*, s32);
-s32 set_tuner_mode(struct poseidon*, unsigned char);
-
-/* bulk urb alloc/free */
-int alloc_bulk_urbs_generic(struct urb **urb_array, int num,
-                       struct usb_device *udev, u8 ep_addr,
-                       int buf_size, gfp_t gfp_flags,
-                       usb_complete_t complete_fn, void *context);
-void free_all_urb_generic(struct urb **urb_array, int num);
-
-/* misc */
-void poseidon_delete(struct kref *kref);
-extern int debug_mode;
-void set_debug_mode(struct video_device *vfd, int debug_mode);
-
-#ifdef CONFIG_PM
-#define in_hibernation(pd) (pd->msg.event == PM_EVENT_FREEZE)
-#else
-#define in_hibernation(pd) (0)
-#endif
-#define get_pm_count(p) (atomic_read(&(p)->interface->pm_usage_cnt))
-
-#define log(a, ...) printk(KERN_DEBUG "\t[ %s : %.3d ] "a"\n", \
-                               __func__, __LINE__,  ## __VA_ARGS__)
-
-/* for power management */
-#define logpm(pd) do {\
-                       if (debug_mode & 0x10)\
-                               log();\
-               } while (0)
-
-#endif
diff --git a/drivers/media/usb/tlg2300/pd-dvb.c b/drivers/media/usb/tlg2300/pd-dvb.c
deleted file mode 100644 (file)
index ca4994a..0000000
+++ /dev/null
@@ -1,597 +0,0 @@
-#include "pd-common.h"
-#include <linux/kernel.h>
-#include <linux/usb.h>
-#include <linux/time.h>
-#include <linux/dvb/dmx.h>
-#include <linux/delay.h>
-#include <linux/gfp.h>
-
-#include "vendorcmds.h"
-#include <linux/sched.h>
-#include <linux/atomic.h>
-
-static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb);
-
-static int dvb_bandwidth[][2] = {
-       { TLG_BW_8, 8000000 },
-       { TLG_BW_7, 7000000 },
-       { TLG_BW_6, 6000000 }
-};
-static int dvb_bandwidth_length = ARRAY_SIZE(dvb_bandwidth);
-
-static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb);
-static int poseidon_check_mode_dvbt(struct poseidon *pd)
-{
-       s32 ret = 0, cmd_status = 0;
-
-       set_current_state(TASK_INTERRUPTIBLE);
-       schedule_timeout(HZ/4);
-
-       ret = usb_set_interface(pd->udev, 0, BULK_ALTERNATE_IFACE);
-       if (ret != 0)
-               return ret;
-
-       ret = set_tuner_mode(pd, TLG_MODE_CAPS_DVB_T);
-       if (ret)
-               return ret;
-
-       /* signal source */
-       ret = send_set_req(pd, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &cmd_status);
-       if (ret|cmd_status)
-               return ret;
-
-       return 0;
-}
-
-/* acquire :
- *     1 == open
- *     0 == release
- */
-static int poseidon_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
-{
-       struct poseidon *pd = fe->demodulator_priv;
-       struct pd_dvb_adapter *pd_dvb;
-       int ret = 0;
-
-       if (!pd)
-               return -ENODEV;
-
-       pd_dvb = container_of(fe, struct pd_dvb_adapter, dvb_fe);
-       if (acquire) {
-               mutex_lock(&pd->lock);
-               if (pd->state & POSEIDON_STATE_DISCONNECT) {
-                       ret = -ENODEV;
-                       goto open_out;
-               }
-
-               if (pd->state && !(pd->state & POSEIDON_STATE_DVBT)) {
-                       ret = -EBUSY;
-                       goto open_out;
-               }
-
-               usb_autopm_get_interface(pd->interface);
-               if (0 == pd->state) {
-                       ret = poseidon_check_mode_dvbt(pd);
-                       if (ret < 0) {
-                               usb_autopm_put_interface(pd->interface);
-                               goto open_out;
-                       }
-                       pd->state |= POSEIDON_STATE_DVBT;
-                       pd_dvb->bandwidth = 0;
-                       pd_dvb->prev_freq = 0;
-               }
-               atomic_inc(&pd_dvb->users);
-               kref_get(&pd->kref);
-open_out:
-               mutex_unlock(&pd->lock);
-       } else {
-               dvb_stop_streaming(pd_dvb);
-
-               if (atomic_dec_and_test(&pd_dvb->users)) {
-                       mutex_lock(&pd->lock);
-                       pd->state &= ~POSEIDON_STATE_DVBT;
-                       mutex_unlock(&pd->lock);
-               }
-               kref_put(&pd->kref, poseidon_delete);
-               usb_autopm_put_interface(pd->interface);
-       }
-       return ret;
-}
-
-#ifdef CONFIG_PM
-static void poseidon_fe_release(struct dvb_frontend *fe)
-{
-       struct poseidon *pd = fe->demodulator_priv;
-
-       pd->pm_suspend = NULL;
-       pd->pm_resume  = NULL;
-}
-#else
-#define poseidon_fe_release NULL
-#endif
-
-static s32 poseidon_fe_sleep(struct dvb_frontend *fe)
-{
-       return 0;
-}
-
-/*
- * return true if we can satisfy the conditions, else return false.
- */
-static bool check_scan_ok(__u32 freq, int bandwidth,
-                       struct pd_dvb_adapter *adapter)
-{
-       if (bandwidth < 0)
-               return false;
-
-       if (adapter->prev_freq == freq
-               && adapter->bandwidth == bandwidth) {
-               long nl = jiffies - adapter->last_jiffies;
-               unsigned int msec ;
-
-               msec = jiffies_to_msecs(abs(nl));
-               return msec > 15000 ? true : false;
-       }
-       return true;
-}
-
-/*
- * Check if the firmware delays too long for an invalid frequency.
- */
-static int fw_delay_overflow(struct pd_dvb_adapter *adapter)
-{
-       long nl = jiffies - adapter->last_jiffies;
-       unsigned int msec ;
-
-       msec = jiffies_to_msecs(abs(nl));
-       return msec > 800 ? true : false;
-}
-
-static int poseidon_set_fe(struct dvb_frontend *fe)
-{
-       struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
-       s32 ret = 0, cmd_status = 0;
-       s32 i, bandwidth = -1;
-       struct poseidon *pd = fe->demodulator_priv;
-       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
-
-       if (in_hibernation(pd))
-               return -EBUSY;
-
-       mutex_lock(&pd->lock);
-       for (i = 0; i < dvb_bandwidth_length; i++)
-               if (fep->bandwidth_hz == dvb_bandwidth[i][1])
-                       bandwidth = dvb_bandwidth[i][0];
-
-       if (check_scan_ok(fep->frequency, bandwidth, pd_dvb)) {
-               ret = send_set_req(pd, TUNE_FREQ_SELECT,
-                                       fep->frequency / 1000, &cmd_status);
-               if (ret | cmd_status) {
-                       log("error line");
-                       goto front_out;
-               }
-
-               ret = send_set_req(pd, DVBT_BANDW_SEL,
-                                               bandwidth, &cmd_status);
-               if (ret | cmd_status) {
-                       log("error line");
-                       goto front_out;
-               }
-
-               ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
-               if (ret | cmd_status) {
-                       log("error line");
-                       goto front_out;
-               }
-
-               /* save the context for future */
-               memcpy(&pd_dvb->fe_param, fep, sizeof(*fep));
-               pd_dvb->bandwidth = bandwidth;
-               pd_dvb->prev_freq = fep->frequency;
-               pd_dvb->last_jiffies = jiffies;
-       }
-front_out:
-       mutex_unlock(&pd->lock);
-       return ret;
-}
-
-#ifdef CONFIG_PM
-static int pm_dvb_suspend(struct poseidon *pd)
-{
-       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
-       dvb_stop_streaming(pd_dvb);
-       dvb_urb_cleanup(pd_dvb);
-       msleep(500);
-       return 0;
-}
-
-static int pm_dvb_resume(struct poseidon *pd)
-{
-       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
-
-       poseidon_check_mode_dvbt(pd);
-       msleep(300);
-       poseidon_set_fe(&pd_dvb->dvb_fe);
-
-       dvb_start_streaming(pd_dvb);
-       return 0;
-}
-#endif
-
-static s32 poseidon_fe_init(struct dvb_frontend *fe)
-{
-       struct poseidon *pd = fe->demodulator_priv;
-       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
-
-#ifdef CONFIG_PM
-       pd->pm_suspend = pm_dvb_suspend;
-       pd->pm_resume  = pm_dvb_resume;
-#endif
-       memset(&pd_dvb->fe_param, 0,
-                       sizeof(struct dtv_frontend_properties));
-       return 0;
-}
-
-static int poseidon_get_fe(struct dvb_frontend *fe)
-{
-       struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
-       struct poseidon *pd = fe->demodulator_priv;
-       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
-
-       memcpy(fep, &pd_dvb->fe_param, sizeof(*fep));
-       return 0;
-}
-
-static int poseidon_fe_get_tune_settings(struct dvb_frontend *fe,
-                               struct dvb_frontend_tune_settings *tune)
-{
-       tune->min_delay_ms = 1000;
-       return 0;
-}
-
-static int poseidon_read_status(struct dvb_frontend *fe, fe_status_t *stat)
-{
-       struct poseidon *pd = fe->demodulator_priv;
-       s32 ret = -1, cmd_status;
-       struct tuner_dtv_sig_stat_s status = {};
-
-       if (in_hibernation(pd))
-               return -EBUSY;
-       mutex_lock(&pd->lock);
-
-       ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T,
-                               &status, &cmd_status, sizeof(status));
-       if (ret | cmd_status) {
-               log("get tuner status error");
-               goto out;
-       }
-
-       if (debug_mode)
-               log("P : %d, L %d, LB :%d", status.sig_present,
-                       status.sig_locked, status.sig_lock_busy);
-
-       if (status.sig_lock_busy) {
-               goto out;
-       } else if (status.sig_present || status.sig_locked) {
-               *stat |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER
-                               | FE_HAS_SYNC | FE_HAS_VITERBI;
-       } else {
-               if (fw_delay_overflow(&pd->dvb_data))
-                       *stat |= FE_TIMEDOUT;
-       }
-out:
-       mutex_unlock(&pd->lock);
-       return ret;
-}
-
-static int poseidon_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
-       struct poseidon *pd = fe->demodulator_priv;
-       struct tuner_ber_rate_s tlg_ber = {};
-       s32 ret = -1, cmd_status;
-
-       mutex_lock(&pd->lock);
-       ret = send_get_req(pd, TUNER_BER_RATE, 0,
-                               &tlg_ber, &cmd_status, sizeof(tlg_ber));
-       if (ret | cmd_status)
-               goto out;
-       *ber = tlg_ber.ber_rate;
-out:
-       mutex_unlock(&pd->lock);
-       return ret;
-}
-
-static s32 poseidon_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
-{
-       struct poseidon *pd = fe->demodulator_priv;
-       struct tuner_dtv_sig_stat_s status = {};
-       s32 ret = 0, cmd_status;
-
-       mutex_lock(&pd->lock);
-       ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T,
-                               &status, &cmd_status, sizeof(status));
-       if (ret | cmd_status)
-               goto out;
-       if ((status.sig_present || status.sig_locked) && !status.sig_strength)
-               *strength = 0xFFFF;
-       else
-               *strength = status.sig_strength;
-out:
-       mutex_unlock(&pd->lock);
-       return ret;
-}
-
-static int poseidon_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
-       return 0;
-}
-
-static int poseidon_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
-{
-       *unc = 0;
-       return 0;
-}
-
-static struct dvb_frontend_ops poseidon_frontend_ops = {
-       .delsys = { SYS_DVBT },
-       .info = {
-               .name           = "Poseidon DVB-T",
-               .frequency_min  = 174000000,
-               .frequency_max  = 862000000,
-               .frequency_stepsize       = 62500,/* FIXME */
-               .caps = FE_CAN_INVERSION_AUTO |
-                       FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
-                       FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
-                       FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
-                       FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
-                       FE_CAN_GUARD_INTERVAL_AUTO |
-                       FE_CAN_RECOVER |
-                       FE_CAN_HIERARCHY_AUTO,
-       },
-
-       .release = poseidon_fe_release,
-
-       .init = poseidon_fe_init,
-       .sleep = poseidon_fe_sleep,
-
-       .set_frontend = poseidon_set_fe,
-       .get_frontend = poseidon_get_fe,
-       .get_tune_settings = poseidon_fe_get_tune_settings,
-
-       .read_status    = poseidon_read_status,
-       .read_ber       = poseidon_read_ber,
-       .read_signal_strength = poseidon_read_signal_strength,
-       .read_snr       = poseidon_read_snr,
-       .read_ucblocks  = poseidon_read_unc_blocks,
-
-       .ts_bus_ctrl = poseidon_ts_bus_ctrl,
-};
-
-static void dvb_urb_irq(struct urb *urb)
-{
-       struct pd_dvb_adapter *pd_dvb = urb->context;
-       int len = urb->transfer_buffer_length;
-       struct dvb_demux *demux = &pd_dvb->demux;
-       s32 ret;
-
-       if (!pd_dvb->is_streaming || urb->status) {
-               if (urb->status == -EPROTO)
-                       goto resend;
-               return;
-       }
-
-       if (urb->actual_length == len)
-               dvb_dmx_swfilter(demux, urb->transfer_buffer, len);
-       else if (urb->actual_length == len - 4) {
-               int offset;
-               u8 *buf = urb->transfer_buffer;
-
-               /*
-                * The packet size is 512,
-                * last packet contains 456 bytes tsp data
-                */
-               for (offset = 456; offset < len; offset += 512) {
-                       if (!strncmp(buf + offset, "DVHS", 4)) {
-                               dvb_dmx_swfilter(demux, buf, offset);
-                               if (len > offset + 52 + 4) {
-                                       /*16 bytes trailer + 36 bytes padding */
-                                       buf += offset + 52;
-                                       len -= offset + 52 + 4;
-                                       dvb_dmx_swfilter(demux, buf, len);
-                               }
-                               break;
-                       }
-               }
-       }
-
-resend:
-       ret = usb_submit_urb(urb, GFP_ATOMIC);
-       if (ret)
-               log(" usb_submit_urb failed: error %d", ret);
-}
-
-static int dvb_urb_init(struct pd_dvb_adapter *pd_dvb)
-{
-       if (pd_dvb->urb_array[0])
-               return 0;
-
-       alloc_bulk_urbs_generic(pd_dvb->urb_array, DVB_SBUF_NUM,
-                       pd_dvb->pd_device->udev, pd_dvb->ep_addr,
-                       DVB_URB_BUF_SIZE, GFP_KERNEL,
-                       dvb_urb_irq, pd_dvb);
-       return 0;
-}
-
-static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb)
-{
-       free_all_urb_generic(pd_dvb->urb_array, DVB_SBUF_NUM);
-}
-
-static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb)
-{
-       struct poseidon *pd = pd_dvb->pd_device;
-       int ret = 0;
-
-       if (pd->state & POSEIDON_STATE_DISCONNECT)
-               return -ENODEV;
-
-       mutex_lock(&pd->lock);
-       if (!pd_dvb->is_streaming) {
-               s32 i, cmd_status = 0;
-               /*
-                * Once upon a time, there was a difficult bug lying here.
-                * ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
-                */
-
-               ret = send_set_req(pd, PLAY_SERVICE, 1, &cmd_status);
-               if (ret | cmd_status)
-                       goto out;
-
-               ret = dvb_urb_init(pd_dvb);
-               if (ret < 0)
-                       goto out;
-
-               pd_dvb->is_streaming = 1;
-               for (i = 0; i < DVB_SBUF_NUM; i++) {
-                       ret = usb_submit_urb(pd_dvb->urb_array[i],
-                                                      GFP_KERNEL);
-                       if (ret) {
-                               log(" submit urb error %d", ret);
-                               goto out;
-                       }
-               }
-       }
-out:
-       mutex_unlock(&pd->lock);
-       return ret;
-}
-
-void dvb_stop_streaming(struct pd_dvb_adapter *pd_dvb)
-{
-       struct poseidon *pd = pd_dvb->pd_device;
-
-       mutex_lock(&pd->lock);
-       if (pd_dvb->is_streaming) {
-               s32 i, ret, cmd_status = 0;
-
-               pd_dvb->is_streaming = 0;
-
-               for (i = 0; i < DVB_SBUF_NUM; i++)
-                       if (pd_dvb->urb_array[i])
-                               usb_kill_urb(pd_dvb->urb_array[i]);
-
-               ret = send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
-                                       &cmd_status);
-               if (ret | cmd_status)
-                       log("error");
-       }
-       mutex_unlock(&pd->lock);
-}
-
-static int pd_start_feed(struct dvb_demux_feed *feed)
-{
-       struct pd_dvb_adapter *pd_dvb = feed->demux->priv;
-       int ret = 0;
-
-       if (!pd_dvb)
-               return -1;
-       if (atomic_inc_return(&pd_dvb->active_feed) == 1)
-               ret = dvb_start_streaming(pd_dvb);
-       return ret;
-}
-
-static int pd_stop_feed(struct dvb_demux_feed *feed)
-{
-       struct pd_dvb_adapter *pd_dvb = feed->demux->priv;
-
-       if (!pd_dvb)
-               return -1;
-       if (atomic_dec_and_test(&pd_dvb->active_feed))
-               dvb_stop_streaming(pd_dvb);
-       return 0;
-}
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-int pd_dvb_usb_device_init(struct poseidon *pd)
-{
-       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
-       struct dvb_demux *dvbdemux;
-       int ret = 0;
-
-       pd_dvb->ep_addr = 0x82;
-       atomic_set(&pd_dvb->users, 0);
-       atomic_set(&pd_dvb->active_feed, 0);
-       pd_dvb->pd_device = pd;
-
-       ret = dvb_register_adapter(&pd_dvb->dvb_adap,
-                               "Poseidon dvbt adapter",
-                               THIS_MODULE,
-                               NULL /* for hibernation correctly*/,
-                               adapter_nr);
-       if (ret < 0)
-               goto error1;
-
-       /* register frontend */
-       pd_dvb->dvb_fe.demodulator_priv = pd;
-       memcpy(&pd_dvb->dvb_fe.ops, &poseidon_frontend_ops,
-                       sizeof(struct dvb_frontend_ops));
-       ret = dvb_register_frontend(&pd_dvb->dvb_adap, &pd_dvb->dvb_fe);
-       if (ret < 0)
-               goto error2;
-
-       /* register demux device */
-       dvbdemux = &pd_dvb->demux;
-       dvbdemux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
-       dvbdemux->priv = pd_dvb;
-       dvbdemux->feednum = dvbdemux->filternum = 64;
-       dvbdemux->start_feed = pd_start_feed;
-       dvbdemux->stop_feed = pd_stop_feed;
-       dvbdemux->write_to_decoder = NULL;
-
-       ret = dvb_dmx_init(dvbdemux);
-       if (ret < 0)
-               goto error3;
-
-       pd_dvb->dmxdev.filternum = pd_dvb->demux.filternum;
-       pd_dvb->dmxdev.demux = &pd_dvb->demux.dmx;
-       pd_dvb->dmxdev.capabilities = 0;
-
-       ret = dvb_dmxdev_init(&pd_dvb->dmxdev, &pd_dvb->dvb_adap);
-       if (ret < 0)
-               goto error3;
-       return 0;
-
-error3:
-       dvb_unregister_frontend(&pd_dvb->dvb_fe);
-error2:
-       dvb_unregister_adapter(&pd_dvb->dvb_adap);
-error1:
-       return ret;
-}
-
-void pd_dvb_usb_device_exit(struct poseidon *pd)
-{
-       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
-
-       while (atomic_read(&pd_dvb->users) != 0
-               || atomic_read(&pd_dvb->active_feed) != 0) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(HZ);
-       }
-       dvb_dmxdev_release(&pd_dvb->dmxdev);
-       dvb_unregister_frontend(&pd_dvb->dvb_fe);
-       dvb_unregister_adapter(&pd_dvb->dvb_adap);
-       pd_dvb_usb_device_cleanup(pd);
-}
-
-void pd_dvb_usb_device_cleanup(struct poseidon *pd)
-{
-       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
-
-       dvb_urb_cleanup(pd_dvb);
-}
-
-int pd_dvb_get_adapter_num(struct pd_dvb_adapter *pd_dvb)
-{
-       return pd_dvb->dvb_adap.num;
-}
diff --git a/drivers/media/usb/tlg2300/pd-main.c b/drivers/media/usb/tlg2300/pd-main.c
deleted file mode 100644 (file)
index b31f479..0000000
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * device driver for Telegent tlg2300 based TV cards
- *
- * Author :
- *     Kang Yong       <kangyong@telegent.com>
- *     Zhang Xiaobing  <xbzhang@telegent.com>
- *     Huang Shijie    <zyziii@telegent.com> or <shijie8@gmail.com>
- *
- *     (c) 2009 Telegent Systems
- *     (c) 2010 Telegent Systems
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/kref.h>
-#include <linux/suspend.h>
-#include <linux/usb/quirks.h>
-#include <linux/ctype.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/firmware.h>
-
-#include "vendorcmds.h"
-#include "pd-common.h"
-
-#define VENDOR_ID      0x1B24
-#define PRODUCT_ID     0x4001
-static struct usb_device_id id_table[] = {
-       { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 0) },
-       { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 1) },
-       { },
-};
-MODULE_DEVICE_TABLE(usb, id_table);
-
-int debug_mode;
-module_param(debug_mode, int, 0644);
-MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose");
-
-#define TLG2300_FIRMWARE "tlg2300_firmware.bin"
-static const char *firmware_name = TLG2300_FIRMWARE;
-static LIST_HEAD(pd_device_list);
-
-/*
- * send set request to USB firmware.
- */
-s32 send_set_req(struct poseidon *pd, u8 cmdid, s32 param, s32 *cmd_status)
-{
-       s32 ret;
-       s8  data[32] = {};
-       u16 lower_16, upper_16;
-
-       if (pd->state & POSEIDON_STATE_DISCONNECT)
-               return -ENODEV;
-
-       mdelay(30);
-
-       if (param == 0) {
-               upper_16 = lower_16 = 0;
-       } else {
-               /* send 32 bit param as  two 16 bit param,little endian */
-               lower_16 = (unsigned short)(param & 0xffff);
-               upper_16 = (unsigned short)((param >> 16) & 0xffff);
-       }
-       ret = usb_control_msg(pd->udev,
-                        usb_rcvctrlpipe(pd->udev, 0),
-                        REQ_SET_CMD | cmdid,
-                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                        lower_16,
-                        upper_16,
-                        &data,
-                        sizeof(*cmd_status),
-                        USB_CTRL_GET_TIMEOUT);
-
-       if (!ret) {
-               return -ENXIO;
-       } else {
-               /*  1st 4 bytes into cmd_status   */
-               memcpy((char *)cmd_status, &(data[0]), sizeof(*cmd_status));
-       }
-       return 0;
-}
-
-/*
- * send get request to Poseidon firmware.
- */
-s32 send_get_req(struct poseidon *pd, u8 cmdid, s32 param,
-                       void *buf, s32 *cmd_status, s32 datalen)
-{
-       s32 ret;
-       s8 data[128] = {};
-       u16 lower_16, upper_16;
-
-       if (pd->state & POSEIDON_STATE_DISCONNECT)
-               return -ENODEV;
-
-       mdelay(30);
-       if (param == 0) {
-               upper_16 = lower_16 = 0;
-       } else {
-               /*send 32 bit param as two 16 bit param, little endian */
-               lower_16 = (unsigned short)(param & 0xffff);
-               upper_16 = (unsigned short)((param >> 16) & 0xffff);
-       }
-       ret = usb_control_msg(pd->udev,
-                        usb_rcvctrlpipe(pd->udev, 0),
-                        REQ_GET_CMD | cmdid,
-                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                        lower_16,
-                        upper_16,
-                        &data,
-                        (datalen + sizeof(*cmd_status)),
-                        USB_CTRL_GET_TIMEOUT);
-
-       if (ret < 0) {
-               return -ENXIO;
-       } else {
-               /* 1st 4 bytes into cmd_status, remaining data into cmd_data */
-               memcpy((char *)cmd_status, &data[0], sizeof(*cmd_status));
-               memcpy((char *)buf, &data[sizeof(*cmd_status)], datalen);
-       }
-       return 0;
-}
-
-static int pm_notifier_block(struct notifier_block *nb,
-                               unsigned long event, void *dummy)
-{
-       struct poseidon *pd = NULL;
-       struct list_head *node, *next;
-
-       switch (event) {
-       case PM_POST_HIBERNATION:
-               list_for_each_safe(node, next, &pd_device_list) {
-                       struct usb_device *udev;
-                       struct usb_interface *iface;
-                       int rc = 0;
-
-                       pd = container_of(node, struct poseidon, device_list);
-                       udev = pd->udev;
-                       iface = pd->interface;
-
-                       /* It will cause the system to reload the firmware */
-                       rc = usb_lock_device_for_reset(udev, iface);
-                       if (rc >= 0) {
-                               usb_reset_device(udev);
-                               usb_unlock_device(udev);
-                       }
-               }
-               break;
-       default:
-               break;
-       }
-       log("event :%ld\n", event);
-       return 0;
-}
-
-static struct notifier_block pm_notifer = {
-       .notifier_call = pm_notifier_block,
-};
-
-int set_tuner_mode(struct poseidon *pd, unsigned char mode)
-{
-       s32 ret, cmd_status;
-
-       if (pd->state & POSEIDON_STATE_DISCONNECT)
-               return -ENODEV;
-
-       ret = send_set_req(pd, TUNE_MODE_SELECT, mode, &cmd_status);
-       if (ret || cmd_status)
-               return -ENXIO;
-       return 0;
-}
-
-void poseidon_delete(struct kref *kref)
-{
-       struct poseidon *pd = container_of(kref, struct poseidon, kref);
-
-       if (!pd)
-               return;
-       list_del_init(&pd->device_list);
-
-       pd_dvb_usb_device_cleanup(pd);
-       /* clean_audio_data(&pd->audio_data);*/
-
-       if (pd->udev) {
-               usb_put_dev(pd->udev);
-               pd->udev = NULL;
-       }
-       if (pd->interface) {
-               usb_put_intf(pd->interface);
-               pd->interface = NULL;
-       }
-       kfree(pd);
-       log();
-}
-
-static int firmware_download(struct usb_device *udev)
-{
-       int ret = 0, actual_length;
-       const struct firmware *fw = NULL;
-       void *fwbuf = NULL;
-       size_t fwlength = 0, offset;
-       size_t max_packet_size;
-
-       ret = request_firmware(&fw, firmware_name, &udev->dev);
-       if (ret) {
-               log("download err : %d", ret);
-               return ret;
-       }
-
-       fwlength = fw->size;
-
-       fwbuf = kmemdup(fw->data, fwlength, GFP_KERNEL);
-       if (!fwbuf) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       max_packet_size = le16_to_cpu(udev->ep_out[0x1]->desc.wMaxPacketSize);
-       log("\t\t download size : %d", (int)max_packet_size);
-
-       for (offset = 0; offset < fwlength; offset += max_packet_size) {
-               actual_length = 0;
-               ret = usb_bulk_msg(udev,
-                               usb_sndbulkpipe(udev, 0x01), /* ep 1 */
-                               fwbuf + offset,
-                               min(max_packet_size, fwlength - offset),
-                               &actual_length,
-                               HZ * 10);
-               if (ret)
-                       break;
-       }
-       kfree(fwbuf);
-out:
-       release_firmware(fw);
-       return ret;
-}
-
-static inline struct poseidon *get_pd(struct usb_interface *intf)
-{
-       return usb_get_intfdata(intf);
-}
-
-#ifdef CONFIG_PM
-/* one-to-one map : poseidon{} <----> usb_device{}'s port */
-static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
-{
-       pd->portnum = udev->portnum;
-}
-
-static inline int get_autopm_ref(struct poseidon *pd)
-{
-       return  pd->video_data.users + pd->vbi_data.users + pd->audio.users
-               + atomic_read(&pd->dvb_data.users) +
-               !list_empty(&pd->radio_data.fm_dev.fh_list);
-}
-
-/* fixup something for poseidon */
-static inline struct poseidon *fixup(struct poseidon *pd)
-{
-       int count;
-
-       /* old udev and interface have gone, so put back reference . */
-       count = get_autopm_ref(pd);
-       log("count : %d, ref count : %d", count, get_pm_count(pd));
-       while (count--)
-               usb_autopm_put_interface(pd->interface);
-       /*usb_autopm_set_interface(pd->interface); */
-
-       usb_put_dev(pd->udev);
-       usb_put_intf(pd->interface);
-       log("event : %d\n", pd->msg.event);
-       return pd;
-}
-
-static struct poseidon *find_old_poseidon(struct usb_device *udev)
-{
-       struct poseidon *pd;
-
-       list_for_each_entry(pd, &pd_device_list, device_list) {
-               if (pd->portnum == udev->portnum && in_hibernation(pd))
-                       return fixup(pd);
-       }
-       return NULL;
-}
-
-/* Is the card working now ? */
-static inline int is_working(struct poseidon *pd)
-{
-       return get_pm_count(pd) > 0;
-}
-
-static int poseidon_suspend(struct usb_interface *intf, pm_message_t msg)
-{
-       struct poseidon *pd = get_pd(intf);
-
-       if (!pd)
-               return 0;
-       if (!is_working(pd)) {
-               if (get_pm_count(pd) <= 0 && !in_hibernation(pd)) {
-                       pd->msg.event = PM_EVENT_AUTO_SUSPEND;
-                       pd->pm_resume = NULL; /*  a good guard */
-                       printk(KERN_DEBUG "TLG2300 auto suspend\n");
-               }
-               return 0;
-       }
-       pd->msg = msg; /* save it here */
-       logpm(pd);
-       return pd->pm_suspend ? pd->pm_suspend(pd) : 0;
-}
-
-static int poseidon_resume(struct usb_interface *intf)
-{
-       struct poseidon *pd = get_pd(intf);
-
-       if (!pd)
-               return 0;
-       printk(KERN_DEBUG "TLG2300 resume\n");
-
-       if (!is_working(pd)) {
-               if (PM_EVENT_AUTO_SUSPEND == pd->msg.event)
-                       pd->msg = PMSG_ON;
-               return 0;
-       }
-       if (in_hibernation(pd)) {
-               logpm(pd);
-               return 0;
-       }
-       logpm(pd);
-       return pd->pm_resume ? pd->pm_resume(pd) : 0;
-}
-
-static void hibernation_resume(struct work_struct *w)
-{
-       struct poseidon *pd = container_of(w, struct poseidon, pm_work);
-       int count;
-
-       pd->msg.event = 0; /* clear it here */
-       pd->state &= ~POSEIDON_STATE_DISCONNECT;
-
-       /* set the new interface's reference */
-       count = get_autopm_ref(pd);
-       while (count--)
-               usb_autopm_get_interface(pd->interface);
-
-       /* resume the context */
-       logpm(pd);
-       if (pd->pm_resume)
-               pd->pm_resume(pd);
-}
-#else /* CONFIG_PM is not enabled: */
-static inline struct poseidon *find_old_poseidon(struct usb_device *udev)
-{
-       return NULL;
-}
-
-static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
-{
-}
-#endif
-
-static int check_firmware(struct usb_device *udev)
-{
-       void *buf;
-       int ret;
-       struct cmd_firmware_vers_s *cmd_firm;
-
-       buf = kzalloc(sizeof(*cmd_firm) + sizeof(u32), GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-       ret = usb_control_msg(udev,
-                        usb_rcvctrlpipe(udev, 0),
-                        REQ_GET_CMD | GET_FW_ID,
-                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                        0,
-                        0,
-                        buf,
-                        sizeof(*cmd_firm) + sizeof(u32),
-                        USB_CTRL_GET_TIMEOUT);
-       kfree(buf);
-
-       if (ret < 0)
-               return firmware_download(udev);
-       return 0;
-}
-
-static int poseidon_probe(struct usb_interface *interface,
-                               const struct usb_device_id *id)
-{
-       struct usb_device *udev = interface_to_usbdev(interface);
-       struct poseidon *pd = NULL;
-       int ret = 0;
-       int new_one = 0;
-
-       /* download firmware */
-       ret = check_firmware(udev);
-       if (ret)
-               return ret;
-
-       /* Do I recovery from the hibernate ? */
-       pd = find_old_poseidon(udev);
-       if (!pd) {
-               pd = kzalloc(sizeof(*pd), GFP_KERNEL);
-               if (!pd)
-                       return -ENOMEM;
-               kref_init(&pd->kref);
-               set_map_flags(pd, udev);
-               new_one = 1;
-       }
-
-       pd->udev        = usb_get_dev(udev);
-       pd->interface   = usb_get_intf(interface);
-       usb_set_intfdata(interface, pd);
-
-       if (new_one) {
-               logpm(pd);
-               mutex_init(&pd->lock);
-
-               /* register v4l2 device */
-               ret = v4l2_device_register(&interface->dev, &pd->v4l2_dev);
-               if (ret)
-                       goto err_v4l2;
-
-               /* register devices in directory /dev */
-               ret = pd_video_init(pd);
-               if (ret)
-                       goto err_video;
-               ret = poseidon_audio_init(pd);
-               if (ret)
-                       goto err_audio;
-               ret = poseidon_fm_init(pd);
-               if (ret)
-                       goto err_fm;
-               ret = pd_dvb_usb_device_init(pd);
-               if (ret)
-                       goto err_dvb;
-
-               INIT_LIST_HEAD(&pd->device_list);
-               list_add_tail(&pd->device_list, &pd_device_list);
-       }
-
-       device_init_wakeup(&udev->dev, 1);
-#ifdef CONFIG_PM
-       pm_runtime_set_autosuspend_delay(&pd->udev->dev,
-                       1000 * PM_SUSPEND_DELAY);
-       usb_enable_autosuspend(pd->udev);
-
-       if (in_hibernation(pd)) {
-               INIT_WORK(&pd->pm_work, hibernation_resume);
-               schedule_work(&pd->pm_work);
-       }
-#endif
-       return 0;
-err_dvb:
-       poseidon_fm_exit(pd);
-err_fm:
-       poseidon_audio_free(pd);
-err_audio:
-       pd_video_exit(pd);
-err_video:
-       v4l2_device_unregister(&pd->v4l2_dev);
-err_v4l2:
-       usb_put_intf(pd->interface);
-       usb_put_dev(pd->udev);
-       kfree(pd);
-       return ret;
-}
-
-static void poseidon_disconnect(struct usb_interface *interface)
-{
-       struct poseidon *pd = get_pd(interface);
-
-       if (!pd)
-               return;
-       logpm(pd);
-       if (in_hibernation(pd))
-               return;
-
-       mutex_lock(&pd->lock);
-       pd->state |= POSEIDON_STATE_DISCONNECT;
-       mutex_unlock(&pd->lock);
-
-       /* stop urb transferring */
-       stop_all_video_stream(pd);
-       dvb_stop_streaming(&pd->dvb_data);
-
-       /*unregister v4l2 device */
-       v4l2_device_unregister(&pd->v4l2_dev);
-
-       pd_dvb_usb_device_exit(pd);
-       poseidon_fm_exit(pd);
-
-       poseidon_audio_free(pd);
-       pd_video_exit(pd);
-
-       usb_set_intfdata(interface, NULL);
-       kref_put(&pd->kref, poseidon_delete);
-}
-
-static struct usb_driver poseidon_driver = {
-       .name           = "poseidon",
-       .probe          = poseidon_probe,
-       .disconnect     = poseidon_disconnect,
-       .id_table       = id_table,
-#ifdef CONFIG_PM
-       .suspend        = poseidon_suspend,
-       .resume         = poseidon_resume,
-#endif
-       .supports_autosuspend = 1,
-};
-
-static int __init poseidon_init(void)
-{
-       int ret;
-
-       ret = usb_register(&poseidon_driver);
-       if (ret)
-               return ret;
-       register_pm_notifier(&pm_notifer);
-       return ret;
-}
-
-static void __exit poseidon_exit(void)
-{
-       log();
-       unregister_pm_notifier(&pm_notifer);
-       usb_deregister(&poseidon_driver);
-}
-
-module_init(poseidon_init);
-module_exit(poseidon_exit);
-
-MODULE_AUTHOR("Telegent Systems");
-MODULE_DESCRIPTION("For tlg2300-based USB device");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.2");
-MODULE_FIRMWARE(TLG2300_FIRMWARE);
diff --git a/drivers/media/usb/tlg2300/pd-radio.c b/drivers/media/usb/tlg2300/pd-radio.c
deleted file mode 100644 (file)
index b391194..0000000
+++ /dev/null
@@ -1,339 +0,0 @@
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/bitmap.h>
-#include <linux/usb.h>
-#include <linux/i2c.h>
-#include <media/v4l2-dev.h>
-#include <linux/mm.h>
-#include <linux/mutex.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-fh.h>
-#include <linux/sched.h>
-
-#include "pd-common.h"
-#include "vendorcmds.h"
-
-static int set_frequency(struct poseidon *p, __u32 frequency);
-static int poseidon_fm_close(struct file *filp);
-static int poseidon_fm_open(struct file *filp);
-
-#define TUNER_FREQ_MIN_FM 76000000U
-#define TUNER_FREQ_MAX_FM 108000000U
-
-#define MAX_PREEMPHASIS (V4L2_PREEMPHASIS_75_uS + 1)
-static int preemphasis[MAX_PREEMPHASIS] = {
-       TLG_TUNE_ASTD_NONE,   /* V4L2_PREEMPHASIS_DISABLED */
-       TLG_TUNE_ASTD_FM_EUR, /* V4L2_PREEMPHASIS_50_uS    */
-       TLG_TUNE_ASTD_FM_US,  /* V4L2_PREEMPHASIS_75_uS    */
-};
-
-static int poseidon_check_mode_radio(struct poseidon *p)
-{
-       int ret;
-       u32 status;
-
-       set_current_state(TASK_INTERRUPTIBLE);
-       schedule_timeout(HZ/2);
-       ret = usb_set_interface(p->udev, 0, BULK_ALTERNATE_IFACE);
-       if (ret < 0)
-               goto out;
-
-       ret = set_tuner_mode(p, TLG_MODE_FM_RADIO);
-       if (ret != 0)
-               goto out;
-
-       ret = send_set_req(p, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &status);
-       ret = send_set_req(p, TUNER_AUD_ANA_STD,
-                               p->radio_data.pre_emphasis, &status);
-       ret |= send_set_req(p, TUNER_AUD_MODE,
-                               TLG_TUNE_TVAUDIO_MODE_STEREO, &status);
-       ret |= send_set_req(p, AUDIO_SAMPLE_RATE_SEL,
-                               ATV_AUDIO_RATE_48K, &status);
-       ret |= send_set_req(p, TUNE_FREQ_SELECT, TUNER_FREQ_MIN_FM, &status);
-out:
-       return ret;
-}
-
-#ifdef CONFIG_PM
-static int pm_fm_suspend(struct poseidon *p)
-{
-       logpm(p);
-       pm_alsa_suspend(p);
-       usb_set_interface(p->udev, 0, 0);
-       msleep(300);
-       return 0;
-}
-
-static int pm_fm_resume(struct poseidon *p)
-{
-       logpm(p);
-       poseidon_check_mode_radio(p);
-       set_frequency(p, p->radio_data.fm_freq);
-       pm_alsa_resume(p);
-       return 0;
-}
-#endif
-
-static int poseidon_fm_open(struct file *filp)
-{
-       struct poseidon *p = video_drvdata(filp);
-       int ret = 0;
-
-       mutex_lock(&p->lock);
-       if (p->state & POSEIDON_STATE_DISCONNECT) {
-               ret = -ENODEV;
-               goto out;
-       }
-
-       if (p->state && !(p->state & POSEIDON_STATE_FM)) {
-               ret = -EBUSY;
-               goto out;
-       }
-       ret = v4l2_fh_open(filp);
-       if (ret)
-               goto out;
-
-       usb_autopm_get_interface(p->interface);
-       if (0 == p->state) {
-               struct video_device *vfd = &p->radio_data.fm_dev;
-
-               /* default pre-emphasis */
-               if (p->radio_data.pre_emphasis == 0)
-                       p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR;
-               set_debug_mode(vfd, debug_mode);
-
-               ret = poseidon_check_mode_radio(p);
-               if (ret < 0) {
-                       usb_autopm_put_interface(p->interface);
-                       goto out;
-               }
-               p->state |= POSEIDON_STATE_FM;
-       }
-       kref_get(&p->kref);
-out:
-       mutex_unlock(&p->lock);
-       return ret;
-}
-
-static int poseidon_fm_close(struct file *filp)
-{
-       struct poseidon *p = video_drvdata(filp);
-       struct radio_data *fm = &p->radio_data;
-       uint32_t status;
-
-       mutex_lock(&p->lock);
-       if (v4l2_fh_is_singular_file(filp))
-               p->state &= ~POSEIDON_STATE_FM;
-
-       if (fm->is_radio_streaming && filp == p->file_for_stream) {
-               fm->is_radio_streaming = 0;
-               send_set_req(p, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, &status);
-       }
-       usb_autopm_put_interface(p->interface);
-       mutex_unlock(&p->lock);
-
-       kref_put(&p->kref, poseidon_delete);
-       return v4l2_fh_release(filp);
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
-                       struct v4l2_capability *v)
-{
-       struct poseidon *p = video_drvdata(file);
-
-       strlcpy(v->driver, "tele-radio", sizeof(v->driver));
-       strlcpy(v->card, "Telegent Poseidon", sizeof(v->card));
-       usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info));
-       v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
-       /* Report all capabilities of the USB device */
-       v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS |
-                       V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE |
-                       V4L2_CAP_AUDIO | V4L2_CAP_STREAMING |
-                       V4L2_CAP_READWRITE;
-       return 0;
-}
-
-static const struct v4l2_file_operations poseidon_fm_fops = {
-       .owner         = THIS_MODULE,
-       .open          = poseidon_fm_open,
-       .release       = poseidon_fm_close,
-       .poll           = v4l2_ctrl_poll,
-       .unlocked_ioctl = video_ioctl2,
-};
-
-static int tlg_fm_vidioc_g_tuner(struct file *file, void *priv,
-                                struct v4l2_tuner *vt)
-{
-       struct poseidon *p = video_drvdata(file);
-       struct tuner_fm_sig_stat_s fm_stat = {};
-       int ret, status, count = 5;
-
-       if (vt->index != 0)
-               return -EINVAL;
-
-       vt->type        = V4L2_TUNER_RADIO;
-       vt->capability  = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
-       vt->rangelow    = TUNER_FREQ_MIN_FM * 2 / 125;
-       vt->rangehigh   = TUNER_FREQ_MAX_FM * 2 / 125;
-       vt->rxsubchans  = V4L2_TUNER_SUB_STEREO;
-       vt->audmode     = V4L2_TUNER_MODE_STEREO;
-       vt->signal      = 0;
-       vt->afc         = 0;
-       strlcpy(vt->name, "Radio", sizeof(vt->name));
-
-       mutex_lock(&p->lock);
-       ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
-                             &fm_stat, &status, sizeof(fm_stat));
-
-       while (fm_stat.sig_lock_busy && count-- && !ret) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(HZ);
-
-               ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
-                                 &fm_stat, &status, sizeof(fm_stat));
-       }
-       mutex_unlock(&p->lock);
-
-       if (ret || status) {
-               vt->signal = 0;
-       } else if ((fm_stat.sig_present || fm_stat.sig_locked)
-                       && fm_stat.sig_strength == 0) {
-               vt->signal = 0xffff;
-       } else
-               vt->signal = (fm_stat.sig_strength * 255 / 10) << 8;
-
-       return 0;
-}
-
-static int fm_get_freq(struct file *file, void *priv,
-                      struct v4l2_frequency *argp)
-{
-       struct poseidon *p = video_drvdata(file);
-
-       if (argp->tuner)
-               return -EINVAL;
-       argp->frequency = p->radio_data.fm_freq;
-       return 0;
-}
-
-static int set_frequency(struct poseidon *p, __u32 frequency)
-{
-       __u32 freq ;
-       int ret, status;
-
-       mutex_lock(&p->lock);
-
-       ret = send_set_req(p, TUNER_AUD_ANA_STD,
-                               p->radio_data.pre_emphasis, &status);
-
-       freq = (frequency * 125) / 2; /* Hz */
-       freq = clamp(freq, TUNER_FREQ_MIN_FM, TUNER_FREQ_MAX_FM);
-
-       ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status);
-       if (ret < 0)
-               goto error ;
-       ret = send_set_req(p, TAKE_REQUEST, 0, &status);
-
-       set_current_state(TASK_INTERRUPTIBLE);
-       schedule_timeout(HZ/4);
-       if (!p->radio_data.is_radio_streaming) {
-               ret = send_set_req(p, TAKE_REQUEST, 0, &status);
-               ret = send_set_req(p, PLAY_SERVICE,
-                               TLG_TUNE_PLAY_SVC_START, &status);
-               p->radio_data.is_radio_streaming = 1;
-       }
-       p->radio_data.fm_freq = freq * 2 / 125;
-error:
-       mutex_unlock(&p->lock);
-       return ret;
-}
-
-static int fm_set_freq(struct file *file, void *priv,
-                      const struct v4l2_frequency *argp)
-{
-       struct poseidon *p = video_drvdata(file);
-
-       if (argp->tuner)
-               return -EINVAL;
-       p->file_for_stream = file;
-#ifdef CONFIG_PM
-       p->pm_suspend = pm_fm_suspend;
-       p->pm_resume  = pm_fm_resume;
-#endif
-       return set_frequency(p, argp->frequency);
-}
-
-static int tlg_fm_s_ctrl(struct v4l2_ctrl *ctrl)
-{
-       struct poseidon *p = container_of(ctrl->handler, struct poseidon,
-                                               radio_data.ctrl_handler);
-       int pre_emphasis;
-       u32 status;
-
-       switch (ctrl->id) {
-       case V4L2_CID_TUNE_PREEMPHASIS:
-               pre_emphasis = preemphasis[ctrl->val];
-               send_set_req(p, TUNER_AUD_ANA_STD, pre_emphasis, &status);
-               p->radio_data.pre_emphasis = pre_emphasis;
-               return 0;
-       }
-       return -EINVAL;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *vt)
-{
-       return vt->index > 0 ? -EINVAL : 0;
-}
-
-static const struct v4l2_ctrl_ops tlg_fm_ctrl_ops = {
-       .s_ctrl = tlg_fm_s_ctrl,
-};
-
-static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = {
-       .vidioc_querycap    = vidioc_querycap,
-       .vidioc_s_tuner     = vidioc_s_tuner,
-       .vidioc_g_tuner     = tlg_fm_vidioc_g_tuner,
-       .vidioc_g_frequency = fm_get_freq,
-       .vidioc_s_frequency = fm_set_freq,
-       .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
-       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static struct video_device poseidon_fm_template = {
-       .name       = "Telegent-Radio",
-       .fops       = &poseidon_fm_fops,
-       .minor      = -1,
-       .release    = video_device_release_empty,
-       .ioctl_ops  = &poseidon_fm_ioctl_ops,
-};
-
-int poseidon_fm_init(struct poseidon *p)
-{
-       struct video_device *vfd = &p->radio_data.fm_dev;
-       struct v4l2_ctrl_handler *hdl = &p->radio_data.ctrl_handler;
-
-       *vfd = poseidon_fm_template;
-
-       set_frequency(p, TUNER_FREQ_MIN_FM);
-       v4l2_ctrl_handler_init(hdl, 1);
-       v4l2_ctrl_new_std_menu(hdl, &tlg_fm_ctrl_ops, V4L2_CID_TUNE_PREEMPHASIS,
-                       V4L2_PREEMPHASIS_75_uS, 0, V4L2_PREEMPHASIS_50_uS);
-       if (hdl->error) {
-               v4l2_ctrl_handler_free(hdl);
-               return hdl->error;
-       }
-       vfd->v4l2_dev = &p->v4l2_dev;
-       vfd->ctrl_handler = hdl;
-       video_set_drvdata(vfd, p);
-       return video_register_device(vfd, VFL_TYPE_RADIO, -1);
-}
-
-int poseidon_fm_exit(struct poseidon *p)
-{
-       video_unregister_device(&p->radio_data.fm_dev);
-       v4l2_ctrl_handler_free(&p->radio_data.ctrl_handler);
-       return 0;
-}
diff --git a/drivers/media/usb/tlg2300/pd-video.c b/drivers/media/usb/tlg2300/pd-video.c
deleted file mode 100644 (file)
index 8cd7f02..0000000
+++ /dev/null
@@ -1,1570 +0,0 @@
-#include <linux/fs.h>
-#include <linux/vmalloc.h>
-#include <linux/videodev2.h>
-#include <linux/usb.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-ctrls.h>
-
-#include "pd-common.h"
-#include "vendorcmds.h"
-
-#ifdef CONFIG_PM
-static int pm_video_suspend(struct poseidon *pd);
-static int pm_video_resume(struct poseidon *pd);
-#endif
-static void iso_bubble_handler(struct work_struct *w);
-
-static int usb_transfer_mode;
-module_param(usb_transfer_mode, int, 0644);
-MODULE_PARM_DESC(usb_transfer_mode, "0 = Bulk, 1 = Isochronous");
-
-static const struct poseidon_format poseidon_formats[] = {
-       { "YUV 422", V4L2_PIX_FMT_YUYV, 16, 0},
-       { "RGB565", V4L2_PIX_FMT_RGB565, 16, 0},
-};
-
-static const struct poseidon_tvnorm poseidon_tvnorms[] = {
-       { V4L2_STD_PAL_D, "PAL-D",  TLG_TUNE_VSTD_PAL_D },
-       { V4L2_STD_PAL_B, "PAL-B",  TLG_TUNE_VSTD_PAL_B },
-       { V4L2_STD_PAL_G, "PAL-G",  TLG_TUNE_VSTD_PAL_G },
-       { V4L2_STD_PAL_H, "PAL-H",  TLG_TUNE_VSTD_PAL_H },
-       { V4L2_STD_PAL_I, "PAL-I",  TLG_TUNE_VSTD_PAL_I },
-       { V4L2_STD_PAL_M, "PAL-M",  TLG_TUNE_VSTD_PAL_M },
-       { V4L2_STD_PAL_N, "PAL-N",  TLG_TUNE_VSTD_PAL_N_COMBO },
-       { V4L2_STD_PAL_Nc, "PAL-Nc", TLG_TUNE_VSTD_PAL_N_COMBO },
-       { V4L2_STD_NTSC_M, "NTSC-M", TLG_TUNE_VSTD_NTSC_M },
-       { V4L2_STD_NTSC_M_JP, "NTSC-JP", TLG_TUNE_VSTD_NTSC_M_J },
-       { V4L2_STD_SECAM_B, "SECAM-B", TLG_TUNE_VSTD_SECAM_B },
-       { V4L2_STD_SECAM_D, "SECAM-D", TLG_TUNE_VSTD_SECAM_D },
-       { V4L2_STD_SECAM_G, "SECAM-G", TLG_TUNE_VSTD_SECAM_G },
-       { V4L2_STD_SECAM_H, "SECAM-H", TLG_TUNE_VSTD_SECAM_H },
-       { V4L2_STD_SECAM_K, "SECAM-K", TLG_TUNE_VSTD_SECAM_K },
-       { V4L2_STD_SECAM_K1, "SECAM-K1", TLG_TUNE_VSTD_SECAM_K1 },
-       { V4L2_STD_SECAM_L, "SECAM-L", TLG_TUNE_VSTD_SECAM_L },
-       { V4L2_STD_SECAM_LC, "SECAM-LC", TLG_TUNE_VSTD_SECAM_L1 },
-};
-static const unsigned int POSEIDON_TVNORMS = ARRAY_SIZE(poseidon_tvnorms);
-
-struct pd_audio_mode {
-       u32 tlg_audio_mode;
-       u32 v4l2_audio_sub;
-       u32 v4l2_audio_mode;
-};
-
-static const struct pd_audio_mode pd_audio_modes[] = {
-       { TLG_TUNE_TVAUDIO_MODE_MONO, V4L2_TUNER_SUB_MONO,
-               V4L2_TUNER_MODE_MONO },
-       { TLG_TUNE_TVAUDIO_MODE_STEREO, V4L2_TUNER_SUB_STEREO,
-               V4L2_TUNER_MODE_STEREO },
-       { TLG_TUNE_TVAUDIO_MODE_LANG_A, V4L2_TUNER_SUB_LANG1,
-               V4L2_TUNER_MODE_LANG1 },
-       { TLG_TUNE_TVAUDIO_MODE_LANG_B, V4L2_TUNER_SUB_LANG2,
-               V4L2_TUNER_MODE_LANG2 },
-       { TLG_TUNE_TVAUDIO_MODE_LANG_C, V4L2_TUNER_SUB_LANG1,
-               V4L2_TUNER_MODE_LANG1_LANG2 }
-};
-static const unsigned int POSEIDON_AUDIOMODS = ARRAY_SIZE(pd_audio_modes);
-
-struct pd_input {
-       char *name;
-       uint32_t tlg_src;
-};
-
-static const struct pd_input pd_inputs[] = {
-       { "TV Antenna", TLG_SIG_SRC_ANTENNA },
-       { "TV Cable", TLG_SIG_SRC_CABLE },
-       { "TV SVideo", TLG_SIG_SRC_SVIDEO },
-       { "TV Composite", TLG_SIG_SRC_COMPOSITE }
-};
-static const unsigned int POSEIDON_INPUTS = ARRAY_SIZE(pd_inputs);
-
-struct video_std_to_audio_std {
-       v4l2_std_id     video_std;
-       int             audio_std;
-};
-
-static const struct video_std_to_audio_std video_to_audio_map[] = {
-       /* country : { 27, 32, 33, 34, 36, 44, 45, 46, 47, 48, 64,
-                       65, 86, 351, 352, 353, 354, 358, 372, 852, 972 } */
-       { (V4L2_STD_PAL_I | V4L2_STD_PAL_B | V4L2_STD_PAL_D |
-               V4L2_STD_SECAM_L | V4L2_STD_SECAM_D), TLG_TUNE_ASTD_NICAM },
-
-       /* country : { 1, 52, 54, 55, 886 } */
-       {V4L2_STD_NTSC_M | V4L2_STD_PAL_N | V4L2_STD_PAL_M, TLG_TUNE_ASTD_BTSC},
-
-       /* country : { 81 } */
-       { V4L2_STD_NTSC_M_JP, TLG_TUNE_ASTD_EIAJ },
-
-       /* other country : TLG_TUNE_ASTD_A2 */
-};
-static const unsigned int map_size = ARRAY_SIZE(video_to_audio_map);
-
-static int get_audio_std(v4l2_std_id v4l2_std)
-{
-       int i = 0;
-
-       for (; i < map_size; i++) {
-               if (v4l2_std & video_to_audio_map[i].video_std)
-                       return video_to_audio_map[i].audio_std;
-       }
-       return TLG_TUNE_ASTD_A2;
-}
-
-static int vidioc_querycap(struct file *file, void *fh,
-                       struct v4l2_capability *cap)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct poseidon *p = video_get_drvdata(vdev);
-
-       strcpy(cap->driver, "tele-video");
-       strcpy(cap->card, "Telegent Poseidon");
-       usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info));
-       cap->device_caps = V4L2_CAP_TUNER | V4L2_CAP_AUDIO |
-                       V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
-       if (vdev->vfl_type == VFL_TYPE_VBI)
-               cap->device_caps |= V4L2_CAP_VBI_CAPTURE;
-       else
-               cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
-       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS |
-               V4L2_CAP_RADIO | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE;
-       return 0;
-}
-
-/*====================================================================*/
-static void init_copy(struct video_data *video, bool index)
-{
-       struct front_face *front = video->front;
-
-       video->field_count      = index;
-       video->lines_copied     = 0;
-       video->prev_left        = 0 ;
-       video->dst              = (char *)videobuf_to_vmalloc(front->curr_frame)
-                                       + index * video->lines_size;
-       video->vbi->copied      = 0; /* set it here */
-}
-
-static bool get_frame(struct front_face *front, int *need_init)
-{
-       struct videobuf_buffer *vb = front->curr_frame;
-
-       if (vb)
-               return true;
-
-       spin_lock(&front->queue_lock);
-       if (!list_empty(&front->active)) {
-               vb = list_entry(front->active.next,
-                              struct videobuf_buffer, queue);
-               if (need_init)
-                       *need_init = 1;
-               front->curr_frame = vb;
-               list_del_init(&vb->queue);
-       }
-       spin_unlock(&front->queue_lock);
-
-       return !!vb;
-}
-
-/* check if the video's buffer is ready */
-static bool get_video_frame(struct front_face *front, struct video_data *video)
-{
-       int need_init = 0;
-       bool ret = true;
-
-       ret = get_frame(front, &need_init);
-       if (ret && need_init)
-               init_copy(video, 0);
-       return ret;
-}
-
-static void submit_frame(struct front_face *front)
-{
-       struct videobuf_buffer *vb = front->curr_frame;
-
-       if (vb == NULL)
-               return;
-
-       front->curr_frame       = NULL;
-       vb->state               = VIDEOBUF_DONE;
-       vb->field_count++;
-       v4l2_get_timestamp(&vb->ts);
-
-       wake_up(&vb->done);
-}
-
-/*
- * A frame is composed of two fields. If we receive all the two fields,
- * call the  submit_frame() to submit the whole frame to applications.
- */
-static void end_field(struct video_data *video)
-{
-       if (1 == video->field_count)
-               submit_frame(video->front);
-       else
-               init_copy(video, 1);
-}
-
-static void copy_video_data(struct video_data *video, char *src,
-                               unsigned int count)
-{
-#define copy_data(len)  \
-       do { \
-               if (++video->lines_copied > video->lines_per_field) \
-                       goto overflow; \
-               memcpy(video->dst, src, len);\
-               video->dst += len + video->lines_size; \
-               src += len; \
-               count -= len; \
-        } while (0)
-
-       while (count && count >= video->lines_size) {
-               if (video->prev_left) {
-                       copy_data(video->prev_left);
-                       video->prev_left = 0;
-                       continue;
-               }
-               copy_data(video->lines_size);
-       }
-       if (count && count < video->lines_size) {
-               memcpy(video->dst, src, count);
-
-               video->prev_left = video->lines_size - count;
-               video->dst += count;
-       }
-       return;
-
-overflow:
-       end_field(video);
-}
-
-static void check_trailer(struct video_data *video, char *src, int count)
-{
-       struct vbi_data *vbi = video->vbi;
-       int offset; /* trailer's offset */
-       char *buf;
-
-       offset = (video->context.pix.sizeimage / 2 + vbi->vbi_size / 2)
-               - (vbi->copied + video->lines_size * video->lines_copied);
-       if (video->prev_left)
-               offset -= (video->lines_size - video->prev_left);
-
-       if (offset > count || offset <= 0)
-               goto short_package;
-
-       buf = src + offset;
-
-       /* trailer : (VFHS) + U32 + U32 + field_num */
-       if (!strncmp(buf, "VFHS", 4)) {
-               int field_num = *((u32 *)(buf + 12));
-
-               if ((field_num & 1) ^ video->field_count) {
-                       init_copy(video, video->field_count);
-                       return;
-               }
-               copy_video_data(video, src, offset);
-       }
-short_package:
-       end_field(video);
-}
-
-/* ==========  Check this more carefully! =========== */
-static inline void copy_vbi_data(struct vbi_data *vbi,
-                               char *src, unsigned int count)
-{
-       struct front_face *front = vbi->front;
-
-       if (front && get_frame(front, NULL)) {
-               char *buf = videobuf_to_vmalloc(front->curr_frame);
-
-               if (vbi->video->field_count)
-                       buf += (vbi->vbi_size / 2);
-               memcpy(buf + vbi->copied, src, count);
-       }
-       vbi->copied += count;
-}
-
-/*
- * Copy the normal data (VBI or VIDEO) without the trailer.
- * VBI is not interlaced, while VIDEO is interlaced.
- */
-static inline void copy_vbi_video_data(struct video_data *video,
-                               char *src, unsigned int count)
-{
-       struct vbi_data *vbi = video->vbi;
-       unsigned int vbi_delta = (vbi->vbi_size / 2) - vbi->copied;
-
-       if (vbi_delta >= count) {
-               copy_vbi_data(vbi, src, count);
-       } else {
-               if (vbi_delta) {
-                       copy_vbi_data(vbi, src, vbi_delta);
-
-                       /* we receive the two fields of the VBI*/
-                       if (vbi->front && video->field_count)
-                               submit_frame(vbi->front);
-               }
-               copy_video_data(video, src + vbi_delta, count - vbi_delta);
-       }
-}
-
-static void urb_complete_bulk(struct urb *urb)
-{
-       struct front_face *front = urb->context;
-       struct video_data *video = &front->pd->video_data;
-       char *src = (char *)urb->transfer_buffer;
-       int count = urb->actual_length;
-       int ret = 0;
-
-       if (!video->is_streaming || urb->status) {
-               if (urb->status == -EPROTO)
-                       goto resend_it;
-               return;
-       }
-       if (!get_video_frame(front, video))
-               goto resend_it;
-
-       if (count == urb->transfer_buffer_length)
-               copy_vbi_video_data(video, src, count);
-       else
-               check_trailer(video, src, count);
-
-resend_it:
-       ret = usb_submit_urb(urb, GFP_ATOMIC);
-       if (ret)
-               log(" submit failed: error %d", ret);
-}
-
-/************************* for ISO *********************/
-#define GET_SUCCESS            (0)
-#define GET_TRAILER            (1)
-#define GET_TOO_MUCH_BUBBLE    (2)
-#define GET_NONE               (3)
-static int get_chunk(int start, struct urb *urb,
-                       int *head, int *tail, int *bubble_err)
-{
-       struct usb_iso_packet_descriptor *pkt = NULL;
-       int ret = GET_SUCCESS;
-
-       for (*head = *tail = -1; start < urb->number_of_packets; start++) {
-               pkt = &urb->iso_frame_desc[start];
-
-               /* handle the bubble of the Hub */
-               if (-EOVERFLOW == pkt->status) {
-                       if (++*bubble_err > urb->number_of_packets / 3)
-                               return GET_TOO_MUCH_BUBBLE;
-                       continue;
-               }
-
-               /* This is the gap */
-               if (pkt->status || pkt->actual_length <= 0
-                               || pkt->actual_length > ISO_PKT_SIZE) {
-                       if (*head != -1)
-                               break;
-                       continue;
-               }
-
-               /* a good isochronous packet */
-               if (pkt->actual_length == ISO_PKT_SIZE) {
-                       if (*head == -1)
-                               *head = start;
-                       *tail = start;
-                       continue;
-               }
-
-               /* trailer is here */
-               if (pkt->actual_length < ISO_PKT_SIZE) {
-                       if (*head == -1) {
-                               *head = start;
-                               *tail = start;
-                               return GET_TRAILER;
-                       }
-                       break;
-               }
-       }
-
-       if (*head == -1 && *tail == -1)
-               ret = GET_NONE;
-       return ret;
-}
-
-/*
- * |__|------|___|-----|_______|
- *       ^          ^
- *       |          |
- *      gap        gap
- */
-static void urb_complete_iso(struct urb *urb)
-{
-       struct front_face *front = urb->context;
-       struct video_data *video = &front->pd->video_data;
-       int bubble_err = 0, head = 0, tail = 0;
-       char *src = (char *)urb->transfer_buffer;
-       int ret = 0;
-
-       if (!video->is_streaming)
-               return;
-
-       do {
-               if (!get_video_frame(front, video))
-                       goto out;
-
-               switch (get_chunk(head, urb, &head, &tail, &bubble_err)) {
-               case GET_SUCCESS:
-                       copy_vbi_video_data(video, src + (head * ISO_PKT_SIZE),
-                                       (tail - head + 1) * ISO_PKT_SIZE);
-                       break;
-               case GET_TRAILER:
-                       check_trailer(video, src + (head * ISO_PKT_SIZE),
-                                       ISO_PKT_SIZE);
-                       break;
-               case GET_NONE:
-                       goto out;
-               case GET_TOO_MUCH_BUBBLE:
-                       log("\t We got too much bubble");
-                       schedule_work(&video->bubble_work);
-                       return;
-               }
-       } while (head = tail + 1, head < urb->number_of_packets);
-
-out:
-       ret = usb_submit_urb(urb, GFP_ATOMIC);
-       if (ret)
-               log("usb_submit_urb err : %d", ret);
-}
-/*============================= [  end  ] =====================*/
-
-static int prepare_iso_urb(struct video_data *video)
-{
-       struct usb_device *udev = video->pd->udev;
-       int i;
-
-       if (video->urb_array[0])
-               return 0;
-
-       for (i = 0; i < SBUF_NUM; i++) {
-               struct urb *urb;
-               void *mem;
-               int j;
-
-               urb = usb_alloc_urb(PK_PER_URB, GFP_KERNEL);
-               if (urb == NULL)
-                       goto out;
-
-               video->urb_array[i] = urb;
-               mem = usb_alloc_coherent(udev,
-                                        ISO_PKT_SIZE * PK_PER_URB,
-                                        GFP_KERNEL,
-                                        &urb->transfer_dma);
-
-               urb->complete   = urb_complete_iso;     /* handler */
-               urb->dev        = udev;
-               urb->context    = video->front;
-               urb->pipe       = usb_rcvisocpipe(udev,
-                                               video->endpoint_addr);
-               urb->interval   = 1;
-               urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
-               urb->number_of_packets  = PK_PER_URB;
-               urb->transfer_buffer    = mem;
-               urb->transfer_buffer_length = PK_PER_URB * ISO_PKT_SIZE;
-
-               for (j = 0; j < PK_PER_URB; j++) {
-                       urb->iso_frame_desc[j].offset = ISO_PKT_SIZE * j;
-                       urb->iso_frame_desc[j].length = ISO_PKT_SIZE;
-               }
-       }
-       return 0;
-out:
-       for (; i > 0; i--)
-               ;
-       return -ENOMEM;
-}
-
-/* return the succeeded number of the allocation */
-int alloc_bulk_urbs_generic(struct urb **urb_array, int num,
-                       struct usb_device *udev, u8 ep_addr,
-                       int buf_size, gfp_t gfp_flags,
-                       usb_complete_t complete_fn, void *context)
-{
-       int i = 0;
-
-       for (; i < num; i++) {
-               void *mem;
-               struct urb *urb = usb_alloc_urb(0, gfp_flags);
-               if (urb == NULL)
-                       return i;
-
-               mem = usb_alloc_coherent(udev, buf_size, gfp_flags,
-                                        &urb->transfer_dma);
-               if (mem == NULL) {
-                       usb_free_urb(urb);
-                       return i;
-               }
-
-               usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, ep_addr),
-                               mem, buf_size, complete_fn, context);
-               urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-               urb_array[i] = urb;
-       }
-       return i;
-}
-
-void free_all_urb_generic(struct urb **urb_array, int num)
-{
-       int i;
-       struct urb *urb;
-
-       for (i = 0; i < num; i++) {
-               urb = urb_array[i];
-               if (urb) {
-                       usb_free_coherent(urb->dev,
-                                       urb->transfer_buffer_length,
-                                       urb->transfer_buffer,
-                                       urb->transfer_dma);
-                       usb_free_urb(urb);
-                       urb_array[i] = NULL;
-               }
-       }
-}
-
-static int prepare_bulk_urb(struct video_data *video)
-{
-       if (video->urb_array[0])
-               return 0;
-
-       alloc_bulk_urbs_generic(video->urb_array, SBUF_NUM,
-                       video->pd->udev, video->endpoint_addr,
-                       0x2000, GFP_KERNEL,
-                       urb_complete_bulk, video->front);
-       return 0;
-}
-
-/* free the URBs */
-static void free_all_urb(struct video_data *video)
-{
-       free_all_urb_generic(video->urb_array, SBUF_NUM);
-}
-
-static void pd_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
-       videobuf_vmalloc_free(vb);
-       vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-static void pd_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
-       struct front_face *front = q->priv_data;
-       vb->state = VIDEOBUF_QUEUED;
-       list_add_tail(&vb->queue, &front->active);
-}
-
-static int pd_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
-                          enum v4l2_field field)
-{
-       struct front_face *front = q->priv_data;
-       int rc;
-
-       switch (front->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               if (VIDEOBUF_NEEDS_INIT == vb->state) {
-                       struct v4l2_pix_format *pix;
-
-                       pix = &front->pd->video_data.context.pix;
-                       vb->size        = pix->sizeimage; /* real frame size */
-                       vb->width       = pix->width;
-                       vb->height      = pix->height;
-                       rc = videobuf_iolock(q, vb, NULL);
-                       if (rc < 0)
-                               return rc;
-               }
-               break;
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               if (VIDEOBUF_NEEDS_INIT == vb->state) {
-                       vb->size        = front->pd->vbi_data.vbi_size;
-                       rc = videobuf_iolock(q, vb, NULL);
-                       if (rc < 0)
-                               return rc;
-               }
-               break;
-       default:
-               return -EINVAL;
-       }
-       vb->field = field;
-       vb->state = VIDEOBUF_PREPARED;
-       return 0;
-}
-
-static int fire_all_urb(struct video_data *video)
-{
-       int i, ret;
-
-       video->is_streaming = 1;
-
-       for (i = 0; i < SBUF_NUM; i++) {
-               ret = usb_submit_urb(video->urb_array[i], GFP_KERNEL);
-               if (ret)
-                       log("(%d) failed: error %d", i, ret);
-       }
-       return ret;
-}
-
-static int start_video_stream(struct poseidon *pd)
-{
-       struct video_data *video = &pd->video_data;
-       s32 cmd_status;
-
-       send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
-       send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_START, &cmd_status);
-
-       if (pd->cur_transfer_mode) {
-               prepare_iso_urb(video);
-               INIT_WORK(&video->bubble_work, iso_bubble_handler);
-       } else {
-               /* The bulk mode does not need a bubble handler */
-               prepare_bulk_urb(video);
-       }
-       fire_all_urb(video);
-       return 0;
-}
-
-static int pd_buf_setup(struct videobuf_queue *q, unsigned int *count,
-                      unsigned int *size)
-{
-       struct front_face *front = q->priv_data;
-       struct poseidon *pd     = front->pd;
-
-       switch (front->type) {
-       default:
-               return -EINVAL;
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
-               struct video_data *video = &pd->video_data;
-               struct v4l2_pix_format *pix = &video->context.pix;
-
-               *size = PAGE_ALIGN(pix->sizeimage);/* page aligned frame size */
-               if (*count < 4)
-                       *count = 4;
-               if (1) {
-                       /* same in different altersetting */
-                       video->endpoint_addr    = 0x82;
-                       video->vbi              = &pd->vbi_data;
-                       video->vbi->video       = video;
-                       video->pd               = pd;
-                       video->lines_per_field  = pix->height / 2;
-                       video->lines_size       = pix->width * 2;
-                       video->front            = front;
-               }
-               return start_video_stream(pd);
-       }
-
-       case V4L2_BUF_TYPE_VBI_CAPTURE: {
-               struct vbi_data *vbi = &pd->vbi_data;
-
-               *size = PAGE_ALIGN(vbi->vbi_size);
-               log("size : %d", *size);
-               if (*count == 0)
-                       *count = 4;
-       }
-               break;
-       }
-       return 0;
-}
-
-static struct videobuf_queue_ops pd_video_qops = {
-       .buf_setup      = pd_buf_setup,
-       .buf_prepare    = pd_buf_prepare,
-       .buf_queue      = pd_buf_queue,
-       .buf_release    = pd_buf_release,
-};
-
-static int vidioc_enum_fmt(struct file *file, void *fh,
-                               struct v4l2_fmtdesc *f)
-{
-       if (ARRAY_SIZE(poseidon_formats) <= f->index)
-               return -EINVAL;
-       f->type         = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-       f->flags        = 0;
-       f->pixelformat  = poseidon_formats[f->index].fourcc;
-       strcpy(f->description, poseidon_formats[f->index].name);
-       return 0;
-}
-
-static int vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
-{
-       struct front_face *front = fh;
-       struct poseidon *pd = front->pd;
-
-       f->fmt.pix = pd->video_data.context.pix;
-       return 0;
-}
-
-/*
- * VLC calls VIDIOC_S_STD before VIDIOC_S_FMT, while
- * Mplayer calls them in the reverse order.
- */
-static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix)
-{
-       struct video_data *video        = &pd->video_data;
-       struct running_context *context = &video->context;
-       struct v4l2_pix_format *pix_def = &context->pix;
-       s32 ret = 0, cmd_status = 0, vid_resol;
-
-       /* set the pixel format to firmware */
-       if (pix->pixelformat == V4L2_PIX_FMT_RGB565) {
-               vid_resol = TLG_TUNER_VID_FORMAT_RGB_565;
-       } else {
-               pix->pixelformat = V4L2_PIX_FMT_YUYV;
-               vid_resol = TLG_TUNER_VID_FORMAT_YUV;
-       }
-       ret = send_set_req(pd, VIDEO_STREAM_FMT_SEL,
-                               vid_resol, &cmd_status);
-
-       /* set the resolution to firmware */
-       vid_resol = TLG_TUNE_VID_RES_720;
-       switch (pix->width) {
-       case 704:
-               vid_resol = TLG_TUNE_VID_RES_704;
-               break;
-       default:
-               pix->width = 720;
-       case 720:
-               break;
-       }
-       ret |= send_set_req(pd, VIDEO_ROSOLU_SEL,
-                               vid_resol, &cmd_status);
-       if (ret || cmd_status)
-               return -EBUSY;
-
-       pix_def->pixelformat = pix->pixelformat; /* save it */
-       pix->height = (context->tvnormid & V4L2_STD_525_60) ?  480 : 576;
-
-       /* Compare with the default setting */
-       if ((pix_def->width != pix->width)
-               || (pix_def->height != pix->height)) {
-               pix_def->width          = pix->width;
-               pix_def->height         = pix->height;
-               pix_def->bytesperline   = pix->width * 2;
-               pix_def->sizeimage      = pix->width * pix->height * 2;
-       }
-       *pix = *pix_def;
-
-       return 0;
-}
-
-static int vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
-{
-       struct front_face *front        = fh;
-       struct poseidon *pd             = front->pd;
-
-       /* stop VBI here */
-       if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type)
-               return -EINVAL;
-
-       mutex_lock(&pd->lock);
-       if (pd->file_for_stream == NULL)
-               pd->file_for_stream = file;
-       else if (file != pd->file_for_stream) {
-               mutex_unlock(&pd->lock);
-               return -EINVAL;
-       }
-
-       pd_vidioc_s_fmt(pd, &f->fmt.pix);
-       mutex_unlock(&pd->lock);
-       return 0;
-}
-
-static int vidioc_g_fmt_vbi(struct file *file, void *fh,
-                              struct v4l2_format *v4l2_f)
-{
-       struct front_face *front        = fh;
-       struct poseidon *pd             = front->pd;
-       struct v4l2_vbi_format *vbi_fmt = &v4l2_f->fmt.vbi;
-
-       vbi_fmt->samples_per_line       = 720 * 2;
-       vbi_fmt->sampling_rate          = 6750000 * 4;
-       vbi_fmt->sample_format          = V4L2_PIX_FMT_GREY;
-       vbi_fmt->offset                 = 64 * 4;  /*FIXME: why offset */
-       if (pd->video_data.context.tvnormid & V4L2_STD_525_60) {
-               vbi_fmt->start[0] = 10;
-               vbi_fmt->start[1] = 264;
-               vbi_fmt->count[0] = V4L_NTSC_VBI_LINES;
-               vbi_fmt->count[1] = V4L_NTSC_VBI_LINES;
-       } else {
-               vbi_fmt->start[0] = 6;
-               vbi_fmt->start[1] = 314;
-               vbi_fmt->count[0] = V4L_PAL_VBI_LINES;
-               vbi_fmt->count[1] = V4L_PAL_VBI_LINES;
-       }
-       vbi_fmt->flags = V4L2_VBI_UNSYNC;
-       return 0;
-}
-
-static int set_std(struct poseidon *pd, v4l2_std_id norm)
-{
-       struct video_data *video = &pd->video_data;
-       struct vbi_data *vbi    = &pd->vbi_data;
-       struct running_context *context;
-       struct v4l2_pix_format *pix;
-       s32 i, ret = 0, cmd_status, param;
-       int height;
-
-       for (i = 0; i < POSEIDON_TVNORMS; i++) {
-               if (norm & poseidon_tvnorms[i].v4l2_id) {
-                       param = poseidon_tvnorms[i].tlg_tvnorm;
-                       log("name : %s", poseidon_tvnorms[i].name);
-                       goto found;
-               }
-       }
-       return -EINVAL;
-found:
-       mutex_lock(&pd->lock);
-       ret = send_set_req(pd, VIDEO_STD_SEL, param, &cmd_status);
-       if (ret || cmd_status)
-               goto out;
-
-       /* Set vbi size and check the height of the frame */
-       context = &video->context;
-       context->tvnormid = poseidon_tvnorms[i].v4l2_id;
-       if (context->tvnormid & V4L2_STD_525_60) {
-               vbi->vbi_size = V4L_NTSC_VBI_FRAMESIZE;
-               height = 480;
-       } else {
-               vbi->vbi_size = V4L_PAL_VBI_FRAMESIZE;
-               height = 576;
-       }
-
-       pix = &context->pix;
-       if (pix->height != height) {
-               pix->height     = height;
-               pix->sizeimage  = pix->width * pix->height * 2;
-       }
-
-out:
-       mutex_unlock(&pd->lock);
-       return ret;
-}
-
-static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id norm)
-{
-       struct front_face *front = fh;
-
-       return set_std(front->pd, norm);
-}
-
-static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm)
-{
-       struct front_face *front = fh;
-
-       *norm = front->pd->video_data.context.tvnormid;
-       return 0;
-}
-
-static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in)
-{
-       if (in->index >= POSEIDON_INPUTS)
-               return -EINVAL;
-       strcpy(in->name, pd_inputs[in->index].name);
-       in->type  = V4L2_INPUT_TYPE_TUNER;
-
-       /*
-        * the audio input index mixed with this video input,
-        * Poseidon only have one audio/video, set to "0"
-        */
-       in->audioset    = 1;
-       in->tuner       = 0;
-       in->std         = V4L2_STD_ALL;
-       in->status      = 0;
-       return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
-{
-       struct front_face *front = fh;
-       struct poseidon *pd = front->pd;
-       struct running_context *context = &pd->video_data.context;
-
-       *i = context->sig_index;
-       return 0;
-}
-
-/* We can support several inputs */
-static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
-{
-       struct front_face *front = fh;
-       struct poseidon *pd = front->pd;
-       s32 ret, cmd_status;
-
-       if (i >= POSEIDON_INPUTS)
-               return -EINVAL;
-       ret = send_set_req(pd, SGNL_SRC_SEL,
-                       pd_inputs[i].tlg_src, &cmd_status);
-       if (ret)
-               return ret;
-
-       pd->video_data.context.sig_index = i;
-       return 0;
-}
-
-static int tlg_s_ctrl(struct v4l2_ctrl *c)
-{
-       struct poseidon *pd = container_of(c->handler, struct poseidon,
-                                               video_data.ctrl_handler);
-       struct tuner_custom_parameter_s param = {0};
-       s32 ret = 0, cmd_status, params;
-
-       switch (c->id) {
-       case V4L2_CID_BRIGHTNESS:
-               param.param_id = CUST_PARM_ID_BRIGHTNESS_CTRL;
-               break;
-       case V4L2_CID_CONTRAST:
-               param.param_id = CUST_PARM_ID_CONTRAST_CTRL;
-               break;
-       case V4L2_CID_HUE:
-               param.param_id = CUST_PARM_ID_HUE_CTRL;
-               break;
-       case V4L2_CID_SATURATION:
-               param.param_id = CUST_PARM_ID_SATURATION_CTRL;
-               break;
-       }
-       param.param_value = c->val;
-       params = *(s32 *)&param; /* temp code */
-
-       mutex_lock(&pd->lock);
-       ret = send_set_req(pd, TUNER_CUSTOM_PARAMETER, params, &cmd_status);
-       ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
-       mutex_unlock(&pd->lock);
-
-       set_current_state(TASK_INTERRUPTIBLE);
-       schedule_timeout(HZ/4);
-       return ret;
-}
-
-/* Audio ioctls */
-static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
-{
-       if (0 != a->index)
-               return -EINVAL;
-       a->capability = V4L2_AUDCAP_STEREO;
-       strcpy(a->name, "USB audio in");
-       /*Poseidon have no AVL function.*/
-       a->mode = 0;
-       return 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
-{
-       a->index = 0;
-       a->capability = V4L2_AUDCAP_STEREO;
-       strcpy(a->name, "USB audio in");
-       a->mode = 0;
-       return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a)
-{
-       return (0 == a->index) ? 0 : -EINVAL;
-}
-
-/* Tuner ioctls */
-static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *tuner)
-{
-       struct front_face *front        = fh;
-       struct poseidon *pd             = front->pd;
-       struct tuner_atv_sig_stat_s atv_stat;
-       s32 count = 5, ret, cmd_status;
-       int index;
-
-       if (0 != tuner->index)
-               return -EINVAL;
-
-       mutex_lock(&pd->lock);
-       ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV,
-                               &atv_stat, &cmd_status, sizeof(atv_stat));
-
-       while (atv_stat.sig_lock_busy && count-- && !ret) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(HZ);
-
-               ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV,
-                               &atv_stat, &cmd_status, sizeof(atv_stat));
-       }
-       mutex_unlock(&pd->lock);
-
-       if (debug_mode)
-               log("P:%d,S:%d", atv_stat.sig_present, atv_stat.sig_strength);
-
-       if (ret || cmd_status)
-               tuner->signal = 0;
-       else if (atv_stat.sig_present && !atv_stat.sig_strength)
-               tuner->signal = 0xFFFF;
-       else
-               tuner->signal = (atv_stat.sig_strength * 255 / 10) << 8;
-
-       strcpy(tuner->name, "Telegent Systems");
-       tuner->type = V4L2_TUNER_ANALOG_TV;
-       tuner->rangelow = TUNER_FREQ_MIN / 62500;
-       tuner->rangehigh = TUNER_FREQ_MAX / 62500;
-       tuner->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
-                               V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
-       index = pd->video_data.context.audio_idx;
-       tuner->rxsubchans = pd_audio_modes[index].v4l2_audio_sub;
-       tuner->audmode = pd_audio_modes[index].v4l2_audio_mode;
-       tuner->afc = 0;
-       return 0;
-}
-
-static int pd_vidioc_s_tuner(struct poseidon *pd, int index)
-{
-       s32 ret = 0, cmd_status, param, audiomode;
-
-       mutex_lock(&pd->lock);
-       param = pd_audio_modes[index].tlg_audio_mode;
-       ret = send_set_req(pd, TUNER_AUD_MODE, param, &cmd_status);
-       audiomode = get_audio_std(pd->video_data.context.tvnormid);
-       ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode,
-                               &cmd_status);
-       if (!ret)
-               pd->video_data.context.audio_idx = index;
-       mutex_unlock(&pd->lock);
-       return ret;
-}
-
-static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *a)
-{
-       struct front_face *front        = fh;
-       struct poseidon *pd             = front->pd;
-       int index;
-
-       if (0 != a->index)
-               return -EINVAL;
-       for (index = 0; index < POSEIDON_AUDIOMODS; index++)
-               if (a->audmode == pd_audio_modes[index].v4l2_audio_mode)
-                       return pd_vidioc_s_tuner(pd, index);
-       return -EINVAL;
-}
-
-static int vidioc_g_frequency(struct file *file, void *fh,
-                       struct v4l2_frequency *freq)
-{
-       struct front_face *front = fh;
-       struct poseidon *pd = front->pd;
-       struct running_context *context = &pd->video_data.context;
-
-       if (0 != freq->tuner)
-               return -EINVAL;
-       freq->frequency = context->freq;
-       freq->type = V4L2_TUNER_ANALOG_TV;
-       return 0;
-}
-
-static int set_frequency(struct poseidon *pd, u32 *frequency)
-{
-       s32 ret = 0, param, cmd_status;
-       struct running_context *context = &pd->video_data.context;
-
-       *frequency = clamp(*frequency,
-                       TUNER_FREQ_MIN / 62500, TUNER_FREQ_MAX / 62500);
-       param = (*frequency) * 62500 / 1000;
-
-       mutex_lock(&pd->lock);
-       ret = send_set_req(pd, TUNE_FREQ_SELECT, param, &cmd_status);
-       ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
-
-       msleep(250); /* wait for a while until the hardware is ready. */
-       context->freq = *frequency;
-       mutex_unlock(&pd->lock);
-       return ret;
-}
-
-static int vidioc_s_frequency(struct file *file, void *fh,
-                               const struct v4l2_frequency *freq)
-{
-       struct front_face *front = fh;
-       struct poseidon *pd = front->pd;
-       u32 frequency = freq->frequency;
-
-       if (freq->tuner)
-               return -EINVAL;
-#ifdef CONFIG_PM
-       pd->pm_suspend = pm_video_suspend;
-       pd->pm_resume = pm_video_resume;
-#endif
-       return set_frequency(pd, &frequency);
-}
-
-static int vidioc_reqbufs(struct file *file, void *fh,
-                               struct v4l2_requestbuffers *b)
-{
-       struct front_face *front = file->private_data;
-       return videobuf_reqbufs(&front->q, b);
-}
-
-static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
-       struct front_face *front = file->private_data;
-       return videobuf_querybuf(&front->q, b);
-}
-
-static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
-       struct front_face *front = file->private_data;
-       return videobuf_qbuf(&front->q, b);
-}
-
-static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
-       struct front_face *front = file->private_data;
-       return videobuf_dqbuf(&front->q, b, file->f_flags & O_NONBLOCK);
-}
-
-/* Just stop the URBs, do not free the URBs */
-static int usb_transfer_stop(struct video_data *video)
-{
-       if (video->is_streaming) {
-               int i;
-               s32 cmd_status;
-               struct poseidon *pd = video->pd;
-
-               video->is_streaming = 0;
-               for (i = 0; i < SBUF_NUM; ++i) {
-                       if (video->urb_array[i])
-                               usb_kill_urb(video->urb_array[i]);
-               }
-
-               send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
-                              &cmd_status);
-       }
-       return 0;
-}
-
-int stop_all_video_stream(struct poseidon *pd)
-{
-       struct video_data *video = &pd->video_data;
-       struct vbi_data *vbi    = &pd->vbi_data;
-
-       mutex_lock(&pd->lock);
-       if (video->is_streaming) {
-               struct front_face *front = video->front;
-
-               /* stop the URBs */
-               usb_transfer_stop(video);
-               free_all_urb(video);
-
-               /* stop the host side of VIDEO */
-               videobuf_stop(&front->q);
-               videobuf_mmap_free(&front->q);
-
-               /* stop the host side of VBI */
-               front = vbi->front;
-               if (front) {
-                       videobuf_stop(&front->q);
-                       videobuf_mmap_free(&front->q);
-               }
-       }
-       mutex_unlock(&pd->lock);
-       return 0;
-}
-
-/*
- * The bubbles can seriously damage the video's quality,
- * though it occurs in very rare situation.
- */
-static void iso_bubble_handler(struct work_struct *w)
-{
-       struct video_data *video;
-       struct poseidon *pd;
-
-       video = container_of(w, struct video_data, bubble_work);
-       pd = video->pd;
-
-       mutex_lock(&pd->lock);
-       usb_transfer_stop(video);
-       msleep(500);
-       start_video_stream(pd);
-       mutex_unlock(&pd->lock);
-}
-
-
-static int vidioc_streamon(struct file *file, void *fh,
-                               enum v4l2_buf_type type)
-{
-       struct front_face *front = fh;
-
-       if (unlikely(type != front->type))
-               return -EINVAL;
-       return videobuf_streamon(&front->q);
-}
-
-static int vidioc_streamoff(struct file *file, void *fh,
-                               enum v4l2_buf_type type)
-{
-       struct front_face *front = file->private_data;
-
-       if (unlikely(type != front->type))
-               return -EINVAL;
-       return videobuf_streamoff(&front->q);
-}
-
-/* Set the firmware's default values : need altersetting */
-static int pd_video_checkmode(struct poseidon *pd)
-{
-       s32 ret = 0, cmd_status, audiomode;
-
-       set_current_state(TASK_INTERRUPTIBLE);
-       schedule_timeout(HZ/2);
-
-       /* choose the altersetting */
-       ret = usb_set_interface(pd->udev, 0,
-                                       (pd->cur_transfer_mode ?
-                                        ISO_3K_BULK_ALTERNATE_IFACE :
-                                        BULK_ALTERNATE_IFACE));
-       if (ret < 0)
-               goto error;
-
-       /* set default parameters for PAL-D , with the VBI enabled*/
-       ret = set_tuner_mode(pd, TLG_MODE_ANALOG_TV);
-       ret |= send_set_req(pd, SGNL_SRC_SEL,
-                               TLG_SIG_SRC_ANTENNA, &cmd_status);
-       ret |= send_set_req(pd, VIDEO_STD_SEL,
-                               TLG_TUNE_VSTD_PAL_D, &cmd_status);
-       ret |= send_set_req(pd, VIDEO_STREAM_FMT_SEL,
-                               TLG_TUNER_VID_FORMAT_YUV, &cmd_status);
-       ret |= send_set_req(pd, VIDEO_ROSOLU_SEL,
-                               TLG_TUNE_VID_RES_720, &cmd_status);
-       ret |= send_set_req(pd, TUNE_FREQ_SELECT, TUNER_FREQ_MIN, &cmd_status);
-       ret |= send_set_req(pd, VBI_DATA_SEL, 1, &cmd_status);/* enable vbi */
-
-       /* set the audio */
-       audiomode = get_audio_std(pd->video_data.context.tvnormid);
-       ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, &cmd_status);
-       ret |= send_set_req(pd, TUNER_AUD_MODE,
-                               TLG_TUNE_TVAUDIO_MODE_STEREO, &cmd_status);
-       ret |= send_set_req(pd, AUDIO_SAMPLE_RATE_SEL,
-                               ATV_AUDIO_RATE_48K, &cmd_status);
-error:
-       return ret;
-}
-
-#ifdef CONFIG_PM
-static int pm_video_suspend(struct poseidon *pd)
-{
-       /* stop audio */
-       pm_alsa_suspend(pd);
-
-       /* stop and free all the URBs */
-       usb_transfer_stop(&pd->video_data);
-       free_all_urb(&pd->video_data);
-
-       /* reset the interface */
-       usb_set_interface(pd->udev, 0, 0);
-       msleep(300);
-       return 0;
-}
-
-static int restore_v4l2_context(struct poseidon *pd,
-                               struct running_context *context)
-{
-       struct front_face *front = pd->video_data.front;
-
-       pd_video_checkmode(pd);
-
-       set_std(pd, context->tvnormid);
-       vidioc_s_input(NULL, front, context->sig_index);
-       pd_vidioc_s_tuner(pd, context->audio_idx);
-       pd_vidioc_s_fmt(pd, &context->pix);
-       set_frequency(pd, &context->freq);
-       return 0;
-}
-
-static int pm_video_resume(struct poseidon *pd)
-{
-       struct video_data *video = &pd->video_data;
-
-       /* resume the video */
-       /* [1] restore the origin V4L2 parameters */
-       restore_v4l2_context(pd, &video->context);
-
-       /* [2] initiate video copy variables */
-       if (video->front->curr_frame)
-               init_copy(video, 0);
-
-       /* [3] fire urbs        */
-       start_video_stream(pd);
-
-       /* resume the audio */
-       pm_alsa_resume(pd);
-       return 0;
-}
-#endif
-
-void set_debug_mode(struct video_device *vfd, int debug_mode)
-{
-       vfd->debug = 0;
-       if (debug_mode & 0x1)
-               vfd->debug = V4L2_DEBUG_IOCTL;
-       if (debug_mode & 0x2)
-               vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
-}
-
-static void init_video_context(struct running_context *context)
-{
-       context->sig_index      = 0;
-       context->audio_idx      = 1; /* stereo */
-       context->tvnormid       = V4L2_STD_PAL_D;
-       context->pix = (struct v4l2_pix_format) {
-                               .width          = 720,
-                               .height         = 576,
-                               .pixelformat    = V4L2_PIX_FMT_YUYV,
-                               .field          = V4L2_FIELD_INTERLACED,
-                               .bytesperline   = 720 * 2,
-                               .sizeimage      = 720 * 576 * 2,
-                               .colorspace     = V4L2_COLORSPACE_SMPTE170M,
-                       };
-}
-
-static int pd_video_open(struct file *file)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct poseidon *pd = video_get_drvdata(vfd);
-       struct front_face *front = NULL;
-       int ret = -ENOMEM;
-
-       mutex_lock(&pd->lock);
-       usb_autopm_get_interface(pd->interface);
-
-       if (pd->state && !(pd->state & POSEIDON_STATE_ANALOG)) {
-               ret = -EBUSY;
-               goto out;
-       }
-       front = kzalloc(sizeof(struct front_face), GFP_KERNEL);
-       if (!front)
-               goto out;
-       if (vfd->vfl_type == VFL_TYPE_GRABBER) {
-               pd->cur_transfer_mode   = usb_transfer_mode;/* bulk or iso */
-               init_video_context(&pd->video_data.context);
-
-               ret = pd_video_checkmode(pd);
-               if (ret < 0) {
-                       kfree(front);
-                       ret = -1;
-                       goto out;
-               }
-
-               front->type             = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-               pd->video_data.users++;
-               set_debug_mode(vfd, debug_mode);
-
-               videobuf_queue_vmalloc_init(&front->q, &pd_video_qops,
-                               NULL, &front->queue_lock,
-                               V4L2_BUF_TYPE_VIDEO_CAPTURE,
-                               V4L2_FIELD_INTERLACED,/* video is interlacd */
-                               sizeof(struct videobuf_buffer),/*it's enough*/
-                               front, NULL);
-       } else {
-               front->type     = V4L2_BUF_TYPE_VBI_CAPTURE;
-               pd->vbi_data.front = front;
-               pd->vbi_data.users++;
-
-               videobuf_queue_vmalloc_init(&front->q, &pd_video_qops,
-                               NULL, &front->queue_lock,
-                               V4L2_BUF_TYPE_VBI_CAPTURE,
-                               V4L2_FIELD_NONE, /* vbi is NONE mode */
-                               sizeof(struct videobuf_buffer),
-                               front, NULL);
-       }
-
-       pd->state |= POSEIDON_STATE_ANALOG;
-       front->pd = pd;
-       front->curr_frame = NULL;
-       INIT_LIST_HEAD(&front->active);
-       spin_lock_init(&front->queue_lock);
-
-       file->private_data = front;
-       kref_get(&pd->kref);
-
-       mutex_unlock(&pd->lock);
-       return 0;
-out:
-       usb_autopm_put_interface(pd->interface);
-       mutex_unlock(&pd->lock);
-       return ret;
-}
-
-static int pd_video_release(struct file *file)
-{
-       struct front_face *front = file->private_data;
-       struct poseidon *pd = front->pd;
-       s32 cmd_status = 0;
-
-       mutex_lock(&pd->lock);
-
-       if (front->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-               /* stop the device, and free the URBs */
-               usb_transfer_stop(&pd->video_data);
-               free_all_urb(&pd->video_data);
-
-               /* stop the firmware */
-               send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
-                              &cmd_status);
-
-               pd->file_for_stream = NULL;
-               pd->video_data.users--;
-       } else if (front->type  == V4L2_BUF_TYPE_VBI_CAPTURE) {
-               pd->vbi_data.front = NULL;
-               pd->vbi_data.users--;
-       }
-       if (!pd->vbi_data.users && !pd->video_data.users)
-               pd->state &= ~POSEIDON_STATE_ANALOG;
-       videobuf_stop(&front->q);
-       videobuf_mmap_free(&front->q);
-
-       usb_autopm_put_interface(pd->interface);
-       mutex_unlock(&pd->lock);
-
-       kfree(front);
-       file->private_data = NULL;
-       kref_put(&pd->kref, poseidon_delete);
-       return 0;
-}
-
-static int pd_video_mmap(struct file *file, struct vm_area_struct *vma)
-{
-       struct front_face *front = file->private_data;
-       return  videobuf_mmap_mapper(&front->q, vma);
-}
-
-static unsigned int pd_video_poll(struct file *file, poll_table *table)
-{
-       struct front_face *front = file->private_data;
-       return videobuf_poll_stream(file, &front->q, table);
-}
-
-static ssize_t pd_video_read(struct file *file, char __user *buffer,
-                       size_t count, loff_t *ppos)
-{
-       struct front_face *front = file->private_data;
-       return videobuf_read_stream(&front->q, buffer, count, ppos,
-                               0, file->f_flags & O_NONBLOCK);
-}
-
-/* This struct works for both VIDEO and VBI */
-static const struct v4l2_file_operations pd_video_fops = {
-       .owner          = THIS_MODULE,
-       .open           = pd_video_open,
-       .release        = pd_video_release,
-       .read           = pd_video_read,
-       .poll           = pd_video_poll,
-       .mmap           = pd_video_mmap,
-       .ioctl          = video_ioctl2, /* maybe changed in future */
-};
-
-static const struct v4l2_ioctl_ops pd_video_ioctl_ops = {
-       .vidioc_querycap        = vidioc_querycap,
-
-       /* Video format */
-       .vidioc_g_fmt_vid_cap   = vidioc_g_fmt,
-       .vidioc_enum_fmt_vid_cap        = vidioc_enum_fmt,
-       .vidioc_s_fmt_vid_cap   = vidioc_s_fmt,
-       .vidioc_g_fmt_vbi_cap   = vidioc_g_fmt_vbi, /* VBI */
-
-       /* Input */
-       .vidioc_g_input         = vidioc_g_input,
-       .vidioc_s_input         = vidioc_s_input,
-       .vidioc_enum_input      = vidioc_enum_input,
-
-       /* Audio ioctls */
-       .vidioc_enumaudio       = vidioc_enumaudio,
-       .vidioc_g_audio         = vidioc_g_audio,
-       .vidioc_s_audio         = vidioc_s_audio,
-
-       /* Tuner ioctls */
-       .vidioc_g_tuner         = vidioc_g_tuner,
-       .vidioc_s_tuner         = vidioc_s_tuner,
-       .vidioc_g_std           = vidioc_g_std,
-       .vidioc_s_std           = vidioc_s_std,
-       .vidioc_g_frequency     = vidioc_g_frequency,
-       .vidioc_s_frequency     = vidioc_s_frequency,
-
-       /* Buffer handlers */
-       .vidioc_reqbufs         = vidioc_reqbufs,
-       .vidioc_querybuf        = vidioc_querybuf,
-       .vidioc_qbuf            = vidioc_qbuf,
-       .vidioc_dqbuf           = vidioc_dqbuf,
-
-       /* Stream on/off */
-       .vidioc_streamon        = vidioc_streamon,
-       .vidioc_streamoff       = vidioc_streamoff,
-};
-
-static struct video_device pd_video_template = {
-       .name = "Telegent-Video",
-       .fops = &pd_video_fops,
-       .minor = -1,
-       .release = video_device_release_empty,
-       .tvnorms = V4L2_STD_ALL,
-       .ioctl_ops = &pd_video_ioctl_ops,
-};
-
-static const struct v4l2_ctrl_ops tlg_ctrl_ops = {
-       .s_ctrl = tlg_s_ctrl,
-};
-
-void pd_video_exit(struct poseidon *pd)
-{
-       struct video_data *video = &pd->video_data;
-       struct vbi_data *vbi = &pd->vbi_data;
-
-       video_unregister_device(&video->v_dev);
-       video_unregister_device(&vbi->v_dev);
-       v4l2_ctrl_handler_free(&video->ctrl_handler);
-       log();
-}
-
-int pd_video_init(struct poseidon *pd)
-{
-       struct video_data *video = &pd->video_data;
-       struct vbi_data *vbi    = &pd->vbi_data;
-       struct v4l2_ctrl_handler *hdl = &video->ctrl_handler;
-       u32 freq = TUNER_FREQ_MIN / 62500;
-       int ret = -ENOMEM;
-
-       v4l2_ctrl_handler_init(hdl, 4);
-       v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_BRIGHTNESS,
-                       0, 10000, 1, 100);
-       v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_CONTRAST,
-                       0, 10000, 1, 100);
-       v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_HUE,
-                       0, 10000, 1, 100);
-       v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_SATURATION,
-                       0, 10000, 1, 100);
-       if (hdl->error) {
-               v4l2_ctrl_handler_free(hdl);
-               return hdl->error;
-       }
-       set_frequency(pd, &freq);
-       video->v_dev = pd_video_template;
-       video->v_dev.v4l2_dev = &pd->v4l2_dev;
-       video->v_dev.ctrl_handler = hdl;
-       video_set_drvdata(&video->v_dev, pd);
-
-       ret = video_register_device(&video->v_dev, VFL_TYPE_GRABBER, -1);
-       if (ret != 0)
-               goto out;
-
-       /* VBI uses the same template as video */
-       vbi->v_dev = pd_video_template;
-       vbi->v_dev.v4l2_dev = &pd->v4l2_dev;
-       vbi->v_dev.ctrl_handler = hdl;
-       video_set_drvdata(&vbi->v_dev, pd);
-       ret = video_register_device(&vbi->v_dev, VFL_TYPE_VBI, -1);
-       if (ret != 0)
-               goto out;
-       log("register VIDEO/VBI devices");
-       return 0;
-out:
-       log("VIDEO/VBI devices register failed, : %d", ret);
-       pd_video_exit(pd);
-       return ret;
-}
diff --git a/drivers/media/usb/tlg2300/vendorcmds.h b/drivers/media/usb/tlg2300/vendorcmds.h
deleted file mode 100644 (file)
index ba6f4ae..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-#ifndef VENDOR_CMD_H_
-#define VENDOR_CMD_H_
-
-#define BULK_ALTERNATE_IFACE           (2)
-#define ISO_3K_BULK_ALTERNATE_IFACE     (1)
-#define REQ_SET_CMD                    (0X00)
-#define REQ_GET_CMD                    (0X80)
-
-enum tlg__analog_audio_standard {
-       TLG_TUNE_ASTD_NONE      = 0x00000000,
-       TLG_TUNE_ASTD_A2        = 0x00000001,
-       TLG_TUNE_ASTD_NICAM     = 0x00000002,
-       TLG_TUNE_ASTD_EIAJ      = 0x00000004,
-       TLG_TUNE_ASTD_BTSC      = 0x00000008,
-       TLG_TUNE_ASTD_FM_US     = 0x00000010,
-       TLG_TUNE_ASTD_FM_EUR    = 0x00000020,
-       TLG_TUNE_ASTD_ALL       = 0x0000003f
-};
-
-/*
- * identifiers for Custom Parameter messages.
- * @typedef cmd_custom_param_id_t
- */
-enum cmd_custom_param_id {
-       CUST_PARM_ID_NONE               = 0x00,
-       CUST_PARM_ID_BRIGHTNESS_CTRL    = 0x01,
-       CUST_PARM_ID_CONTRAST_CTRL      = 0x02,
-       CUST_PARM_ID_HUE_CTRL           = 0x03,
-       CUST_PARM_ID_SATURATION_CTRL      = 0x04,
-       CUST_PARM_ID_AUDIO_SNR_THRESHOLD  = 0x10,
-       CUST_PARM_ID_AUDIO_AGC_THRESHOLD  = 0x11,
-       CUST_PARM_ID_MAX
-};
-
-struct  tuner_custom_parameter_s {
-       uint16_t        param_id;        /*  Parameter identifier  */
-       uint16_t        param_value;     /*  Parameter value       */
-};
-
-struct  tuner_ber_rate_s {
-       uint32_t        ber_rate;  /*  BER sample rate in seconds   */
-};
-
-struct tuner_atv_sig_stat_s {
-       uint32_t        sig_present;
-       uint32_t        sig_locked;
-       uint32_t        sig_lock_busy;
-       uint32_t        sig_strength;      /*  milliDb    */
-       uint32_t        tv_audio_chan;    /*  mono/stereo/sap*/
-       uint32_t        mvision_stat;      /*  macrovision status */
-};
-
-struct tuner_dtv_sig_stat_s {
-       uint32_t sig_present;   /*  Boolean*/
-       uint32_t sig_locked;    /*  Boolean */
-       uint32_t sig_lock_busy; /*  Boolean     (Can this time-out?) */
-       uint32_t sig_strength;  /*  milliDb*/
-};
-
-struct tuner_fm_sig_stat_s {
-       uint32_t sig_present;   /* Boolean*/
-       uint32_t sig_locked;     /* Boolean */
-       uint32_t sig_lock_busy;  /* Boolean */
-       uint32_t sig_stereo_mono;/* TBD*/
-       uint32_t sig_strength;   /* milliDb*/
-};
-
-enum _tag_tlg_tune_srv_cmd {
-       TLG_TUNE_PLAY_SVC_START = 1,
-       TLG_TUNE_PLAY_SVC_STOP
-};
-
-enum  _tag_tune_atv_audio_mode_caps {
-       TLG_TUNE_TVAUDIO_MODE_MONO      = 0x00000001,
-       TLG_TUNE_TVAUDIO_MODE_STEREO    = 0x00000002,
-       TLG_TUNE_TVAUDIO_MODE_LANG_A    = 0x00000010,/* Primary language*/
-       TLG_TUNE_TVAUDIO_MODE_LANG_B    = 0x00000020,/* 2nd avail language*/
-       TLG_TUNE_TVAUDIO_MODE_LANG_C    = 0x00000040
-};
-
-
-enum   _tag_tuner_atv_audio_rates {
-       ATV_AUDIO_RATE_NONE     = 0x00,/* Audio not supported*/
-       ATV_AUDIO_RATE_32K      = 0x01,/* Audio rate = 32 KHz*/
-       ATV_AUDIO_RATE_48K      = 0x02, /* Audio rate = 48 KHz*/
-       ATV_AUDIO_RATE_31_25K   = 0x04 /* Audio rate = 31.25KHz */
-};
-
-enum  _tag_tune_atv_vid_res_caps {
-       TLG_TUNE_VID_RES_NONE   = 0x00000000,
-       TLG_TUNE_VID_RES_720    = 0x00000001,
-       TLG_TUNE_VID_RES_704    = 0x00000002,
-       TLG_TUNE_VID_RES_360    = 0x00000004
-};
-
-enum _tag_tuner_analog_video_format {
-       TLG_TUNER_VID_FORMAT_YUV        = 0x00000001,
-       TLG_TUNER_VID_FORMAT_YCRCB      = 0x00000002,
-       TLG_TUNER_VID_FORMAT_RGB_565    = 0x00000004,
-};
-
-enum  tlg_ext_audio_support {
-       TLG_EXT_AUDIO_NONE      = 0x00,/*  No external audio input supported */
-       TLG_EXT_AUDIO_LR        = 0x01/*  LR external audio inputs supported*/
-};
-
-enum {
-       TLG_MODE_NONE                   = 0x00, /* No Mode specified*/
-       TLG_MODE_ANALOG_TV              = 0x01, /* Analog Television mode*/
-       TLG_MODE_ANALOG_TV_UNCOMP       = 0x01, /* Analog Television mode*/
-       TLG_MODE_ANALOG_TV_COMP         = 0x02, /* Analog TV mode (compressed)*/
-       TLG_MODE_FM_RADIO               = 0x04, /* FM Radio mode*/
-       TLG_MODE_DVB_T                  = 0x08, /* Digital TV (DVB-T)*/
-};
-
-enum  tlg_signal_sources_t {
-       TLG_SIG_SRC_NONE        = 0x00,/* Signal source not specified */
-       TLG_SIG_SRC_ANTENNA     = 0x01,/* Signal src is: Antenna */
-       TLG_SIG_SRC_CABLE       = 0x02,/* Signal src is: Coax Cable*/
-       TLG_SIG_SRC_SVIDEO      = 0x04,/* Signal src is: S_VIDEO   */
-       TLG_SIG_SRC_COMPOSITE   = 0x08 /* Signal src is: Composite Video */
-};
-
-enum tuner_analog_video_standard {
-       TLG_TUNE_VSTD_NONE      = 0x00000000,
-       TLG_TUNE_VSTD_NTSC_M    = 0x00000001,
-       TLG_TUNE_VSTD_NTSC_M_J  = 0x00000002,/* Japan   */
-       TLG_TUNE_VSTD_PAL_B     = 0x00000010,
-       TLG_TUNE_VSTD_PAL_D     = 0x00000020,
-       TLG_TUNE_VSTD_PAL_G     = 0x00000040,
-       TLG_TUNE_VSTD_PAL_H     = 0x00000080,
-       TLG_TUNE_VSTD_PAL_I     = 0x00000100,
-       TLG_TUNE_VSTD_PAL_M     = 0x00000200,
-       TLG_TUNE_VSTD_PAL_N     = 0x00000400,
-       TLG_TUNE_VSTD_SECAM_B   = 0x00001000,
-       TLG_TUNE_VSTD_SECAM_D   = 0x00002000,
-       TLG_TUNE_VSTD_SECAM_G   = 0x00004000,
-       TLG_TUNE_VSTD_SECAM_H   = 0x00008000,
-       TLG_TUNE_VSTD_SECAM_K   = 0x00010000,
-       TLG_TUNE_VSTD_SECAM_K1  = 0x00020000,
-       TLG_TUNE_VSTD_SECAM_L   = 0x00040000,
-       TLG_TUNE_VSTD_SECAM_L1  = 0x00080000,
-       TLG_TUNE_VSTD_PAL_N_COMBO = 0x00100000
-};
-
-enum tlg_mode_caps {
-       TLG_MODE_CAPS_NONE              = 0x00,  /*  No Mode specified  */
-       TLG_MODE_CAPS_ANALOG_TV_UNCOMP  = 0x01,  /*  Analog TV mode     */
-       TLG_MODE_CAPS_ANALOG_TV_COMP    = 0x02,  /*  Analog TV (compressed)*/
-       TLG_MODE_CAPS_FM_RADIO          = 0x04,  /*  FM Radio mode      */
-       TLG_MODE_CAPS_DVB_T             = 0x08,  /*  Digital TV (DVB-T) */
-};
-
-enum poseidon_vendor_cmds {
-       LAST_CMD_STAT           = 0x00,
-       GET_CHIP_ID             = 0x01,
-       GET_FW_ID               = 0x02,
-       PRODUCT_CAPS            = 0x03,
-
-       TUNE_MODE_CAP_ATV       = 0x10,
-       TUNE_MODE_CAP_ATVCOMP   = 0X10,
-       TUNE_MODE_CAP_DVBT      = 0x10,
-       TUNE_MODE_CAP_FM        = 0x10,
-       TUNE_MODE_SELECT        = 0x11,
-       TUNE_FREQ_SELECT        = 0x12,
-       SGNL_SRC_SEL            = 0x13,
-
-       VIDEO_STD_SEL           = 0x14,
-       VIDEO_STREAM_FMT_SEL    = 0x15,
-       VIDEO_ROSOLU_AVAIL      = 0x16,
-       VIDEO_ROSOLU_SEL        = 0x17,
-       VIDEO_CONT_PROTECT      = 0x20,
-
-       VCR_TIMING_MODSEL       = 0x21,
-       EXT_AUDIO_CAP           = 0x22,
-       EXT_AUDIO_SEL           = 0x23,
-       TEST_PATTERN_SEL        = 0x24,
-       VBI_DATA_SEL            = 0x25,
-       AUDIO_SAMPLE_RATE_CAP   = 0x28,
-       AUDIO_SAMPLE_RATE_SEL   = 0x29,
-       TUNER_AUD_MODE          = 0x2a,
-       TUNER_AUD_MODE_AVAIL    = 0x2b,
-       TUNER_AUD_ANA_STD       = 0x2c,
-       TUNER_CUSTOM_PARAMETER  = 0x2f,
-
-       DVBT_TUNE_MODE_SEL      = 0x30,
-       DVBT_BANDW_CAP          = 0x31,
-       DVBT_BANDW_SEL          = 0x32,
-       DVBT_GUARD_INTERV_CAP   = 0x33,
-       DVBT_GUARD_INTERV_SEL   = 0x34,
-       DVBT_MODULATION_CAP     = 0x35,
-       DVBT_MODULATION_SEL     = 0x36,
-       DVBT_INNER_FEC_RATE_CAP = 0x37,
-       DVBT_INNER_FEC_RATE_SEL = 0x38,
-       DVBT_TRANS_MODE_CAP     = 0x39,
-       DVBT_TRANS_MODE_SEL     = 0x3a,
-       DVBT_SEARCH_RANG        = 0x3c,
-
-       TUNER_SETUP_ANALOG      = 0x40,
-       TUNER_SETUP_DIGITAL     = 0x41,
-       TUNER_SETUP_FM_RADIO    = 0x42,
-       TAKE_REQUEST            = 0x43, /* Take effect of the command */
-       PLAY_SERVICE            = 0x44, /* Play start or Play stop */
-       TUNER_STATUS            = 0x45,
-       TUNE_PROP_DVBT          = 0x46,
-       ERR_RATE_STATS          = 0x47,
-       TUNER_BER_RATE          = 0x48,
-
-       SCAN_CAPS               = 0x50,
-       SCAN_SETUP              = 0x51,
-       SCAN_SERVICE            = 0x52,
-       SCAN_STATS              = 0x53,
-
-       PID_SET                 = 0x58,
-       PID_UNSET               = 0x59,
-       PID_LIST                = 0x5a,
-
-       IRD_CAP                 = 0x60,
-       IRD_MODE_SEL            = 0x61,
-       IRD_SETUP               = 0x62,
-
-       PTM_MODE_CAP            = 0x70,
-       PTM_MODE_SEL            = 0x71,
-       PTM_SERVICE             = 0x72,
-       TUNER_REG_SCRIPT        = 0x73,
-       CMD_CHIP_RST            = 0x74,
-};
-
-enum tlg_bw {
-       TLG_BW_5 = 5,
-       TLG_BW_6 = 6,
-       TLG_BW_7 = 7,
-       TLG_BW_8 = 8,
-       TLG_BW_12 = 12,
-       TLG_BW_15 = 15
-};
-
-struct cmd_firmware_vers_s {
-       uint8_t  fw_rev_major;
-       uint8_t  fw_rev_minor;
-       uint16_t fw_patch;
-};
-#endif /* VENDOR_CMD_H_ */
index 7565871..faac2f4 100644 (file)
@@ -1017,6 +1017,12 @@ static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
        ret = ops->vidioc_querycap(file, fh, cap);
 
        cap->capabilities |= V4L2_CAP_EXT_PIX_FORMAT;
+       /*
+        * Drivers MUST fill in device_caps, so check for this and
+        * warn if it was forgotten.
+        */
+       WARN_ON(!(cap->capabilities & V4L2_CAP_DEVICE_CAPS) ||
+               !cap->device_caps);
        cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT;
 
        return ret;
index 028ba5d..687e9aa 100644 (file)
@@ -326,21 +326,27 @@ static int mic_vdev_info_show(struct seq_file *s, void *unused)
                        }
                        avail = vrh->vring.avail;
                        seq_printf(s, "avail flags 0x%x idx %d\n",
-                                  avail->flags, avail->idx & (num - 1));
+                                  vringh16_to_cpu(vrh, avail->flags),
+                                  vringh16_to_cpu(vrh, avail->idx) & (num - 1));
                        seq_printf(s, "avail flags 0x%x idx %d\n",
-                                  avail->flags, avail->idx);
+                                  vringh16_to_cpu(vrh, avail->flags),
+                                  vringh16_to_cpu(vrh, avail->idx));
                        for (j = 0; j < num; j++)
                                seq_printf(s, "avail ring[%d] %d\n",
                                           j, avail->ring[j]);
                        used = vrh->vring.used;
                        seq_printf(s, "used flags 0x%x idx %d\n",
-                                  used->flags, used->idx & (num - 1));
+                                  vringh16_to_cpu(vrh, used->flags),
+                                  vringh16_to_cpu(vrh, used->idx) & (num - 1));
                        seq_printf(s, "used flags 0x%x idx %d\n",
-                                  used->flags, used->idx);
+                                  vringh16_to_cpu(vrh, used->flags),
+                                  vringh16_to_cpu(vrh, used->idx));
                        for (j = 0; j < num; j++)
                                seq_printf(s, "used ring[%d] id %d len %d\n",
-                                          j, used->ring[j].id,
-                                          used->ring[j].len);
+                                          j, vringh32_to_cpu(vrh,
+                                                             used->ring[j].id),
+                                          vringh32_to_cpu(vrh,
+                                                          used->ring[j].len));
                }
        }
        mutex_unlock(&mdev->mic_mutex);
index 62aba9a..03d7c75 100644 (file)
@@ -2561,7 +2561,7 @@ static int atmci_runtime_resume(struct device *dev)
 static const struct dev_pm_ops atmci_dev_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
                                pm_runtime_force_resume)
-       SET_PM_RUNTIME_PM_OPS(atmci_runtime_suspend, atmci_runtime_resume, NULL)
+       SET_RUNTIME_PM_OPS(atmci_runtime_suspend, atmci_runtime_resume, NULL)
 };
 
 static struct platform_driver atmci_driver = {
index 868d0f6..705f334 100644 (file)
@@ -1060,10 +1060,14 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
                                     PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3);
                }
 
-               if ((netdev->features & NETIF_F_RXCSUM) && !csum_not_calc) {
-                       skb->csum = htons(checksum);
-                       skb->ip_summed = CHECKSUM_COMPLETE;
-               }
+               /* Hardware does not provide whole packet checksum. It only
+                * provides pseudo checksum. Since hw validates the packet
+                * checksum but not provide us the checksum value. use
+                * CHECSUM_UNNECESSARY.
+                */
+               if ((netdev->features & NETIF_F_RXCSUM) && tcp_udp_csum_ok &&
+                   ipv4_csum_ok)
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
 
                if (vlan_stripped)
                        __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tci);
index b935bf3..943cbd4 100644 (file)
@@ -171,9 +171,9 @@ int mlx4_check_port_params(struct mlx4_dev *dev,
 {
        int i;
 
-       for (i = 0; i < dev->caps.num_ports - 1; i++) {
-               if (port_type[i] != port_type[i + 1]) {
-                       if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_DPDP)) {
+       if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_DPDP)) {
+               for (i = 0; i < dev->caps.num_ports - 1; i++) {
+                       if (port_type[i] != port_type[i + 1]) {
                                mlx4_err(dev, "Only same port types supported on this HCA, aborting\n");
                                return -EINVAL;
                        }
index ab68446..da82991 100644 (file)
@@ -157,6 +157,8 @@ static const char *eqe_type_str(u8 type)
                return "MLX5_EVENT_TYPE_CMD";
        case MLX5_EVENT_TYPE_PAGE_REQUEST:
                return "MLX5_EVENT_TYPE_PAGE_REQUEST";
+       case MLX5_EVENT_TYPE_PAGE_FAULT:
+               return "MLX5_EVENT_TYPE_PAGE_FAULT";
        default:
                return "Unrecognized event";
        }
@@ -279,6 +281,11 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
                        }
                        break;
 
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+               case MLX5_EVENT_TYPE_PAGE_FAULT:
+                       mlx5_eq_pagefault(dev, eqe);
+                       break;
+#endif
 
                default:
                        mlx5_core_warn(dev, "Unhandled event 0x%x on EQ 0x%x\n",
@@ -446,8 +453,12 @@ void mlx5_eq_cleanup(struct mlx5_core_dev *dev)
 int mlx5_start_eqs(struct mlx5_core_dev *dev)
 {
        struct mlx5_eq_table *table = &dev->priv.eq_table;
+       u32 async_event_mask = MLX5_ASYNC_EVENT_MASK;
        int err;
 
+       if (dev->caps.gen.flags & MLX5_DEV_CAP_FLAG_ON_DMND_PG)
+               async_event_mask |= (1ull << MLX5_EVENT_TYPE_PAGE_FAULT);
+
        err = mlx5_create_map_eq(dev, &table->cmd_eq, MLX5_EQ_VEC_CMD,
                                 MLX5_NUM_CMD_EQE, 1ull << MLX5_EVENT_TYPE_CMD,
                                 "mlx5_cmd_eq", &dev->priv.uuari.uars[0]);
@@ -459,7 +470,7 @@ int mlx5_start_eqs(struct mlx5_core_dev *dev)
        mlx5_cmd_use_events(dev);
 
        err = mlx5_create_map_eq(dev, &table->async_eq, MLX5_EQ_VEC_ASYNC,
-                                MLX5_NUM_ASYNC_EQE, MLX5_ASYNC_EVENT_MASK,
+                                MLX5_NUM_ASYNC_EQE, async_event_mask,
                                 "mlx5_async_eq", &dev->priv.uuari.uars[0]);
        if (err) {
                mlx5_core_warn(dev, "failed to create async EQ %d\n", err);
index 087c4c7..06f9036 100644 (file)
@@ -69,6 +69,46 @@ int mlx5_cmd_query_hca_cap(struct mlx5_core_dev *dev, struct mlx5_caps *caps)
        return mlx5_core_get_caps(dev, caps, HCA_CAP_OPMOD_GET_CUR);
 }
 
+int mlx5_query_odp_caps(struct mlx5_core_dev *dev, struct mlx5_odp_caps *caps)
+{
+       u8 in[MLX5_ST_SZ_BYTES(query_hca_cap_in)];
+       int out_sz = MLX5_ST_SZ_BYTES(query_hca_cap_out);
+       void *out;
+       int err;
+
+       if (!(dev->caps.gen.flags & MLX5_DEV_CAP_FLAG_ON_DMND_PG))
+               return -ENOTSUPP;
+
+       memset(in, 0, sizeof(in));
+       out = kzalloc(out_sz, GFP_KERNEL);
+       if (!out)
+               return -ENOMEM;
+       MLX5_SET(query_hca_cap_in, in, opcode, MLX5_CMD_OP_QUERY_HCA_CAP);
+       MLX5_SET(query_hca_cap_in, in, op_mod, HCA_CAP_OPMOD_GET_ODP_CUR);
+       err = mlx5_cmd_exec(dev, in, sizeof(in), out, out_sz);
+       if (err)
+               goto out;
+
+       err = mlx5_cmd_status_to_err_v2(out);
+       if (err) {
+               mlx5_core_warn(dev, "query cur hca ODP caps failed, %d\n", err);
+               goto out;
+       }
+
+       memcpy(caps, MLX5_ADDR_OF(query_hca_cap_out, out, capability_struct),
+              sizeof(*caps));
+
+       mlx5_core_dbg(dev, "on-demand paging capabilities:\nrc: %08x\nuc: %08x\nud: %08x\n",
+               be32_to_cpu(caps->per_transport_caps.rc_odp_caps),
+               be32_to_cpu(caps->per_transport_caps.uc_odp_caps),
+               be32_to_cpu(caps->per_transport_caps.ud_odp_caps));
+
+out:
+       kfree(out);
+       return err;
+}
+EXPORT_SYMBOL(mlx5_query_odp_caps);
+
 int mlx5_cmd_init_hca(struct mlx5_core_dev *dev)
 {
        struct mlx5_cmd_init_hca_mbox_in in;
index 5261a2b..575d853 100644 (file)
@@ -88,6 +88,95 @@ void mlx5_rsc_event(struct mlx5_core_dev *dev, u32 rsn, int event_type)
        mlx5_core_put_rsc(common);
 }
 
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+void mlx5_eq_pagefault(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe)
+{
+       struct mlx5_eqe_page_fault *pf_eqe = &eqe->data.page_fault;
+       int qpn = be32_to_cpu(pf_eqe->flags_qpn) & MLX5_QPN_MASK;
+       struct mlx5_core_rsc_common *common = mlx5_get_rsc(dev, qpn);
+       struct mlx5_core_qp *qp =
+               container_of(common, struct mlx5_core_qp, common);
+       struct mlx5_pagefault pfault;
+
+       if (!qp) {
+               mlx5_core_warn(dev, "ODP event for non-existent QP %06x\n",
+                              qpn);
+               return;
+       }
+
+       pfault.event_subtype = eqe->sub_type;
+       pfault.flags = (be32_to_cpu(pf_eqe->flags_qpn) >> MLX5_QPN_BITS) &
+               (MLX5_PFAULT_REQUESTOR | MLX5_PFAULT_WRITE | MLX5_PFAULT_RDMA);
+       pfault.bytes_committed = be32_to_cpu(
+               pf_eqe->bytes_committed);
+
+       mlx5_core_dbg(dev,
+                     "PAGE_FAULT: subtype: 0x%02x, flags: 0x%02x,\n",
+                     eqe->sub_type, pfault.flags);
+
+       switch (eqe->sub_type) {
+       case MLX5_PFAULT_SUBTYPE_RDMA:
+               /* RDMA based event */
+               pfault.rdma.r_key =
+                       be32_to_cpu(pf_eqe->rdma.r_key);
+               pfault.rdma.packet_size =
+                       be16_to_cpu(pf_eqe->rdma.packet_length);
+               pfault.rdma.rdma_op_len =
+                       be32_to_cpu(pf_eqe->rdma.rdma_op_len);
+               pfault.rdma.rdma_va =
+                       be64_to_cpu(pf_eqe->rdma.rdma_va);
+               mlx5_core_dbg(dev,
+                             "PAGE_FAULT: qpn: 0x%06x, r_key: 0x%08x,\n",
+                             qpn, pfault.rdma.r_key);
+               mlx5_core_dbg(dev,
+                             "PAGE_FAULT: rdma_op_len: 0x%08x,\n",
+                             pfault.rdma.rdma_op_len);
+               mlx5_core_dbg(dev,
+                             "PAGE_FAULT: rdma_va: 0x%016llx,\n",
+                             pfault.rdma.rdma_va);
+               mlx5_core_dbg(dev,
+                             "PAGE_FAULT: bytes_committed: 0x%06x\n",
+                             pfault.bytes_committed);
+               break;
+
+       case MLX5_PFAULT_SUBTYPE_WQE:
+               /* WQE based event */
+               pfault.wqe.wqe_index =
+                       be16_to_cpu(pf_eqe->wqe.wqe_index);
+               pfault.wqe.packet_size =
+                       be16_to_cpu(pf_eqe->wqe.packet_length);
+               mlx5_core_dbg(dev,
+                             "PAGE_FAULT: qpn: 0x%06x, wqe_index: 0x%04x,\n",
+                             qpn, pfault.wqe.wqe_index);
+               mlx5_core_dbg(dev,
+                             "PAGE_FAULT: bytes_committed: 0x%06x\n",
+                             pfault.bytes_committed);
+               break;
+
+       default:
+               mlx5_core_warn(dev,
+                              "Unsupported page fault event sub-type: 0x%02hhx, QP %06x\n",
+                              eqe->sub_type, qpn);
+               /* Unsupported page faults should still be resolved by the
+                * page fault handler
+                */
+       }
+
+       if (qp->pfault_handler) {
+               qp->pfault_handler(qp, &pfault);
+       } else {
+               mlx5_core_err(dev,
+                             "ODP event for QP %08x, without a fault handler in QP\n",
+                             qpn);
+               /* Page fault will remain unresolved. QP will hang until it is
+                * destroyed
+                */
+       }
+
+       mlx5_core_put_rsc(common);
+}
+#endif
+
 int mlx5_core_create_qp(struct mlx5_core_dev *dev,
                        struct mlx5_core_qp *qp,
                        struct mlx5_create_qp_mbox_in *in,
@@ -322,3 +411,33 @@ int mlx5_core_xrcd_dealloc(struct mlx5_core_dev *dev, u32 xrcdn)
        return err;
 }
 EXPORT_SYMBOL_GPL(mlx5_core_xrcd_dealloc);
+
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+int mlx5_core_page_fault_resume(struct mlx5_core_dev *dev, u32 qpn,
+                               u8 flags, int error)
+{
+       struct mlx5_page_fault_resume_mbox_in in;
+       struct mlx5_page_fault_resume_mbox_out out;
+       int err;
+
+       memset(&in, 0, sizeof(in));
+       memset(&out, 0, sizeof(out));
+       in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_PAGE_FAULT_RESUME);
+       in.hdr.opmod = 0;
+       flags &= (MLX5_PAGE_FAULT_RESUME_REQUESTOR |
+                 MLX5_PAGE_FAULT_RESUME_WRITE     |
+                 MLX5_PAGE_FAULT_RESUME_RDMA);
+       flags |= (error ? MLX5_PAGE_FAULT_RESUME_ERROR : 0);
+       in.flags_qpn = cpu_to_be32((qpn & MLX5_QPN_MASK) |
+                                  (flags << MLX5_QPN_BITS));
+       err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+       if (err)
+               return err;
+
+       if (out.hdr.status)
+               err = mlx5_cmd_status_to_err(&out.hdr);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_core_page_fault_resume);
+#endif
index 45c408e..d2835bf 100644 (file)
@@ -1201,6 +1201,7 @@ static int vnet_handle_offloads(struct vnet_port *port, struct sk_buff *skb)
                segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO);
        if (IS_ERR(segs)) {
                dev->stats.tx_dropped++;
+               dev_kfree_skb_any(skb);
                return NETDEV_TX_OK;
        }
 
index d2ccd28..aa6a333 100644 (file)
@@ -2154,7 +2154,7 @@ static int trf7970a_resume(struct device *dev)
 }
 #endif
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int trf7970a_pm_runtime_suspend(struct device *dev)
 {
        struct spi_device *spi = container_of(dev, struct spi_device, dev);
index 4e489a8..6f4aef3 100644 (file)
@@ -318,7 +318,7 @@ static int omap_usb2_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 
 static int omap_usb2_runtime_suspend(struct device *dev)
 {
index c297b7a..1387b4d 100644 (file)
@@ -423,7 +423,7 @@ static int ti_pipe3_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 
 static int ti_pipe3_runtime_suspend(struct device *dev)
 {
index a2eabe6..638e797 100644 (file)
@@ -38,7 +38,8 @@ config ACER_WMI
 
 config ACERHDF
        tristate "Acer Aspire One temperature and fan driver"
-       depends on THERMAL && ACPI
+       depends on ACPI && THERMAL
+       select THERMAL_GOV_BANG_BANG
        ---help---
          This is a driver for Acer Aspire One netbooks. It allows to access
          the temperature sensor and to control the fan.
@@ -128,10 +129,10 @@ config DELL_WMI_AIO
          be called dell-wmi-aio.
 
 config DELL_SMO8800
-       tristate "Dell Latitude freefall driver (ACPI SMO8800/SMO8810)"
+       tristate "Dell Latitude freefall driver (ACPI SMO88XX)"
        depends on ACPI
        ---help---
-         Say Y here if you want to support SMO8800/SMO8810 freefall device
+         Say Y here if you want to support SMO88XX freefall devices
          on Dell Latitude laptops.
 
          To compile this driver as a module, choose M here: the module will
index aaf37c5..594c918 100644 (file)
@@ -50,7 +50,7 @@
  */
 #undef START_IN_KERNEL_MODE
 
-#define DRV_VER "0.5.26"
+#define DRV_VER "0.7.0"
 
 /*
  * According to the Atom N270 datasheet,
@@ -119,116 +119,152 @@ struct fancmd {
        u8 cmd_auto;
 };
 
+struct manualcmd {
+       u8 mreg;
+       u8 moff;
+};
+
+/* default register and command to disable fan in manual mode */
+static const struct manualcmd mcmd = {
+       .mreg = 0x94,
+       .moff = 0xff,
+};
+
 /* BIOS settings */
-struct bios_settings_t {
+struct bios_settings {
        const char *vendor;
        const char *product;
        const char *version;
-       unsigned char fanreg;
-       unsigned char tempreg;
+       u8 fanreg;
+       u8 tempreg;
        struct fancmd cmd;
+       int mcmd_enable;
 };
 
 /* Register addresses and values for different BIOS versions */
-static const struct bios_settings_t bios_tbl[] = {
+static const struct bios_settings bios_tbl[] = {
        /* AOA110 */
-       {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00} },
-       {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
-       {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0x00} },
-       {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0x00} },
-       {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0x00} },
-       {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0x00} },
-       {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x00} },
-       {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00} },
-       {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00} },
+       {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00}, 0},
+       {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00}, 0},
        /* AOA150 */
-       {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
-       {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00} },
+       {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x1f, 0x00}, 0},
+       {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00}, 0},
        /* LT1005u */
-       {"Acer", "LT-10Q", "v0.3310", 0x55, 0x58, {0x20, 0x00} },
+       {"Acer", "LT-10Q", "v0.3310", 0x55, 0x58, {0x20, 0x00}, 0},
        /* Acer 1410 */
-       {"Acer", "Aspire 1410", "v0.3108", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v0.3113", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v0.3115", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v0.3117", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v0.3119", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v0.3120", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v1.3204", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v1.3303", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v1.3308", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v1.3310", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1410", "v1.3314", 0x55, 0x58, {0x9e, 0x00} },
+       {"Acer", "Aspire 1410", "v0.3108", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v0.3113", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v0.3115", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v0.3117", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v0.3119", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v0.3120", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v1.3204", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v1.3303", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v1.3308", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v1.3310", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1410", "v1.3314", 0x55, 0x58, {0x9e, 0x00}, 0},
        /* Acer 1810xx */
-       {"Acer", "Aspire 1810TZ", "v0.3108", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v0.3108", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v0.3113", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v0.3113", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v0.3115", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v0.3115", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v0.3117", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v0.3117", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v0.3119", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v0.3119", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v0.3120", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v0.3120", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v1.3204", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v1.3204", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v1.3303", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v1.3303", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v1.3308", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v1.3308", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v1.3310", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v1.3310", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810TZ", "v1.3314", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1810T",  "v1.3314", 0x55, 0x58, {0x9e, 0x00} },
+       {"Acer", "Aspire 1810TZ", "v0.3108", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v0.3108", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v0.3113", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v0.3113", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v0.3115", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v0.3115", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v0.3117", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v0.3117", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v0.3119", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v0.3119", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v0.3120", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v0.3120", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v1.3204", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v1.3204", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v1.3303", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v1.3303", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v1.3308", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v1.3308", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v1.3310", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v1.3310", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810TZ", "v1.3314", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1810T",  "v1.3314", 0x55, 0x58, {0x9e, 0x00}, 0},
+       /* Acer 5755G */
+       {"Acer", "Aspire 5755G",  "V1.20",   0xab, 0xb4, {0x00, 0x08}, 0},
+       {"Acer", "Aspire 5755G",  "V1.21",   0xab, 0xb3, {0x00, 0x08}, 0},
+       /* Acer 521 */
+       {"Acer", "AO521", "V1.11", 0x55, 0x58, {0x1f, 0x00}, 0},
        /* Acer 531 */
-       {"Acer", "AO531h", "v0.3104", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AO531h", "v0.3201", 0x55, 0x58, {0x20, 0x00} },
-       {"Acer", "AO531h", "v0.3304", 0x55, 0x58, {0x20, 0x00} },
+       {"Acer", "AO531h", "v0.3104", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AO531h", "v0.3201", 0x55, 0x58, {0x20, 0x00}, 0},
+       {"Acer", "AO531h", "v0.3304", 0x55, 0x58, {0x20, 0x00}, 0},
        /* Acer 751 */
-       {"Acer", "AO751h", "V0.3212", 0x55, 0x58, {0x21, 0x00} },
+       {"Acer", "AO751h", "V0.3206", 0x55, 0x58, {0x21, 0x00}, 0},
+       {"Acer", "AO751h", "V0.3212", 0x55, 0x58, {0x21, 0x00}, 0},
+       /* Acer 753 */
+       {"Acer", "Aspire One 753", "V1.24", 0x93, 0xac, {0x14, 0x04}, 1},
        /* Acer 1825 */
-       {"Acer", "Aspire 1825PTZ", "V1.3118", 0x55, 0x58, {0x9e, 0x00} },
-       {"Acer", "Aspire 1825PTZ", "V1.3127", 0x55, 0x58, {0x9e, 0x00} },
+       {"Acer", "Aspire 1825PTZ", "V1.3118", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Acer", "Aspire 1825PTZ", "V1.3127", 0x55, 0x58, {0x9e, 0x00}, 0},
+       /* Acer Extensa 5420 */
+       {"Acer", "Extensa 5420", "V1.17", 0x93, 0xac, {0x14, 0x04}, 1},
+       /* Acer Aspire 5315 */
+       {"Acer", "Aspire 5315", "V1.19", 0x93, 0xac, {0x14, 0x04}, 1},
+       /* Acer Aspire 5739 */
+       {"Acer", "Aspire 5739G", "V1.3311", 0x55, 0x58, {0x20, 0x00}, 0},
        /* Acer TravelMate 7730 */
-       {"Acer", "TravelMate 7730G", "v0.3509", 0x55, 0x58, {0xaf, 0x00} },
+       {"Acer", "TravelMate 7730G", "v0.3509", 0x55, 0x58, {0xaf, 0x00}, 0},
+       /* Acer TravelMate TM8573T */
+       {"Acer", "TM8573T", "V1.13", 0x93, 0xa8, {0x14, 0x04}, 1},
        /* Gateway */
-       {"Gateway", "AOA110", "v0.3103",  0x55, 0x58, {0x21, 0x00} },
-       {"Gateway", "AOA150", "v0.3103",  0x55, 0x58, {0x20, 0x00} },
-       {"Gateway", "LT31",   "v1.3103",  0x55, 0x58, {0x9e, 0x00} },
-       {"Gateway", "LT31",   "v1.3201",  0x55, 0x58, {0x9e, 0x00} },
-       {"Gateway", "LT31",   "v1.3302",  0x55, 0x58, {0x9e, 0x00} },
-       {"Gateway", "LT31",   "v1.3303t", 0x55, 0x58, {0x9e, 0x00} },
+       {"Gateway", "AOA110", "v0.3103",  0x55, 0x58, {0x21, 0x00}, 0},
+       {"Gateway", "AOA150", "v0.3103",  0x55, 0x58, {0x20, 0x00}, 0},
+       {"Gateway", "LT31",   "v1.3103",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Gateway", "LT31",   "v1.3201",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Gateway", "LT31",   "v1.3302",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Gateway", "LT31",   "v1.3303t", 0x55, 0x58, {0x9e, 0x00}, 0},
        /* Packard Bell */
-       {"Packard Bell", "DOA150",  "v0.3104",  0x55, 0x58, {0x21, 0x00} },
-       {"Packard Bell", "DOA150",  "v0.3105",  0x55, 0x58, {0x20, 0x00} },
-       {"Packard Bell", "AOA110",  "v0.3105",  0x55, 0x58, {0x21, 0x00} },
-       {"Packard Bell", "AOA150",  "v0.3105",  0x55, 0x58, {0x20, 0x00} },
-       {"Packard Bell", "ENBFT",   "V1.3118",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "ENBFT",   "V1.3127",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v1.3303",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v0.3120",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v0.3108",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v0.3113",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v0.3115",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v0.3117",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v0.3119",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMU",   "v1.3204",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMA",   "v1.3201",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMA",   "v1.3302",  0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTMA",   "v1.3303t", 0x55, 0x58, {0x9e, 0x00} },
-       {"Packard Bell", "DOTVR46", "v1.3308",  0x55, 0x58, {0x9e, 0x00} },
+       {"Packard Bell", "DOA150",  "v0.3104",  0x55, 0x58, {0x21, 0x00}, 0},
+       {"Packard Bell", "DOA150",  "v0.3105",  0x55, 0x58, {0x20, 0x00}, 0},
+       {"Packard Bell", "AOA110",  "v0.3105",  0x55, 0x58, {0x21, 0x00}, 0},
+       {"Packard Bell", "AOA150",  "v0.3105",  0x55, 0x58, {0x20, 0x00}, 0},
+       {"Packard Bell", "ENBFT",   "V1.3118",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "ENBFT",   "V1.3127",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v1.3303",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v0.3120",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v0.3108",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v0.3113",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v0.3115",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v0.3117",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v0.3119",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMU",   "v1.3204",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMA",   "v1.3201",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMA",   "v1.3302",  0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTMA",   "v1.3303t", 0x55, 0x58, {0x9e, 0x00}, 0},
+       {"Packard Bell", "DOTVR46", "v1.3308",  0x55, 0x58, {0x9e, 0x00}, 0},
        /* pewpew-terminator */
-       {"", "", "", 0, 0, {0, 0} }
+       {"", "", "", 0, 0, {0, 0}, 0}
 };
 
-static const struct bios_settings_t *bios_cfg __read_mostly;
+static const struct bios_settings *bios_cfg __read_mostly;
+
+/*
+ * this struct is used to instruct thermal layer to use bang_bang instead of
+ * default governor for acerhdf
+ */
+static struct thermal_zone_params acerhdf_zone_params = {
+       .governor_name = "bang_bang",
+};
 
 static int acerhdf_get_temp(int *temp)
 {
@@ -275,6 +311,12 @@ static void acerhdf_change_fanstate(int state)
        fanstate = state;
 
        ec_write(bios_cfg->fanreg, cmd);
+
+       if (bios_cfg->mcmd_enable && state == ACERHDF_FAN_OFF) {
+               if (verbose)
+                       pr_notice("turning off fan manually\n");
+               ec_write(mcmd.mreg, mcmd.moff);
+       }
 }
 
 static void acerhdf_check_param(struct thermal_zone_device *thermal)
@@ -401,6 +443,21 @@ static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip,
 {
        if (trip == 0)
                *type = THERMAL_TRIP_ACTIVE;
+       else if (trip == 1)
+               *type = THERMAL_TRIP_CRITICAL;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int acerhdf_get_trip_hyst(struct thermal_zone_device *thermal, int trip,
+                                unsigned long *temp)
+{
+       if (trip != 0)
+               return -EINVAL;
+
+       *temp = fanon - fanoff;
 
        return 0;
 }
@@ -410,6 +467,10 @@ static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip,
 {
        if (trip == 0)
                *temp = fanon;
+       else if (trip == 1)
+               *temp = ACERHDF_TEMP_CRIT;
+       else
+               return -EINVAL;
 
        return 0;
 }
@@ -429,6 +490,7 @@ static struct thermal_zone_device_ops acerhdf_dev_ops = {
        .get_mode = acerhdf_get_mode,
        .set_mode = acerhdf_set_mode,
        .get_trip_type = acerhdf_get_trip_type,
+       .get_trip_hyst = acerhdf_get_trip_hyst,
        .get_trip_temp = acerhdf_get_trip_temp,
        .get_crit_temp = acerhdf_get_crit_temp,
 };
@@ -481,9 +543,7 @@ static int acerhdf_set_cur_state(struct thermal_cooling_device *cdev,
        }
 
        if (state == 0) {
-               /* turn fan off only if below fanoff temperature */
-               if ((cur_state == ACERHDF_FAN_AUTO) &&
-                   (cur_temp < fanoff))
+               if (cur_state == ACERHDF_FAN_AUTO)
                        acerhdf_change_fanstate(ACERHDF_FAN_OFF);
        } else {
                if (cur_state == ACERHDF_FAN_OFF)
@@ -558,7 +618,7 @@ static int str_starts_with(const char *str, const char *start)
 static int acerhdf_check_hardware(void)
 {
        char const *vendor, *version, *product;
-       const struct bios_settings_t *bt = NULL;
+       const struct bios_settings *bt = NULL;
 
        /* get BIOS data */
        vendor  = dmi_get_system_info(DMI_SYS_VENDOR);
@@ -660,12 +720,20 @@ static int acerhdf_register_thermal(void)
        if (IS_ERR(cl_dev))
                return -EINVAL;
 
-       thz_dev = thermal_zone_device_register("acerhdf", 1, 0, NULL,
-                                             &acerhdf_dev_ops, NULL, 0,
+       thz_dev = thermal_zone_device_register("acerhdf", 2, 0, NULL,
+                                             &acerhdf_dev_ops,
+                                             &acerhdf_zone_params, 0,
                                              (kernelmode) ? interval*1000 : 0);
        if (IS_ERR(thz_dev))
                return -EINVAL;
 
+       if (strcmp(thz_dev->governor->name,
+                               acerhdf_zone_params.governor_name)) {
+               pr_err("Didn't get thermal governor %s, perhaps not compiled into thermal subsystem.\n",
+                               acerhdf_zone_params.governor_name);
+               return -EINVAL;
+       }
+
        return 0;
 }
 
@@ -722,9 +790,15 @@ MODULE_ALIAS("dmi:*:*Acer*:pnAOA*:");
 MODULE_ALIAS("dmi:*:*Acer*:pnAO751h*:");
 MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1410*:");
 MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1810*:");
+MODULE_ALIAS("dmi:*:*Acer*:pnAspire*5755G:");
 MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1825PTZ:");
+MODULE_ALIAS("dmi:*:*Acer*:pnAO521*:");
 MODULE_ALIAS("dmi:*:*Acer*:pnAO531*:");
+MODULE_ALIAS("dmi:*:*Acer*:pnAspire*5739G:");
+MODULE_ALIAS("dmi:*:*Acer*:pnAspire*One*753:");
+MODULE_ALIAS("dmi:*:*Acer*:pnAspire*5315:");
 MODULE_ALIAS("dmi:*:*Acer*:TravelMate*7730G:");
+MODULE_ALIAS("dmi:*:*Acer*:TM8573T:");
 MODULE_ALIAS("dmi:*:*Gateway*:pnAOA*:");
 MODULE_ALIAS("dmi:*:*Gateway*:pnLT31*:");
 MODULE_ALIAS("dmi:*:*Packard*Bell*:pnAOA*:");
@@ -733,6 +807,7 @@ MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMU*:");
 MODULE_ALIAS("dmi:*:*Packard*Bell*:pnENBFT*:");
 MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMA*:");
 MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTVR46*:");
+MODULE_ALIAS("dmi:*:*Acer*:pnExtensa 5420*:");
 
 module_init(acerhdf_init);
 module_exit(acerhdf_exit);
index 05647f1..f71700e 100644 (file)
@@ -843,8 +843,7 @@ static int asus_backlight_init(struct asus_laptop *asus)
 
 static void asus_backlight_exit(struct asus_laptop *asus)
 {
-       if (asus->backlight_device)
-               backlight_device_unregister(asus->backlight_device);
+       backlight_device_unregister(asus->backlight_device);
        asus->backlight_device = NULL;
 }
 
index c1a6cd6..abdaed3 100644 (file)
@@ -189,6 +189,15 @@ static const struct dmi_system_id asus_quirks[] = {
                },
                .driver_data = &quirk_asus_wapf4,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. X551CA",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X551CA"),
+               },
+               .driver_data = &quirk_asus_wapf4,
+       },
        {
                .callback = dmi_matched,
                .ident = "ASUSTeK COMPUTER INC. X55A",
index 21fc932..7543a56 100644 (file)
@@ -1308,8 +1308,7 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus)
 
 static void asus_wmi_backlight_exit(struct asus_wmi *asus)
 {
-       if (asus->backlight_device)
-               backlight_device_unregister(asus->backlight_device);
+       backlight_device_unregister(asus->backlight_device);
 
        asus->backlight_device = NULL;
 }
index f6a28d7..9411eae 100644 (file)
@@ -2,9 +2,11 @@
  *  Driver for Dell laptop extras
  *
  *  Copyright (c) Red Hat <mjg@redhat.com>
+ *  Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
+ *  Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
  *
- *  Based on documentation in the libsmbios package, Copyright (C) 2005 Dell
- *  Inc.
+ *  Based on documentation in the libsmbios package:
+ *  Copyright (C) 2005-2014 Dell Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 #include "../../firmware/dcdbas.h"
 
 #define BRIGHTNESS_TOKEN 0x7d
+#define KBD_LED_OFF_TOKEN 0x01E1
+#define KBD_LED_ON_TOKEN 0x01E2
+#define KBD_LED_AUTO_TOKEN 0x01E3
+#define KBD_LED_AUTO_25_TOKEN 0x02EA
+#define KBD_LED_AUTO_50_TOKEN 0x02EB
+#define KBD_LED_AUTO_75_TOKEN 0x02EC
+#define KBD_LED_AUTO_100_TOKEN 0x02F6
 
 /* This structure will be modified by the firmware when we enter
  * system management mode, hence the volatiles */
@@ -62,6 +71,13 @@ struct calling_interface_structure {
 
 struct quirk_entry {
        u8 touchpad_led;
+
+       int needs_kbd_timeouts;
+       /*
+        * Ordered list of timeouts expressed in seconds.
+        * The list must end with -1
+        */
+       int kbd_timeouts[];
 };
 
 static struct quirk_entry *quirks;
@@ -76,6 +92,15 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
        return 1;
 }
 
+/*
+ * These values come from Windows utility provided by Dell. If any other value
+ * is used then BIOS silently set timeout to 0 without any error message.
+ */
+static struct quirk_entry quirk_dell_xps13_9333 = {
+       .needs_kbd_timeouts = 1,
+       .kbd_timeouts = { 0, 5, 15, 60, 5 * 60, 15 * 60, -1 },
+};
+
 static int da_command_address;
 static int da_command_code;
 static int da_num_tokens;
@@ -267,6 +292,15 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
                },
                .driver_data = &quirk_dell_vostro_v130,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "Dell XPS13 9333",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"),
+               },
+               .driver_data = &quirk_dell_xps13_9333,
+       },
        { }
 };
 
@@ -331,17 +365,29 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
        }
 }
 
-static int find_token_location(int tokenid)
+static int find_token_id(int tokenid)
 {
        int i;
+
        for (i = 0; i < da_num_tokens; i++) {
                if (da_tokens[i].tokenID == tokenid)
-                       return da_tokens[i].location;
+                       return i;
        }
 
        return -1;
 }
 
+static int find_token_location(int tokenid)
+{
+       int id;
+
+       id = find_token_id(tokenid);
+       if (id == -1)
+               return -1;
+
+       return da_tokens[id].location;
+}
+
 static struct calling_interface_buffer *
 dell_send_request(struct calling_interface_buffer *buffer, int class,
                  int select)
@@ -362,6 +408,20 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
        return buffer;
 }
 
+static inline int dell_smi_error(int value)
+{
+       switch (value) {
+       case 0: /* Completed successfully */
+               return 0;
+       case -1: /* Completed with error */
+               return -EIO;
+       case -2: /* Function not supported */
+               return -ENXIO;
+       default: /* Unknown error */
+               return -EINVAL;
+       }
+}
+
 /* Derived from information in DellWirelessCtl.cpp:
    Class 17, select 11 is radio control. It returns an array of 32-bit values.
 
@@ -563,7 +623,7 @@ static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
 {
        static bool extended;
 
-       if (str & 0x20)
+       if (str & I8042_STR_AUXDATA)
                return false;
 
        if (unlikely(data == 0xe0)) {
@@ -716,7 +776,7 @@ static int dell_send_intensity(struct backlight_device *bd)
        else
                dell_send_request(buffer, 1, 1);
 
-out:
+ out:
        release_buffer();
        return ret;
 }
@@ -740,7 +800,7 @@ static int dell_get_intensity(struct backlight_device *bd)
 
        ret = buffer->output[1];
 
-out:
+ out:
        release_buffer();
        return ret;
 }
@@ -789,6 +849,984 @@ static void touchpad_led_exit(void)
        led_classdev_unregister(&touchpad_led);
 }
 
+/*
+ * Derived from information in smbios-keyboard-ctl:
+ *
+ * cbClass 4
+ * cbSelect 11
+ * Keyboard illumination
+ * cbArg1 determines the function to be performed
+ *
+ * cbArg1 0x0 = Get Feature Information
+ *  cbRES1         Standard return codes (0, -1, -2)
+ *  cbRES2, word0  Bitmap of user-selectable modes
+ *     bit 0     Always off (All systems)
+ *     bit 1     Always on (Travis ATG, Siberia)
+ *     bit 2     Auto: ALS-based On; ALS-based Off (Travis ATG)
+ *     bit 3     Auto: ALS- and input-activity-based On; input-activity based Off
+ *     bit 4     Auto: Input-activity-based On; input-activity based Off
+ *     bit 5     Auto: Input-activity-based On (illumination level 25%); input-activity based Off
+ *     bit 6     Auto: Input-activity-based On (illumination level 50%); input-activity based Off
+ *     bit 7     Auto: Input-activity-based On (illumination level 75%); input-activity based Off
+ *     bit 8     Auto: Input-activity-based On (illumination level 100%); input-activity based Off
+ *     bits 9-15 Reserved for future use
+ *  cbRES2, byte2  Reserved for future use
+ *  cbRES2, byte3  Keyboard illumination type
+ *     0         Reserved
+ *     1         Tasklight
+ *     2         Backlight
+ *     3-255     Reserved for future use
+ *  cbRES3, byte0  Supported auto keyboard illumination trigger bitmap.
+ *     bit 0     Any keystroke
+ *     bit 1     Touchpad activity
+ *     bit 2     Pointing stick
+ *     bit 3     Any mouse
+ *     bits 4-7  Reserved for future use
+ *  cbRES3, byte1  Supported timeout unit bitmap
+ *     bit 0     Seconds
+ *     bit 1     Minutes
+ *     bit 2     Hours
+ *     bit 3     Days
+ *     bits 4-7  Reserved for future use
+ *  cbRES3, byte2  Number of keyboard light brightness levels
+ *  cbRES4, byte0  Maximum acceptable seconds value (0 if seconds not supported).
+ *  cbRES4, byte1  Maximum acceptable minutes value (0 if minutes not supported).
+ *  cbRES4, byte2  Maximum acceptable hours value (0 if hours not supported).
+ *  cbRES4, byte3  Maximum acceptable days value (0 if days not supported)
+ *
+ * cbArg1 0x1 = Get Current State
+ *  cbRES1         Standard return codes (0, -1, -2)
+ *  cbRES2, word0  Bitmap of current mode state
+ *     bit 0     Always off (All systems)
+ *     bit 1     Always on (Travis ATG, Siberia)
+ *     bit 2     Auto: ALS-based On; ALS-based Off (Travis ATG)
+ *     bit 3     Auto: ALS- and input-activity-based On; input-activity based Off
+ *     bit 4     Auto: Input-activity-based On; input-activity based Off
+ *     bit 5     Auto: Input-activity-based On (illumination level 25%); input-activity based Off
+ *     bit 6     Auto: Input-activity-based On (illumination level 50%); input-activity based Off
+ *     bit 7     Auto: Input-activity-based On (illumination level 75%); input-activity based Off
+ *     bit 8     Auto: Input-activity-based On (illumination level 100%); input-activity based Off
+ *     bits 9-15 Reserved for future use
+ *     Note: Only One bit can be set
+ *  cbRES2, byte2  Currently active auto keyboard illumination triggers.
+ *     bit 0     Any keystroke
+ *     bit 1     Touchpad activity
+ *     bit 2     Pointing stick
+ *     bit 3     Any mouse
+ *     bits 4-7  Reserved for future use
+ *  cbRES2, byte3  Current Timeout
+ *     bits 7:6  Timeout units indicator:
+ *     00b       Seconds
+ *     01b       Minutes
+ *     10b       Hours
+ *     11b       Days
+ *     bits 5:0  Timeout value (0-63) in sec/min/hr/day
+ *     NOTE: A value of 0 means always on (no timeout) if any bits of RES3 byte
+ *     are set upon return from the [Get feature information] call.
+ *  cbRES3, byte0  Current setting of ALS value that turns the light on or off.
+ *  cbRES3, byte1  Current ALS reading
+ *  cbRES3, byte2  Current keyboard light level.
+ *
+ * cbArg1 0x2 = Set New State
+ *  cbRES1         Standard return codes (0, -1, -2)
+ *  cbArg2, word0  Bitmap of current mode state
+ *     bit 0     Always off (All systems)
+ *     bit 1     Always on (Travis ATG, Siberia)
+ *     bit 2     Auto: ALS-based On; ALS-based Off (Travis ATG)
+ *     bit 3     Auto: ALS- and input-activity-based On; input-activity based Off
+ *     bit 4     Auto: Input-activity-based On; input-activity based Off
+ *     bit 5     Auto: Input-activity-based On (illumination level 25%); input-activity based Off
+ *     bit 6     Auto: Input-activity-based On (illumination level 50%); input-activity based Off
+ *     bit 7     Auto: Input-activity-based On (illumination level 75%); input-activity based Off
+ *     bit 8     Auto: Input-activity-based On (illumination level 100%); input-activity based Off
+ *     bits 9-15 Reserved for future use
+ *     Note: Only One bit can be set
+ *  cbArg2, byte2  Desired auto keyboard illumination triggers. Must remain inactive to allow
+ *                 keyboard to turn off automatically.
+ *     bit 0     Any keystroke
+ *     bit 1     Touchpad activity
+ *     bit 2     Pointing stick
+ *     bit 3     Any mouse
+ *     bits 4-7  Reserved for future use
+ *  cbArg2, byte3  Desired Timeout
+ *     bits 7:6  Timeout units indicator:
+ *     00b       Seconds
+ *     01b       Minutes
+ *     10b       Hours
+ *     11b       Days
+ *     bits 5:0  Timeout value (0-63) in sec/min/hr/day
+ *  cbArg3, byte0  Desired setting of ALS value that turns the light on or off.
+ *  cbArg3, byte2  Desired keyboard light level.
+ */
+
+
+enum kbd_timeout_unit {
+       KBD_TIMEOUT_SECONDS = 0,
+       KBD_TIMEOUT_MINUTES,
+       KBD_TIMEOUT_HOURS,
+       KBD_TIMEOUT_DAYS,
+};
+
+enum kbd_mode_bit {
+       KBD_MODE_BIT_OFF = 0,
+       KBD_MODE_BIT_ON,
+       KBD_MODE_BIT_ALS,
+       KBD_MODE_BIT_TRIGGER_ALS,
+       KBD_MODE_BIT_TRIGGER,
+       KBD_MODE_BIT_TRIGGER_25,
+       KBD_MODE_BIT_TRIGGER_50,
+       KBD_MODE_BIT_TRIGGER_75,
+       KBD_MODE_BIT_TRIGGER_100,
+};
+
+#define kbd_is_als_mode_bit(bit) \
+       ((bit) == KBD_MODE_BIT_ALS || (bit) == KBD_MODE_BIT_TRIGGER_ALS)
+#define kbd_is_trigger_mode_bit(bit) \
+       ((bit) >= KBD_MODE_BIT_TRIGGER_ALS && (bit) <= KBD_MODE_BIT_TRIGGER_100)
+#define kbd_is_level_mode_bit(bit) \
+       ((bit) >= KBD_MODE_BIT_TRIGGER_25 && (bit) <= KBD_MODE_BIT_TRIGGER_100)
+
+struct kbd_info {
+       u16 modes;
+       u8 type;
+       u8 triggers;
+       u8 levels;
+       u8 seconds;
+       u8 minutes;
+       u8 hours;
+       u8 days;
+};
+
+struct kbd_state {
+       u8 mode_bit;
+       u8 triggers;
+       u8 timeout_value;
+       u8 timeout_unit;
+       u8 als_setting;
+       u8 als_value;
+       u8 level;
+};
+
+static const int kbd_tokens[] = {
+       KBD_LED_OFF_TOKEN,
+       KBD_LED_AUTO_25_TOKEN,
+       KBD_LED_AUTO_50_TOKEN,
+       KBD_LED_AUTO_75_TOKEN,
+       KBD_LED_AUTO_100_TOKEN,
+       KBD_LED_ON_TOKEN,
+};
+
+static u16 kbd_token_bits;
+
+static struct kbd_info kbd_info;
+static bool kbd_als_supported;
+static bool kbd_triggers_supported;
+
+static u8 kbd_mode_levels[16];
+static int kbd_mode_levels_count;
+
+static u8 kbd_previous_level;
+static u8 kbd_previous_mode_bit;
+
+static bool kbd_led_present;
+
+/*
+ * NOTE: there are three ways to set the keyboard backlight level.
+ * First, via kbd_state.mode_bit (assigning KBD_MODE_BIT_TRIGGER_* value).
+ * Second, via kbd_state.level (assigning numerical value <= kbd_info.levels).
+ * Third, via SMBIOS tokens (KBD_LED_* in kbd_tokens)
+ *
+ * There are laptops which support only one of these methods. If we want to
+ * support as many machines as possible we need to implement all three methods.
+ * The first two methods use the kbd_state structure. The third uses SMBIOS
+ * tokens. If kbd_info.levels == 0, the machine does not support setting the
+ * keyboard backlight level via kbd_state.level.
+ */
+
+static int kbd_get_info(struct kbd_info *info)
+{
+       u8 units;
+       int ret;
+
+       get_buffer();
+
+       buffer->input[0] = 0x0;
+       dell_send_request(buffer, 4, 11);
+       ret = buffer->output[0];
+
+       if (ret) {
+               ret = dell_smi_error(ret);
+               goto out;
+       }
+
+       info->modes = buffer->output[1] & 0xFFFF;
+       info->type = (buffer->output[1] >> 24) & 0xFF;
+       info->triggers = buffer->output[2] & 0xFF;
+       units = (buffer->output[2] >> 8) & 0xFF;
+       info->levels = (buffer->output[2] >> 16) & 0xFF;
+
+       if (units & BIT(0))
+               info->seconds = (buffer->output[3] >> 0) & 0xFF;
+       if (units & BIT(1))
+               info->minutes = (buffer->output[3] >> 8) & 0xFF;
+       if (units & BIT(2))
+               info->hours = (buffer->output[3] >> 16) & 0xFF;
+       if (units & BIT(3))
+               info->days = (buffer->output[3] >> 24) & 0xFF;
+
+ out:
+       release_buffer();
+       return ret;
+}
+
+static unsigned int kbd_get_max_level(void)
+{
+       if (kbd_info.levels != 0)
+               return kbd_info.levels;
+       if (kbd_mode_levels_count > 0)
+               return kbd_mode_levels_count - 1;
+       return 0;
+}
+
+static int kbd_get_level(struct kbd_state *state)
+{
+       int i;
+
+       if (kbd_info.levels != 0)
+               return state->level;
+
+       if (kbd_mode_levels_count > 0) {
+               for (i = 0; i < kbd_mode_levels_count; ++i)
+                       if (kbd_mode_levels[i] == state->mode_bit)
+                               return i;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int kbd_set_level(struct kbd_state *state, u8 level)
+{
+       if (kbd_info.levels != 0) {
+               if (level != 0)
+                       kbd_previous_level = level;
+               if (state->level == level)
+                       return 0;
+               state->level = level;
+               if (level != 0 && state->mode_bit == KBD_MODE_BIT_OFF)
+                       state->mode_bit = kbd_previous_mode_bit;
+               else if (level == 0 && state->mode_bit != KBD_MODE_BIT_OFF) {
+                       kbd_previous_mode_bit = state->mode_bit;
+                       state->mode_bit = KBD_MODE_BIT_OFF;
+               }
+               return 0;
+       }
+
+       if (kbd_mode_levels_count > 0 && level < kbd_mode_levels_count) {
+               if (level != 0)
+                       kbd_previous_level = level;
+               state->mode_bit = kbd_mode_levels[level];
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int kbd_get_state(struct kbd_state *state)
+{
+       int ret;
+
+       get_buffer();
+
+       buffer->input[0] = 0x1;
+       dell_send_request(buffer, 4, 11);
+       ret = buffer->output[0];
+
+       if (ret) {
+               ret = dell_smi_error(ret);
+               goto out;
+       }
+
+       state->mode_bit = ffs(buffer->output[1] & 0xFFFF);
+       if (state->mode_bit != 0)
+               state->mode_bit--;
+
+       state->triggers = (buffer->output[1] >> 16) & 0xFF;
+       state->timeout_value = (buffer->output[1] >> 24) & 0x3F;
+       state->timeout_unit = (buffer->output[1] >> 30) & 0x3;
+       state->als_setting = buffer->output[2] & 0xFF;
+       state->als_value = (buffer->output[2] >> 8) & 0xFF;
+       state->level = (buffer->output[2] >> 16) & 0xFF;
+
+ out:
+       release_buffer();
+       return ret;
+}
+
+static int kbd_set_state(struct kbd_state *state)
+{
+       int ret;
+
+       get_buffer();
+       buffer->input[0] = 0x2;
+       buffer->input[1] = BIT(state->mode_bit) & 0xFFFF;
+       buffer->input[1] |= (state->triggers & 0xFF) << 16;
+       buffer->input[1] |= (state->timeout_value & 0x3F) << 24;
+       buffer->input[1] |= (state->timeout_unit & 0x3) << 30;
+       buffer->input[2] = state->als_setting & 0xFF;
+       buffer->input[2] |= (state->level & 0xFF) << 16;
+       dell_send_request(buffer, 4, 11);
+       ret = buffer->output[0];
+       release_buffer();
+
+       return dell_smi_error(ret);
+}
+
+static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
+{
+       int ret;
+
+       ret = kbd_set_state(state);
+       if (ret == 0)
+               return 0;
+
+       /*
+        * When setting the new state fails,try to restore the previous one.
+        * This is needed on some machines where BIOS sets a default state when
+        * setting a new state fails. This default state could be all off.
+        */
+
+       if (kbd_set_state(old))
+               pr_err("Setting old previous keyboard state failed\n");
+
+       return ret;
+}
+
+static int kbd_set_token_bit(u8 bit)
+{
+       int id;
+       int ret;
+
+       if (bit >= ARRAY_SIZE(kbd_tokens))
+               return -EINVAL;
+
+       id = find_token_id(kbd_tokens[bit]);
+       if (id == -1)
+               return -EINVAL;
+
+       get_buffer();
+       buffer->input[0] = da_tokens[id].location;
+       buffer->input[1] = da_tokens[id].value;
+       dell_send_request(buffer, 1, 0);
+       ret = buffer->output[0];
+       release_buffer();
+
+       return dell_smi_error(ret);
+}
+
+static int kbd_get_token_bit(u8 bit)
+{
+       int id;
+       int ret;
+       int val;
+
+       if (bit >= ARRAY_SIZE(kbd_tokens))
+               return -EINVAL;
+
+       id = find_token_id(kbd_tokens[bit]);
+       if (id == -1)
+               return -EINVAL;
+
+       get_buffer();
+       buffer->input[0] = da_tokens[id].location;
+       dell_send_request(buffer, 0, 0);
+       ret = buffer->output[0];
+       val = buffer->output[1];
+       release_buffer();
+
+       if (ret)
+               return dell_smi_error(ret);
+
+       return (val == da_tokens[id].value);
+}
+
+static int kbd_get_first_active_token_bit(void)
+{
+       int i;
+       int ret;
+
+       for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i) {
+               ret = kbd_get_token_bit(i);
+               if (ret == 1)
+                       return i;
+       }
+
+       return ret;
+}
+
+static int kbd_get_valid_token_counts(void)
+{
+       return hweight16(kbd_token_bits);
+}
+
+static inline int kbd_init_info(void)
+{
+       struct kbd_state state;
+       int ret;
+       int i;
+
+       ret = kbd_get_info(&kbd_info);
+       if (ret)
+               return ret;
+
+       kbd_get_state(&state);
+
+       /* NOTE: timeout value is stored in 6 bits so max value is 63 */
+       if (kbd_info.seconds > 63)
+               kbd_info.seconds = 63;
+       if (kbd_info.minutes > 63)
+               kbd_info.minutes = 63;
+       if (kbd_info.hours > 63)
+               kbd_info.hours = 63;
+       if (kbd_info.days > 63)
+               kbd_info.days = 63;
+
+       /* NOTE: On tested machines ON mode did not work and caused
+        *       problems (turned backlight off) so do not use it
+        */
+       kbd_info.modes &= ~BIT(KBD_MODE_BIT_ON);
+
+       kbd_previous_level = kbd_get_level(&state);
+       kbd_previous_mode_bit = state.mode_bit;
+
+       if (kbd_previous_level == 0 && kbd_get_max_level() != 0)
+               kbd_previous_level = 1;
+
+       if (kbd_previous_mode_bit == KBD_MODE_BIT_OFF) {
+               kbd_previous_mode_bit =
+                       ffs(kbd_info.modes & ~BIT(KBD_MODE_BIT_OFF));
+               if (kbd_previous_mode_bit != 0)
+                       kbd_previous_mode_bit--;
+       }
+
+       if (kbd_info.modes & (BIT(KBD_MODE_BIT_ALS) |
+                             BIT(KBD_MODE_BIT_TRIGGER_ALS)))
+               kbd_als_supported = true;
+
+       if (kbd_info.modes & (
+           BIT(KBD_MODE_BIT_TRIGGER_ALS) | BIT(KBD_MODE_BIT_TRIGGER) |
+           BIT(KBD_MODE_BIT_TRIGGER_25) | BIT(KBD_MODE_BIT_TRIGGER_50) |
+           BIT(KBD_MODE_BIT_TRIGGER_75) | BIT(KBD_MODE_BIT_TRIGGER_100)
+          ))
+               kbd_triggers_supported = true;
+
+       /* kbd_mode_levels[0] is reserved, see below */
+       for (i = 0; i < 16; ++i)
+               if (kbd_is_level_mode_bit(i) && (BIT(i) & kbd_info.modes))
+                       kbd_mode_levels[1 + kbd_mode_levels_count++] = i;
+
+       /*
+        * Find the first supported mode and assign to kbd_mode_levels[0].
+        * This should be 0 (off), but we cannot depend on the BIOS to
+        * support 0.
+        */
+       if (kbd_mode_levels_count > 0) {
+               for (i = 0; i < 16; ++i) {
+                       if (BIT(i) & kbd_info.modes) {
+                               kbd_mode_levels[0] = i;
+                               break;
+                       }
+               }
+               kbd_mode_levels_count++;
+       }
+
+       return 0;
+
+}
+
+static inline void kbd_init_tokens(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i)
+               if (find_token_id(kbd_tokens[i]) != -1)
+                       kbd_token_bits |= BIT(i);
+}
+
+static void kbd_init(void)
+{
+       int ret;
+
+       ret = kbd_init_info();
+       kbd_init_tokens();
+
+       if (kbd_token_bits != 0 || ret == 0)
+               kbd_led_present = true;
+}
+
+static ssize_t kbd_led_timeout_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct kbd_state new_state;
+       struct kbd_state state;
+       bool convert;
+       int value;
+       int ret;
+       char ch;
+       u8 unit;
+       int i;
+
+       ret = sscanf(buf, "%d %c", &value, &ch);
+       if (ret < 1)
+               return -EINVAL;
+       else if (ret == 1)
+               ch = 's';
+
+       if (value < 0)
+               return -EINVAL;
+
+       convert = false;
+
+       switch (ch) {
+       case 's':
+               if (value > kbd_info.seconds)
+                       convert = true;
+               unit = KBD_TIMEOUT_SECONDS;
+               break;
+       case 'm':
+               if (value > kbd_info.minutes)
+                       convert = true;
+               unit = KBD_TIMEOUT_MINUTES;
+               break;
+       case 'h':
+               if (value > kbd_info.hours)
+                       convert = true;
+               unit = KBD_TIMEOUT_HOURS;
+               break;
+       case 'd':
+               if (value > kbd_info.days)
+                       convert = true;
+               unit = KBD_TIMEOUT_DAYS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (quirks && quirks->needs_kbd_timeouts)
+               convert = true;
+
+       if (convert) {
+               /* Convert value from current units to seconds */
+               switch (unit) {
+               case KBD_TIMEOUT_DAYS:
+                       value *= 24;
+               case KBD_TIMEOUT_HOURS:
+                       value *= 60;
+               case KBD_TIMEOUT_MINUTES:
+                       value *= 60;
+                       unit = KBD_TIMEOUT_SECONDS;
+               }
+
+               if (quirks && quirks->needs_kbd_timeouts) {
+                       for (i = 0; quirks->kbd_timeouts[i] != -1; i++) {
+                               if (value <= quirks->kbd_timeouts[i]) {
+                                       value = quirks->kbd_timeouts[i];
+                                       break;
+                               }
+                       }
+               }
+
+               if (value <= kbd_info.seconds && kbd_info.seconds) {
+                       unit = KBD_TIMEOUT_SECONDS;
+               } else if (value / 60 <= kbd_info.minutes && kbd_info.minutes) {
+                       value /= 60;
+                       unit = KBD_TIMEOUT_MINUTES;
+               } else if (value / (60 * 60) <= kbd_info.hours && kbd_info.hours) {
+                       value /= (60 * 60);
+                       unit = KBD_TIMEOUT_HOURS;
+               } else if (value / (60 * 60 * 24) <= kbd_info.days && kbd_info.days) {
+                       value /= (60 * 60 * 24);
+                       unit = KBD_TIMEOUT_DAYS;
+               } else {
+                       return -EINVAL;
+               }
+       }
+
+       ret = kbd_get_state(&state);
+       if (ret)
+               return ret;
+
+       new_state = state;
+       new_state.timeout_value = value;
+       new_state.timeout_unit = unit;
+
+       ret = kbd_set_state_safe(&new_state, &state);
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+static ssize_t kbd_led_timeout_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct kbd_state state;
+       int ret;
+       int len;
+
+       ret = kbd_get_state(&state);
+       if (ret)
+               return ret;
+
+       len = sprintf(buf, "%d", state.timeout_value);
+
+       switch (state.timeout_unit) {
+       case KBD_TIMEOUT_SECONDS:
+               return len + sprintf(buf+len, "s\n");
+       case KBD_TIMEOUT_MINUTES:
+               return len + sprintf(buf+len, "m\n");
+       case KBD_TIMEOUT_HOURS:
+               return len + sprintf(buf+len, "h\n");
+       case KBD_TIMEOUT_DAYS:
+               return len + sprintf(buf+len, "d\n");
+       default:
+               return -EINVAL;
+       }
+
+       return len;
+}
+
+static DEVICE_ATTR(stop_timeout, S_IRUGO | S_IWUSR,
+                  kbd_led_timeout_show, kbd_led_timeout_store);
+
+static const char * const kbd_led_triggers[] = {
+       "keyboard",
+       "touchpad",
+       /*"trackstick"*/ NULL, /* NOTE: trackstick is just alias for touchpad */
+       "mouse",
+};
+
+static ssize_t kbd_led_triggers_store(struct device *dev,
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t count)
+{
+       struct kbd_state new_state;
+       struct kbd_state state;
+       bool triggers_enabled = false;
+       bool als_enabled = false;
+       bool disable_als = false;
+       bool enable_als = false;
+       int trigger_bit = -1;
+       char trigger[21];
+       int i, ret;
+
+       ret = sscanf(buf, "%20s", trigger);
+       if (ret != 1)
+               return -EINVAL;
+
+       if (trigger[0] != '+' && trigger[0] != '-')
+               return -EINVAL;
+
+       ret = kbd_get_state(&state);
+       if (ret)
+               return ret;
+
+       if (kbd_als_supported)
+               als_enabled = kbd_is_als_mode_bit(state.mode_bit);
+
+       if (kbd_triggers_supported)
+               triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit);
+
+       if (kbd_als_supported) {
+               if (strcmp(trigger, "+als") == 0) {
+                       if (als_enabled)
+                               return count;
+                       enable_als = true;
+               } else if (strcmp(trigger, "-als") == 0) {
+                       if (!als_enabled)
+                               return count;
+                       disable_als = true;
+               }
+       }
+
+       if (enable_als || disable_als) {
+               new_state = state;
+               if (enable_als) {
+                       if (triggers_enabled)
+                               new_state.mode_bit = KBD_MODE_BIT_TRIGGER_ALS;
+                       else
+                               new_state.mode_bit = KBD_MODE_BIT_ALS;
+               } else {
+                       if (triggers_enabled) {
+                               new_state.mode_bit = KBD_MODE_BIT_TRIGGER;
+                               kbd_set_level(&new_state, kbd_previous_level);
+                       } else {
+                               new_state.mode_bit = KBD_MODE_BIT_ON;
+                       }
+               }
+               if (!(kbd_info.modes & BIT(new_state.mode_bit)))
+                       return -EINVAL;
+               ret = kbd_set_state_safe(&new_state, &state);
+               if (ret)
+                       return ret;
+               kbd_previous_mode_bit = new_state.mode_bit;
+               return count;
+       }
+
+       if (kbd_triggers_supported) {
+               for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); ++i) {
+                       if (!(kbd_info.triggers & BIT(i)))
+                               continue;
+                       if (!kbd_led_triggers[i])
+                               continue;
+                       if (strcmp(trigger+1, kbd_led_triggers[i]) != 0)
+                               continue;
+                       if (trigger[0] == '+' &&
+                           triggers_enabled && (state.triggers & BIT(i)))
+                               return count;
+                       if (trigger[0] == '-' &&
+                           (!triggers_enabled || !(state.triggers & BIT(i))))
+                               return count;
+                       trigger_bit = i;
+                       break;
+               }
+       }
+
+       if (trigger_bit != -1) {
+               new_state = state;
+               if (trigger[0] == '+')
+                       new_state.triggers |= BIT(trigger_bit);
+               else {
+                       new_state.triggers &= ~BIT(trigger_bit);
+                       /* NOTE: trackstick bit (2) must be disabled when
+                        *       disabling touchpad bit (1), otherwise touchpad
+                        *       bit (1) will not be disabled */
+                       if (trigger_bit == 1)
+                               new_state.triggers &= ~BIT(2);
+               }
+               if ((kbd_info.triggers & new_state.triggers) !=
+                   new_state.triggers)
+                       return -EINVAL;
+               if (new_state.triggers && !triggers_enabled) {
+                       if (als_enabled)
+                               new_state.mode_bit = KBD_MODE_BIT_TRIGGER_ALS;
+                       else {
+                               new_state.mode_bit = KBD_MODE_BIT_TRIGGER;
+                               kbd_set_level(&new_state, kbd_previous_level);
+                       }
+               } else if (new_state.triggers == 0) {
+                       if (als_enabled)
+                               new_state.mode_bit = KBD_MODE_BIT_ALS;
+                       else
+                               kbd_set_level(&new_state, 0);
+               }
+               if (!(kbd_info.modes & BIT(new_state.mode_bit)))
+                       return -EINVAL;
+               ret = kbd_set_state_safe(&new_state, &state);
+               if (ret)
+                       return ret;
+               if (new_state.mode_bit != KBD_MODE_BIT_OFF)
+                       kbd_previous_mode_bit = new_state.mode_bit;
+               return count;
+       }
+
+       return -EINVAL;
+}
+
+static ssize_t kbd_led_triggers_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct kbd_state state;
+       bool triggers_enabled;
+       int level, i, ret;
+       int len = 0;
+
+       ret = kbd_get_state(&state);
+       if (ret)
+               return ret;
+
+       len = 0;
+
+       if (kbd_triggers_supported) {
+               triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit);
+               level = kbd_get_level(&state);
+               for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); ++i) {
+                       if (!(kbd_info.triggers & BIT(i)))
+                               continue;
+                       if (!kbd_led_triggers[i])
+                               continue;
+                       if ((triggers_enabled || level <= 0) &&
+                           (state.triggers & BIT(i)))
+                               buf[len++] = '+';
+                       else
+                               buf[len++] = '-';
+                       len += sprintf(buf+len, "%s ", kbd_led_triggers[i]);
+               }
+       }
+
+       if (kbd_als_supported) {
+               if (kbd_is_als_mode_bit(state.mode_bit))
+                       len += sprintf(buf+len, "+als ");
+               else
+                       len += sprintf(buf+len, "-als ");
+       }
+
+       if (len)
+               buf[len - 1] = '\n';
+
+       return len;
+}
+
+static DEVICE_ATTR(start_triggers, S_IRUGO | S_IWUSR,
+                  kbd_led_triggers_show, kbd_led_triggers_store);
+
+static ssize_t kbd_led_als_store(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t count)
+{
+       struct kbd_state state;
+       struct kbd_state new_state;
+       u8 setting;
+       int ret;
+
+       ret = kstrtou8(buf, 10, &setting);
+       if (ret)
+               return ret;
+
+       ret = kbd_get_state(&state);
+       if (ret)
+               return ret;
+
+       new_state = state;
+       new_state.als_setting = setting;
+
+       ret = kbd_set_state_safe(&new_state, &state);
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+static ssize_t kbd_led_als_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct kbd_state state;
+       int ret;
+
+       ret = kbd_get_state(&state);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%d\n", state.als_setting);
+}
+
+static DEVICE_ATTR(als_setting, S_IRUGO | S_IWUSR,
+                  kbd_led_als_show, kbd_led_als_store);
+
+static struct attribute *kbd_led_attrs[] = {
+       &dev_attr_stop_timeout.attr,
+       &dev_attr_start_triggers.attr,
+       &dev_attr_als_setting.attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(kbd_led);
+
+static enum led_brightness kbd_led_level_get(struct led_classdev *led_cdev)
+{
+       int ret;
+       u16 num;
+       struct kbd_state state;
+
+       if (kbd_get_max_level()) {
+               ret = kbd_get_state(&state);
+               if (ret)
+                       return 0;
+               ret = kbd_get_level(&state);
+               if (ret < 0)
+                       return 0;
+               return ret;
+       }
+
+       if (kbd_get_valid_token_counts()) {
+               ret = kbd_get_first_active_token_bit();
+               if (ret < 0)
+                       return 0;
+               for (num = kbd_token_bits; num != 0 && ret > 0; --ret)
+                       num &= num - 1; /* clear the first bit set */
+               if (num == 0)
+                       return 0;
+               return ffs(num) - 1;
+       }
+
+       pr_warn("Keyboard brightness level control not supported\n");
+       return 0;
+}
+
+static void kbd_led_level_set(struct led_classdev *led_cdev,
+                             enum led_brightness value)
+{
+       struct kbd_state state;
+       struct kbd_state new_state;
+       u16 num;
+
+       if (kbd_get_max_level()) {
+               if (kbd_get_state(&state))
+                       return;
+               new_state = state;
+               if (kbd_set_level(&new_state, value))
+                       return;
+               kbd_set_state_safe(&new_state, &state);
+               return;
+       }
+
+       if (kbd_get_valid_token_counts()) {
+               for (num = kbd_token_bits; num != 0 && value > 0; --value)
+                       num &= num - 1; /* clear the first bit set */
+               if (num == 0)
+                       return;
+               kbd_set_token_bit(ffs(num) - 1);
+               return;
+       }
+
+       pr_warn("Keyboard brightness level control not supported\n");
+}
+
+static struct led_classdev kbd_led = {
+       .name           = "dell::kbd_backlight",
+       .brightness_set = kbd_led_level_set,
+       .brightness_get = kbd_led_level_get,
+       .groups         = kbd_led_groups,
+};
+
+static int __init kbd_led_init(struct device *dev)
+{
+       kbd_init();
+       if (!kbd_led_present)
+               return -ENODEV;
+       kbd_led.max_brightness = kbd_get_max_level();
+       if (!kbd_led.max_brightness) {
+               kbd_led.max_brightness = kbd_get_valid_token_counts();
+               if (kbd_led.max_brightness)
+                       kbd_led.max_brightness--;
+       }
+       return led_classdev_register(dev, &kbd_led);
+}
+
+static void brightness_set_exit(struct led_classdev *led_cdev,
+                               enum led_brightness value)
+{
+       /* Don't change backlight level on exit */
+};
+
+static void kbd_led_exit(void)
+{
+       if (!kbd_led_present)
+               return;
+       kbd_led.brightness_set = brightness_set_exit;
+       led_classdev_unregister(&kbd_led);
+}
+
 static int __init dell_init(void)
 {
        int max_intensity = 0;
@@ -841,6 +1879,8 @@ static int __init dell_init(void)
        if (quirks && quirks->touchpad_led)
                touchpad_led_init(&platform_device->dev);
 
+       kbd_led_init(&platform_device->dev);
+
        dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
        if (dell_laptop_dir != NULL)
                debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
@@ -908,6 +1948,7 @@ static void __exit dell_exit(void)
        debugfs_remove_recursive(dell_laptop_dir);
        if (quirks && quirks->touchpad_led)
                touchpad_led_exit();
+       kbd_led_exit();
        i8042_remove_filter(dell_laptop_i8042_filter);
        cancel_delayed_work_sync(&dell_rfkill_work);
        backlight_device_unregister(dell_backlight_device);
@@ -924,5 +1965,7 @@ module_init(dell_init);
 module_exit(dell_exit);
 
 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
+MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
+MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
 MODULE_DESCRIPTION("Dell laptop driver");
 MODULE_LICENSE("GPL");
index a653716..0aec4fd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  dell-smo8800.c - Dell Latitude ACPI SMO8800/SMO8810 freefall sensor driver
+ *  dell-smo8800.c - Dell Latitude ACPI SMO88XX freefall sensor driver
  *
  *  Copyright (C) 2012 Sonal Santan <sonal.santan@gmail.com>
  *  Copyright (C) 2014 Pali Rohár <pali.rohar@gmail.com>
@@ -209,7 +209,13 @@ static int smo8800_remove(struct acpi_device *device)
 
 static const struct acpi_device_id smo8800_ids[] = {
        { "SMO8800", 0 },
+       { "SMO8801", 0 },
        { "SMO8810", 0 },
+       { "SMO8811", 0 },
+       { "SMO8820", 0 },
+       { "SMO8821", 0 },
+       { "SMO8830", 0 },
+       { "SMO8831", 0 },
        { "", 0 },
 };
 
@@ -228,6 +234,6 @@ static struct acpi_driver smo8800_driver = {
 
 module_acpi_driver(smo8800_driver);
 
-MODULE_DESCRIPTION("Dell Latitude freefall driver (ACPI SMO8800/SMO8810)");
+MODULE_DESCRIPTION("Dell Latitude freefall driver (ACPI SMO88XX)");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Sonal Santan, Pali Rohár");
index 25721bf..6512a06 100644 (file)
@@ -65,10 +65,8 @@ static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
        /* Battery health status button */
        { KE_KEY, 0xe007, { KEY_BATTERY } },
 
-       /* This is actually for all radios. Although physically a
-        * switch, the notification does not provide an indication of
-        * state and so it should be reported as a key */
-       { KE_KEY, 0xe008, { KEY_WLAN } },
+       /* Radio devices state change */
+       { KE_IGNORE, 0xe008, { KEY_RFKILL } },
 
        /* The next device is at offset 6, the active devices are at
           offset 8 and the attached devices at offset 10 */
@@ -145,57 +143,154 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
 
 static struct input_dev *dell_wmi_input_dev;
 
+static void dell_wmi_process_key(int reported_key)
+{
+       const struct key_entry *key;
+
+       key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
+                                               reported_key);
+       if (!key) {
+               pr_info("Unknown key %x pressed\n", reported_key);
+               return;
+       }
+
+       pr_debug("Key %x pressed\n", reported_key);
+
+       /* Don't report brightness notifications that will also come via ACPI */
+       if ((key->keycode == KEY_BRIGHTNESSUP ||
+            key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video)
+               return;
+
+       sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
+}
+
 static void dell_wmi_notify(u32 value, void *context)
 {
        struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
        union acpi_object *obj;
        acpi_status status;
+       acpi_size buffer_size;
+       u16 *buffer_entry, *buffer_end;
+       int len, i;
 
        status = wmi_get_event_data(value, &response);
        if (status != AE_OK) {
-               pr_info("bad event status 0x%x\n", status);
+               pr_warn("bad event status 0x%x\n", status);
                return;
        }
 
        obj = (union acpi_object *)response.pointer;
+       if (!obj) {
+               pr_warn("no response\n");
+               return;
+       }
 
-       if (obj && obj->type == ACPI_TYPE_BUFFER) {
-               const struct key_entry *key;
-               int reported_key;
-               u16 *buffer_entry = (u16 *)obj->buffer.pointer;
-               int buffer_size = obj->buffer.length/2;
-
-               if (buffer_size >= 2 && dell_new_hk_type && buffer_entry[1] != 0x10) {
-                       pr_info("Received unknown WMI event (0x%x)\n",
-                               buffer_entry[1]);
-                       kfree(obj);
-                       return;
-               }
+       if (obj->type != ACPI_TYPE_BUFFER) {
+               pr_warn("bad response type %x\n", obj->type);
+               kfree(obj);
+               return;
+       }
 
-               if (buffer_size >= 3 && (dell_new_hk_type || buffer_entry[1] == 0x0))
-                       reported_key = (int)buffer_entry[2];
+       pr_debug("Received WMI event (%*ph)\n",
+               obj->buffer.length, obj->buffer.pointer);
+
+       buffer_entry = (u16 *)obj->buffer.pointer;
+       buffer_size = obj->buffer.length/2;
+
+       if (!dell_new_hk_type) {
+               if (buffer_size >= 3 && buffer_entry[1] == 0x0)
+                       dell_wmi_process_key(buffer_entry[2]);
                else if (buffer_size >= 2)
-                       reported_key = (int)buffer_entry[1] & 0xffff;
-               else {
+                       dell_wmi_process_key(buffer_entry[1]);
+               else
                        pr_info("Received unknown WMI event\n");
-                       kfree(obj);
-                       return;
+               kfree(obj);
+               return;
+       }
+
+       buffer_end = buffer_entry + buffer_size;
+
+       while (buffer_entry < buffer_end) {
+
+               len = buffer_entry[0];
+               if (len == 0)
+                       break;
+
+               len++;
+
+               if (buffer_entry + len > buffer_end) {
+                       pr_warn("Invalid length of WMI event\n");
+                       break;
                }
 
-               key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
-                                                       reported_key);
-               if (!key) {
-                       pr_info("Unknown key %x pressed\n", reported_key);
-               } else if ((key->keycode == KEY_BRIGHTNESSUP ||
-                           key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) {
-                       /* Don't report brightness notifications that will also
-                        * come via ACPI */
-                       ;
-               } else {
-                       sparse_keymap_report_entry(dell_wmi_input_dev, key,
-                                                  1, true);
+               pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry);
+
+               switch (buffer_entry[1]) {
+               case 0x00:
+                       for (i = 2; i < len; ++i) {
+                               switch (buffer_entry[i]) {
+                               case 0xe043:
+                                       /* NIC Link is Up */
+                                       pr_debug("NIC Link is Up\n");
+                                       break;
+                               case 0xe044:
+                                       /* NIC Link is Down */
+                                       pr_debug("NIC Link is Down\n");
+                                       break;
+                               case 0xe045:
+                                       /* Unknown event but defined in DSDT */
+                               default:
+                                       /* Unknown event */
+                                       pr_info("Unknown WMI event type 0x00: "
+                                               "0x%x\n", (int)buffer_entry[i]);
+                                       break;
+                               }
+                       }
+                       break;
+               case 0x10:
+                       /* Keys pressed */
+                       for (i = 2; i < len; ++i)
+                               dell_wmi_process_key(buffer_entry[i]);
+                       break;
+               case 0x11:
+                       for (i = 2; i < len; ++i) {
+                               switch (buffer_entry[i]) {
+                               case 0xfff0:
+                                       /* Battery unplugged */
+                                       pr_debug("Battery unplugged\n");
+                                       break;
+                               case 0xfff1:
+                                       /* Battery inserted */
+                                       pr_debug("Battery inserted\n");
+                                       break;
+                               case 0x01e1:
+                               case 0x02ea:
+                               case 0x02eb:
+                               case 0x02ec:
+                               case 0x02f6:
+                                       /* Keyboard backlight level changed */
+                                       pr_debug("Keyboard backlight level "
+                                                "changed\n");
+                                       break;
+                               default:
+                                       /* Unknown event */
+                                       pr_info("Unknown WMI event type 0x11: "
+                                               "0x%x\n", (int)buffer_entry[i]);
+                                       break;
+                               }
+                       }
+                       break;
+               default:
+                       /* Unknown event */
+                       pr_info("Unknown WMI event type 0x%x\n",
+                               (int)buffer_entry[1]);
+                       break;
                }
+
+               buffer_entry += len;
+
        }
+
        kfree(obj);
 }
 
@@ -213,11 +308,16 @@ static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
        for (i = 0; i < hotkey_num; i++) {
                const struct dell_bios_keymap_entry *bios_entry =
                                        &dell_bios_hotkey_table->keymap[i];
-               keymap[i].type = KE_KEY;
-               keymap[i].code = bios_entry->scancode;
-               keymap[i].keycode = bios_entry->keycode < 256 ?
+               u16 keycode = bios_entry->keycode < 256 ?
                                    bios_to_linux_keycode[bios_entry->keycode] :
                                    KEY_RESERVED;
+
+               if (keycode == KEY_KBDILLUMTOGGLE)
+                       keymap[i].type = KE_IGNORE;
+               else
+                       keymap[i].type = KE_KEY;
+               keymap[i].code = bios_entry->scancode;
+               keymap[i].keycode = keycode;
        }
 
        keymap[hotkey_num].type = KE_END;
index 5a54d35..844c209 100644 (file)
@@ -417,8 +417,7 @@ static ssize_t cpufv_disabled_store(struct device *dev,
        switch (value) {
        case 0:
                if (eeepc->cpufv_disabled)
-                       pr_warn("cpufv enabled (not officially supported "
-                               "on this model)\n");
+                       pr_warn("cpufv enabled (not officially supported on this model)\n");
                eeepc->cpufv_disabled = false;
                return count;
        case 1:
@@ -580,59 +579,58 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
        mutex_lock(&eeepc->hotplug_lock);
        pci_lock_rescan_remove();
 
-       if (eeepc->hotplug_slot) {
-               port = acpi_get_pci_dev(handle);
-               if (!port) {
-                       pr_warning("Unable to find port\n");
-                       goto out_unlock;
-               }
+       if (!eeepc->hotplug_slot)
+               goto out_unlock;
 
-               bus = port->subordinate;
+       port = acpi_get_pci_dev(handle);
+       if (!port) {
+               pr_warning("Unable to find port\n");
+               goto out_unlock;
+       }
 
-               if (!bus) {
-                       pr_warn("Unable to find PCI bus 1?\n");
-                       goto out_put_dev;
-               }
+       bus = port->subordinate;
 
-               if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
-                       pr_err("Unable to read PCI config space?\n");
-                       goto out_put_dev;
-               }
+       if (!bus) {
+               pr_warn("Unable to find PCI bus 1?\n");
+               goto out_put_dev;
+       }
+
+       if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
+               pr_err("Unable to read PCI config space?\n");
+               goto out_put_dev;
+       }
 
-               absent = (l == 0xffffffff);
+       absent = (l == 0xffffffff);
 
-               if (blocked != absent) {
-                       pr_warn("BIOS says wireless lan is %s, "
-                               "but the pci device is %s\n",
-                               blocked ? "blocked" : "unblocked",
-                               absent ? "absent" : "present");
-                       pr_warn("skipped wireless hotplug as probably "
-                               "inappropriate for this model\n");
+       if (blocked != absent) {
+               pr_warn("BIOS says wireless lan is %s, but the pci device is %s\n",
+                       blocked ? "blocked" : "unblocked",
+                       absent ? "absent" : "present");
+               pr_warn("skipped wireless hotplug as probably inappropriate for this model\n");
+               goto out_put_dev;
+       }
+
+       if (!blocked) {
+               dev = pci_get_slot(bus, 0);
+               if (dev) {
+                       /* Device already present */
+                       pci_dev_put(dev);
                        goto out_put_dev;
                }
-
-               if (!blocked) {
-                       dev = pci_get_slot(bus, 0);
-                       if (dev) {
-                               /* Device already present */
-                               pci_dev_put(dev);
-                               goto out_put_dev;
-                       }
-                       dev = pci_scan_single_device(bus, 0);
-                       if (dev) {
-                               pci_bus_assign_resources(bus);
-                               pci_bus_add_device(dev);
-                       }
-               } else {
-                       dev = pci_get_slot(bus, 0);
-                       if (dev) {
-                               pci_stop_and_remove_bus_device(dev);
-                               pci_dev_put(dev);
-                       }
+               dev = pci_scan_single_device(bus, 0);
+               if (dev) {
+                       pci_bus_assign_resources(bus);
+                       pci_bus_add_device(dev);
+               }
+       } else {
+               dev = pci_get_slot(bus, 0);
+               if (dev) {
+                       pci_stop_and_remove_bus_device(dev);
+                       pci_dev_put(dev);
                }
-out_put_dev:
-               pci_dev_put(port);
        }
+out_put_dev:
+       pci_dev_put(port);
 
 out_unlock:
        pci_unlock_rescan_remove();
@@ -821,11 +819,15 @@ static int eeepc_new_rfkill(struct eeepc_laptop *eeepc,
        return 0;
 }
 
+static char EEEPC_RFKILL_NODE_1[] = "\\_SB.PCI0.P0P5";
+static char EEEPC_RFKILL_NODE_2[] = "\\_SB.PCI0.P0P6";
+static char EEEPC_RFKILL_NODE_3[] = "\\_SB.PCI0.P0P7";
+
 static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
 {
-       eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
-       eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
-       eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
+       eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_1);
+       eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_2);
+       eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_3);
        if (eeepc->wlan_rfkill) {
                rfkill_unregister(eeepc->wlan_rfkill);
                rfkill_destroy(eeepc->wlan_rfkill);
@@ -897,9 +899,9 @@ static int eeepc_rfkill_init(struct eeepc_laptop *eeepc)
        if (result == -EBUSY)
                result = 0;
 
-       eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
-       eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
-       eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
+       eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_1);
+       eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_2);
+       eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_3);
 
 exit:
        if (result && result != -ENODEV)
@@ -915,7 +917,7 @@ static int eeepc_hotk_thaw(struct device *device)
        struct eeepc_laptop *eeepc = dev_get_drvdata(device);
 
        if (eeepc->wlan_rfkill) {
-               bool wlan;
+               int wlan;
 
                /*
                 * Work around bios bug - acpi _PTS turns off the wireless led
@@ -923,7 +925,8 @@ static int eeepc_hotk_thaw(struct device *device)
                 * we should kick it ourselves in case hibernation is aborted.
                 */
                wlan = get_acpi(eeepc, CM_ASL_WLAN);
-               set_acpi(eeepc, CM_ASL_WLAN, wlan);
+               if (wlan >= 0)
+                       set_acpi(eeepc, CM_ASL_WLAN, wlan);
        }
 
        return 0;
@@ -935,9 +938,9 @@ static int eeepc_hotk_restore(struct device *device)
 
        /* Refresh both wlan rfkill state and pci hotplug */
        if (eeepc->wlan_rfkill) {
-               eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P5");
-               eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P6");
-               eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P7");
+               eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_1);
+               eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_2);
+               eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_3);
        }
 
        if (eeepc->bluetooth_rfkill)
@@ -977,18 +980,28 @@ static struct platform_driver platform_driver = {
 #define EEEPC_EC_SFB0      0xD0
 #define EEEPC_EC_FAN_CTRL  (EEEPC_EC_SFB0 + 3) /* Byte containing SF25  */
 
+static inline int eeepc_pwm_to_lmsensors(int value)
+{
+       return value * 255 / 100;
+}
+
+static inline int eeepc_lmsensors_to_pwm(int value)
+{
+       value = clamp_val(value, 0, 255);
+       return value * 100 / 255;
+}
+
 static int eeepc_get_fan_pwm(void)
 {
        u8 value = 0;
 
        ec_read(EEEPC_EC_FAN_PWM, &value);
-       return value * 255 / 100;
+       return eeepc_pwm_to_lmsensors(value);
 }
 
 static void eeepc_set_fan_pwm(int value)
 {
-       value = clamp_val(value, 0, 255);
-       value = value * 100 / 255;
+       value = eeepc_lmsensors_to_pwm(value);
        ec_write(EEEPC_EC_FAN_PWM, value);
 }
 
@@ -1002,15 +1015,19 @@ static int eeepc_get_fan_rpm(void)
        return high << 8 | low;
 }
 
+#define EEEPC_EC_FAN_CTRL_BIT  0x02
+#define EEEPC_FAN_CTRL_MANUAL  1
+#define EEEPC_FAN_CTRL_AUTO    2
+
 static int eeepc_get_fan_ctrl(void)
 {
        u8 value = 0;
 
        ec_read(EEEPC_EC_FAN_CTRL, &value);
-       if (value & 0x02)
-               return 1; /* manual */
+       if (value & EEEPC_EC_FAN_CTRL_BIT)
+               return EEEPC_FAN_CTRL_MANUAL;
        else
-               return 2; /* automatic */
+               return EEEPC_FAN_CTRL_AUTO;
 }
 
 static void eeepc_set_fan_ctrl(int manual)
@@ -1018,10 +1035,10 @@ static void eeepc_set_fan_ctrl(int manual)
        u8 value = 0;
 
        ec_read(EEEPC_EC_FAN_CTRL, &value);
-       if (manual == 1)
-               value |= 0x02;
+       if (manual == EEEPC_FAN_CTRL_MANUAL)
+               value |= EEEPC_EC_FAN_CTRL_BIT;
        else
-               value &= ~0x02;
+               value &= ~EEEPC_EC_FAN_CTRL_BIT;
        ec_write(EEEPC_EC_FAN_CTRL, value);
 }
 
@@ -1156,8 +1173,7 @@ static int eeepc_backlight_init(struct eeepc_laptop *eeepc)
 
 static void eeepc_backlight_exit(struct eeepc_laptop *eeepc)
 {
-       if (eeepc->backlight_device)
-               backlight_device_unregister(eeepc->backlight_device);
+       backlight_device_unregister(eeepc->backlight_device);
        eeepc->backlight_device = NULL;
 }
 
@@ -1216,7 +1232,7 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc)
 static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event)
 {
        if (!eeepc->inputdev)
-               return ;
+               return;
        if (!sparse_keymap_report_event(eeepc->inputdev, event, 1, true))
                pr_info("Unknown key %x pressed\n", event);
 }
@@ -1224,6 +1240,7 @@ static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event)
 static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
 {
        struct eeepc_laptop *eeepc = acpi_driver_data(device);
+       int old_brightness, new_brightness;
        u16 count;
 
        if (event > ACPI_MAX_SYS_NOTIFY)
@@ -1234,34 +1251,32 @@ static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
                                        count);
 
        /* Brightness events are special */
-       if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) {
-
-               /* Ignore them completely if the acpi video driver is used */
-               if (eeepc->backlight_device != NULL) {
-                       int old_brightness, new_brightness;
-
-                       /* Update the backlight device. */
-                       old_brightness = eeepc_backlight_notify(eeepc);
-
-                       /* Convert event to keypress (obsolescent hack) */
-                       new_brightness = event - NOTIFY_BRN_MIN;
-
-                       if (new_brightness < old_brightness) {
-                               event = NOTIFY_BRN_MIN; /* brightness down */
-                       } else if (new_brightness > old_brightness) {
-                               event = NOTIFY_BRN_MAX; /* brightness up */
-                       } else {
-                               /*
-                               * no change in brightness - already at min/max,
-                               * event will be desired value (or else ignored)
-                               */
-                       }
-                       eeepc_input_notify(eeepc, event);
-               }
-       } else {
-               /* Everything else is a bona-fide keypress event */
+       if (event < NOTIFY_BRN_MIN || event > NOTIFY_BRN_MAX) {
                eeepc_input_notify(eeepc, event);
+               return;
+       }
+
+       /* Ignore them completely if the acpi video driver is used */
+       if (!eeepc->backlight_device)
+               return;
+
+       /* Update the backlight device. */
+       old_brightness = eeepc_backlight_notify(eeepc);
+
+       /* Convert event to keypress (obsolescent hack) */
+       new_brightness = event - NOTIFY_BRN_MIN;
+
+       if (new_brightness < old_brightness) {
+               event = NOTIFY_BRN_MIN; /* brightness down */
+       } else if (new_brightness > old_brightness) {
+               event = NOTIFY_BRN_MAX; /* brightness up */
+       } else {
+               /*
+                * no change in brightness - already at min/max,
+                * event will be desired value (or else ignored)
+                */
        }
+       eeepc_input_notify(eeepc, event);
 }
 
 static void eeepc_dmi_check(struct eeepc_laptop *eeepc)
@@ -1293,8 +1308,8 @@ static void eeepc_dmi_check(struct eeepc_laptop *eeepc)
         */
        if (strcmp(model, "701") == 0 || strcmp(model, "702") == 0) {
                eeepc->cpufv_disabled = true;
-               pr_info("model %s does not officially support setting cpu "
-                       "speed\n", model);
+               pr_info("model %s does not officially support setting cpu speed\n",
+                       model);
                pr_info("cpufv disabled to avoid instability\n");
        }
 
@@ -1320,8 +1335,8 @@ static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name)
           Check if cm_getv[cm] works and, if yes, assume cm should be set. */
        if (!(eeepc->cm_supported & (1 << cm))
            && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
-               pr_info("%s (%x) not reported by BIOS,"
-                       " enabling anyway\n", name, 1 << cm);
+               pr_info("%s (%x) not reported by BIOS, enabling anyway\n",
+                       name, 1 << cm);
                eeepc->cm_supported |= 1 << cm;
        }
 }
index be55bd7..7c21c1c 100644 (file)
@@ -1153,8 +1153,7 @@ fail_hotkey1:
 fail_hotkey:
        platform_driver_unregister(&fujitsupf_driver);
 fail_backlight:
-       if (fujitsu->bl_device)
-               backlight_device_unregister(fujitsu->bl_device);
+       backlight_device_unregister(fujitsu->bl_device);
 fail_sysfs_group:
        sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
                           &fujitsupf_attribute_group);
@@ -1178,8 +1177,7 @@ static void __exit fujitsu_cleanup(void)
 
        platform_driver_unregister(&fujitsupf_driver);
 
-       if (fujitsu->bl_device)
-               backlight_device_unregister(fujitsu->bl_device);
+       backlight_device_unregister(fujitsu->bl_device);
 
        sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
                           &fujitsupf_attribute_group);
index 415348f..4e4cc8b 100644 (file)
@@ -85,6 +85,9 @@ static int hpwl_add(struct acpi_device *device)
        int err;
 
        err = hp_wireless_input_setup();
+       if (err)
+               pr_err("Failed to setup hp wireless hotkeys\n");
+
        return err;
 }
 
index 6bec745..10ce6cb 100644 (file)
@@ -246,6 +246,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = {
        AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap),
        AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap),
        AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted),
+       AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted),
        { NULL, }
 /* Laptop models without axis info (yet):
  * "NC6910" "HP Compaq 6910"
index c860eac..b3d419a 100644 (file)
@@ -729,8 +729,7 @@ static int ideapad_backlight_init(struct ideapad_private *priv)
 
 static void ideapad_backlight_exit(struct ideapad_private *priv)
 {
-       if (priv->blightdev)
-               backlight_device_unregister(priv->blightdev);
+       backlight_device_unregister(priv->blightdev);
        priv->blightdev = NULL;
 }
 
index ecd36e3..e2065e0 100644 (file)
@@ -33,7 +33,7 @@
  * performance by allocating more power or thermal budget to the CPU or GPU
  * based on available headroom and activity.
  *
- * The basic algorithm is driven by a 5s moving average of tempurature.  If
+ * The basic algorithm is driven by a 5s moving average of temperature.  If
  * thermal headroom is available, the CPU and/or GPU power clamps may be
  * adjusted upwards.  If we hit the thermal ceiling or a thermal trigger,
  * we scale back the clamp.  Aside from trigger events (when we're critically
index 0afaaef..a4a4258 100644 (file)
@@ -271,8 +271,7 @@ static int oaktrail_backlight_init(void)
 
 static void oaktrail_backlight_exit(void)
 {
-       if (oaktrail_bl_device)
-               backlight_device_unregister(oaktrail_bl_device);
+       backlight_device_unregister(oaktrail_bl_device);
 }
 
 static int oaktrail_probe(struct platform_device *pdev)
index a3f06cb..0859877 100644 (file)
@@ -820,7 +820,7 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
 {
        static bool extended;
 
-       if (str & 0x20)
+       if (str & I8042_STR_AUXDATA)
                return false;
 
        /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/
index 70222f2..6d2bac0 100644 (file)
@@ -354,8 +354,7 @@ static void __exit msi_wmi_exit(void)
                sparse_keymap_free(msi_wmi_input_dev);
                input_unregister_device(msi_wmi_input_dev);
        }
-       if (backlight)
-               backlight_device_unregister(backlight);
+       backlight_device_unregister(backlight);
 }
 
 module_init(msi_wmi_init);
index a1a0fd7..6dd1c0e 100644 (file)
@@ -3140,8 +3140,7 @@ static void sony_nc_backlight_setup(void)
 
 static void sony_nc_backlight_cleanup(void)
 {
-       if (sony_bl_props.dev)
-               backlight_device_unregister(sony_bl_props.dev);
+       backlight_device_unregister(sony_bl_props.dev);
 }
 
 static int sony_nc_add(struct acpi_device *device)
@@ -3716,8 +3715,7 @@ static void sony_pic_detect_device_type(struct sony_pic_dev *dev)
        dev->event_types = type2_events;
 
 out:
-       if (pcidev)
-               pci_dev_put(pcidev);
+       pci_dev_put(pcidev);
 
        pr_info("detected Type%d model\n",
                dev->model == SONYPI_DEVICE_TYPE1 ? 1 :
index 6414cfe..c3d11fa 100644 (file)
@@ -6557,6 +6557,17 @@ static struct ibm_struct brightness_driver_data = {
  * bits 3-0 (volume).  Other bits in NVRAM may have other functions,
  * such as bit 7 which is used to detect repeated presses of MUTE,
  * and we leave them unchanged.
+ *
+ * On newer Lenovo ThinkPads, the EC can automatically change the volume
+ * in response to user input.  Unfortunately, this rarely works well.
+ * The laptop changes the state of its internal MUTE gate and, on some
+ * models, sends KEY_MUTE, causing any user code that responds to the
+ * mute button to get confused.  The hardware MUTE gate is also
+ * unnecessary, since user code can handle the mute button without
+ * kernel or EC help.
+ *
+ * To avoid confusing userspace, we simply disable all EC-based mute
+ * and volume controls when possible.
  */
 
 #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT
@@ -6611,11 +6622,21 @@ enum tpacpi_volume_capabilities {
        TPACPI_VOL_CAP_MAX
 };
 
+enum tpacpi_mute_btn_mode {
+       TP_EC_MUTE_BTN_LATCH  = 0,      /* Mute mutes; up/down unmutes */
+       /* We don't know what mode 1 is. */
+       TP_EC_MUTE_BTN_NONE   = 2,      /* Mute and up/down are just keys */
+       TP_EC_MUTE_BTN_TOGGLE = 3,      /* Mute toggles; up/down unmutes */
+};
+
 static enum tpacpi_volume_access_mode volume_mode =
        TPACPI_VOL_MODE_MAX;
 
 static enum tpacpi_volume_capabilities volume_capabilities;
 static bool volume_control_allowed;
+static bool software_mute_requested = true;
+static bool software_mute_active;
+static int software_mute_orig_mode;
 
 /*
  * Used to syncronize writers to TP_EC_AUDIO and
@@ -6633,6 +6654,8 @@ static void tpacpi_volume_checkpoint_nvram(void)
                return;
        if (!volume_control_allowed)
                return;
+       if (software_mute_active)
+               return;
 
        vdbg_printk(TPACPI_DBG_MIXER,
                "trying to checkpoint mixer state to NVRAM...\n");
@@ -6694,6 +6717,12 @@ static int volume_set_status_ec(const u8 status)
 
        dbg_printk(TPACPI_DBG_MIXER, "set EC mixer to 0x%02x\n", status);
 
+       /*
+        * On X200s, and possibly on others, it can take a while for
+        * reads to become correct.
+        */
+       msleep(1);
+
        return 0;
 }
 
@@ -6776,6 +6805,57 @@ unlock:
        return rc;
 }
 
+static int volume_set_software_mute(bool startup)
+{
+       int result;
+
+       if (!tpacpi_is_lenovo())
+               return -ENODEV;
+
+       if (startup) {
+               if (!acpi_evalf(ec_handle, &software_mute_orig_mode,
+                               "HAUM", "qd"))
+                       return -EIO;
+
+               dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
+                           "Initial HAUM setting was %d\n",
+                           software_mute_orig_mode);
+       }
+
+       if (!acpi_evalf(ec_handle, &result, "SAUM", "qdd",
+                       (int)TP_EC_MUTE_BTN_NONE))
+               return -EIO;
+
+       if (result != TP_EC_MUTE_BTN_NONE)
+               pr_warn("Unexpected SAUM result %d\n",
+                       result);
+
+       /*
+        * In software mute mode, the standard codec controls take
+        * precendence, so we unmute the ThinkPad HW switch at
+        * startup.  Just on case there are SAUM-capable ThinkPads
+        * with level controls, set max HW volume as well.
+        */
+       if (tp_features.mixer_no_level_control)
+               result = volume_set_mute(false);
+       else
+               result = volume_set_status(TP_EC_VOLUME_MAX);
+
+       if (result != 0)
+               pr_warn("Failed to unmute the HW mute switch\n");
+
+       return 0;
+}
+
+static void volume_exit_software_mute(void)
+{
+       int r;
+
+       if (!acpi_evalf(ec_handle, &r, "SAUM", "qdd", software_mute_orig_mode)
+           || r != software_mute_orig_mode)
+               pr_warn("Failed to restore mute mode\n");
+}
+
 static int volume_alsa_set_volume(const u8 vol)
 {
        dbg_printk(TPACPI_DBG_MIXER,
@@ -6883,7 +6963,12 @@ static void volume_suspend(void)
 
 static void volume_resume(void)
 {
-       volume_alsa_notify_change();
+       if (software_mute_active) {
+               if (volume_set_software_mute(false) < 0)
+                       pr_warn("Failed to restore software mute\n");
+       } else {
+               volume_alsa_notify_change();
+       }
 }
 
 static void volume_shutdown(void)
@@ -6899,6 +6984,9 @@ static void volume_exit(void)
        }
 
        tpacpi_volume_checkpoint_nvram();
+
+       if (software_mute_active)
+               volume_exit_software_mute();
 }
 
 static int __init volume_create_alsa_mixer(void)
@@ -7083,16 +7171,20 @@ static int __init volume_init(struct ibm_init_struct *iibm)
                        "mute is supported, volume control is %s\n",
                        str_supported(!tp_features.mixer_no_level_control));
 
-       rc = volume_create_alsa_mixer();
-       if (rc) {
-               pr_err("Could not create the ALSA mixer interface\n");
-               return rc;
-       }
+       if (software_mute_requested && volume_set_software_mute(true) == 0) {
+               software_mute_active = true;
+       } else {
+               rc = volume_create_alsa_mixer();
+               if (rc) {
+                       pr_err("Could not create the ALSA mixer interface\n");
+                       return rc;
+               }
 
-       pr_info("Console audio control enabled, mode: %s\n",
-               (volume_control_allowed) ?
-                       "override (read/write)" :
-                       "monitor (read only)");
+               pr_info("Console audio control enabled, mode: %s\n",
+                       (volume_control_allowed) ?
+                               "override (read/write)" :
+                               "monitor (read only)");
+       }
 
        vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
                "registering volume hotkeys as change notification\n");
@@ -9089,6 +9181,10 @@ MODULE_PARM_DESC(volume_control,
                 "Enables software override for the console audio "
                 "control when true");
 
+module_param_named(software_mute, software_mute_requested, bool, 0444);
+MODULE_PARM_DESC(software_mute,
+                "Request full software mute control");
+
 /* ALSA module API parameters */
 module_param_named(index, alsa_index, int, 0444);
 MODULE_PARM_DESC(index, "ALSA index for the ACPI EC Mixer");
index ab6151f..fc34a71 100644 (file)
@@ -186,6 +186,7 @@ static struct toshiba_acpi_dev *toshiba_acpi;
 
 static const struct acpi_device_id toshiba_device_ids[] = {
        {"TOS6200", 0},
+       {"TOS6207", 0},
        {"TOS6208", 0},
        {"TOS1900", 0},
        {"", 0},
@@ -928,9 +929,7 @@ static int lcd_proc_open(struct inode *inode, struct file *file)
 
 static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value)
 {
-       u32 in[TCI_WORDS] = { HCI_SET, HCI_LCD_BRIGHTNESS, 0, 0, 0, 0 };
-       u32 out[TCI_WORDS];
-       acpi_status status;
+       u32 hci_result;
 
        if (dev->tr_backlight_supported) {
                bool enable = !value;
@@ -941,20 +940,9 @@ static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value)
                        value--;
        }
 
-       in[2] = value << HCI_LCD_BRIGHTNESS_SHIFT;
-       status = tci_raw(dev, in, out);
-       if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
-               pr_err("ACPI call to set brightness failed");
-               return -EIO;
-       }
-       /* Extra check for "incomplete" backlight method, where the AML code
-        * doesn't check for HCI_SET or HCI_GET and returns TOS_SUCCESS,
-        * the actual brightness, and in some cases the max brightness.
-        */
-       if (out[2] > 0  || out[3] == 0xE000)
-               return -ENODEV;
-
-       return out[0] == TOS_SUCCESS ? 0 : -EIO;
+       value = value << HCI_LCD_BRIGHTNESS_SHIFT;
+       hci_result = hci_write1(dev, HCI_LCD_BRIGHTNESS, value);
+       return hci_result == TOS_SUCCESS ? 0 : -EIO;
 }
 
 static int set_lcd_status(struct backlight_device *bd)
@@ -1406,12 +1394,6 @@ static ssize_t toshiba_kbd_bl_mode_store(struct device *dev,
                if (ret)
                        return ret;
 
-               /* Update sysfs entries on successful mode change*/
-               ret = sysfs_update_group(&toshiba->acpi_dev->dev.kobj,
-                                        &toshiba_attr_group);
-               if (ret)
-                       return ret;
-
                toshiba->kbd_mode = mode;
        }
 
@@ -1586,10 +1568,32 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
        return exists ? attr->mode : 0;
 }
 
+/*
+ * Hotkeys
+ */
+static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev)
+{
+       acpi_status status;
+       u32 result;
+
+       status = acpi_evaluate_object(dev->acpi_dev->handle,
+                                     "ENAB", NULL, NULL);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       result = hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE);
+       if (result == TOS_FAILURE)
+               return -EIO;
+       else if (result == TOS_NOT_SUPPORTED)
+               return -ENODEV;
+
+       return 0;
+}
+
 static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
                                      struct serio *port)
 {
-       if (str & 0x20)
+       if (str & I8042_STR_AUXDATA)
                return false;
 
        if (unlikely(data == 0xe0))
@@ -1648,9 +1652,45 @@ static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev,
                pr_info("Unknown key %x\n", scancode);
 }
 
+static void toshiba_acpi_process_hotkeys(struct toshiba_acpi_dev *dev)
+{
+       u32 hci_result, value;
+       int retries = 3;
+       int scancode;
+
+       if (dev->info_supported) {
+               scancode = toshiba_acpi_query_hotkey(dev);
+               if (scancode < 0)
+                       pr_err("Failed to query hotkey event\n");
+               else if (scancode != 0)
+                       toshiba_acpi_report_hotkey(dev, scancode);
+       } else if (dev->system_event_supported) {
+               do {
+                       hci_result = hci_read1(dev, HCI_SYSTEM_EVENT, &value);
+                       switch (hci_result) {
+                       case TOS_SUCCESS:
+                               toshiba_acpi_report_hotkey(dev, (int)value);
+                               break;
+                       case TOS_NOT_SUPPORTED:
+                               /*
+                                * This is a workaround for an unresolved
+                                * issue on some machines where system events
+                                * sporadically become disabled.
+                                */
+                               hci_result =
+                                       hci_write1(dev, HCI_SYSTEM_EVENT, 1);
+                               pr_notice("Re-enabled hotkeys\n");
+                               /* fall through */
+                       default:
+                               retries--;
+                               break;
+                       }
+               } while (retries && hci_result != TOS_FIFO_EMPTY);
+       }
+}
+
 static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
 {
-       acpi_status status;
        acpi_handle ec_handle;
        int error;
        u32 hci_result;
@@ -1677,7 +1717,6 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
         * supported, so if it's present set up an i8042 key filter
         * for this purpose.
         */
-       status = AE_ERROR;
        ec_handle = ec_get_handle();
        if (ec_handle && acpi_has_method(ec_handle, "NTFY")) {
                INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);
@@ -1708,10 +1747,9 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
                goto err_remove_filter;
        }
 
-       status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL);
-       if (ACPI_FAILURE(status)) {
+       error = toshiba_acpi_enable_hotkeys(dev);
+       if (error) {
                pr_info("Unable to enable hotkeys\n");
-               error = -ENODEV;
                goto err_remove_filter;
        }
 
@@ -1721,7 +1759,6 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
                goto err_remove_filter;
        }
 
-       hci_result = hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE);
        return 0;
 
  err_remove_filter:
@@ -1810,8 +1847,7 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
                rfkill_destroy(dev->bt_rfk);
        }
 
-       if (dev->backlight_dev)
-               backlight_device_unregister(dev->backlight_dev);
+       backlight_device_unregister(dev->backlight_dev);
 
        if (dev->illumination_supported)
                led_classdev_unregister(&dev->led_dev);
@@ -1967,41 +2003,29 @@ error:
 static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
 {
        struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
-       u32 hci_result, value;
-       int retries = 3;
-       int scancode;
-
-       if (event != 0x80)
-               return;
+       int ret;
 
-       if (dev->info_supported) {
-               scancode = toshiba_acpi_query_hotkey(dev);
-               if (scancode < 0)
-                       pr_err("Failed to query hotkey event\n");
-               else if (scancode != 0)
-                       toshiba_acpi_report_hotkey(dev, scancode);
-       } else if (dev->system_event_supported) {
-               do {
-                       hci_result = hci_read1(dev, HCI_SYSTEM_EVENT, &value);
-                       switch (hci_result) {
-                       case TOS_SUCCESS:
-                               toshiba_acpi_report_hotkey(dev, (int)value);
-                               break;
-                       case TOS_NOT_SUPPORTED:
-                               /*
-                                * This is a workaround for an unresolved
-                                * issue on some machines where system events
-                                * sporadically become disabled.
-                                */
-                               hci_result =
-                                       hci_write1(dev, HCI_SYSTEM_EVENT, 1);
-                               pr_notice("Re-enabled hotkeys\n");
-                               /* fall through */
-                       default:
-                               retries--;
-                               break;
-                       }
-               } while (retries && hci_result != TOS_FIFO_EMPTY);
+       switch (event) {
+       case 0x80: /* Hotkeys and some system events */
+               toshiba_acpi_process_hotkeys(dev);
+               break;
+       case 0x92: /* Keyboard backlight mode changed */
+               /* Update sysfs entries */
+               ret = sysfs_update_group(&acpi_dev->dev.kobj,
+                                        &toshiba_attr_group);
+               if (ret)
+                       pr_err("Unable to update sysfs entries\n");
+               break;
+       case 0x81: /* Unknown */
+       case 0x82: /* Unknown */
+       case 0x83: /* Unknown */
+       case 0x8c: /* Unknown */
+       case 0x8e: /* Unknown */
+       case 0x8f: /* Unknown */
+       case 0x90: /* Unknown */
+       default:
+               pr_info("Unknown event received %x\n", event);
+               break;
        }
 }
 
@@ -2020,16 +2044,12 @@ static int toshiba_acpi_suspend(struct device *device)
 static int toshiba_acpi_resume(struct device *device)
 {
        struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device));
-       u32 result;
-       acpi_status status;
+       int error;
 
        if (dev->hotkey_dev) {
-               status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB",
-                               NULL, NULL);
-               if (ACPI_FAILURE(status))
+               error = toshiba_acpi_enable_hotkeys(dev);
+               if (error)
                        pr_info("Unable to re-enable hotkeys\n");
-
-               result = hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE);
        }
 
        return 0;
index 62c15af..7773249 100644 (file)
@@ -951,8 +951,6 @@ static int pm2xxx_wall_charger_suspend(struct device *dev)
 
 #endif
 
-#ifdef CONFIG_PM_RUNTIME
-
 static int  pm2xxx_runtime_suspend(struct device *dev)
 {
        struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
@@ -977,8 +975,6 @@ static int  pm2xxx_runtime_resume(struct device *dev)
        return 0;
 }
 
-#endif
-
 static const struct dev_pm_ops pm2xxx_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(pm2xxx_wall_charger_suspend,
                pm2xxx_wall_charger_resume)
index 7454498..9e43ae1 100644 (file)
@@ -213,8 +213,6 @@ static int scsi_bus_restore(struct device *dev)
 
 #endif /* CONFIG_PM_SLEEP */
 
-#ifdef CONFIG_PM_RUNTIME
-
 static int sdev_runtime_suspend(struct device *dev)
 {
        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
@@ -332,14 +330,6 @@ void scsi_autopm_put_host(struct Scsi_Host *shost)
        pm_runtime_put_sync(&shost->shost_gendev);
 }
 
-#else
-
-#define scsi_runtime_suspend   NULL
-#define scsi_runtime_resume    NULL
-#define scsi_runtime_idle      NULL
-
-#endif /* CONFIG_PM_RUNTIME */
-
 const struct dev_pm_ops scsi_bus_pm_ops = {
        .prepare =              scsi_bus_prepare,
        .suspend =              scsi_bus_suspend,
index 2dc4a83..e3902fc 100644 (file)
@@ -155,8 +155,7 @@ static inline void scsi_netlink_exit(void) {}
 /* scsi_pm.c */
 #ifdef CONFIG_PM
 extern const struct dev_pm_ops scsi_bus_pm_ops;
-#endif
-#ifdef CONFIG_PM_RUNTIME
+
 extern void scsi_autopm_get_target(struct scsi_target *);
 extern void scsi_autopm_put_target(struct scsi_target *);
 extern int scsi_autopm_get_host(struct Scsi_Host *);
@@ -166,7 +165,7 @@ static inline void scsi_autopm_get_target(struct scsi_target *t) {}
 static inline void scsi_autopm_put_target(struct scsi_target *t) {}
 static inline int scsi_autopm_get_host(struct Scsi_Host *h) { return 0; }
 static inline void scsi_autopm_put_host(struct Scsi_Host *h) {}
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM */
 
 extern struct async_domain scsi_sd_pm_domain;
 extern struct async_domain scsi_sd_probe_domain;
index 955ed55..d15eaa4 100644 (file)
@@ -62,12 +62,7 @@ static int ufshcd_pci_resume(struct device *dev)
 {
        return ufshcd_system_resume(dev_get_drvdata(dev));
 }
-#else
-#define ufshcd_pci_suspend     NULL
-#define ufshcd_pci_resume      NULL
-#endif /* CONFIG_PM */
 
-#ifdef CONFIG_PM_RUNTIME
 static int ufshcd_pci_runtime_suspend(struct device *dev)
 {
        return ufshcd_runtime_suspend(dev_get_drvdata(dev));
@@ -80,11 +75,13 @@ static int ufshcd_pci_runtime_idle(struct device *dev)
 {
        return ufshcd_runtime_idle(dev_get_drvdata(dev));
 }
-#else /* !CONFIG_PM_RUNTIME */
+#else /* !CONFIG_PM */
+#define ufshcd_pci_suspend     NULL
+#define ufshcd_pci_resume      NULL
 #define ufshcd_pci_runtime_suspend     NULL
 #define ufshcd_pci_runtime_resume      NULL
 #define ufshcd_pci_runtime_idle        NULL
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM */
 
 /**
  * ufshcd_pci_shutdown - main function to put the controller in reset state
index 0c030ad..7db9564 100644 (file)
@@ -261,12 +261,7 @@ static int ufshcd_pltfrm_resume(struct device *dev)
 {
        return ufshcd_system_resume(dev_get_drvdata(dev));
 }
-#else
-#define ufshcd_pltfrm_suspend  NULL
-#define ufshcd_pltfrm_resume   NULL
-#endif
 
-#ifdef CONFIG_PM_RUNTIME
 static int ufshcd_pltfrm_runtime_suspend(struct device *dev)
 {
        return ufshcd_runtime_suspend(dev_get_drvdata(dev));
@@ -279,11 +274,13 @@ static int ufshcd_pltfrm_runtime_idle(struct device *dev)
 {
        return ufshcd_runtime_idle(dev_get_drvdata(dev));
 }
-#else /* !CONFIG_PM_RUNTIME */
+#else /* !CONFIG_PM */
+#define ufshcd_pltfrm_suspend  NULL
+#define ufshcd_pltfrm_resume   NULL
 #define ufshcd_pltfrm_runtime_suspend  NULL
 #define ufshcd_pltfrm_runtime_resume   NULL
 #define ufshcd_pltfrm_runtime_idle     NULL
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM */
 
 static void ufshcd_pltfrm_shutdown(struct platform_device *pdev)
 {
index e2fa628..41b5dc4 100644 (file)
@@ -491,7 +491,7 @@ static int mcfqspi_resume(struct device *dev)
 }
 #endif
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int mcfqspi_runtime_suspend(struct device *dev)
 {
        struct spi_master *master = dev_get_drvdata(dev);
index 932da48..3dec9e0 100644 (file)
@@ -523,7 +523,7 @@ static int orion_spi_remove(struct platform_device *pdev)
 
 MODULE_ALIAS("platform:" DRIVER_NAME);
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int orion_spi_runtime_suspend(struct device *dev)
 {
        struct spi_master *master = dev_get_drvdata(dev);
index 2a41b2d..05c623c 100644 (file)
@@ -1531,7 +1531,7 @@ static int pxa2xx_spi_resume(struct device *dev)
 }
 #endif
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int pxa2xx_spi_runtime_suspend(struct device *dev)
 {
        struct driver_data *drv_data = dev_get_drvdata(dev);
index 390ed71..e7fb5a0 100644 (file)
@@ -646,7 +646,7 @@ error:
        return ret;
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int spi_qup_pm_suspend_runtime(struct device *device)
 {
        struct spi_master *master = dev_get_drvdata(device);
@@ -672,7 +672,7 @@ static int spi_qup_pm_resume_runtime(struct device *device)
        writel_relaxed(config, controller->base + QUP_CONFIG);
        return 0;
 }
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM */
 
 #ifdef CONFIG_PM_SLEEP
 static int spi_qup_suspend(struct device *device)
index 44c1225..daabbab 100644 (file)
@@ -799,7 +799,7 @@ static int rockchip_spi_resume(struct device *dev)
 }
 #endif /* CONFIG_PM_SLEEP */
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int rockchip_spi_runtime_suspend(struct device *dev)
 {
        struct spi_master *master = dev_get_drvdata(dev);
@@ -827,7 +827,7 @@ static int rockchip_spi_runtime_resume(struct device *dev)
 
        return ret;
 }
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM */
 
 static const struct dev_pm_ops rockchip_spi_pm = {
        SET_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume)
index 197bcf0..37b1983 100644 (file)
@@ -1267,7 +1267,7 @@ static int s3c64xx_spi_resume(struct device *dev)
 }
 #endif /* CONFIG_PM_SLEEP */
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int s3c64xx_spi_runtime_suspend(struct device *dev)
 {
        struct spi_master *master = dev_get_drvdata(dev);
@@ -1297,7 +1297,7 @@ static int s3c64xx_spi_runtime_resume(struct device *dev)
 
        return 0;
 }
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM */
 
 static const struct dev_pm_ops s3c64xx_spi_pm = {
        SET_SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume)
index 5836503..bf11a7f 100644 (file)
@@ -53,7 +53,7 @@ if WIMAX_GDM72XX_USB
 
 config WIMAX_GDM72XX_USB_PM
        bool "Enable power management support"
-       depends on PM_RUNTIME
+       depends on PM
        help
          Enable USB power management in order to reduce power consumption
          while the interface is not in use.
index 96498b7..2a054a9 100644 (file)
@@ -27,12 +27,18 @@ source "drivers/staging/media/davinci_vpfe/Kconfig"
 
 source "drivers/staging/media/dt3155v4l/Kconfig"
 
+source "drivers/staging/media/tlg2300/Kconfig"
+
 source "drivers/staging/media/mn88472/Kconfig"
 
 source "drivers/staging/media/mn88473/Kconfig"
 
 source "drivers/staging/media/omap4iss/Kconfig"
 
+source "drivers/staging/media/parport/Kconfig"
+
+source "drivers/staging/media/vino/Kconfig"
+
 # Keep LIRC at the end, as it has sub-menus
 source "drivers/staging/media/lirc/Kconfig"
 
index 30fb352..412b284 100644 (file)
@@ -6,4 +6,7 @@ obj-$(CONFIG_VIDEO_DM365_VPFE)  += davinci_vpfe/
 obj-$(CONFIG_VIDEO_OMAP4)      += omap4iss/
 obj-$(CONFIG_DVB_MN88472)       += mn88472/
 obj-$(CONFIG_DVB_MN88473)       += mn88473/
+obj-y                          += parport/
+obj-$(CONFIG_VIDEO_TLG2300)    += tlg2300/
+obj-y                           += vino/
 
diff --git a/drivers/staging/media/parport/Kconfig b/drivers/staging/media/parport/Kconfig
new file mode 100644 (file)
index 0000000..15974ef
--- /dev/null
@@ -0,0 +1,69 @@
+menuconfig MEDIA_PARPORT_SUPPORT
+       bool "ISA and parallel port devices"
+       depends on (ISA || PARPORT) && MEDIA_CAMERA_SUPPORT
+       help
+         Enables drivers for ISA and parallel port bus. If you
+         need media drivers using those legacy buses, say Y.
+
+if MEDIA_PARPORT_SUPPORT
+config VIDEO_BWQCAM
+       tristate "Quickcam BW Video For Linux (Deprecated)"
+       depends on PARPORT && VIDEO_V4L2
+       select VIDEOBUF2_VMALLOC
+       help
+         Say Y have if you the black and white version of the QuickCam
+         camera. See the next option for the color version.
+
+         This driver is deprecated and will be removed soon. If you have
+         hardware for this and you want to work on this driver, then contact
+         the linux-media mailinglist.
+
+         To compile this driver as a module, choose M here: the
+         module will be called bw-qcam.
+
+config VIDEO_CQCAM
+       tristate "QuickCam Colour Video For Linux (Deprecated)"
+       depends on PARPORT && VIDEO_V4L2
+       help
+         This is the video4linux driver for the colour version of the
+         Connectix QuickCam.  If you have one of these cameras, say Y here,
+         otherwise say N.  This driver does not work with the original
+         monochrome QuickCam, QuickCam VC or QuickClip.  It is also available
+         as a module (c-qcam).
+         Read <file:Documentation/video4linux/CQcam.txt> for more information.
+
+         This driver is deprecated and will be removed soon. If you have
+         hardware for this and you want to work on this driver, then contact
+         the linux-media mailinglist.
+
+config VIDEO_PMS
+       tristate "Mediavision Pro Movie Studio Video For Linux (Deprecated)"
+       depends on ISA && VIDEO_V4L2
+       help
+         Say Y if you have the ISA Mediavision Pro Movie Studio
+         capture card.
+
+         This driver is deprecated and will be removed soon. If you have
+         hardware for this and you want to work on this driver, then contact
+         the linux-media mailinglist.
+
+         To compile this driver as a module, choose M here: the
+         module will be called pms.
+
+config VIDEO_W9966
+       tristate "W9966CF Webcam (FlyCam Supra and others) Video For Linux (Deprecated)"
+       depends on PARPORT_1284 && PARPORT && VIDEO_V4L2
+       help
+         Video4linux driver for Winbond's w9966 based Webcams.
+         Currently tested with the LifeView FlyCam Supra.
+         If you have one of these cameras, say Y here
+         otherwise say N.
+         This driver is also available as a module (w9966).
+
+         Check out <file:Documentation/video4linux/w9966.txt> for more
+         information.
+
+         This driver is deprecated and will be removed soon. If you have
+         hardware for this and you want to work on this driver, then contact
+         the linux-media mailinglist.
+endif
diff --git a/drivers/staging/media/parport/Makefile b/drivers/staging/media/parport/Makefile
new file mode 100644 (file)
index 0000000..4eea06d
--- /dev/null
@@ -0,0 +1,4 @@
+obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o
+obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o
+obj-$(CONFIG_VIDEO_W9966) += w9966.o
+obj-$(CONFIG_VIDEO_PMS) += pms.o
diff --git a/drivers/staging/media/parport/bw-qcam.c b/drivers/staging/media/parport/bw-qcam.c
new file mode 100644 (file)
index 0000000..67b9da1
--- /dev/null
@@ -0,0 +1,1177 @@
+/*
+ *    QuickCam Driver For Video4Linux.
+ *
+ *     Video4Linux conversion work by Alan Cox.
+ *     Parport compatibility by Phil Blundell.
+ *     Busy loop avoidance by Mark Cooke.
+ *
+ *    Module parameters:
+ *
+ *     maxpoll=<1 - 5000>
+ *
+ *       When polling the QuickCam for a response, busy-wait for a
+ *       maximum of this many loops. The default of 250 gives little
+ *       impact on interactive response.
+ *
+ *       NOTE: If this parameter is set too high, the processor
+ *             will busy wait until this loop times out, and then
+ *             slowly poll for a further 5 seconds before failing
+ *             the transaction. You have been warned.
+ *
+ *     yieldlines=<1 - 250>
+ *
+ *       When acquiring a frame from the camera, the data gathering
+ *       loop will yield back to the scheduler after completing
+ *       this many lines. The default of 4 provides a trade-off
+ *       between increased frame acquisition time and impact on
+ *       interactive response.
+ */
+
+/* qcam-lib.c -- Library for programming with the Connectix QuickCam.
+ * See the included documentation for usage instructions and details
+ * of the protocol involved. */
+
+
+/* Version 0.5, August 4, 1996 */
+/* Version 0.7, August 27, 1996 */
+/* Version 0.9, November 17, 1996 */
+
+
+/******************************************************************
+
+Copyright (C) 1996 by Scott Laird
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/parport.h>
+#include <linux/sched.h>
+#include <linux/videodev2.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-vmalloc.h>
+
+/* One from column A... */
+#define QC_NOTSET 0
+#define QC_UNIDIR 1
+#define QC_BIDIR  2
+#define QC_SERIAL 3
+
+/* ... and one from column B */
+#define QC_ANY          0x00
+#define QC_FORCE_UNIDIR 0x10
+#define QC_FORCE_BIDIR  0x20
+#define QC_FORCE_SERIAL 0x30
+/* in the port_mode member */
+
+#define QC_MODE_MASK    0x07
+#define QC_FORCE_MASK   0x70
+
+#define MAX_HEIGHT 243
+#define MAX_WIDTH 336
+
+/* Bit fields for status flags */
+#define QC_PARAM_CHANGE        0x01 /* Camera status change has occurred */
+
+struct qcam {
+       struct v4l2_device v4l2_dev;
+       struct video_device vdev;
+       struct v4l2_ctrl_handler hdl;
+       struct vb2_queue vb_vidq;
+       struct pardevice *pdev;
+       struct parport *pport;
+       struct mutex lock;
+       struct mutex queue_lock;
+       int width, height;
+       int bpp;
+       int mode;
+       int contrast, brightness, whitebal;
+       int port_mode;
+       int transfer_scale;
+       int top, left;
+       int status;
+       unsigned int saved_bits;
+       unsigned long in_use;
+};
+
+static unsigned int maxpoll = 250;   /* Maximum busy-loop count for qcam I/O */
+static unsigned int yieldlines = 4;  /* Yield after this many during capture */
+static int video_nr = -1;
+static unsigned int force_init;                /* Whether to probe aggressively */
+
+module_param(maxpoll, int, 0);
+module_param(yieldlines, int, 0);
+module_param(video_nr, int, 0);
+
+/* Set force_init=1 to avoid detection by polling status register and
+ * immediately attempt to initialize qcam */
+module_param(force_init, int, 0);
+
+#define MAX_CAMS 4
+static struct qcam *qcams[MAX_CAMS];
+static unsigned int num_cams;
+
+static inline int read_lpstatus(struct qcam *q)
+{
+       return parport_read_status(q->pport);
+}
+
+static inline int read_lpdata(struct qcam *q)
+{
+       return parport_read_data(q->pport);
+}
+
+static inline void write_lpdata(struct qcam *q, int d)
+{
+       parport_write_data(q->pport, d);
+}
+
+static void write_lpcontrol(struct qcam *q, int d)
+{
+       if (d & 0x20) {
+               /* Set bidirectional mode to reverse (data in) */
+               parport_data_reverse(q->pport);
+       } else {
+               /* Set bidirectional mode to forward (data out) */
+               parport_data_forward(q->pport);
+       }
+
+       /* Now issue the regular port command, but strip out the
+        * direction flag */
+       d &= ~0x20;
+       parport_write_control(q->pport, d);
+}
+
+
+/* qc_waithand busy-waits for a handshake signal from the QuickCam.
+ * Almost all communication with the camera requires handshaking. */
+
+static int qc_waithand(struct qcam *q, int val)
+{
+       int status;
+       int runs = 0;
+
+       if (val) {
+               while (!((status = read_lpstatus(q)) & 8)) {
+                       /* 1000 is enough spins on the I/O for all normal
+                          cases, at that point we start to poll slowly
+                          until the camera wakes up. However, we are
+                          busy blocked until the camera responds, so
+                          setting it lower is much better for interactive
+                          response. */
+
+                       if (runs++ > maxpoll)
+                               msleep_interruptible(5);
+                       if (runs > (maxpoll + 1000)) /* 5 seconds */
+                               return -1;
+               }
+       } else {
+               while (((status = read_lpstatus(q)) & 8)) {
+                       /* 1000 is enough spins on the I/O for all normal
+                          cases, at that point we start to poll slowly
+                          until the camera wakes up. However, we are
+                          busy blocked until the camera responds, so
+                          setting it lower is much better for interactive
+                          response. */
+
+                       if (runs++ > maxpoll)
+                               msleep_interruptible(5);
+                       if (runs++ > (maxpoll + 1000)) /* 5 seconds */
+                               return -1;
+               }
+       }
+
+       return status;
+}
+
+/* Waithand2 is used when the qcam is in bidirectional mode, and the
+ * handshaking signal is CamRdy2 (bit 0 of data reg) instead of CamRdy1
+ * (bit 3 of status register).  It also returns the last value read,
+ * since this data is useful. */
+
+static unsigned int qc_waithand2(struct qcam *q, int val)
+{
+       unsigned int status;
+       int runs = 0;
+
+       do {
+               status = read_lpdata(q);
+               /* 1000 is enough spins on the I/O for all normal
+                  cases, at that point we start to poll slowly
+                  until the camera wakes up. However, we are
+                  busy blocked until the camera responds, so
+                  setting it lower is much better for interactive
+                  response. */
+
+               if (runs++ > maxpoll)
+                       msleep_interruptible(5);
+               if (runs++ > (maxpoll + 1000)) /* 5 seconds */
+                       return 0;
+       } while ((status & 1) != val);
+
+       return status;
+}
+
+/* qc_command is probably a bit of a misnomer -- it's used to send
+ * bytes *to* the camera.  Generally, these bytes are either commands
+ * or arguments to commands, so the name fits, but it still bugs me a
+ * bit.  See the documentation for a list of commands. */
+
+static int qc_command(struct qcam *q, int command)
+{
+       int n1, n2;
+       int cmd;
+
+       write_lpdata(q, command);
+       write_lpcontrol(q, 6);
+
+       n1 = qc_waithand(q, 1);
+
+       write_lpcontrol(q, 0xe);
+       n2 = qc_waithand(q, 0);
+
+       cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+       return cmd;
+}
+
+static int qc_readparam(struct qcam *q)
+{
+       int n1, n2;
+       int cmd;
+
+       write_lpcontrol(q, 6);
+       n1 = qc_waithand(q, 1);
+
+       write_lpcontrol(q, 0xe);
+       n2 = qc_waithand(q, 0);
+
+       cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+       return cmd;
+}
+
+
+/* Try to detect a QuickCam.  It appears to flash the upper 4 bits of
+   the status register at 5-10 Hz.  This is only used in the autoprobe
+   code.  Be aware that this isn't the way Connectix detects the
+   camera (they send a reset and try to handshake), but this should be
+   almost completely safe, while their method screws up my printer if
+   I plug it in before the camera. */
+
+static int qc_detect(struct qcam *q)
+{
+       int reg, lastreg;
+       int count = 0;
+       int i;
+
+       if (force_init)
+               return 1;
+
+       lastreg = reg = read_lpstatus(q) & 0xf0;
+
+       for (i = 0; i < 500; i++) {
+               reg = read_lpstatus(q) & 0xf0;
+               if (reg != lastreg)
+                       count++;
+               lastreg = reg;
+               mdelay(2);
+       }
+
+
+#if 0
+       /* Force camera detection during testing. Sometimes the camera
+          won't be flashing these bits. Possibly unloading the module
+          in the middle of a grab? Or some timeout condition?
+          I've seen this parameter as low as 19 on my 450Mhz box - mpc */
+       printk(KERN_DEBUG "Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count);
+       return 1;
+#endif
+
+       /* Be (even more) liberal in what you accept...  */
+
+       if (count > 20 && count < 400) {
+               return 1;       /* found */
+       } else {
+               printk(KERN_ERR "No Quickcam found on port %s\n",
+                               q->pport->name);
+               printk(KERN_DEBUG "Quickcam detection counter: %u\n", count);
+               return 0;       /* not found */
+       }
+}
+
+/* Decide which scan mode to use.  There's no real requirement that
+ * the scanmode match the resolution in q->height and q-> width -- the
+ * camera takes the picture at the resolution specified in the
+ * "scanmode" and then returns the image at the resolution specified
+ * with the resolution commands.  If the scan is bigger than the
+ * requested resolution, the upper-left hand corner of the scan is
+ * returned.  If the scan is smaller, then the rest of the image
+ * returned contains garbage. */
+
+static int qc_setscanmode(struct qcam *q)
+{
+       int old_mode = q->mode;
+
+       switch (q->transfer_scale) {
+       case 1:
+               q->mode = 0;
+               break;
+       case 2:
+               q->mode = 4;
+               break;
+       case 4:
+               q->mode = 8;
+               break;
+       }
+
+       switch (q->bpp) {
+       case 4:
+               break;
+       case 6:
+               q->mode += 2;
+               break;
+       }
+
+       switch (q->port_mode & QC_MODE_MASK) {
+       case QC_BIDIR:
+               q->mode += 1;
+               break;
+       case QC_NOTSET:
+       case QC_UNIDIR:
+               break;
+       }
+
+       if (q->mode != old_mode)
+               q->status |= QC_PARAM_CHANGE;
+
+       return 0;
+}
+
+
+/* Reset the QuickCam.  This uses the same sequence the Windows
+ * QuickPic program uses.  Someone with a bi-directional port should
+ * check that bi-directional mode is detected right, and then
+ * implement bi-directional mode in qc_readbyte(). */
+
+static void qc_reset(struct qcam *q)
+{
+       switch (q->port_mode & QC_FORCE_MASK) {
+       case QC_FORCE_UNIDIR:
+               q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR;
+               break;
+
+       case QC_FORCE_BIDIR:
+               q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR;
+               break;
+
+       case QC_ANY:
+               write_lpcontrol(q, 0x20);
+               write_lpdata(q, 0x75);
+
+               if (read_lpdata(q) != 0x75)
+                       q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR;
+               else
+                       q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR;
+               break;
+       }
+
+       write_lpcontrol(q, 0xb);
+       udelay(250);
+       write_lpcontrol(q, 0xe);
+       qc_setscanmode(q);              /* in case port_mode changed */
+}
+
+
+
+/* Reset the QuickCam and program for brightness, contrast,
+ * white-balance, and resolution. */
+
+static void qc_set(struct qcam *q)
+{
+       int val;
+       int val2;
+
+       /* Set the brightness.  Yes, this is repetitive, but it works.
+        * Shorter versions seem to fail subtly.  Feel free to try :-). */
+       /* I think the problem was in qc_command, not here -- bls */
+
+       qc_command(q, 0xb);
+       qc_command(q, q->brightness);
+
+       val = q->height / q->transfer_scale;
+       qc_command(q, 0x11);
+       qc_command(q, val);
+       if ((q->port_mode & QC_MODE_MASK) == QC_UNIDIR && q->bpp == 6) {
+               /* The normal "transfers per line" calculation doesn't seem to work
+                  as expected here (and yet it works fine in qc_scan).  No idea
+                  why this case is the odd man out.  Fortunately, Laird's original
+                  working version gives me a good way to guess at working values.
+                  -- bls */
+               val = q->width;
+               val2 = q->transfer_scale * 4;
+       } else {
+               val = q->width * q->bpp;
+               val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) *
+                       q->transfer_scale;
+       }
+       val = DIV_ROUND_UP(val, val2);
+       qc_command(q, 0x13);
+       qc_command(q, val);
+
+       /* Setting top and left -- bls */
+       qc_command(q, 0xd);
+       qc_command(q, q->top);
+       qc_command(q, 0xf);
+       qc_command(q, q->left / 2);
+
+       qc_command(q, 0x19);
+       qc_command(q, q->contrast);
+       qc_command(q, 0x1f);
+       qc_command(q, q->whitebal);
+
+       /* Clear flag that we must update the grabbing parameters on the camera
+          before we grab the next frame */
+       q->status &= (~QC_PARAM_CHANGE);
+}
+
+/* Qc_readbytes reads some bytes from the QC and puts them in
+   the supplied buffer.  It returns the number of bytes read,
+   or -1 on error. */
+
+static inline int qc_readbytes(struct qcam *q, char buffer[])
+{
+       int ret = 1;
+       unsigned int hi, lo;
+       unsigned int hi2, lo2;
+       static int state;
+
+       if (buffer == NULL) {
+               state = 0;
+               return 0;
+       }
+
+       switch (q->port_mode & QC_MODE_MASK) {
+       case QC_BIDIR:          /* Bi-directional Port */
+               write_lpcontrol(q, 0x26);
+               lo = (qc_waithand2(q, 1) >> 1);
+               hi = (read_lpstatus(q) >> 3) & 0x1f;
+               write_lpcontrol(q, 0x2e);
+               lo2 = (qc_waithand2(q, 0) >> 1);
+               hi2 = (read_lpstatus(q) >> 3) & 0x1f;
+               switch (q->bpp) {
+               case 4:
+                       buffer[0] = lo & 0xf;
+                       buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3);
+                       buffer[2] = (hi & 0x1e) >> 1;
+                       buffer[3] = lo2 & 0xf;
+                       buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3);
+                       buffer[5] = (hi2 & 0x1e) >> 1;
+                       ret = 6;
+                       break;
+               case 6:
+                       buffer[0] = lo & 0x3f;
+                       buffer[1] = ((lo & 0x40) >> 6) | (hi << 1);
+                       buffer[2] = lo2 & 0x3f;
+                       buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1);
+                       ret = 4;
+                       break;
+               }
+               break;
+
+       case QC_UNIDIR: /* Unidirectional Port */
+               write_lpcontrol(q, 6);
+               lo = (qc_waithand(q, 1) & 0xf0) >> 4;
+               write_lpcontrol(q, 0xe);
+               hi = (qc_waithand(q, 0) & 0xf0) >> 4;
+
+               switch (q->bpp) {
+               case 4:
+                       buffer[0] = lo;
+                       buffer[1] = hi;
+                       ret = 2;
+                       break;
+               case 6:
+                       switch (state) {
+                       case 0:
+                               buffer[0] = (lo << 2) | ((hi & 0xc) >> 2);
+                               q->saved_bits = (hi & 3) << 4;
+                               state = 1;
+                               ret = 1;
+                               break;
+                       case 1:
+                               buffer[0] = lo | q->saved_bits;
+                               q->saved_bits = hi << 2;
+                               state = 2;
+                               ret = 1;
+                               break;
+                       case 2:
+                               buffer[0] = ((lo & 0xc) >> 2) | q->saved_bits;
+                               buffer[1] = ((lo & 3) << 4) | hi;
+                               state = 0;
+                               ret = 2;
+                               break;
+                       }
+                       break;
+               }
+               break;
+       }
+       return ret;
+}
+
+/* requests a scan from the camera.  It sends the correct instructions
+ * to the camera and then reads back the correct number of bytes.  In
+ * previous versions of this routine the return structure contained
+ * the raw output from the camera, and there was a 'qc_convertscan'
+ * function that converted that to a useful format.  In version 0.3 I
+ * rolled qc_convertscan into qc_scan and now I only return the
+ * converted scan.  The format is just an one-dimensional array of
+ * characters, one for each pixel, with 0=black up to n=white, where
+ * n=2^(bit depth)-1.  Ask me for more details if you don't understand
+ * this. */
+
+static long qc_capture(struct qcam *q, u8 *buf, unsigned long len)
+{
+       int i, j, k, yield;
+       int bytes;
+       int linestotrans, transperline;
+       int divisor;
+       int pixels_per_line;
+       int pixels_read = 0;
+       int got = 0;
+       char buffer[6];
+       int  shift = 8 - q->bpp;
+       char invert;
+
+       if (q->mode == -1)
+               return -ENXIO;
+
+       qc_command(q, 0x7);
+       qc_command(q, q->mode);
+
+       if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) {
+               write_lpcontrol(q, 0x2e);       /* turn port around */
+               write_lpcontrol(q, 0x26);
+               qc_waithand(q, 1);
+               write_lpcontrol(q, 0x2e);
+               qc_waithand(q, 0);
+       }
+
+       /* strange -- should be 15:63 below, but 4bpp is odd */
+       invert = (q->bpp == 4) ? 16 : 63;
+
+       linestotrans = q->height / q->transfer_scale;
+       pixels_per_line = q->width / q->transfer_scale;
+       transperline = q->width * q->bpp;
+       divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) *
+               q->transfer_scale;
+       transperline = DIV_ROUND_UP(transperline, divisor);
+
+       for (i = 0, yield = yieldlines; i < linestotrans; i++) {
+               for (pixels_read = j = 0; j < transperline; j++) {
+                       bytes = qc_readbytes(q, buffer);
+                       for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++) {
+                               int o;
+                               if (buffer[k] == 0 && invert == 16) {
+                                       /* 4bpp is odd (again) -- inverter is 16, not 15, but output
+                                          must be 0-15 -- bls */
+                                       buffer[k] = 16;
+                               }
+                               o = i * pixels_per_line + pixels_read + k;
+                               if (o < len) {
+                                       u8 ch = invert - buffer[k];
+                                       got++;
+                                       buf[o] = ch << shift;
+                               }
+                       }
+                       pixels_read += bytes;
+               }
+               qc_readbytes(q, NULL);  /* reset state machine */
+
+               /* Grabbing an entire frame from the quickcam is a lengthy
+                  process. We don't (usually) want to busy-block the
+                  processor for the entire frame. yieldlines is a module
+                  parameter. If we yield every line, the minimum frame
+                  time will be 240 / 200 = 1.2 seconds. The compile-time
+                  default is to yield every 4 lines. */
+               if (i >= yield) {
+                       msleep_interruptible(5);
+                       yield = i + yieldlines;
+               }
+       }
+
+       if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) {
+               write_lpcontrol(q, 2);
+               write_lpcontrol(q, 6);
+               udelay(3);
+               write_lpcontrol(q, 0xe);
+       }
+       if (got < len)
+               return got;
+       return len;
+}
+
+/* ------------------------------------------------------------------
+       Videobuf operations
+   ------------------------------------------------------------------*/
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+                               unsigned int *nbuffers, unsigned int *nplanes,
+                               unsigned int sizes[], void *alloc_ctxs[])
+{
+       struct qcam *dev = vb2_get_drv_priv(vq);
+
+       if (0 == *nbuffers)
+               *nbuffers = 3;
+       *nplanes = 1;
+       mutex_lock(&dev->lock);
+       if (fmt)
+               sizes[0] = fmt->fmt.pix.width * fmt->fmt.pix.height;
+       else
+               sizes[0] = (dev->width / dev->transfer_scale) *
+                  (dev->height / dev->transfer_scale);
+       mutex_unlock(&dev->lock);
+       return 0;
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+       vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+}
+
+static void buffer_finish(struct vb2_buffer *vb)
+{
+       struct qcam *qcam = vb2_get_drv_priv(vb->vb2_queue);
+       void *vbuf = vb2_plane_vaddr(vb, 0);
+       int size = vb->vb2_queue->plane_sizes[0];
+       int len;
+
+       if (!vb2_is_streaming(vb->vb2_queue))
+               return;
+
+       mutex_lock(&qcam->lock);
+       parport_claim_or_block(qcam->pdev);
+
+       qc_reset(qcam);
+
+       /* Update the camera parameters if we need to */
+       if (qcam->status & QC_PARAM_CHANGE)
+               qc_set(qcam);
+
+       len = qc_capture(qcam, vbuf, size);
+
+       parport_release(qcam->pdev);
+       mutex_unlock(&qcam->lock);
+       v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
+       if (len != size)
+               vb->state = VB2_BUF_STATE_ERROR;
+       vb2_set_plane_payload(vb, 0, len);
+}
+
+static struct vb2_ops qcam_video_qops = {
+       .queue_setup            = queue_setup,
+       .buf_queue              = buffer_queue,
+       .buf_finish             = buffer_finish,
+       .wait_prepare           = vb2_ops_wait_prepare,
+       .wait_finish            = vb2_ops_wait_finish,
+};
+
+/*
+ *     Video4linux interfacing
+ */
+
+static int qcam_querycap(struct file *file, void  *priv,
+                                       struct v4l2_capability *vcap)
+{
+       struct qcam *qcam = video_drvdata(file);
+
+       strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
+       strlcpy(vcap->card, "Connectix B&W Quickcam", sizeof(vcap->card));
+       strlcpy(vcap->bus_info, qcam->pport->name, sizeof(vcap->bus_info));
+       vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+                               V4L2_CAP_STREAMING;
+       vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
+       return 0;
+}
+
+static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+       if (vin->index > 0)
+               return -EINVAL;
+       strlcpy(vin->name, "Camera", sizeof(vin->name));
+       vin->type = V4L2_INPUT_TYPE_CAMERA;
+       vin->audioset = 0;
+       vin->tuner = 0;
+       vin->std = 0;
+       vin->status = 0;
+       return 0;
+}
+
+static int qcam_g_input(struct file *file, void *fh, unsigned int *inp)
+{
+       *inp = 0;
+       return 0;
+}
+
+static int qcam_s_input(struct file *file, void *fh, unsigned int inp)
+{
+       return (inp > 0) ? -EINVAL : 0;
+}
+
+static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct qcam *qcam = video_drvdata(file);
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+       pix->width = qcam->width / qcam->transfer_scale;
+       pix->height = qcam->height / qcam->transfer_scale;
+       pix->pixelformat = (qcam->bpp == 4) ? V4L2_PIX_FMT_Y4 : V4L2_PIX_FMT_Y6;
+       pix->field = V4L2_FIELD_NONE;
+       pix->bytesperline = pix->width;
+       pix->sizeimage = pix->width * pix->height;
+       /* Just a guess */
+       pix->colorspace = V4L2_COLORSPACE_SRGB;
+       return 0;
+}
+
+static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+       if (pix->height <= 60 || pix->width <= 80) {
+               pix->height = 60;
+               pix->width = 80;
+       } else if (pix->height <= 120 || pix->width <= 160) {
+               pix->height = 120;
+               pix->width = 160;
+       } else {
+               pix->height = 240;
+               pix->width = 320;
+       }
+       if (pix->pixelformat != V4L2_PIX_FMT_Y4 &&
+           pix->pixelformat != V4L2_PIX_FMT_Y6)
+               pix->pixelformat = V4L2_PIX_FMT_Y4;
+       pix->field = V4L2_FIELD_NONE;
+       pix->bytesperline = pix->width;
+       pix->sizeimage = pix->width * pix->height;
+       /* Just a guess */
+       pix->colorspace = V4L2_COLORSPACE_SRGB;
+       return 0;
+}
+
+static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct qcam *qcam = video_drvdata(file);
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+       int ret = qcam_try_fmt_vid_cap(file, fh, fmt);
+
+       if (ret)
+               return ret;
+       if (vb2_is_busy(&qcam->vb_vidq))
+               return -EBUSY;
+       qcam->width = 320;
+       qcam->height = 240;
+       if (pix->height == 60)
+               qcam->transfer_scale = 4;
+       else if (pix->height == 120)
+               qcam->transfer_scale = 2;
+       else
+               qcam->transfer_scale = 1;
+       if (pix->pixelformat == V4L2_PIX_FMT_Y6)
+               qcam->bpp = 6;
+       else
+               qcam->bpp = 4;
+
+       qc_setscanmode(qcam);
+       /* We must update the camera before we grab. We could
+          just have changed the grab size */
+       qcam->status |= QC_PARAM_CHANGE;
+       return 0;
+}
+
+static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+       static struct v4l2_fmtdesc formats[] = {
+               { 0, 0, 0,
+                 "4-Bit Monochrome", V4L2_PIX_FMT_Y4,
+                 { 0, 0, 0, 0 }
+               },
+               { 1, 0, 0,
+                 "6-Bit Monochrome", V4L2_PIX_FMT_Y6,
+                 { 0, 0, 0, 0 }
+               },
+       };
+       enum v4l2_buf_type type = fmt->type;
+
+       if (fmt->index > 1)
+               return -EINVAL;
+
+       *fmt = formats[fmt->index];
+       fmt->type = type;
+       return 0;
+}
+
+static int qcam_enum_framesizes(struct file *file, void *fh,
+                                        struct v4l2_frmsizeenum *fsize)
+{
+       static const struct v4l2_frmsize_discrete sizes[] = {
+               {  80,  60 },
+               { 160, 120 },
+               { 320, 240 },
+       };
+
+       if (fsize->index > 2)
+               return -EINVAL;
+       if (fsize->pixel_format != V4L2_PIX_FMT_Y4 &&
+           fsize->pixel_format != V4L2_PIX_FMT_Y6)
+               return -EINVAL;
+       fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+       fsize->discrete = sizes[fsize->index];
+       return 0;
+}
+
+static int qcam_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct qcam *qcam =
+               container_of(ctrl->handler, struct qcam, hdl);
+       int ret = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               qcam->brightness = ctrl->val;
+               break;
+       case V4L2_CID_CONTRAST:
+               qcam->contrast = ctrl->val;
+               break;
+       case V4L2_CID_GAMMA:
+               qcam->whitebal = ctrl->val;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       if (ret == 0)
+               qcam->status |= QC_PARAM_CHANGE;
+       return ret;
+}
+
+static const struct v4l2_file_operations qcam_fops = {
+       .owner          = THIS_MODULE,
+       .open           = v4l2_fh_open,
+       .release        = vb2_fop_release,
+       .poll           = vb2_fop_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .read           = vb2_fop_read,
+       .mmap           = vb2_fop_mmap,
+};
+
+static const struct v4l2_ioctl_ops qcam_ioctl_ops = {
+       .vidioc_querycap                    = qcam_querycap,
+       .vidioc_g_input                     = qcam_g_input,
+       .vidioc_s_input                     = qcam_s_input,
+       .vidioc_enum_input                  = qcam_enum_input,
+       .vidioc_enum_fmt_vid_cap            = qcam_enum_fmt_vid_cap,
+       .vidioc_enum_framesizes             = qcam_enum_framesizes,
+       .vidioc_g_fmt_vid_cap               = qcam_g_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap               = qcam_s_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap             = qcam_try_fmt_vid_cap,
+       .vidioc_reqbufs                     = vb2_ioctl_reqbufs,
+       .vidioc_create_bufs                 = vb2_ioctl_create_bufs,
+       .vidioc_prepare_buf                 = vb2_ioctl_prepare_buf,
+       .vidioc_querybuf                    = vb2_ioctl_querybuf,
+       .vidioc_qbuf                        = vb2_ioctl_qbuf,
+       .vidioc_dqbuf                       = vb2_ioctl_dqbuf,
+       .vidioc_streamon                    = vb2_ioctl_streamon,
+       .vidioc_streamoff                   = vb2_ioctl_streamoff,
+       .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 qcam_ctrl_ops = {
+       .s_ctrl = qcam_s_ctrl,
+};
+
+/* Initialize the QuickCam driver control structure.  This is where
+ * defaults are set for people who don't have a config file.*/
+
+static struct qcam *qcam_init(struct parport *port)
+{
+       struct qcam *qcam;
+       struct v4l2_device *v4l2_dev;
+       struct vb2_queue *q;
+       int err;
+
+       qcam = kzalloc(sizeof(struct qcam), GFP_KERNEL);
+       if (qcam == NULL)
+               return NULL;
+
+       v4l2_dev = &qcam->v4l2_dev;
+       snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "bw-qcam%u", num_cams);
+
+       if (v4l2_device_register(port->dev, v4l2_dev) < 0) {
+               v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+               kfree(qcam);
+               return NULL;
+       }
+
+       v4l2_ctrl_handler_init(&qcam->hdl, 3);
+       v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
+                         V4L2_CID_BRIGHTNESS, 0, 255, 1, 180);
+       v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
+                         V4L2_CID_CONTRAST, 0, 255, 1, 192);
+       v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
+                         V4L2_CID_GAMMA, 0, 255, 1, 105);
+       if (qcam->hdl.error) {
+               v4l2_err(v4l2_dev, "couldn't register controls\n");
+               goto exit;
+       }
+
+       mutex_init(&qcam->lock);
+       mutex_init(&qcam->queue_lock);
+
+       /* initialize queue */
+       q = &qcam->vb_vidq;
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+       q->drv_priv = qcam;
+       q->ops = &qcam_video_qops;
+       q->mem_ops = &vb2_vmalloc_memops;
+       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       err = vb2_queue_init(q);
+       if (err < 0) {
+               v4l2_err(v4l2_dev, "couldn't init vb2_queue for %s.\n", port->name);
+               goto exit;
+       }
+       qcam->vdev.queue = q;
+       qcam->vdev.queue->lock = &qcam->queue_lock;
+
+       qcam->pport = port;
+       qcam->pdev = parport_register_device(port, v4l2_dev->name, NULL, NULL,
+                       NULL, 0, NULL);
+       if (qcam->pdev == NULL) {
+               v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name);
+               goto exit;
+       }
+
+       strlcpy(qcam->vdev.name, "Connectix QuickCam", sizeof(qcam->vdev.name));
+       qcam->vdev.v4l2_dev = v4l2_dev;
+       qcam->vdev.ctrl_handler = &qcam->hdl;
+       qcam->vdev.fops = &qcam_fops;
+       qcam->vdev.lock = &qcam->lock;
+       qcam->vdev.ioctl_ops = &qcam_ioctl_ops;
+       qcam->vdev.release = video_device_release_empty;
+       video_set_drvdata(&qcam->vdev, qcam);
+
+       qcam->port_mode = (QC_ANY | QC_NOTSET);
+       qcam->width = 320;
+       qcam->height = 240;
+       qcam->bpp = 4;
+       qcam->transfer_scale = 2;
+       qcam->contrast = 192;
+       qcam->brightness = 180;
+       qcam->whitebal = 105;
+       qcam->top = 1;
+       qcam->left = 14;
+       qcam->mode = -1;
+       qcam->status = QC_PARAM_CHANGE;
+       return qcam;
+
+exit:
+       v4l2_ctrl_handler_free(&qcam->hdl);
+       kfree(qcam);
+       return NULL;
+}
+
+static int qc_calibrate(struct qcam *q)
+{
+       /*
+        *      Bugfix by Hanno Mueller hmueller@kabel.de, Mai 21 96
+        *      The white balance is an individual value for each
+        *      quickcam.
+        */
+
+       int value;
+       int count = 0;
+
+       qc_command(q, 27);      /* AutoAdjustOffset */
+       qc_command(q, 0);       /* Dummy Parameter, ignored by the camera */
+
+       /* GetOffset (33) will read 255 until autocalibration */
+       /* is finished. After that, a value of 1-254 will be */
+       /* returned. */
+
+       do {
+               qc_command(q, 33);
+               value = qc_readparam(q);
+               mdelay(1);
+               schedule();
+               count++;
+       } while (value == 0xff && count < 2048);
+
+       q->whitebal = value;
+       return value;
+}
+
+static int init_bwqcam(struct parport *port)
+{
+       struct qcam *qcam;
+
+       if (num_cams == MAX_CAMS) {
+               printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS);
+               return -ENOSPC;
+       }
+
+       qcam = qcam_init(port);
+       if (qcam == NULL)
+               return -ENODEV;
+
+       parport_claim_or_block(qcam->pdev);
+
+       qc_reset(qcam);
+
+       if (qc_detect(qcam) == 0) {
+               parport_release(qcam->pdev);
+               parport_unregister_device(qcam->pdev);
+               kfree(qcam);
+               return -ENODEV;
+       }
+       qc_calibrate(qcam);
+       v4l2_ctrl_handler_setup(&qcam->hdl);
+
+       parport_release(qcam->pdev);
+
+       v4l2_info(&qcam->v4l2_dev, "Connectix Quickcam on %s\n", qcam->pport->name);
+
+       if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
+               parport_unregister_device(qcam->pdev);
+               kfree(qcam);
+               return -ENODEV;
+       }
+
+       qcams[num_cams++] = qcam;
+
+       return 0;
+}
+
+static void close_bwqcam(struct qcam *qcam)
+{
+       video_unregister_device(&qcam->vdev);
+       v4l2_ctrl_handler_free(&qcam->hdl);
+       parport_unregister_device(qcam->pdev);
+       kfree(qcam);
+}
+
+/* The parport parameter controls which parports will be scanned.
+ * Scanning all parports causes some printers to print a garbage page.
+ *       -- March 14, 1999  Billy Donahue <billy@escape.com> */
+#ifdef MODULE
+static char *parport[MAX_CAMS] = { NULL, };
+module_param_array(parport, charp, NULL, 0);
+#endif
+
+static int accept_bwqcam(struct parport *port)
+{
+#ifdef MODULE
+       int n;
+
+       if (parport[0] && strncmp(parport[0], "auto", 4) != 0) {
+               /* user gave parport parameters */
+               for (n = 0; n < MAX_CAMS && parport[n]; n++) {
+                       char *ep;
+                       unsigned long r;
+                       r = simple_strtoul(parport[n], &ep, 0);
+                       if (ep == parport[n]) {
+                               printk(KERN_ERR
+                                       "bw-qcam: bad port specifier \"%s\"\n",
+                                       parport[n]);
+                               continue;
+                       }
+                       if (r == port->number)
+                               return 1;
+               }
+               return 0;
+       }
+#endif
+       return 1;
+}
+
+static void bwqcam_attach(struct parport *port)
+{
+       if (accept_bwqcam(port))
+               init_bwqcam(port);
+}
+
+static void bwqcam_detach(struct parport *port)
+{
+       int i;
+       for (i = 0; i < num_cams; i++) {
+               struct qcam *qcam = qcams[i];
+               if (qcam && qcam->pdev->port == port) {
+                       qcams[i] = NULL;
+                       close_bwqcam(qcam);
+               }
+       }
+}
+
+static struct parport_driver bwqcam_driver = {
+       .name   = "bw-qcam",
+       .attach = bwqcam_attach,
+       .detach = bwqcam_detach,
+};
+
+static void __exit exit_bw_qcams(void)
+{
+       parport_unregister_driver(&bwqcam_driver);
+}
+
+static int __init init_bw_qcams(void)
+{
+#ifdef MODULE
+       /* Do some sanity checks on the module parameters. */
+       if (maxpoll > 5000) {
+               printk(KERN_INFO "Connectix Quickcam max-poll was above 5000. Using 5000.\n");
+               maxpoll = 5000;
+       }
+
+       if (yieldlines < 1) {
+               printk(KERN_INFO "Connectix Quickcam yieldlines was less than 1. Using 1.\n");
+               yieldlines = 1;
+       }
+#endif
+       return parport_register_driver(&bwqcam_driver);
+}
+
+module_init(init_bw_qcams);
+module_exit(exit_bw_qcams);
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
diff --git a/drivers/staging/media/parport/c-qcam.c b/drivers/staging/media/parport/c-qcam.c
new file mode 100644 (file)
index 0000000..b9010bd
--- /dev/null
@@ -0,0 +1,882 @@
+/*
+ *     Video4Linux Colour QuickCam driver
+ *     Copyright 1997-2000 Philip Blundell <philb@gnu.org>
+ *
+ *    Module parameters:
+ *
+ *     parport=auto      -- probe all parports (default)
+ *     parport=0         -- parport0 becomes qcam1
+ *     parport=2,0,1     -- parports 2,0,1 are tried in that order
+ *
+ *     probe=0           -- do no probing, assume camera is present
+ *     probe=1           -- use IEEE-1284 autoprobe data only (default)
+ *     probe=2           -- probe aggressively for cameras
+ *
+ *     force_rgb=1       -- force data format to RGB (default is BGR)
+ *
+ * The parport parameter controls which parports will be scanned.
+ * Scanning all parports causes some printers to print a garbage page.
+ *       -- March 14, 1999  Billy Donahue <billy@escape.com>
+ *
+ * Fixed data format to BGR, added force_rgb parameter. Added missing
+ * parport_unregister_driver() on module removal.
+ *       -- May 28, 2000  Claudio Matsuoka <claudio@conectiva.com>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/parport.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/jiffies.h>
+#include <linux/videodev2.h>
+#include <asm/uaccess.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+
+struct qcam {
+       struct v4l2_device v4l2_dev;
+       struct video_device vdev;
+       struct v4l2_ctrl_handler hdl;
+       struct pardevice *pdev;
+       struct parport *pport;
+       int width, height;
+       int ccd_width, ccd_height;
+       int mode;
+       int contrast, brightness, whitebal;
+       int top, left;
+       unsigned int bidirectional;
+       struct mutex lock;
+};
+
+/* cameras maximum */
+#define MAX_CAMS 4
+
+/* The three possible QuickCam modes */
+#define QC_MILLIONS    0x18
+#define QC_BILLIONS    0x10
+#define QC_THOUSANDS   0x08    /* with VIDEC compression (not supported) */
+
+/* The three possible decimations */
+#define QC_DECIMATION_1                0
+#define QC_DECIMATION_2                2
+#define QC_DECIMATION_4                4
+
+#define BANNER "Colour QuickCam for Video4Linux v0.06"
+
+static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 };
+static int probe = 2;
+static bool force_rgb;
+static int video_nr = -1;
+
+/* FIXME: parport=auto would never have worked, surely? --RR */
+MODULE_PARM_DESC(parport, "parport=<auto|n[,n]...> for port detection method\n"
+                         "probe=<0|1|2> for camera detection method\n"
+                         "force_rgb=<0|1> for RGB data format (default BGR)");
+module_param_array(parport, int, NULL, 0);
+module_param(probe, int, 0);
+module_param(force_rgb, bool, 0);
+module_param(video_nr, int, 0);
+
+static struct qcam *qcams[MAX_CAMS];
+static unsigned int num_cams;
+
+static inline void qcam_set_ack(struct qcam *qcam, unsigned int i)
+{
+       /* note: the QC specs refer to the PCAck pin by voltage, not
+          software level.  PC ports have builtin inverters. */
+       parport_frob_control(qcam->pport, 8, i ? 8 : 0);
+}
+
+static inline unsigned int qcam_ready1(struct qcam *qcam)
+{
+       return (parport_read_status(qcam->pport) & 0x8) ? 1 : 0;
+}
+
+static inline unsigned int qcam_ready2(struct qcam *qcam)
+{
+       return (parport_read_data(qcam->pport) & 0x1) ? 1 : 0;
+}
+
+static unsigned int qcam_await_ready1(struct qcam *qcam, int value)
+{
+       struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
+       unsigned long oldjiffies = jiffies;
+       unsigned int i;
+
+       for (oldjiffies = jiffies;
+            time_before(jiffies, oldjiffies + msecs_to_jiffies(40));)
+               if (qcam_ready1(qcam) == value)
+                       return 0;
+
+       /* If the camera didn't respond within 1/25 second, poll slowly
+          for a while. */
+       for (i = 0; i < 50; i++) {
+               if (qcam_ready1(qcam) == value)
+                       return 0;
+               msleep_interruptible(100);
+       }
+
+       /* Probably somebody pulled the plug out.  Not much we can do. */
+       v4l2_err(v4l2_dev, "ready1 timeout (%d) %x %x\n", value,
+              parport_read_status(qcam->pport),
+              parport_read_control(qcam->pport));
+       return 1;
+}
+
+static unsigned int qcam_await_ready2(struct qcam *qcam, int value)
+{
+       struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
+       unsigned long oldjiffies = jiffies;
+       unsigned int i;
+
+       for (oldjiffies = jiffies;
+            time_before(jiffies, oldjiffies + msecs_to_jiffies(40));)
+               if (qcam_ready2(qcam) == value)
+                       return 0;
+
+       /* If the camera didn't respond within 1/25 second, poll slowly
+          for a while. */
+       for (i = 0; i < 50; i++) {
+               if (qcam_ready2(qcam) == value)
+                       return 0;
+               msleep_interruptible(100);
+       }
+
+       /* Probably somebody pulled the plug out.  Not much we can do. */
+       v4l2_err(v4l2_dev, "ready2 timeout (%d) %x %x %x\n", value,
+              parport_read_status(qcam->pport),
+              parport_read_control(qcam->pport),
+              parport_read_data(qcam->pport));
+       return 1;
+}
+
+static int qcam_read_data(struct qcam *qcam)
+{
+       unsigned int idata;
+
+       qcam_set_ack(qcam, 0);
+       if (qcam_await_ready1(qcam, 1))
+               return -1;
+       idata = parport_read_status(qcam->pport) & 0xf0;
+       qcam_set_ack(qcam, 1);
+       if (qcam_await_ready1(qcam, 0))
+               return -1;
+       idata |= parport_read_status(qcam->pport) >> 4;
+       return idata;
+}
+
+static int qcam_write_data(struct qcam *qcam, unsigned int data)
+{
+       struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
+       unsigned int idata;
+
+       parport_write_data(qcam->pport, data);
+       idata = qcam_read_data(qcam);
+       if (data != idata) {
+               v4l2_warn(v4l2_dev, "sent %x but received %x\n", data,
+                      idata);
+               return 1;
+       }
+       return 0;
+}
+
+static inline int qcam_set(struct qcam *qcam, unsigned int cmd, unsigned int data)
+{
+       if (qcam_write_data(qcam, cmd))
+               return -1;
+       if (qcam_write_data(qcam, data))
+               return -1;
+       return 0;
+}
+
+static inline int qcam_get(struct qcam *qcam, unsigned int cmd)
+{
+       if (qcam_write_data(qcam, cmd))
+               return -1;
+       return qcam_read_data(qcam);
+}
+
+static int qc_detect(struct qcam *qcam)
+{
+       unsigned int stat, ostat, i, count = 0;
+
+       /* The probe routine below is not very reliable.  The IEEE-1284
+          probe takes precedence. */
+       /* XXX Currently parport provides no way to distinguish between
+          "the IEEE probe was not done" and "the probe was done, but
+          no device was found".  Fix this one day. */
+       if (qcam->pport->probe_info[0].class == PARPORT_CLASS_MEDIA
+           && qcam->pport->probe_info[0].model
+           && !strcmp(qcam->pdev->port->probe_info[0].model,
+                      "Color QuickCam 2.0")) {
+               printk(KERN_DEBUG "QuickCam: Found by IEEE1284 probe.\n");
+               return 1;
+       }
+
+       if (probe < 2)
+               return 0;
+
+       parport_write_control(qcam->pport, 0xc);
+
+       /* look for a heartbeat */
+       ostat = stat = parport_read_status(qcam->pport);
+       for (i = 0; i < 250; i++) {
+               mdelay(1);
+               stat = parport_read_status(qcam->pport);
+               if (ostat != stat) {
+                       if (++count >= 3)
+                               return 1;
+                       ostat = stat;
+               }
+       }
+
+       /* Reset the camera and try again */
+       parport_write_control(qcam->pport, 0xc);
+       parport_write_control(qcam->pport, 0x8);
+       mdelay(1);
+       parport_write_control(qcam->pport, 0xc);
+       mdelay(1);
+       count = 0;
+
+       ostat = stat = parport_read_status(qcam->pport);
+       for (i = 0; i < 250; i++) {
+               mdelay(1);
+               stat = parport_read_status(qcam->pport);
+               if (ostat != stat) {
+                       if (++count >= 3)
+                               return 1;
+                       ostat = stat;
+               }
+       }
+
+       /* no (or flatline) camera, give up */
+       return 0;
+}
+
+static void qc_reset(struct qcam *qcam)
+{
+       parport_write_control(qcam->pport, 0xc);
+       parport_write_control(qcam->pport, 0x8);
+       mdelay(1);
+       parport_write_control(qcam->pport, 0xc);
+       mdelay(1);
+}
+
+/* Reset the QuickCam and program for brightness, contrast,
+ * white-balance, and resolution. */
+
+static void qc_setup(struct qcam *qcam)
+{
+       qc_reset(qcam);
+
+       /* Set the brightness. */
+       qcam_set(qcam, 11, qcam->brightness);
+
+       /* Set the height and width.  These refer to the actual
+          CCD area *before* applying the selected decimation.  */
+       qcam_set(qcam, 17, qcam->ccd_height);
+       qcam_set(qcam, 19, qcam->ccd_width / 2);
+
+       /* Set top and left.  */
+       qcam_set(qcam, 0xd, qcam->top);
+       qcam_set(qcam, 0xf, qcam->left);
+
+       /* Set contrast and white balance.  */
+       qcam_set(qcam, 0x19, qcam->contrast);
+       qcam_set(qcam, 0x1f, qcam->whitebal);
+
+       /* Set the speed.  */
+       qcam_set(qcam, 45, 2);
+}
+
+/* Read some bytes from the camera and put them in the buffer.
+   nbytes should be a multiple of 3, because bidirectional mode gives
+   us three bytes at a time.  */
+
+static unsigned int qcam_read_bytes(struct qcam *qcam, unsigned char *buf, unsigned int nbytes)
+{
+       unsigned int bytes = 0;
+
+       qcam_set_ack(qcam, 0);
+       if (qcam->bidirectional) {
+               /* It's a bidirectional port */
+               while (bytes < nbytes) {
+                       unsigned int lo1, hi1, lo2, hi2;
+                       unsigned char r, g, b;
+
+                       if (qcam_await_ready2(qcam, 1))
+                               return bytes;
+                       lo1 = parport_read_data(qcam->pport) >> 1;
+                       hi1 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
+                       qcam_set_ack(qcam, 1);
+                       if (qcam_await_ready2(qcam, 0))
+                               return bytes;
+                       lo2 = parport_read_data(qcam->pport) >> 1;
+                       hi2 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
+                       qcam_set_ack(qcam, 0);
+                       r = lo1 | ((hi1 & 1) << 7);
+                       g = ((hi1 & 0x1e) << 3) | ((hi2 & 0x1e) >> 1);
+                       b = lo2 | ((hi2 & 1) << 7);
+                       if (force_rgb) {
+                               buf[bytes++] = r;
+                               buf[bytes++] = g;
+                               buf[bytes++] = b;
+                       } else {
+                               buf[bytes++] = b;
+                               buf[bytes++] = g;
+                               buf[bytes++] = r;
+                       }
+               }
+       } else {
+               /* It's a unidirectional port */
+               int i = 0, n = bytes;
+               unsigned char rgb[3];
+
+               while (bytes < nbytes) {
+                       unsigned int hi, lo;
+
+                       if (qcam_await_ready1(qcam, 1))
+                               return bytes;
+                       hi = (parport_read_status(qcam->pport) & 0xf0);
+                       qcam_set_ack(qcam, 1);
+                       if (qcam_await_ready1(qcam, 0))
+                               return bytes;
+                       lo = (parport_read_status(qcam->pport) & 0xf0);
+                       qcam_set_ack(qcam, 0);
+                       /* flip some bits */
+                       rgb[(i = bytes++ % 3)] = (hi | (lo >> 4)) ^ 0x88;
+                       if (i >= 2) {
+get_fragment:
+                               if (force_rgb) {
+                                       buf[n++] = rgb[0];
+                                       buf[n++] = rgb[1];
+                                       buf[n++] = rgb[2];
+                               } else {
+                                       buf[n++] = rgb[2];
+                                       buf[n++] = rgb[1];
+                                       buf[n++] = rgb[0];
+                               }
+                       }
+               }
+               if (i) {
+                       i = 0;
+                       goto get_fragment;
+               }
+       }
+       return bytes;
+}
+
+#define BUFSZ  150
+
+static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len)
+{
+       struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
+       unsigned lines, pixelsperline;
+       unsigned int is_bi_dir = qcam->bidirectional;
+       size_t wantlen, outptr = 0;
+       char tmpbuf[BUFSZ];
+
+       if (!access_ok(VERIFY_WRITE, buf, len))
+               return -EFAULT;
+
+       /* Wait for camera to become ready */
+       for (;;) {
+               int i = qcam_get(qcam, 41);
+
+               if (i == -1) {
+                       qc_setup(qcam);
+                       return -EIO;
+               }
+               if ((i & 0x80) == 0)
+                       break;
+               schedule();
+       }
+
+       if (qcam_set(qcam, 7, (qcam->mode | (is_bi_dir ? 1 : 0)) + 1))
+               return -EIO;
+
+       lines = qcam->height;
+       pixelsperline = qcam->width;
+
+       if (is_bi_dir) {
+               /* Turn the port around */
+               parport_data_reverse(qcam->pport);
+               mdelay(3);
+               qcam_set_ack(qcam, 0);
+               if (qcam_await_ready1(qcam, 1)) {
+                       qc_setup(qcam);
+                       return -EIO;
+               }
+               qcam_set_ack(qcam, 1);
+               if (qcam_await_ready1(qcam, 0)) {
+                       qc_setup(qcam);
+                       return -EIO;
+               }
+       }
+
+       wantlen = lines * pixelsperline * 24 / 8;
+
+       while (wantlen) {
+               size_t t, s;
+
+               s = (wantlen > BUFSZ) ? BUFSZ : wantlen;
+               t = qcam_read_bytes(qcam, tmpbuf, s);
+               if (outptr < len) {
+                       size_t sz = len - outptr;
+
+                       if (sz > t)
+                               sz = t;
+                       if (__copy_to_user(buf + outptr, tmpbuf, sz))
+                               break;
+                       outptr += sz;
+               }
+               wantlen -= t;
+               if (t < s)
+                       break;
+               cond_resched();
+       }
+
+       len = outptr;
+
+       if (wantlen) {
+               v4l2_err(v4l2_dev, "short read.\n");
+               if (is_bi_dir)
+                       parport_data_forward(qcam->pport);
+               qc_setup(qcam);
+               return len;
+       }
+
+       if (is_bi_dir) {
+               int l;
+
+               do {
+                       l = qcam_read_bytes(qcam, tmpbuf, 3);
+                       cond_resched();
+               } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e));
+               if (force_rgb) {
+                       if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
+                               v4l2_err(v4l2_dev, "bad EOF\n");
+               } else {
+                       if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
+                               v4l2_err(v4l2_dev, "bad EOF\n");
+               }
+               qcam_set_ack(qcam, 0);
+               if (qcam_await_ready1(qcam, 1)) {
+                       v4l2_err(v4l2_dev, "no ack after EOF\n");
+                       parport_data_forward(qcam->pport);
+                       qc_setup(qcam);
+                       return len;
+               }
+               parport_data_forward(qcam->pport);
+               mdelay(3);
+               qcam_set_ack(qcam, 1);
+               if (qcam_await_ready1(qcam, 0)) {
+                       v4l2_err(v4l2_dev, "no ack to port turnaround\n");
+                       qc_setup(qcam);
+                       return len;
+               }
+       } else {
+               int l;
+
+               do {
+                       l = qcam_read_bytes(qcam, tmpbuf, 1);
+                       cond_resched();
+               } while (l && tmpbuf[0] == 0x7e);
+               l = qcam_read_bytes(qcam, tmpbuf + 1, 2);
+               if (force_rgb) {
+                       if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
+                               v4l2_err(v4l2_dev, "bad EOF\n");
+               } else {
+                       if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
+                               v4l2_err(v4l2_dev, "bad EOF\n");
+               }
+       }
+
+       qcam_write_data(qcam, 0);
+       return len;
+}
+
+/*
+ *     Video4linux interfacing
+ */
+
+static int qcam_querycap(struct file *file, void  *priv,
+                                       struct v4l2_capability *vcap)
+{
+       struct qcam *qcam = video_drvdata(file);
+
+       strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
+       strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card));
+       strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
+       vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+       vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
+       return 0;
+}
+
+static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+       if (vin->index > 0)
+               return -EINVAL;
+       strlcpy(vin->name, "Camera", sizeof(vin->name));
+       vin->type = V4L2_INPUT_TYPE_CAMERA;
+       vin->audioset = 0;
+       vin->tuner = 0;
+       vin->std = 0;
+       vin->status = 0;
+       return 0;
+}
+
+static int qcam_g_input(struct file *file, void *fh, unsigned int *inp)
+{
+       *inp = 0;
+       return 0;
+}
+
+static int qcam_s_input(struct file *file, void *fh, unsigned int inp)
+{
+       return (inp > 0) ? -EINVAL : 0;
+}
+
+static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct qcam *qcam = video_drvdata(file);
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+       pix->width = qcam->width;
+       pix->height = qcam->height;
+       pix->pixelformat = V4L2_PIX_FMT_RGB24;
+       pix->field = V4L2_FIELD_NONE;
+       pix->bytesperline = 3 * qcam->width;
+       pix->sizeimage = 3 * qcam->width * qcam->height;
+       /* Just a guess */
+       pix->colorspace = V4L2_COLORSPACE_SRGB;
+       return 0;
+}
+
+static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+       if (pix->height < 60 || pix->width < 80) {
+               pix->height = 60;
+               pix->width = 80;
+       } else if (pix->height < 120 || pix->width < 160) {
+               pix->height = 120;
+               pix->width = 160;
+       } else {
+               pix->height = 240;
+               pix->width = 320;
+       }
+       pix->pixelformat = V4L2_PIX_FMT_RGB24;
+       pix->field = V4L2_FIELD_NONE;
+       pix->bytesperline = 3 * pix->width;
+       pix->sizeimage = 3 * pix->width * pix->height;
+       /* Just a guess */
+       pix->colorspace = V4L2_COLORSPACE_SRGB;
+       return 0;
+}
+
+static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct qcam *qcam = video_drvdata(file);
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+       int ret = qcam_try_fmt_vid_cap(file, fh, fmt);
+
+       if (ret)
+               return ret;
+       switch (pix->height) {
+       case 60:
+               qcam->mode = QC_DECIMATION_4;
+               break;
+       case 120:
+               qcam->mode = QC_DECIMATION_2;
+               break;
+       default:
+               qcam->mode = QC_DECIMATION_1;
+               break;
+       }
+
+       mutex_lock(&qcam->lock);
+       qcam->mode |= QC_MILLIONS;
+       qcam->height = pix->height;
+       qcam->width = pix->width;
+       parport_claim_or_block(qcam->pdev);
+       qc_setup(qcam);
+       parport_release(qcam->pdev);
+       mutex_unlock(&qcam->lock);
+       return 0;
+}
+
+static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+       static struct v4l2_fmtdesc formats[] = {
+               { 0, 0, 0,
+                 "RGB 8:8:8", V4L2_PIX_FMT_RGB24,
+                 { 0, 0, 0, 0 }
+               },
+       };
+       enum v4l2_buf_type type = fmt->type;
+
+       if (fmt->index > 0)
+               return -EINVAL;
+
+       *fmt = formats[fmt->index];
+       fmt->type = type;
+       return 0;
+}
+
+static ssize_t qcam_read(struct file *file, char __user *buf,
+                        size_t count, loff_t *ppos)
+{
+       struct qcam *qcam = video_drvdata(file);
+       int len;
+
+       mutex_lock(&qcam->lock);
+       parport_claim_or_block(qcam->pdev);
+       /* Probably should have a semaphore against multiple users */
+       len = qc_capture(qcam, buf, count);
+       parport_release(qcam->pdev);
+       mutex_unlock(&qcam->lock);
+       return len;
+}
+
+static int qcam_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct qcam *qcam =
+               container_of(ctrl->handler, struct qcam, hdl);
+       int ret = 0;
+
+       mutex_lock(&qcam->lock);
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               qcam->brightness = ctrl->val;
+               break;
+       case V4L2_CID_CONTRAST:
+               qcam->contrast = ctrl->val;
+               break;
+       case V4L2_CID_GAMMA:
+               qcam->whitebal = ctrl->val;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       if (ret == 0) {
+               parport_claim_or_block(qcam->pdev);
+               qc_setup(qcam);
+               parport_release(qcam->pdev);
+       }
+       mutex_unlock(&qcam->lock);
+       return ret;
+}
+
+static const struct v4l2_file_operations qcam_fops = {
+       .owner          = THIS_MODULE,
+       .open           = v4l2_fh_open,
+       .release        = v4l2_fh_release,
+       .poll           = v4l2_ctrl_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .read           = qcam_read,
+};
+
+static const struct v4l2_ioctl_ops qcam_ioctl_ops = {
+       .vidioc_querycap                    = qcam_querycap,
+       .vidioc_g_input                     = qcam_g_input,
+       .vidioc_s_input                     = qcam_s_input,
+       .vidioc_enum_input                  = qcam_enum_input,
+       .vidioc_enum_fmt_vid_cap            = qcam_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap               = qcam_g_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap               = qcam_s_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap             = qcam_try_fmt_vid_cap,
+       .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 qcam_ctrl_ops = {
+       .s_ctrl = qcam_s_ctrl,
+};
+
+/* Initialize the QuickCam driver control structure. */
+
+static struct qcam *qcam_init(struct parport *port)
+{
+       struct qcam *qcam;
+       struct v4l2_device *v4l2_dev;
+
+       qcam = kzalloc(sizeof(*qcam), GFP_KERNEL);
+       if (qcam == NULL)
+               return NULL;
+
+       v4l2_dev = &qcam->v4l2_dev;
+       strlcpy(v4l2_dev->name, "c-qcam", sizeof(v4l2_dev->name));
+
+       if (v4l2_device_register(NULL, v4l2_dev) < 0) {
+               v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+               kfree(qcam);
+               return NULL;
+       }
+
+       v4l2_ctrl_handler_init(&qcam->hdl, 3);
+       v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
+                         V4L2_CID_BRIGHTNESS, 0, 255, 1, 240);
+       v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
+                         V4L2_CID_CONTRAST, 0, 255, 1, 192);
+       v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
+                         V4L2_CID_GAMMA, 0, 255, 1, 128);
+       if (qcam->hdl.error) {
+               v4l2_err(v4l2_dev, "couldn't register controls\n");
+               v4l2_ctrl_handler_free(&qcam->hdl);
+               kfree(qcam);
+               return NULL;
+       }
+
+       qcam->pport = port;
+       qcam->pdev = parport_register_device(port, "c-qcam", NULL, NULL,
+                                         NULL, 0, NULL);
+
+       qcam->bidirectional = (qcam->pport->modes & PARPORT_MODE_TRISTATE) ? 1 : 0;
+
+       if (qcam->pdev == NULL) {
+               v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name);
+               v4l2_ctrl_handler_free(&qcam->hdl);
+               kfree(qcam);
+               return NULL;
+       }
+
+       strlcpy(qcam->vdev.name, "Colour QuickCam", sizeof(qcam->vdev.name));
+       qcam->vdev.v4l2_dev = v4l2_dev;
+       qcam->vdev.fops = &qcam_fops;
+       qcam->vdev.ioctl_ops = &qcam_ioctl_ops;
+       qcam->vdev.release = video_device_release_empty;
+       qcam->vdev.ctrl_handler = &qcam->hdl;
+       video_set_drvdata(&qcam->vdev, qcam);
+
+       mutex_init(&qcam->lock);
+       qcam->width = qcam->ccd_width = 320;
+       qcam->height = qcam->ccd_height = 240;
+       qcam->mode = QC_MILLIONS | QC_DECIMATION_1;
+       qcam->contrast = 192;
+       qcam->brightness = 240;
+       qcam->whitebal = 128;
+       qcam->top = 1;
+       qcam->left = 14;
+       return qcam;
+}
+
+static int init_cqcam(struct parport *port)
+{
+       struct qcam *qcam;
+       struct v4l2_device *v4l2_dev;
+
+       if (parport[0] != -1) {
+               /* The user gave specific instructions */
+               int i, found = 0;
+
+               for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) {
+                       if (parport[0] == port->number)
+                               found = 1;
+               }
+               if (!found)
+                       return -ENODEV;
+       }
+
+       if (num_cams == MAX_CAMS)
+               return -ENOSPC;
+
+       qcam = qcam_init(port);
+       if (qcam == NULL)
+               return -ENODEV;
+
+       v4l2_dev = &qcam->v4l2_dev;
+
+       parport_claim_or_block(qcam->pdev);
+
+       qc_reset(qcam);
+
+       if (probe && qc_detect(qcam) == 0) {
+               parport_release(qcam->pdev);
+               parport_unregister_device(qcam->pdev);
+               kfree(qcam);
+               return -ENODEV;
+       }
+
+       qc_setup(qcam);
+
+       parport_release(qcam->pdev);
+
+       if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
+               v4l2_err(v4l2_dev, "Unable to register Colour QuickCam on %s\n",
+                      qcam->pport->name);
+               parport_unregister_device(qcam->pdev);
+               kfree(qcam);
+               return -ENODEV;
+       }
+
+       v4l2_info(v4l2_dev, "%s: Colour QuickCam found on %s\n",
+              video_device_node_name(&qcam->vdev), qcam->pport->name);
+
+       qcams[num_cams++] = qcam;
+
+       return 0;
+}
+
+static void close_cqcam(struct qcam *qcam)
+{
+       video_unregister_device(&qcam->vdev);
+       v4l2_ctrl_handler_free(&qcam->hdl);
+       parport_unregister_device(qcam->pdev);
+       kfree(qcam);
+}
+
+static void cq_attach(struct parport *port)
+{
+       init_cqcam(port);
+}
+
+static void cq_detach(struct parport *port)
+{
+       /* Write this some day. */
+}
+
+static struct parport_driver cqcam_driver = {
+       .name = "cqcam",
+       .attach = cq_attach,
+       .detach = cq_detach,
+};
+
+static int __init cqcam_init(void)
+{
+       printk(KERN_INFO BANNER "\n");
+
+       return parport_register_driver(&cqcam_driver);
+}
+
+static void __exit cqcam_cleanup(void)
+{
+       unsigned int i;
+
+       for (i = 0; i < num_cams; i++)
+               close_cqcam(qcams[i]);
+
+       parport_unregister_driver(&cqcam_driver);
+}
+
+MODULE_AUTHOR("Philip Blundell <philb@gnu.org>");
+MODULE_DESCRIPTION(BANNER);
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.4");
+
+module_init(cqcam_init);
+module_exit(cqcam_cleanup);
diff --git a/drivers/staging/media/parport/pms.c b/drivers/staging/media/parport/pms.c
new file mode 100644 (file)
index 0000000..e6b4975
--- /dev/null
@@ -0,0 +1,1156 @@
+/*
+ *     Media Vision Pro Movie Studio
+ *                     or
+ *     "all you need is an I2C bus some RAM and a prayer"
+ *
+ *     This draws heavily on code
+ *
+ *     (c) Wolfgang Koehler,  wolf@first.gmd.de, Dec. 1994
+ *     Kiefernring 15
+ *     14478 Potsdam, Germany
+ *
+ *     Most of this code is directly derived from his userspace driver.
+ *     His driver works so send any reports to alan@lxorguk.ukuu.org.uk
+ *     unless the userspace driver also doesn't work for you...
+ *
+ *      Changes:
+ *     25-11-2009      Hans Verkuil <hverkuil@xs4all.nl>
+ *                     - converted to version 2 of the V4L API.
+ *      08/07/2003      Daniele Bellucci <bellucda@tiscali.it>
+ *                      - pms_capture: report back -EFAULT
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/isa.h>
+#include <asm/io.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-device.h>
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.5");
+
+#define MOTOROLA       1
+#define PHILIPS2       2               /* SAA7191 */
+#define PHILIPS1       3
+#define MVVMEMORYWIDTH 0x40            /* 512 bytes */
+
+struct i2c_info {
+       u8 slave;
+       u8 sub;
+       u8 data;
+       u8 hits;
+};
+
+struct pms {
+       struct v4l2_device v4l2_dev;
+       struct video_device vdev;
+       struct v4l2_ctrl_handler hdl;
+       int height;
+       int width;
+       int depth;
+       int input;
+       struct mutex lock;
+       int i2c_count;
+       struct i2c_info i2cinfo[64];
+
+       int decoder;
+       int standard;   /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
+       v4l2_std_id std;
+       int io;
+       int data;
+       void __iomem *mem;
+};
+
+/*
+ *     I/O ports and Shared Memory
+ */
+
+static int io_port = 0x250;
+module_param(io_port, int, 0);
+
+static int mem_base = 0xc8000;
+module_param(mem_base, int, 0);
+
+static int video_nr = -1;
+module_param(video_nr, int, 0);
+
+
+static inline void mvv_write(struct pms *dev, u8 index, u8 value)
+{
+       outw(index | (value << 8), dev->io);
+}
+
+static inline u8 mvv_read(struct pms *dev, u8 index)
+{
+       outb(index, dev->io);
+       return inb(dev->data);
+}
+
+static int pms_i2c_stat(struct pms *dev, u8 slave)
+{
+       int counter = 0;
+       int i;
+
+       outb(0x28, dev->io);
+
+       while ((inb(dev->data) & 0x01) == 0)
+               if (counter++ == 256)
+                       break;
+
+       while ((inb(dev->data) & 0x01) != 0)
+               if (counter++ == 256)
+                       break;
+
+       outb(slave, dev->io);
+
+       counter = 0;
+       while ((inb(dev->data) & 0x01) == 0)
+               if (counter++ == 256)
+                       break;
+
+       while ((inb(dev->data) & 0x01) != 0)
+               if (counter++ == 256)
+                       break;
+
+       for (i = 0; i < 12; i++) {
+               char st = inb(dev->data);
+
+               if ((st & 2) != 0)
+                       return -1;
+               if ((st & 1) == 0)
+                       break;
+       }
+       outb(0x29, dev->io);
+       return inb(dev->data);
+}
+
+static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data)
+{
+       int skip = 0;
+       int count;
+       int i;
+
+       for (i = 0; i < dev->i2c_count; i++) {
+               if ((dev->i2cinfo[i].slave == slave) &&
+                   (dev->i2cinfo[i].sub == sub)) {
+                       if (dev->i2cinfo[i].data == data)
+                               skip = 1;
+                       dev->i2cinfo[i].data = data;
+                       i = dev->i2c_count + 1;
+               }
+       }
+
+       if (i == dev->i2c_count && dev->i2c_count < 64) {
+               dev->i2cinfo[dev->i2c_count].slave = slave;
+               dev->i2cinfo[dev->i2c_count].sub = sub;
+               dev->i2cinfo[dev->i2c_count].data = data;
+               dev->i2c_count++;
+       }
+
+       if (skip)
+               return 0;
+
+       mvv_write(dev, 0x29, sub);
+       mvv_write(dev, 0x2A, data);
+       mvv_write(dev, 0x28, slave);
+
+       outb(0x28, dev->io);
+
+       count = 0;
+       while ((inb(dev->data) & 1) == 0)
+               if (count > 255)
+                       break;
+       while ((inb(dev->data) & 1) != 0)
+               if (count > 255)
+                       break;
+
+       count = inb(dev->data);
+
+       if (count & 2)
+               return -1;
+       return count;
+}
+
+static int pms_i2c_read(struct pms *dev, int slave, int sub)
+{
+       int i;
+
+       for (i = 0; i < dev->i2c_count; i++) {
+               if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub)
+                       return dev->i2cinfo[i].data;
+       }
+       return 0;
+}
+
+
+static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or)
+{
+       u8 tmp;
+
+       tmp = pms_i2c_read(dev, slave, sub);
+       tmp = (tmp & and) | or;
+       pms_i2c_write(dev, slave, sub, tmp);
+}
+
+/*
+ *     Control functions
+ */
+
+
+static void pms_videosource(struct pms *dev, short source)
+{
+       switch (dev->decoder) {
+       case MOTOROLA:
+               break;
+       case PHILIPS2:
+               pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0);
+               break;
+       case PHILIPS1:
+               break;
+       }
+       mvv_write(dev, 0x2E, 0x31);
+       /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30);
+          But could not make this work correctly. Only Composite input
+          worked for me. */
+}
+
+static void pms_hue(struct pms *dev, short hue)
+{
+       switch (dev->decoder) {
+       case MOTOROLA:
+               pms_i2c_write(dev, 0x8a, 0x00, hue);
+               break;
+       case PHILIPS2:
+               pms_i2c_write(dev, 0x8a, 0x07, hue);
+               break;
+       case PHILIPS1:
+               pms_i2c_write(dev, 0x42, 0x07, hue);
+               break;
+       }
+}
+
+static void pms_saturation(struct pms *dev, short sat)
+{
+       switch (dev->decoder) {
+       case MOTOROLA:
+               pms_i2c_write(dev, 0x8a, 0x00, sat);
+               break;
+       case PHILIPS1:
+               pms_i2c_write(dev, 0x42, 0x12, sat);
+               break;
+       }
+}
+
+
+static void pms_contrast(struct pms *dev, short contrast)
+{
+       switch (dev->decoder) {
+       case MOTOROLA:
+               pms_i2c_write(dev, 0x8a, 0x00, contrast);
+               break;
+       case PHILIPS1:
+               pms_i2c_write(dev, 0x42, 0x13, contrast);
+               break;
+       }
+}
+
+static void pms_brightness(struct pms *dev, short brightness)
+{
+       switch (dev->decoder) {
+       case MOTOROLA:
+               pms_i2c_write(dev, 0x8a, 0x00, brightness);
+               pms_i2c_write(dev, 0x8a, 0x00, brightness);
+               pms_i2c_write(dev, 0x8a, 0x00, brightness);
+               break;
+       case PHILIPS1:
+               pms_i2c_write(dev, 0x42, 0x19, brightness);
+               break;
+       }
+}
+
+
+static void pms_format(struct pms *dev, short format)
+{
+       int target;
+
+       dev->standard = format;
+
+       if (dev->decoder == PHILIPS1)
+               target = 0x42;
+       else if (dev->decoder == PHILIPS2)
+               target = 0x8a;
+       else
+               return;
+
+       switch (format) {
+       case 0: /* Auto */
+               pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
+               pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80);
+               break;
+       case 1: /* NTSC */
+               pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
+               pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40);
+               break;
+       case 2: /* PAL */
+               pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
+               pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
+               break;
+       case 3: /* SECAM */
+               pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01);
+               pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
+               break;
+       }
+}
+
+#ifdef FOR_FUTURE_EXPANSION
+
+/*
+ *     These features of the PMS card are not currently exposes. They
+ *     could become a private v4l ioctl for PMSCONFIG or somesuch if
+ *     people need it. We also don't yet use the PMS interrupt.
+ */
+
+static void pms_hstart(struct pms *dev, short start)
+{
+       switch (dev->decoder) {
+       case PHILIPS1:
+               pms_i2c_write(dev, 0x8a, 0x05, start);
+               pms_i2c_write(dev, 0x8a, 0x18, start);
+               break;
+       case PHILIPS2:
+               pms_i2c_write(dev, 0x42, 0x05, start);
+               pms_i2c_write(dev, 0x42, 0x18, start);
+               break;
+       }
+}
+
+/*
+ *     Bandpass filters
+ */
+
+static void pms_bandpass(struct pms *dev, short pass)
+{
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4);
+}
+
+static void pms_antisnow(struct pms *dev, short snow)
+{
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2);
+}
+
+static void pms_sharpness(struct pms *dev, short sharp)
+{
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03);
+}
+
+static void pms_chromaagc(struct pms *dev, short agc)
+{
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5);
+}
+
+static void pms_vertnoise(struct pms *dev, short noise)
+{
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3);
+}
+
+static void pms_forcecolour(struct pms *dev, short colour)
+{
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7);
+}
+
+static void pms_antigamma(struct pms *dev, short gamma)
+{
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7);
+}
+
+static void pms_prefilter(struct pms *dev, short filter)
+{
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6);
+}
+
+static void pms_hfilter(struct pms *dev, short filter)
+{
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5);
+}
+
+static void pms_vfilter(struct pms *dev, short filter)
+{
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5);
+}
+
+static void pms_killcolour(struct pms *dev, short colour)
+{
+       if (dev->decoder == PHILIPS2) {
+               pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3);
+               pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3);
+       } else if (dev->decoder == PHILIPS1) {
+               pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3);
+               pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3);
+       }
+}
+
+static void pms_chromagain(struct pms *dev, short chroma)
+{
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_write(dev, 0x8a, 0x11, chroma);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_write(dev, 0x42, 0x11, chroma);
+}
+
+
+static void pms_spacialcompl(struct pms *dev, short data)
+{
+       mvv_write(dev, 0x3b, data);
+}
+
+static void pms_spacialcomph(struct pms *dev, short data)
+{
+       mvv_write(dev, 0x3a, data);
+}
+
+static void pms_vstart(struct pms *dev, short start)
+{
+       mvv_write(dev, 0x16, start);
+       mvv_write(dev, 0x17, (start >> 8) & 0x01);
+}
+
+#endif
+
+static void pms_secamcross(struct pms *dev, short cross)
+{
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5);
+}
+
+
+static void pms_swsense(struct pms *dev, short sense)
+{
+       if (dev->decoder == PHILIPS2) {
+               pms_i2c_write(dev, 0x8a, 0x0a, sense);
+               pms_i2c_write(dev, 0x8a, 0x0b, sense);
+       } else if (dev->decoder == PHILIPS1) {
+               pms_i2c_write(dev, 0x42, 0x0a, sense);
+               pms_i2c_write(dev, 0x42, 0x0b, sense);
+       }
+}
+
+
+static void pms_framerate(struct pms *dev, short frr)
+{
+       int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25;
+
+       if (frr == 0)
+               return;
+       fps = fps/frr;
+       mvv_write(dev, 0x14, 0x80 | fps);
+       mvv_write(dev, 0x15, 1);
+}
+
+static void pms_vert(struct pms *dev, u8 deciden, u8 decinum)
+{
+       mvv_write(dev, 0x1c, deciden);  /* Denominator */
+       mvv_write(dev, 0x1d, decinum);  /* Numerator */
+}
+
+/*
+ *     Turn 16bit ratios into best small ratio the chipset can grok
+ */
+
+static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden)
+{
+       /* Knock it down by / 5 once */
+       if (decinum % 5 == 0) {
+               deciden /= 5;
+               decinum /= 5;
+       }
+       /*
+        *      3's
+        */
+       while (decinum % 3 == 0 && deciden % 3 == 0) {
+               deciden /= 3;
+               decinum /= 3;
+       }
+       /*
+        *      2's
+        */
+       while (decinum % 2 == 0 && deciden % 2 == 0) {
+               decinum /= 2;
+               deciden /= 2;
+       }
+       /*
+        *      Fudgyify
+        */
+       while (deciden > 32) {
+               deciden /= 2;
+               decinum = (decinum + 1) / 2;
+       }
+       if (deciden == 32)
+               deciden--;
+       pms_vert(dev, deciden, decinum);
+}
+
+static void pms_horzdeci(struct pms *dev, short decinum, short deciden)
+{
+       if (decinum <= 512) {
+               if (decinum % 5 == 0) {
+                       decinum /= 5;
+                       deciden /= 5;
+               }
+       } else {
+               decinum = 512;
+               deciden = 640;  /* 768 would be ideal */
+       }
+
+       while (((decinum | deciden) & 1) == 0) {
+               decinum >>= 1;
+               deciden >>= 1;
+       }
+       while (deciden > 32) {
+               deciden >>= 1;
+               decinum = (decinum + 1) >> 1;
+       }
+       if (deciden == 32)
+               deciden--;
+
+       mvv_write(dev, 0x24, 0x80 | deciden);
+       mvv_write(dev, 0x25, decinum);
+}
+
+static void pms_resolution(struct pms *dev, short width, short height)
+{
+       int fg_height;
+
+       fg_height = height;
+       if (fg_height > 280)
+               fg_height = 280;
+
+       mvv_write(dev, 0x18, fg_height);
+       mvv_write(dev, 0x19, fg_height >> 8);
+
+       if (dev->std & V4L2_STD_525_60) {
+               mvv_write(dev, 0x1a, 0xfc);
+               mvv_write(dev, 0x1b, 0x00);
+               if (height > fg_height)
+                       pms_vertdeci(dev, 240, 240);
+               else
+                       pms_vertdeci(dev, fg_height, 240);
+       } else {
+               mvv_write(dev, 0x1a, 0x1a);
+               mvv_write(dev, 0x1b, 0x01);
+               if (fg_height > 256)
+                       pms_vertdeci(dev, 270, 270);
+               else
+                       pms_vertdeci(dev, fg_height, 270);
+       }
+       mvv_write(dev, 0x12, 0);
+       mvv_write(dev, 0x13, MVVMEMORYWIDTH);
+       mvv_write(dev, 0x42, 0x00);
+       mvv_write(dev, 0x43, 0x00);
+       mvv_write(dev, 0x44, MVVMEMORYWIDTH);
+
+       mvv_write(dev, 0x22, width + 8);
+       mvv_write(dev, 0x23, (width + 8) >> 8);
+
+       if (dev->std & V4L2_STD_525_60)
+               pms_horzdeci(dev, width, 640);
+       else
+               pms_horzdeci(dev, width + 8, 768);
+
+       mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe);
+       mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01);
+       mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd);
+       mvv_write(dev, 0x32, 0x00);
+       mvv_write(dev, 0x33, MVVMEMORYWIDTH);
+}
+
+
+/*
+ *     Set Input
+ */
+
+static void pms_vcrinput(struct pms *dev, short input)
+{
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7);
+}
+
+
+static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count)
+{
+       int y;
+       int dw = 2 * dev->width;
+       char *tmp; /* using a temp buffer is faster than direct  */
+       int cnt = 0;
+       int len = 0;
+       unsigned char r8 = 0x5;  /* value for reg8  */
+
+       tmp = kmalloc(dw + 32, GFP_KERNEL);
+       if (!tmp)
+               return 0;
+
+       if (rgb555)
+               r8 |= 0x20; /* else use untranslated rgb = 565 */
+       mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */
+
+/*     printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
+
+       for (y = 0; y < dev->height; y++) {
+               writeb(0, dev->mem);  /* synchronisiert neue Zeile */
+
+               /*
+                *      This is in truth a fifo, be very careful as if you
+                *      forgot this odd things will occur 8)
+                */
+
+               memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word   */
+               cnt -= dev->height;
+               while (cnt <= 0) {
+                       /*
+                        *      Don't copy too far
+                        */
+                       int dt = dw;
+                       if (dt + len > count)
+                               dt = count - len;
+                       cnt += dev->height;
+                       if (copy_to_user(buf, tmp + 32, dt))
+                               return len ? len : -EFAULT;
+                       buf += dt;
+                       len += dt;
+               }
+       }
+       kfree(tmp);
+       return len;
+}
+
+
+/*
+ *     Video4linux interfacing
+ */
+
+static int pms_querycap(struct file *file, void  *priv,
+                                       struct v4l2_capability *vcap)
+{
+       struct pms *dev = video_drvdata(file);
+
+       strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver));
+       strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card));
+       snprintf(vcap->bus_info, sizeof(vcap->bus_info),
+                       "ISA:%s", dev->v4l2_dev.name);
+       vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+       vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
+       return 0;
+}
+
+static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+       static const char *inputs[4] = {
+               "Composite",
+               "S-Video",
+               "Composite (VCR)",
+               "S-Video (VCR)"
+       };
+
+       if (vin->index > 3)
+               return -EINVAL;
+       strlcpy(vin->name, inputs[vin->index], sizeof(vin->name));
+       vin->type = V4L2_INPUT_TYPE_CAMERA;
+       vin->audioset = 0;
+       vin->tuner = 0;
+       vin->std = V4L2_STD_ALL;
+       vin->status = 0;
+       return 0;
+}
+
+static int pms_g_input(struct file *file, void *fh, unsigned int *inp)
+{
+       struct pms *dev = video_drvdata(file);
+
+       *inp = dev->input;
+       return 0;
+}
+
+static int pms_s_input(struct file *file, void *fh, unsigned int inp)
+{
+       struct pms *dev = video_drvdata(file);
+
+       if (inp > 3)
+               return -EINVAL;
+
+       dev->input = inp;
+       pms_videosource(dev, inp & 1);
+       pms_vcrinput(dev, inp >> 1);
+       return 0;
+}
+
+static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+       struct pms *dev = video_drvdata(file);
+
+       *std = dev->std;
+       return 0;
+}
+
+static int pms_s_std(struct file *file, void *fh, v4l2_std_id std)
+{
+       struct pms *dev = video_drvdata(file);
+       int ret = 0;
+
+       dev->std = std;
+       if (dev->std & V4L2_STD_NTSC) {
+               pms_framerate(dev, 30);
+               pms_secamcross(dev, 0);
+               pms_format(dev, 1);
+       } else if (dev->std & V4L2_STD_PAL) {
+               pms_framerate(dev, 25);
+               pms_secamcross(dev, 0);
+               pms_format(dev, 2);
+       } else if (dev->std & V4L2_STD_SECAM) {
+               pms_framerate(dev, 25);
+               pms_secamcross(dev, 1);
+               pms_format(dev, 2);
+       } else {
+               ret = -EINVAL;
+       }
+       /*
+       switch (v->mode) {
+       case VIDEO_MODE_AUTO:
+               pms_framerate(dev, 25);
+               pms_secamcross(dev, 0);
+               pms_format(dev, 0);
+               break;
+       }*/
+       return ret;
+}
+
+static int pms_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct pms *dev = container_of(ctrl->handler, struct pms, hdl);
+       int ret = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               pms_brightness(dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               pms_contrast(dev, ctrl->val);
+               break;
+       case V4L2_CID_SATURATION:
+               pms_saturation(dev, ctrl->val);
+               break;
+       case V4L2_CID_HUE:
+               pms_hue(dev, ctrl->val);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct pms *dev = video_drvdata(file);
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+       pix->width = dev->width;
+       pix->height = dev->height;
+       pix->pixelformat = dev->width == 15 ?
+                           V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565;
+       pix->field = V4L2_FIELD_NONE;
+       pix->bytesperline = 2 * dev->width;
+       pix->sizeimage = 2 * dev->width * dev->height;
+       /* Just a guess */
+       pix->colorspace = V4L2_COLORSPACE_SRGB;
+       return 0;
+}
+
+static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+       if (pix->height < 16 || pix->height > 480)
+               return -EINVAL;
+       if (pix->width < 16 || pix->width > 640)
+               return -EINVAL;
+       if (pix->pixelformat != V4L2_PIX_FMT_RGB555 &&
+           pix->pixelformat != V4L2_PIX_FMT_RGB565)
+               return -EINVAL;
+       pix->field = V4L2_FIELD_NONE;
+       pix->bytesperline = 2 * pix->width;
+       pix->sizeimage = 2 * pix->width * pix->height;
+       /* Just a guess */
+       pix->colorspace = V4L2_COLORSPACE_SRGB;
+       return 0;
+}
+
+static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct pms *dev = video_drvdata(file);
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+       int ret = pms_try_fmt_vid_cap(file, fh, fmt);
+
+       if (ret)
+               return ret;
+       dev->width = pix->width;
+       dev->height = pix->height;
+       dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16;
+       pms_resolution(dev, dev->width, dev->height);
+       /* Ok we figured out what to use from our wide choice */
+       return 0;
+}
+
+static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+       static struct v4l2_fmtdesc formats[] = {
+               { 0, 0, 0,
+                 "RGB 5:5:5", V4L2_PIX_FMT_RGB555,
+                 { 0, 0, 0, 0 }
+               },
+               { 1, 0, 0,
+                 "RGB 5:6:5", V4L2_PIX_FMT_RGB565,
+                 { 0, 0, 0, 0 }
+               },
+       };
+       enum v4l2_buf_type type = fmt->type;
+
+       if (fmt->index > 1)
+               return -EINVAL;
+
+       *fmt = formats[fmt->index];
+       fmt->type = type;
+       return 0;
+}
+
+static ssize_t pms_read(struct file *file, char __user *buf,
+                   size_t count, loff_t *ppos)
+{
+       struct pms *dev = video_drvdata(file);
+       int len;
+
+       len = pms_capture(dev, buf, (dev->depth == 15), count);
+       return len;
+}
+
+static unsigned int pms_poll(struct file *file, struct poll_table_struct *wait)
+{
+       struct v4l2_fh *fh = file->private_data;
+       unsigned int res = POLLIN | POLLRDNORM;
+
+       if (v4l2_event_pending(fh))
+               res |= POLLPRI;
+       poll_wait(file, &fh->wait, wait);
+       return res;
+}
+
+static const struct v4l2_file_operations pms_fops = {
+       .owner          = THIS_MODULE,
+       .open           = v4l2_fh_open,
+       .release        = v4l2_fh_release,
+       .poll           = pms_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .read           = pms_read,
+};
+
+static const struct v4l2_ioctl_ops pms_ioctl_ops = {
+       .vidioc_querycap            = pms_querycap,
+       .vidioc_g_input             = pms_g_input,
+       .vidioc_s_input             = pms_s_input,
+       .vidioc_enum_input          = pms_enum_input,
+       .vidioc_g_std               = pms_g_std,
+       .vidioc_s_std               = pms_s_std,
+       .vidioc_enum_fmt_vid_cap    = pms_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap       = pms_g_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap       = pms_s_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap     = pms_try_fmt_vid_cap,
+       .vidioc_subscribe_event     = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event   = v4l2_event_unsubscribe,
+};
+
+/*
+ *     Probe for and initialise the Mediavision PMS
+ */
+
+static int init_mediavision(struct pms *dev)
+{
+       int idec, decst;
+       int i;
+       static const unsigned char i2c_defs[] = {
+               0x4c, 0x30, 0x00, 0xe8,
+               0xb6, 0xe2, 0x00, 0x00,
+               0xff, 0xff, 0x00, 0x00,
+               0x00, 0x00, 0x78, 0x98,
+               0x00, 0x00, 0x00, 0x00,
+               0x34, 0x0a, 0xf4, 0xce,
+               0xe4
+       };
+
+       dev->mem = ioremap(mem_base, 0x800);
+       if (!dev->mem)
+               return -ENOMEM;
+
+       if (!request_region(0x9a01, 1, "Mediavision PMS config")) {
+               printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n");
+               iounmap(dev->mem);
+               return -EBUSY;
+       }
+       if (!request_region(dev->io, 3, "Mediavision PMS")) {
+               printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io);
+               release_region(0x9a01, 1);
+               iounmap(dev->mem);
+               return -EBUSY;
+       }
+       outb(0xb8, 0x9a01);             /* Unlock */
+       outb(dev->io >> 4, 0x9a01);     /* Set IO port */
+
+
+       decst = pms_i2c_stat(dev, 0x43);
+
+       if (decst != -1)
+               idec = 2;
+       else if (pms_i2c_stat(dev, 0xb9) != -1)
+               idec = 3;
+       else if (pms_i2c_stat(dev, 0x8b) != -1)
+               idec = 1;
+       else
+               idec = 0;
+
+       printk(KERN_INFO "PMS type is %d\n", idec);
+       if (idec == 0) {
+               release_region(dev->io, 3);
+               release_region(0x9a01, 1);
+               iounmap(dev->mem);
+               return -ENODEV;
+       }
+
+       /*
+        *      Ok we have a PMS of some sort
+        */
+
+       mvv_write(dev, 0x04, mem_base >> 12);   /* Set the memory area */
+
+       /* Ok now load the defaults */
+
+       for (i = 0; i < 0x19; i++) {
+               if (i2c_defs[i] == 0xff)
+                       pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00);
+               else
+                       pms_i2c_write(dev, 0x8a, i, i2c_defs[i]);
+       }
+
+       pms_i2c_write(dev, 0xb8, 0x00, 0x12);
+       pms_i2c_write(dev, 0xb8, 0x04, 0x00);
+       pms_i2c_write(dev, 0xb8, 0x07, 0x00);
+       pms_i2c_write(dev, 0xb8, 0x08, 0x00);
+       pms_i2c_write(dev, 0xb8, 0x09, 0xff);
+       pms_i2c_write(dev, 0xb8, 0x0a, 0x00);
+       pms_i2c_write(dev, 0xb8, 0x0b, 0x10);
+       pms_i2c_write(dev, 0xb8, 0x10, 0x03);
+
+       mvv_write(dev, 0x01, 0x00);
+       mvv_write(dev, 0x05, 0xa0);
+       mvv_write(dev, 0x08, 0x25);
+       mvv_write(dev, 0x09, 0x00);
+       mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH);
+
+       mvv_write(dev, 0x10, 0x02);
+       mvv_write(dev, 0x1e, 0x0c);
+       mvv_write(dev, 0x1f, 0x03);
+       mvv_write(dev, 0x26, 0x06);
+
+       mvv_write(dev, 0x2b, 0x00);
+       mvv_write(dev, 0x2c, 0x20);
+       mvv_write(dev, 0x2d, 0x00);
+       mvv_write(dev, 0x2f, 0x70);
+       mvv_write(dev, 0x32, 0x00);
+       mvv_write(dev, 0x33, MVVMEMORYWIDTH);
+       mvv_write(dev, 0x34, 0x00);
+       mvv_write(dev, 0x35, 0x00);
+       mvv_write(dev, 0x3a, 0x80);
+       mvv_write(dev, 0x3b, 0x10);
+       mvv_write(dev, 0x20, 0x00);
+       mvv_write(dev, 0x21, 0x00);
+       mvv_write(dev, 0x30, 0x22);
+       return 0;
+}
+
+/*
+ *     Initialization and module stuff
+ */
+
+#ifndef MODULE
+static int enable;
+module_param(enable, int, 0);
+#endif
+
+static const struct v4l2_ctrl_ops pms_ctrl_ops = {
+       .s_ctrl = pms_s_ctrl,
+};
+
+static int pms_probe(struct device *pdev, unsigned int card)
+{
+       struct pms *dev;
+       struct v4l2_device *v4l2_dev;
+       struct v4l2_ctrl_handler *hdl;
+       int res;
+
+#ifndef MODULE
+       if (!enable) {
+               pr_err("PMS: not enabled, use pms.enable=1 to probe\n");
+               return -ENODEV;
+       }
+#endif
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (dev == NULL)
+               return -ENOMEM;
+
+       dev->decoder = PHILIPS2;
+       dev->io = io_port;
+       dev->data = io_port + 1;
+       v4l2_dev = &dev->v4l2_dev;
+       hdl = &dev->hdl;
+
+       res = v4l2_device_register(pdev, v4l2_dev);
+       if (res < 0) {
+               v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+               goto free_dev;
+       }
+       v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.05\n");
+
+       res = init_mediavision(dev);
+       if (res) {
+               v4l2_err(v4l2_dev, "Board not found.\n");
+               goto free_io;
+       }
+
+       v4l2_ctrl_handler_init(hdl, 4);
+       v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 139);
+       v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 255, 1, 70);
+       v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 255, 1, 64);
+       v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
+                       V4L2_CID_HUE, 0, 255, 1, 0);
+       if (hdl->error) {
+               res = hdl->error;
+               goto free_hdl;
+       }
+
+       mutex_init(&dev->lock);
+       strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
+       dev->vdev.v4l2_dev = v4l2_dev;
+       dev->vdev.ctrl_handler = hdl;
+       dev->vdev.fops = &pms_fops;
+       dev->vdev.ioctl_ops = &pms_ioctl_ops;
+       dev->vdev.release = video_device_release_empty;
+       dev->vdev.lock = &dev->lock;
+       dev->vdev.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
+       video_set_drvdata(&dev->vdev, dev);
+       dev->std = V4L2_STD_NTSC_M;
+       dev->height = 240;
+       dev->width = 320;
+       dev->depth = 16;
+       pms_swsense(dev, 75);
+       pms_resolution(dev, 320, 240);
+       pms_videosource(dev, 0);
+       pms_vcrinput(dev, 0);
+       v4l2_ctrl_handler_setup(hdl);
+       res = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr);
+       if (res >= 0)
+               return 0;
+
+free_hdl:
+       v4l2_ctrl_handler_free(hdl);
+       v4l2_device_unregister(&dev->v4l2_dev);
+free_io:
+       release_region(dev->io, 3);
+       release_region(0x9a01, 1);
+       iounmap(dev->mem);
+free_dev:
+       kfree(dev);
+       return res;
+}
+
+static int pms_remove(struct device *pdev, unsigned int card)
+{
+       struct pms *dev = dev_get_drvdata(pdev);
+
+       video_unregister_device(&dev->vdev);
+       v4l2_ctrl_handler_free(&dev->hdl);
+       release_region(dev->io, 3);
+       release_region(0x9a01, 1);
+       iounmap(dev->mem);
+       return 0;
+}
+
+static struct isa_driver pms_driver = {
+       .probe          = pms_probe,
+       .remove         = pms_remove,
+       .driver         = {
+               .name   = "pms",
+       },
+};
+
+static int __init pms_init(void)
+{
+       return isa_register_driver(&pms_driver, 1);
+}
+
+static void __exit pms_exit(void)
+{
+       isa_unregister_driver(&pms_driver);
+}
+
+module_init(pms_init);
+module_exit(pms_exit);
diff --git a/drivers/staging/media/parport/w9966.c b/drivers/staging/media/parport/w9966.c
new file mode 100644 (file)
index 0000000..f7502f3
--- /dev/null
@@ -0,0 +1,980 @@
+/*
+       Winbond w9966cf Webcam parport driver.
+
+       Version 0.33
+
+       Copyright (C) 2001 Jakob Kemi <jakob.kemi@post.utfors.se>
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation; either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program; if not, write to the Free Software
+       Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+       Supported devices:
+       *Lifeview FlyCam Supra (using the Philips saa7111a chip)
+
+       Does any other model using the w9966 interface chip exist ?
+
+       Todo:
+
+       *Add a working EPP mode, since DMA ECP read isn't implemented
+       in the parport drivers. (That's why it's so sloow)
+
+       *Add support for other ccd-control chips than the saa7111
+       please send me feedback on what kind of chips you have.
+
+       *Add proper probing. I don't know what's wrong with the IEEE1284
+       parport drivers but (IEEE1284_MODE_NIBBLE|IEEE1284_DEVICE_ID)
+       and nibble read seems to be broken for some peripherals.
+
+       *Add probing for onboard SRAM, port directions etc. (if possible)
+
+       *Add support for the hardware compressed modes (maybe using v4l2)
+
+       *Fix better support for the capture window (no skewed images, v4l
+       interface to capt. window)
+
+       *Probably some bugs that I don't know of
+
+       Please support me by sending feedback!
+
+       Changes:
+
+       Alan Cox:       Removed RGB mode for kernel merge, added THIS_MODULE
+                       and owner support for newer module locks
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <linux/parport.h>
+
+/*#define DEBUG*/                              /* Undef me for production */
+
+#ifdef DEBUG
+#define DPRINTF(x, a...) printk(KERN_DEBUG "W9966: %s(): "x, __func__ , ##a)
+#else
+#define DPRINTF(x...)
+#endif
+
+/*
+ *     Defines, simple typedefs etc.
+ */
+
+#define W9966_DRIVERNAME       "W9966CF Webcam"
+#define W9966_MAXCAMS          4       /* Maximum number of cameras */
+#define W9966_RBUFFER          2048    /* Read buffer (must be an even number) */
+#define W9966_SRAMSIZE         131072  /* 128kb */
+#define W9966_SRAMID           0x02    /* check w9966cf.pdf */
+
+/* Empirically determined window limits */
+#define W9966_WND_MIN_X                16
+#define W9966_WND_MIN_Y                14
+#define W9966_WND_MAX_X                705
+#define W9966_WND_MAX_Y                253
+#define W9966_WND_MAX_W                (W9966_WND_MAX_X - W9966_WND_MIN_X)
+#define W9966_WND_MAX_H                (W9966_WND_MAX_Y - W9966_WND_MIN_Y)
+
+/* Keep track of our current state */
+#define W9966_STATE_PDEV       0x01
+#define W9966_STATE_CLAIMED    0x02
+#define W9966_STATE_VDEV       0x04
+
+#define W9966_I2C_W_ID         0x48
+#define W9966_I2C_R_ID         0x49
+#define W9966_I2C_R_DATA       0x08
+#define W9966_I2C_R_CLOCK      0x04
+#define W9966_I2C_W_DATA       0x02
+#define W9966_I2C_W_CLOCK      0x01
+
+struct w9966 {
+       struct v4l2_device v4l2_dev;
+       struct v4l2_ctrl_handler hdl;
+       unsigned char dev_state;
+       unsigned char i2c_state;
+       unsigned short ppmode;
+       struct parport *pport;
+       struct pardevice *pdev;
+       struct video_device vdev;
+       unsigned short width;
+       unsigned short height;
+       unsigned char brightness;
+       signed char contrast;
+       signed char color;
+       signed char hue;
+       struct mutex lock;
+};
+
+/*
+ *     Module specific properties
+ */
+
+MODULE_AUTHOR("Jakob Kemi <jakob.kemi@post.utfors.se>");
+MODULE_DESCRIPTION("Winbond w9966cf WebCam driver (0.32)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.33.1");
+
+#ifdef MODULE
+static char *pardev[] = {[0 ... W9966_MAXCAMS] = ""};
+#else
+static char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"};
+#endif
+module_param_array(pardev, charp, NULL, 0);
+MODULE_PARM_DESC(pardev, "pardev: where to search for\n"
+               "\teach camera. 'aggressive' means brute-force search.\n"
+               "\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n"
+               "\tcam 1 to parport3 and search every parport for cam 2 etc...");
+
+static int parmode;
+module_param(parmode, int, 0);
+MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp");
+
+static int video_nr = -1;
+module_param(video_nr, int, 0);
+
+static struct w9966 w9966_cams[W9966_MAXCAMS];
+
+/*
+ *     Private function defines
+ */
+
+
+/* Set camera phase flags, so we know what to uninit when terminating */
+static inline void w9966_set_state(struct w9966 *cam, int mask, int val)
+{
+       cam->dev_state = (cam->dev_state & ~mask) ^ val;
+}
+
+/* Get camera phase flags */
+static inline int w9966_get_state(struct w9966 *cam, int mask, int val)
+{
+       return ((cam->dev_state & mask) == val);
+}
+
+/* Claim parport for ourself */
+static void w9966_pdev_claim(struct w9966 *cam)
+{
+       if (w9966_get_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED))
+               return;
+       parport_claim_or_block(cam->pdev);
+       w9966_set_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED);
+}
+
+/* Release parport for others to use */
+static void w9966_pdev_release(struct w9966 *cam)
+{
+       if (w9966_get_state(cam, W9966_STATE_CLAIMED, 0))
+               return;
+       parport_release(cam->pdev);
+       w9966_set_state(cam, W9966_STATE_CLAIMED, 0);
+}
+
+/* Read register from W9966 interface-chip
+   Expects a claimed pdev
+   -1 on error, else register data (byte) */
+static int w9966_read_reg(struct w9966 *cam, int reg)
+{
+       /* ECP, read, regtransfer, REG, REG, REG, REG, REG */
+       const unsigned char addr = 0x80 | (reg & 0x1f);
+       unsigned char val;
+
+       if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
+               return -1;
+       if (parport_write(cam->pport, &addr, 1) != 1)
+               return -1;
+       if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
+               return -1;
+       if (parport_read(cam->pport, &val, 1) != 1)
+               return -1;
+
+       return val;
+}
+
+/* Write register to W9966 interface-chip
+   Expects a claimed pdev
+   -1 on error */
+static int w9966_write_reg(struct w9966 *cam, int reg, int data)
+{
+       /* ECP, write, regtransfer, REG, REG, REG, REG, REG */
+       const unsigned char addr = 0xc0 | (reg & 0x1f);
+       const unsigned char val = data;
+
+       if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
+               return -1;
+       if (parport_write(cam->pport, &addr, 1) != 1)
+               return -1;
+       if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
+               return -1;
+       if (parport_write(cam->pport, &val, 1) != 1)
+               return -1;
+
+       return 0;
+}
+
+/*
+ *     Ugly and primitive i2c protocol functions
+ */
+
+/* Sets the data line on the i2c bus.
+   Expects a claimed pdev. */
+static void w9966_i2c_setsda(struct w9966 *cam, int state)
+{
+       if (state)
+               cam->i2c_state |= W9966_I2C_W_DATA;
+       else
+               cam->i2c_state &= ~W9966_I2C_W_DATA;
+
+       w9966_write_reg(cam, 0x18, cam->i2c_state);
+       udelay(5);
+}
+
+/* Get peripheral clock line
+   Expects a claimed pdev. */
+static int w9966_i2c_getscl(struct w9966 *cam)
+{
+       const unsigned char state = w9966_read_reg(cam, 0x18);
+       return ((state & W9966_I2C_R_CLOCK) > 0);
+}
+
+/* Sets the clock line on the i2c bus.
+   Expects a claimed pdev. -1 on error */
+static int w9966_i2c_setscl(struct w9966 *cam, int state)
+{
+       unsigned long timeout;
+
+       if (state)
+               cam->i2c_state |= W9966_I2C_W_CLOCK;
+       else
+               cam->i2c_state &= ~W9966_I2C_W_CLOCK;
+
+       w9966_write_reg(cam, 0x18, cam->i2c_state);
+       udelay(5);
+
+       /* we go to high, we also expect the peripheral to ack. */
+       if (state) {
+               timeout = jiffies + 100;
+               while (!w9966_i2c_getscl(cam)) {
+                       if (time_after(jiffies, timeout))
+                               return -1;
+               }
+       }
+       return 0;
+}
+
+#if 0
+/* Get peripheral data line
+   Expects a claimed pdev. */
+static int w9966_i2c_getsda(struct w9966 *cam)
+{
+       const unsigned char state = w9966_read_reg(cam, 0x18);
+       return ((state & W9966_I2C_R_DATA) > 0);
+}
+#endif
+
+/* Write a byte with ack to the i2c bus.
+   Expects a claimed pdev. -1 on error */
+static int w9966_i2c_wbyte(struct w9966 *cam, int data)
+{
+       int i;
+
+       for (i = 7; i >= 0; i--) {
+               w9966_i2c_setsda(cam, (data >> i) & 0x01);
+
+               if (w9966_i2c_setscl(cam, 1) == -1)
+                       return -1;
+               w9966_i2c_setscl(cam, 0);
+       }
+
+       w9966_i2c_setsda(cam, 1);
+
+       if (w9966_i2c_setscl(cam, 1) == -1)
+               return -1;
+       w9966_i2c_setscl(cam, 0);
+
+       return 0;
+}
+
+/* Read a data byte with ack from the i2c-bus
+   Expects a claimed pdev. -1 on error */
+#if 0
+static int w9966_i2c_rbyte(struct w9966 *cam)
+{
+       unsigned char data = 0x00;
+       int i;
+
+       w9966_i2c_setsda(cam, 1);
+
+       for (i = 0; i < 8; i++) {
+               if (w9966_i2c_setscl(cam, 1) == -1)
+                       return -1;
+               data = data << 1;
+               if (w9966_i2c_getsda(cam))
+                       data |= 0x01;
+
+               w9966_i2c_setscl(cam, 0);
+       }
+       return data;
+}
+#endif
+
+/* Read a register from the i2c device.
+   Expects claimed pdev. -1 on error */
+#if 0
+static int w9966_read_reg_i2c(struct w9966 *cam, int reg)
+{
+       int data;
+
+       w9966_i2c_setsda(cam, 0);
+       w9966_i2c_setscl(cam, 0);
+
+       if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
+           w9966_i2c_wbyte(cam, reg) == -1)
+               return -1;
+
+       w9966_i2c_setsda(cam, 1);
+       if (w9966_i2c_setscl(cam, 1) == -1)
+               return -1;
+       w9966_i2c_setsda(cam, 0);
+       w9966_i2c_setscl(cam, 0);
+
+       if (w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1)
+               return -1;
+       data = w9966_i2c_rbyte(cam);
+       if (data == -1)
+               return -1;
+
+       w9966_i2c_setsda(cam, 0);
+
+       if (w9966_i2c_setscl(cam, 1) == -1)
+               return -1;
+       w9966_i2c_setsda(cam, 1);
+
+       return data;
+}
+#endif
+
+/* Write a register to the i2c device.
+   Expects claimed pdev. -1 on error */
+static int w9966_write_reg_i2c(struct w9966 *cam, int reg, int data)
+{
+       w9966_i2c_setsda(cam, 0);
+       w9966_i2c_setscl(cam, 0);
+
+       if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
+                       w9966_i2c_wbyte(cam, reg) == -1 ||
+                       w9966_i2c_wbyte(cam, data) == -1)
+               return -1;
+
+       w9966_i2c_setsda(cam, 0);
+       if (w9966_i2c_setscl(cam, 1) == -1)
+               return -1;
+
+       w9966_i2c_setsda(cam, 1);
+
+       return 0;
+}
+
+/* Find a good length for capture window (used both for W and H)
+   A bit ugly but pretty functional. The capture length
+   have to match the downscale */
+static int w9966_findlen(int near, int size, int maxlen)
+{
+       int bestlen = size;
+       int besterr = abs(near - bestlen);
+       int len;
+
+       for (len = size + 1; len < maxlen; len++) {
+               int err;
+               if (((64 * size) % len) != 0)
+                       continue;
+
+               err = abs(near - len);
+
+               /* Only continue as long as we keep getting better values */
+               if (err > besterr)
+                       break;
+
+               besterr = err;
+               bestlen = len;
+       }
+
+       return bestlen;
+}
+
+/* Modify capture window (if necessary)
+   and calculate downscaling
+   Return -1 on error */
+static int w9966_calcscale(int size, int min, int max, int *beg, int *end, unsigned char *factor)
+{
+       int maxlen = max - min;
+       int len = *end - *beg + 1;
+       int newlen = w9966_findlen(len, size, maxlen);
+       int err = newlen - len;
+
+       /* Check for bad format */
+       if (newlen > maxlen || newlen < size)
+               return -1;
+
+       /* Set factor (6 bit fixed) */
+       *factor = (64 * size) / newlen;
+       if (*factor == 64)
+               *factor = 0x00; /* downscale is disabled */
+       else
+               *factor |= 0x80; /* set downscale-enable bit */
+
+       /* Modify old beginning and end */
+       *beg -= err / 2;
+       *end += err - (err / 2);
+
+       /* Move window if outside borders */
+       if (*beg < min) {
+               *end += min - *beg;
+               *beg += min - *beg;
+       }
+       if (*end > max) {
+               *beg -= *end - max;
+               *end -= *end - max;
+       }
+
+       return 0;
+}
+
+/* Setup the cameras capture window etc.
+   Expects a claimed pdev
+   return -1 on error */
+static int w9966_setup(struct w9966 *cam, int x1, int y1, int x2, int y2, int w, int h)
+{
+       unsigned int i;
+       unsigned int enh_s, enh_e;
+       unsigned char scale_x, scale_y;
+       unsigned char regs[0x1c];
+       unsigned char saa7111_regs[] = {
+               0x21, 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00,
+               0x88, 0x10, 0x80, 0x40, 0x40, 0x00, 0x01, 0x00,
+               0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x71, 0xe7, 0x00, 0x00, 0xc0
+       };
+
+
+       if (w * h * 2 > W9966_SRAMSIZE) {
+               DPRINTF("capture window exceeds SRAM size!.\n");
+               w = 200; h = 160;       /* Pick default values */
+       }
+
+       w &= ~0x1;
+       if (w < 2)
+               w = 2;
+       if (h < 1)
+               h = 1;
+       if (w > W9966_WND_MAX_W)
+               w = W9966_WND_MAX_W;
+       if (h > W9966_WND_MAX_H)
+               h = W9966_WND_MAX_H;
+
+       cam->width = w;
+       cam->height = h;
+
+       enh_s = 0;
+       enh_e = w * h * 2;
+
+       /* Modify capture window if necessary and calculate downscaling */
+       if (w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 ||
+                       w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0)
+               return -1;
+
+       DPRINTF("%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n",
+                       w, h, x1, x2, y1, y2, scale_x & ~0x80, scale_y & ~0x80);
+
+       /* Setup registers */
+       regs[0x00] = 0x00;                      /* Set normal operation */
+       regs[0x01] = 0x18;                      /* Capture mode */
+       regs[0x02] = scale_y;                   /* V-scaling */
+       regs[0x03] = scale_x;                   /* H-scaling */
+
+       /* Capture window */
+       regs[0x04] = (x1 & 0x0ff);              /* X-start (8 low bits) */
+       regs[0x05] = (x1 & 0x300)>>8;           /* X-start (2 high bits) */
+       regs[0x06] = (y1 & 0x0ff);              /* Y-start (8 low bits) */
+       regs[0x07] = (y1 & 0x300)>>8;           /* Y-start (2 high bits) */
+       regs[0x08] = (x2 & 0x0ff);              /* X-end (8 low bits) */
+       regs[0x09] = (x2 & 0x300)>>8;           /* X-end (2 high bits) */
+       regs[0x0a] = (y2 & 0x0ff);              /* Y-end (8 low bits) */
+
+       regs[0x0c] = W9966_SRAMID;              /* SRAM-banks (1x 128kb) */
+
+       /* Enhancement layer */
+       regs[0x0d] = (enh_s & 0x000ff);         /* Enh. start (0-7) */
+       regs[0x0e] = (enh_s & 0x0ff00) >> 8;    /* Enh. start (8-15) */
+       regs[0x0f] = (enh_s & 0x70000) >> 16;   /* Enh. start (16-17/18??) */
+       regs[0x10] = (enh_e & 0x000ff);         /* Enh. end (0-7) */
+       regs[0x11] = (enh_e & 0x0ff00) >> 8;    /* Enh. end (8-15) */
+       regs[0x12] = (enh_e & 0x70000) >> 16;   /* Enh. end (16-17/18??) */
+
+       /* Misc */
+       regs[0x13] = 0x40;                      /* VEE control (raw 4:2:2) */
+       regs[0x17] = 0x00;                      /* ??? */
+       regs[0x18] = cam->i2c_state = 0x00;     /* Serial bus */
+       regs[0x19] = 0xff;                      /* I/O port direction control */
+       regs[0x1a] = 0xff;                      /* I/O port data register */
+       regs[0x1b] = 0x10;                      /* ??? */
+
+       /* SAA7111 chip settings */
+       saa7111_regs[0x0a] = cam->brightness;
+       saa7111_regs[0x0b] = cam->contrast;
+       saa7111_regs[0x0c] = cam->color;
+       saa7111_regs[0x0d] = cam->hue;
+
+       /* Reset (ECP-fifo & serial-bus) */
+       if (w9966_write_reg(cam, 0x00, 0x03) == -1)
+               return -1;
+
+       /* Write regs to w9966cf chip */
+       for (i = 0; i < 0x1c; i++)
+               if (w9966_write_reg(cam, i, regs[i]) == -1)
+                       return -1;
+
+       /* Write regs to saa7111 chip */
+       for (i = 0; i < 0x20; i++)
+               if (w9966_write_reg_i2c(cam, i, saa7111_regs[i]) == -1)
+                       return -1;
+
+       return 0;
+}
+
+/*
+ *     Video4linux interfacing
+ */
+
+static int cam_querycap(struct file *file, void  *priv,
+                                       struct v4l2_capability *vcap)
+{
+       struct w9966 *cam = video_drvdata(file);
+
+       strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver));
+       strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card));
+       strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
+       vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+       vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
+       return 0;
+}
+
+static int cam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+       if (vin->index > 0)
+               return -EINVAL;
+       strlcpy(vin->name, "Camera", sizeof(vin->name));
+       vin->type = V4L2_INPUT_TYPE_CAMERA;
+       vin->audioset = 0;
+       vin->tuner = 0;
+       vin->std = 0;
+       vin->status = 0;
+       return 0;
+}
+
+static int cam_g_input(struct file *file, void *fh, unsigned int *inp)
+{
+       *inp = 0;
+       return 0;
+}
+
+static int cam_s_input(struct file *file, void *fh, unsigned int inp)
+{
+       return (inp > 0) ? -EINVAL : 0;
+}
+
+static int cam_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct w9966 *cam =
+               container_of(ctrl->handler, struct w9966, hdl);
+       int ret = 0;
+
+       mutex_lock(&cam->lock);
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               cam->brightness = ctrl->val;
+               break;
+       case V4L2_CID_CONTRAST:
+               cam->contrast = ctrl->val;
+               break;
+       case V4L2_CID_SATURATION:
+               cam->color = ctrl->val;
+               break;
+       case V4L2_CID_HUE:
+               cam->hue = ctrl->val;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       if (ret == 0) {
+               w9966_pdev_claim(cam);
+
+               if (w9966_write_reg_i2c(cam, 0x0a, cam->brightness) == -1 ||
+                   w9966_write_reg_i2c(cam, 0x0b, cam->contrast) == -1 ||
+                   w9966_write_reg_i2c(cam, 0x0c, cam->color) == -1 ||
+                   w9966_write_reg_i2c(cam, 0x0d, cam->hue) == -1) {
+                       ret = -EIO;
+               }
+
+               w9966_pdev_release(cam);
+       }
+       mutex_unlock(&cam->lock);
+       return ret;
+}
+
+static int cam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct w9966 *cam = video_drvdata(file);
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+       pix->width = cam->width;
+       pix->height = cam->height;
+       pix->pixelformat = V4L2_PIX_FMT_YUYV;
+       pix->field = V4L2_FIELD_NONE;
+       pix->bytesperline = 2 * cam->width;
+       pix->sizeimage = 2 * cam->width * cam->height;
+       /* Just a guess */
+       pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+       return 0;
+}
+
+static int cam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+       if (pix->width < 2)
+               pix->width = 2;
+       if (pix->height < 1)
+               pix->height = 1;
+       if (pix->width > W9966_WND_MAX_W)
+               pix->width = W9966_WND_MAX_W;
+       if (pix->height > W9966_WND_MAX_H)
+               pix->height = W9966_WND_MAX_H;
+       pix->pixelformat = V4L2_PIX_FMT_YUYV;
+       pix->field = V4L2_FIELD_NONE;
+       pix->bytesperline = 2 * pix->width;
+       pix->sizeimage = 2 * pix->width * pix->height;
+       /* Just a guess */
+       pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+       return 0;
+}
+
+static int cam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct w9966 *cam = video_drvdata(file);
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+       int ret = cam_try_fmt_vid_cap(file, fh, fmt);
+
+       if (ret)
+               return ret;
+
+       mutex_lock(&cam->lock);
+       /* Update camera regs */
+       w9966_pdev_claim(cam);
+       ret = w9966_setup(cam, 0, 0, 1023, 1023, pix->width, pix->height);
+       w9966_pdev_release(cam);
+       mutex_unlock(&cam->lock);
+       return ret;
+}
+
+static int cam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+       static struct v4l2_fmtdesc formats[] = {
+               { 0, 0, 0,
+                 "YUV 4:2:2", V4L2_PIX_FMT_YUYV,
+                 { 0, 0, 0, 0 }
+               },
+       };
+       enum v4l2_buf_type type = fmt->type;
+
+       if (fmt->index > 0)
+               return -EINVAL;
+
+       *fmt = formats[fmt->index];
+       fmt->type = type;
+       return 0;
+}
+
+/* Capture data */
+static ssize_t w9966_v4l_read(struct file *file, char  __user *buf,
+               size_t count, loff_t *ppos)
+{
+       struct w9966 *cam = video_drvdata(file);
+       unsigned char addr = 0xa0;      /* ECP, read, CCD-transfer, 00000 */
+       unsigned char __user *dest = (unsigned char __user *)buf;
+       unsigned long dleft = count;
+       unsigned char *tbuf;
+
+       /* Why would anyone want more than this?? */
+       if (count > cam->width * cam->height * 2)
+               return -EINVAL;
+
+       mutex_lock(&cam->lock);
+       w9966_pdev_claim(cam);
+       w9966_write_reg(cam, 0x00, 0x02);       /* Reset ECP-FIFO buffer */
+       w9966_write_reg(cam, 0x00, 0x00);       /* Return to normal operation */
+       w9966_write_reg(cam, 0x01, 0x98);       /* Enable capture */
+
+       /* write special capture-addr and negotiate into data transfer */
+       if ((parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0) ||
+                       (parport_write(cam->pport, &addr, 1) != 1) ||
+                       (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0)) {
+               w9966_pdev_release(cam);
+               mutex_unlock(&cam->lock);
+               return -EFAULT;
+       }
+
+       tbuf = kmalloc(W9966_RBUFFER, GFP_KERNEL);
+       if (tbuf == NULL) {
+               count = -ENOMEM;
+               goto out;
+       }
+
+       while (dleft > 0) {
+               unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft;
+
+               if (parport_read(cam->pport, tbuf, tsize) < tsize) {
+                       count = -EFAULT;
+                       goto out;
+               }
+               if (copy_to_user(dest, tbuf, tsize) != 0) {
+                       count = -EFAULT;
+                       goto out;
+               }
+               dest += tsize;
+               dleft -= tsize;
+       }
+
+       w9966_write_reg(cam, 0x01, 0x18);       /* Disable capture */
+
+out:
+       kfree(tbuf);
+       w9966_pdev_release(cam);
+       mutex_unlock(&cam->lock);
+
+       return count;
+}
+
+static const struct v4l2_file_operations w9966_fops = {
+       .owner          = THIS_MODULE,
+       .open           = v4l2_fh_open,
+       .release        = v4l2_fh_release,
+       .poll           = v4l2_ctrl_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .read           = w9966_v4l_read,
+};
+
+static const struct v4l2_ioctl_ops w9966_ioctl_ops = {
+       .vidioc_querycap                    = cam_querycap,
+       .vidioc_g_input                     = cam_g_input,
+       .vidioc_s_input                     = cam_s_input,
+       .vidioc_enum_input                  = cam_enum_input,
+       .vidioc_enum_fmt_vid_cap            = cam_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap               = cam_g_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap               = cam_s_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap             = cam_try_fmt_vid_cap,
+       .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 cam_ctrl_ops = {
+       .s_ctrl = cam_s_ctrl,
+};
+
+
+/* Initialize camera device. Setup all internal flags, set a
+   default video mode, setup ccd-chip, register v4l device etc..
+   Also used for 'probing' of hardware.
+   -1 on error */
+static int w9966_init(struct w9966 *cam, struct parport *port)
+{
+       struct v4l2_device *v4l2_dev = &cam->v4l2_dev;
+
+       if (cam->dev_state != 0)
+               return -1;
+
+       strlcpy(v4l2_dev->name, "w9966", sizeof(v4l2_dev->name));
+
+       if (v4l2_device_register(NULL, v4l2_dev) < 0) {
+               v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+               return -1;
+       }
+
+       v4l2_ctrl_handler_init(&cam->hdl, 4);
+       v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
+                         V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+       v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
+                         V4L2_CID_CONTRAST, -64, 64, 1, 64);
+       v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
+                         V4L2_CID_SATURATION, -64, 64, 1, 64);
+       v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
+                         V4L2_CID_HUE, -128, 127, 1, 0);
+       if (cam->hdl.error) {
+               v4l2_err(v4l2_dev, "couldn't register controls\n");
+               return -1;
+       }
+       cam->pport = port;
+       cam->brightness = 128;
+       cam->contrast = 64;
+       cam->color = 64;
+       cam->hue = 0;
+
+       /* Select requested transfer mode */
+       switch (parmode) {
+       default:        /* Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) */
+       case 0:
+               if (port->modes & PARPORT_MODE_ECP)
+                       cam->ppmode = IEEE1284_MODE_ECP;
+               else if (port->modes & PARPORT_MODE_EPP)
+                       cam->ppmode = IEEE1284_MODE_EPP;
+               else
+                       cam->ppmode = IEEE1284_MODE_ECP;
+               break;
+       case 1:         /* hw- or sw-ecp */
+               cam->ppmode = IEEE1284_MODE_ECP;
+               break;
+       case 2:         /* hw- or sw-epp */
+               cam->ppmode = IEEE1284_MODE_EPP;
+               break;
+       }
+
+       /* Tell the parport driver that we exists */
+       cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL);
+       if (cam->pdev == NULL) {
+               DPRINTF("parport_register_device() failed\n");
+               return -1;
+       }
+       w9966_set_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV);
+
+       w9966_pdev_claim(cam);
+
+       /* Setup a default capture mode */
+       if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) {
+               DPRINTF("w9966_setup() failed.\n");
+               return -1;
+       }
+
+       w9966_pdev_release(cam);
+
+       /* Fill in the video_device struct and register us to v4l */
+       strlcpy(cam->vdev.name, W9966_DRIVERNAME, sizeof(cam->vdev.name));
+       cam->vdev.v4l2_dev = v4l2_dev;
+       cam->vdev.fops = &w9966_fops;
+       cam->vdev.ioctl_ops = &w9966_ioctl_ops;
+       cam->vdev.release = video_device_release_empty;
+       cam->vdev.ctrl_handler = &cam->hdl;
+       video_set_drvdata(&cam->vdev, cam);
+
+       mutex_init(&cam->lock);
+
+       if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0)
+               return -1;
+
+       w9966_set_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV);
+
+       /* All ok */
+       v4l2_info(v4l2_dev, "Found and initialized a webcam on %s.\n",
+                       cam->pport->name);
+       return 0;
+}
+
+
+/* Terminate everything gracefully */
+static void w9966_term(struct w9966 *cam)
+{
+       /* Unregister from v4l */
+       if (w9966_get_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) {
+               video_unregister_device(&cam->vdev);
+               w9966_set_state(cam, W9966_STATE_VDEV, 0);
+       }
+
+       v4l2_ctrl_handler_free(&cam->hdl);
+
+       /* Terminate from IEEE1284 mode and release pdev block */
+       if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
+               w9966_pdev_claim(cam);
+               parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT);
+               w9966_pdev_release(cam);
+       }
+
+       /* Unregister from parport */
+       if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
+               parport_unregister_device(cam->pdev);
+               w9966_set_state(cam, W9966_STATE_PDEV, 0);
+       }
+       memset(cam, 0, sizeof(*cam));
+}
+
+
+/* Called once for every parport on init */
+static void w9966_attach(struct parport *port)
+{
+       int i;
+
+       for (i = 0; i < W9966_MAXCAMS; i++) {
+               if (w9966_cams[i].dev_state != 0)       /* Cam is already assigned */
+                       continue;
+               if (strcmp(pardev[i], "aggressive") == 0 || strcmp(pardev[i], port->name) == 0) {
+                       if (w9966_init(&w9966_cams[i], port) != 0)
+                               w9966_term(&w9966_cams[i]);
+                       break;  /* return */
+               }
+       }
+}
+
+/* Called once for every parport on termination */
+static void w9966_detach(struct parport *port)
+{
+       int i;
+
+       for (i = 0; i < W9966_MAXCAMS; i++)
+               if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port)
+                       w9966_term(&w9966_cams[i]);
+}
+
+
+static struct parport_driver w9966_ppd = {
+       .name = W9966_DRIVERNAME,
+       .attach = w9966_attach,
+       .detach = w9966_detach,
+};
+
+/* Module entry point */
+static int __init w9966_mod_init(void)
+{
+       int i;
+
+       for (i = 0; i < W9966_MAXCAMS; i++)
+               w9966_cams[i].dev_state = 0;
+
+       return parport_register_driver(&w9966_ppd);
+}
+
+/* Module cleanup */
+static void __exit w9966_mod_term(void)
+{
+       parport_unregister_driver(&w9966_ppd);
+}
+
+module_init(w9966_mod_init);
+module_exit(w9966_mod_term);
diff --git a/drivers/staging/media/tlg2300/Kconfig b/drivers/staging/media/tlg2300/Kconfig
new file mode 100644 (file)
index 0000000..81784c6
--- /dev/null
@@ -0,0 +1,20 @@
+config VIDEO_TLG2300
+       tristate "Telegent TLG2300 USB video capture support (Deprecated)"
+       depends on VIDEO_DEV && I2C && SND && DVB_CORE
+       select VIDEO_TUNER
+       select VIDEO_TVEEPROM
+       depends on RC_CORE
+       select VIDEOBUF_VMALLOC
+       select SND_PCM
+       select VIDEOBUF_DVB
+
+       ---help---
+         This is a video4linux driver for Telegent tlg2300 based TV cards.
+         The driver supports V4L2, DVB-T and radio.
+
+         This driver is deprecated and will be removed soon. If you have
+         hardware for this and you want to work on this driver, then contact
+         the linux-media mailinglist.
+
+         To compile this driver as a module, choose M here: the
+         module will be called poseidon
diff --git a/drivers/staging/media/tlg2300/Makefile b/drivers/staging/media/tlg2300/Makefile
new file mode 100644 (file)
index 0000000..137f8e3
--- /dev/null
@@ -0,0 +1,9 @@
+poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o
+
+obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+
diff --git a/drivers/staging/media/tlg2300/pd-alsa.c b/drivers/staging/media/tlg2300/pd-alsa.c
new file mode 100644 (file)
index 0000000..dd8fe10
--- /dev/null
@@ -0,0 +1,337 @@
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+#include <linux/sound.h>
+#include <linux/spinlock.h>
+#include <linux/soundcard.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/gfp.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <media/v4l2-common.h>
+#include "pd-common.h"
+#include "vendorcmds.h"
+
+static void complete_handler_audio(struct urb *urb);
+#define AUDIO_EP       (0x83)
+#define AUDIO_BUF_SIZE (512)
+#define PERIOD_SIZE    (1024 * 8)
+#define PERIOD_MIN     (4)
+#define PERIOD_MAX     PERIOD_MIN
+
+static struct snd_pcm_hardware snd_pd_hw_capture = {
+       .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP           |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_MMAP_VALID,
+
+       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       .rates = SNDRV_PCM_RATE_48000,
+
+       .rate_min = 48000,
+       .rate_max = 48000,
+       .channels_min = 2,
+       .channels_max = 2,
+       .buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN,
+       .period_bytes_min = PERIOD_SIZE,
+       .period_bytes_max = PERIOD_SIZE,
+       .periods_min = PERIOD_MIN,
+       .periods_max = PERIOD_MAX,
+       /*
+       .buffer_bytes_max = 62720 * 8,
+       .period_bytes_min = 64,
+       .period_bytes_max = 12544,
+       .periods_min = 2,
+       .periods_max = 98
+       */
+};
+
+static int snd_pd_capture_open(struct snd_pcm_substream *substream)
+{
+       struct poseidon *p = snd_pcm_substream_chip(substream);
+       struct poseidon_audio *pa = &p->audio;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       if (!p)
+               return -ENODEV;
+       pa->users++;
+       pa->card_close          = 0;
+       pa->capture_pcm_substream       = substream;
+       runtime->private_data           = p;
+
+       runtime->hw = snd_pd_hw_capture;
+       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+       usb_autopm_get_interface(p->interface);
+       kref_get(&p->kref);
+       return 0;
+}
+
+static int snd_pd_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct poseidon *p = snd_pcm_substream_chip(substream);
+       struct poseidon_audio *pa = &p->audio;
+
+       pa->users--;
+       pa->card_close          = 1;
+       usb_autopm_put_interface(p->interface);
+       kref_put(&p->kref, poseidon_delete);
+       return 0;
+}
+
+static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned int size;
+
+       size = params_buffer_bytes(hw_params);
+       if (runtime->dma_area) {
+               if (runtime->dma_bytes > size)
+                       return 0;
+               vfree(runtime->dma_area);
+       }
+       runtime->dma_area = vmalloc(size);
+       if (!runtime->dma_area)
+               return -ENOMEM;
+       else
+               runtime->dma_bytes = size;
+       return 0;
+}
+
+static int audio_buf_free(struct poseidon *p)
+{
+       struct poseidon_audio *pa = &p->audio;
+       int i;
+
+       for (i = 0; i < AUDIO_BUFS; i++)
+               if (pa->urb_array[i])
+                       usb_kill_urb(pa->urb_array[i]);
+       free_all_urb_generic(pa->urb_array, AUDIO_BUFS);
+       logpm();
+       return 0;
+}
+
+static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream)
+{
+       struct poseidon *p = snd_pcm_substream_chip(substream);
+
+       logpm();
+       audio_buf_free(p);
+       return 0;
+}
+
+static int snd_pd_prepare(struct snd_pcm_substream *substream)
+{
+       return 0;
+}
+
+#define AUDIO_TRAILER_SIZE     (16)
+static inline void handle_audio_data(struct urb *urb, int *period_elapsed)
+{
+       struct poseidon_audio *pa = urb->context;
+       struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime;
+
+       int stride      = runtime->frame_bits >> 3;
+       int len         = urb->actual_length / stride;
+       unsigned char *cp       = urb->transfer_buffer;
+       unsigned int oldptr     = pa->rcv_position;
+
+       if (urb->actual_length == AUDIO_BUF_SIZE - 4)
+               len -= (AUDIO_TRAILER_SIZE / stride);
+
+       /* do the copy */
+       if (oldptr + len >= runtime->buffer_size) {
+               unsigned int cnt = runtime->buffer_size - oldptr;
+
+               memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride);
+               memcpy(runtime->dma_area, (cp + cnt * stride),
+                                       (len * stride - cnt * stride));
+       } else
+               memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
+
+       /* update the statas */
+       snd_pcm_stream_lock(pa->capture_pcm_substream);
+       pa->rcv_position        += len;
+       if (pa->rcv_position >= runtime->buffer_size)
+               pa->rcv_position -= runtime->buffer_size;
+
+       pa->copied_position += (len);
+       if (pa->copied_position >= runtime->period_size) {
+               pa->copied_position -= runtime->period_size;
+               *period_elapsed = 1;
+       }
+       snd_pcm_stream_unlock(pa->capture_pcm_substream);
+}
+
+static void complete_handler_audio(struct urb *urb)
+{
+       struct poseidon_audio *pa = urb->context;
+       struct snd_pcm_substream *substream = pa->capture_pcm_substream;
+       int    period_elapsed = 0;
+       int    ret;
+
+       if (1 == pa->card_close || pa->capture_stream != STREAM_ON)
+               return;
+
+       if (urb->status != 0) {
+               /*if (urb->status == -ESHUTDOWN)*/
+                       return;
+       }
+
+       if (substream) {
+               if (urb->actual_length) {
+                       handle_audio_data(urb, &period_elapsed);
+                       if (period_elapsed)
+                               snd_pcm_period_elapsed(substream);
+               }
+       }
+
+       ret = usb_submit_urb(urb, GFP_ATOMIC);
+       if (ret < 0)
+               log("audio urb failed (errcod = %i)", ret);
+       return;
+}
+
+static int fire_audio_urb(struct poseidon *p)
+{
+       int i, ret = 0;
+       struct poseidon_audio *pa = &p->audio;
+
+       alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS,
+                       p->udev, AUDIO_EP,
+                       AUDIO_BUF_SIZE, GFP_ATOMIC,
+                       complete_handler_audio, pa);
+
+       for (i = 0; i < AUDIO_BUFS; i++) {
+               ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL);
+               if (ret)
+                       log("urb err : %d", ret);
+       }
+       log();
+       return ret;
+}
+
+static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct poseidon *p = snd_pcm_substream_chip(substream);
+       struct poseidon_audio *pa = &p->audio;
+
+       if (debug_mode)
+               log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_START:
+               if (pa->capture_stream == STREAM_ON)
+                       return 0;
+
+               pa->rcv_position = pa->copied_position = 0;
+               pa->capture_stream = STREAM_ON;
+
+               if (in_hibernation(p))
+                       return 0;
+               fire_audio_urb(p);
+               return 0;
+
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               pa->capture_stream = STREAM_SUSPEND;
+               return 0;
+       case SNDRV_PCM_TRIGGER_STOP:
+               pa->capture_stream = STREAM_OFF;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+static snd_pcm_uframes_t
+snd_pd_capture_pointer(struct snd_pcm_substream *substream)
+{
+       struct poseidon *p = snd_pcm_substream_chip(substream);
+       struct poseidon_audio *pa = &p->audio;
+       return pa->rcv_position;
+}
+
+static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs,
+                                            unsigned long offset)
+{
+       void *pageptr = subs->runtime->dma_area + offset;
+       return vmalloc_to_page(pageptr);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+       .open      = snd_pd_capture_open,
+       .close     = snd_pd_pcm_close,
+       .ioctl     = snd_pcm_lib_ioctl,
+       .hw_params = snd_pd_hw_capture_params,
+       .hw_free   = snd_pd_hw_capture_free,
+       .prepare   = snd_pd_prepare,
+       .trigger   = snd_pd_capture_trigger,
+       .pointer   = snd_pd_capture_pointer,
+       .page      = snd_pcm_pd_get_page,
+};
+
+#ifdef CONFIG_PM
+int pm_alsa_suspend(struct poseidon *p)
+{
+       logpm(p);
+       audio_buf_free(p);
+       return 0;
+}
+
+int pm_alsa_resume(struct poseidon *p)
+{
+       logpm(p);
+       fire_audio_urb(p);
+       return 0;
+}
+#endif
+
+int poseidon_audio_init(struct poseidon *p)
+{
+       struct poseidon_audio *pa = &p->audio;
+       struct snd_card *card;
+       struct snd_pcm *pcm;
+       int ret;
+
+       ret = snd_card_new(&p->interface->dev, -1, "Telegent",
+                          THIS_MODULE, 0, &card);
+       if (ret != 0)
+               return ret;
+
+       ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm);
+       if (ret < 0) {
+               snd_card_free(card);
+               return ret;
+       }
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+       pcm->info_flags   = 0;
+       pcm->private_data = p;
+       strcpy(pcm->name, "poseidon audio capture");
+
+       strcpy(card->driver, "ALSA driver");
+       strcpy(card->shortname, "poseidon Audio");
+       strcpy(card->longname, "poseidon ALSA Audio");
+
+       if (snd_card_register(card)) {
+               snd_card_free(card);
+               return -ENOMEM;
+       }
+       pa->card = card;
+       return 0;
+}
+
+int poseidon_audio_free(struct poseidon *p)
+{
+       struct poseidon_audio *pa = &p->audio;
+
+       if (pa->card)
+               snd_card_free(pa->card);
+       return 0;
+}
diff --git a/drivers/staging/media/tlg2300/pd-common.h b/drivers/staging/media/tlg2300/pd-common.h
new file mode 100644 (file)
index 0000000..9e23ad3
--- /dev/null
@@ -0,0 +1,271 @@
+#ifndef PD_COMMON_H
+#define PD_COMMON_H
+
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/videodev2.h>
+#include <linux/semaphore.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+#include <media/videobuf-vmalloc.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+#include "dvb_frontend.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dmxdev.h"
+
+#define SBUF_NUM       8
+#define MAX_BUFFER_NUM 6
+#define PK_PER_URB     32
+#define ISO_PKT_SIZE   3072
+
+#define POSEIDON_STATE_NONE            (0x0000)
+#define POSEIDON_STATE_ANALOG          (0x0001)
+#define POSEIDON_STATE_FM              (0x0002)
+#define POSEIDON_STATE_DVBT            (0x0004)
+#define POSEIDON_STATE_DISCONNECT      (0x0080)
+
+#define PM_SUSPEND_DELAY       3
+
+#define V4L_PAL_VBI_LINES      18
+#define V4L_NTSC_VBI_LINES     12
+#define V4L_PAL_VBI_FRAMESIZE  (V4L_PAL_VBI_LINES * 1440 * 2)
+#define V4L_NTSC_VBI_FRAMESIZE (V4L_NTSC_VBI_LINES * 1440 * 2)
+
+#define TUNER_FREQ_MIN         (45000000U)
+#define TUNER_FREQ_MAX         (862000000U)
+
+struct vbi_data {
+       struct video_device     v_dev;
+       struct video_data       *video;
+       struct front_face       *front;
+
+       unsigned int            copied;
+       unsigned int            vbi_size; /* the whole size of two fields */
+       int                     users;
+};
+
+/*
+ * This is the running context of the video, it is useful for
+ * resume()
+ */
+struct running_context {
+       u32             freq;           /* VIDIOC_S_FREQUENCY */
+       int             audio_idx;      /* VIDIOC_S_TUNER    */
+       v4l2_std_id     tvnormid;       /* VIDIOC_S_STD     */
+       int             sig_index;      /* VIDIOC_S_INPUT  */
+       struct v4l2_pix_format pix;     /* VIDIOC_S_FMT   */
+};
+
+struct video_data {
+       /* v4l2 video device */
+       struct video_device     v_dev;
+       struct v4l2_ctrl_handler ctrl_handler;
+
+       /* the working context */
+       struct running_context  context;
+
+       /* for data copy */
+       int             field_count;
+
+       char            *dst;
+       int             lines_copied;
+       int             prev_left;
+
+       int             lines_per_field;
+       int             lines_size;
+
+       /* for communication */
+       u8                      endpoint_addr;
+       struct urb              *urb_array[SBUF_NUM];
+       struct vbi_data         *vbi;
+       struct poseidon         *pd;
+       struct front_face       *front;
+
+       int                     is_streaming;
+       int                     users;
+
+       /* for bubble handler */
+       struct work_struct      bubble_work;
+};
+
+enum pcm_stream_state {
+       STREAM_OFF,
+       STREAM_ON,
+       STREAM_SUSPEND,
+};
+
+#define AUDIO_BUFS (3)
+#define CAPTURE_STREAM_EN 1
+struct poseidon_audio {
+       struct urb              *urb_array[AUDIO_BUFS];
+       unsigned int            copied_position;
+       struct snd_pcm_substream   *capture_pcm_substream;
+
+       unsigned int            rcv_position;
+       struct  snd_card        *card;
+       int                     card_close;
+
+       int                     users;
+       int                     pm_state;
+       enum pcm_stream_state   capture_stream;
+};
+
+struct radio_data {
+       __u32           fm_freq;
+       unsigned int    is_radio_streaming;
+       int             pre_emphasis;
+       struct video_device fm_dev;
+       struct v4l2_ctrl_handler ctrl_handler;
+};
+
+#define DVB_SBUF_NUM           4
+#define DVB_URB_BUF_SIZE       0x2000
+struct pd_dvb_adapter {
+       struct dvb_adapter      dvb_adap;
+       struct dvb_frontend     dvb_fe;
+       struct dmxdev           dmxdev;
+       struct dvb_demux        demux;
+
+       atomic_t                users;
+       atomic_t                active_feed;
+
+       /* data transfer */
+       s32                     is_streaming;
+       struct urb              *urb_array[DVB_SBUF_NUM];
+       struct poseidon         *pd_device;
+       u8                      ep_addr;
+       u8                      reserved[3];
+
+       /* data for power resume*/
+       struct dtv_frontend_properties fe_param;
+
+       /* for channel scanning */
+       int             prev_freq;
+       int             bandwidth;
+       unsigned long   last_jiffies;
+};
+
+struct front_face {
+       /* use this field to distinguish VIDEO and VBI */
+       enum v4l2_buf_type      type;
+
+       /* for host */
+       struct videobuf_queue   q;
+
+       /* the bridge for host and device */
+       struct videobuf_buffer  *curr_frame;
+
+       /* for device */
+       spinlock_t              queue_lock;
+       struct list_head        active;
+       struct poseidon         *pd;
+};
+
+struct poseidon {
+       struct list_head        device_list;
+
+       struct mutex            lock;
+       struct kref             kref;
+
+       /* for V4L2 */
+       struct v4l2_device      v4l2_dev;
+
+       /* hardware info */
+       struct usb_device       *udev;
+       struct usb_interface    *interface;
+       int                     cur_transfer_mode;
+
+       struct video_data       video_data;     /* video */
+       struct vbi_data         vbi_data;       /* vbi   */
+       struct poseidon_audio   audio;          /* audio (alsa) */
+       struct radio_data       radio_data;     /* FM    */
+       struct pd_dvb_adapter   dvb_data;       /* DVB   */
+
+       u32                     state;
+       struct file             *file_for_stream; /* the active stream*/
+
+#ifdef CONFIG_PM
+       int (*pm_suspend)(struct poseidon *);
+       int (*pm_resume)(struct poseidon *);
+       pm_message_t            msg;
+
+       struct work_struct      pm_work;
+       u8                      portnum;
+#endif
+};
+
+struct poseidon_format {
+       char    *name;
+       int     fourcc;          /* video4linux 2         */
+       int     depth;           /* bit/pixel             */
+       int     flags;
+};
+
+struct poseidon_tvnorm {
+       v4l2_std_id     v4l2_id;
+       char            name[12];
+       u32             tlg_tvnorm;
+};
+
+/* video */
+int pd_video_init(struct poseidon *);
+void pd_video_exit(struct poseidon *);
+int stop_all_video_stream(struct poseidon *);
+
+/* alsa audio */
+int poseidon_audio_init(struct poseidon *);
+int poseidon_audio_free(struct poseidon *);
+#ifdef CONFIG_PM
+int pm_alsa_suspend(struct poseidon *);
+int pm_alsa_resume(struct poseidon *);
+#endif
+
+/* dvb */
+int pd_dvb_usb_device_init(struct poseidon *);
+void pd_dvb_usb_device_exit(struct poseidon *);
+void pd_dvb_usb_device_cleanup(struct poseidon *);
+int pd_dvb_get_adapter_num(struct pd_dvb_adapter *);
+void dvb_stop_streaming(struct pd_dvb_adapter *);
+
+/* FM */
+int poseidon_fm_init(struct poseidon *);
+int poseidon_fm_exit(struct poseidon *);
+
+/* vendor command ops */
+int send_set_req(struct poseidon*, u8, s32, s32*);
+int send_get_req(struct poseidon*, u8, s32, void*, s32*, s32);
+s32 set_tuner_mode(struct poseidon*, unsigned char);
+
+/* bulk urb alloc/free */
+int alloc_bulk_urbs_generic(struct urb **urb_array, int num,
+                       struct usb_device *udev, u8 ep_addr,
+                       int buf_size, gfp_t gfp_flags,
+                       usb_complete_t complete_fn, void *context);
+void free_all_urb_generic(struct urb **urb_array, int num);
+
+/* misc */
+void poseidon_delete(struct kref *kref);
+extern int debug_mode;
+void set_debug_mode(struct video_device *vfd, int debug_mode);
+
+#ifdef CONFIG_PM
+#define in_hibernation(pd) (pd->msg.event == PM_EVENT_FREEZE)
+#else
+#define in_hibernation(pd) (0)
+#endif
+#define get_pm_count(p) (atomic_read(&(p)->interface->pm_usage_cnt))
+
+#define log(a, ...) printk(KERN_DEBUG "\t[ %s : %.3d ] "a"\n", \
+                               __func__, __LINE__,  ## __VA_ARGS__)
+
+/* for power management */
+#define logpm(pd) do {\
+                       if (debug_mode & 0x10)\
+                               log();\
+               } while (0)
+
+#endif
diff --git a/drivers/staging/media/tlg2300/pd-dvb.c b/drivers/staging/media/tlg2300/pd-dvb.c
new file mode 100644 (file)
index 0000000..ca4994a
--- /dev/null
@@ -0,0 +1,597 @@
+#include "pd-common.h"
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/time.h>
+#include <linux/dvb/dmx.h>
+#include <linux/delay.h>
+#include <linux/gfp.h>
+
+#include "vendorcmds.h"
+#include <linux/sched.h>
+#include <linux/atomic.h>
+
+static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb);
+
+static int dvb_bandwidth[][2] = {
+       { TLG_BW_8, 8000000 },
+       { TLG_BW_7, 7000000 },
+       { TLG_BW_6, 6000000 }
+};
+static int dvb_bandwidth_length = ARRAY_SIZE(dvb_bandwidth);
+
+static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb);
+static int poseidon_check_mode_dvbt(struct poseidon *pd)
+{
+       s32 ret = 0, cmd_status = 0;
+
+       set_current_state(TASK_INTERRUPTIBLE);
+       schedule_timeout(HZ/4);
+
+       ret = usb_set_interface(pd->udev, 0, BULK_ALTERNATE_IFACE);
+       if (ret != 0)
+               return ret;
+
+       ret = set_tuner_mode(pd, TLG_MODE_CAPS_DVB_T);
+       if (ret)
+               return ret;
+
+       /* signal source */
+       ret = send_set_req(pd, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &cmd_status);
+       if (ret|cmd_status)
+               return ret;
+
+       return 0;
+}
+
+/* acquire :
+ *     1 == open
+ *     0 == release
+ */
+static int poseidon_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+       struct poseidon *pd = fe->demodulator_priv;
+       struct pd_dvb_adapter *pd_dvb;
+       int ret = 0;
+
+       if (!pd)
+               return -ENODEV;
+
+       pd_dvb = container_of(fe, struct pd_dvb_adapter, dvb_fe);
+       if (acquire) {
+               mutex_lock(&pd->lock);
+               if (pd->state & POSEIDON_STATE_DISCONNECT) {
+                       ret = -ENODEV;
+                       goto open_out;
+               }
+
+               if (pd->state && !(pd->state & POSEIDON_STATE_DVBT)) {
+                       ret = -EBUSY;
+                       goto open_out;
+               }
+
+               usb_autopm_get_interface(pd->interface);
+               if (0 == pd->state) {
+                       ret = poseidon_check_mode_dvbt(pd);
+                       if (ret < 0) {
+                               usb_autopm_put_interface(pd->interface);
+                               goto open_out;
+                       }
+                       pd->state |= POSEIDON_STATE_DVBT;
+                       pd_dvb->bandwidth = 0;
+                       pd_dvb->prev_freq = 0;
+               }
+               atomic_inc(&pd_dvb->users);
+               kref_get(&pd->kref);
+open_out:
+               mutex_unlock(&pd->lock);
+       } else {
+               dvb_stop_streaming(pd_dvb);
+
+               if (atomic_dec_and_test(&pd_dvb->users)) {
+                       mutex_lock(&pd->lock);
+                       pd->state &= ~POSEIDON_STATE_DVBT;
+                       mutex_unlock(&pd->lock);
+               }
+               kref_put(&pd->kref, poseidon_delete);
+               usb_autopm_put_interface(pd->interface);
+       }
+       return ret;
+}
+
+#ifdef CONFIG_PM
+static void poseidon_fe_release(struct dvb_frontend *fe)
+{
+       struct poseidon *pd = fe->demodulator_priv;
+
+       pd->pm_suspend = NULL;
+       pd->pm_resume  = NULL;
+}
+#else
+#define poseidon_fe_release NULL
+#endif
+
+static s32 poseidon_fe_sleep(struct dvb_frontend *fe)
+{
+       return 0;
+}
+
+/*
+ * return true if we can satisfy the conditions, else return false.
+ */
+static bool check_scan_ok(__u32 freq, int bandwidth,
+                       struct pd_dvb_adapter *adapter)
+{
+       if (bandwidth < 0)
+               return false;
+
+       if (adapter->prev_freq == freq
+               && adapter->bandwidth == bandwidth) {
+               long nl = jiffies - adapter->last_jiffies;
+               unsigned int msec ;
+
+               msec = jiffies_to_msecs(abs(nl));
+               return msec > 15000 ? true : false;
+       }
+       return true;
+}
+
+/*
+ * Check if the firmware delays too long for an invalid frequency.
+ */
+static int fw_delay_overflow(struct pd_dvb_adapter *adapter)
+{
+       long nl = jiffies - adapter->last_jiffies;
+       unsigned int msec ;
+
+       msec = jiffies_to_msecs(abs(nl));
+       return msec > 800 ? true : false;
+}
+
+static int poseidon_set_fe(struct dvb_frontend *fe)
+{
+       struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
+       s32 ret = 0, cmd_status = 0;
+       s32 i, bandwidth = -1;
+       struct poseidon *pd = fe->demodulator_priv;
+       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+       if (in_hibernation(pd))
+               return -EBUSY;
+
+       mutex_lock(&pd->lock);
+       for (i = 0; i < dvb_bandwidth_length; i++)
+               if (fep->bandwidth_hz == dvb_bandwidth[i][1])
+                       bandwidth = dvb_bandwidth[i][0];
+
+       if (check_scan_ok(fep->frequency, bandwidth, pd_dvb)) {
+               ret = send_set_req(pd, TUNE_FREQ_SELECT,
+                                       fep->frequency / 1000, &cmd_status);
+               if (ret | cmd_status) {
+                       log("error line");
+                       goto front_out;
+               }
+
+               ret = send_set_req(pd, DVBT_BANDW_SEL,
+                                               bandwidth, &cmd_status);
+               if (ret | cmd_status) {
+                       log("error line");
+                       goto front_out;
+               }
+
+               ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+               if (ret | cmd_status) {
+                       log("error line");
+                       goto front_out;
+               }
+
+               /* save the context for future */
+               memcpy(&pd_dvb->fe_param, fep, sizeof(*fep));
+               pd_dvb->bandwidth = bandwidth;
+               pd_dvb->prev_freq = fep->frequency;
+               pd_dvb->last_jiffies = jiffies;
+       }
+front_out:
+       mutex_unlock(&pd->lock);
+       return ret;
+}
+
+#ifdef CONFIG_PM
+static int pm_dvb_suspend(struct poseidon *pd)
+{
+       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+       dvb_stop_streaming(pd_dvb);
+       dvb_urb_cleanup(pd_dvb);
+       msleep(500);
+       return 0;
+}
+
+static int pm_dvb_resume(struct poseidon *pd)
+{
+       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+       poseidon_check_mode_dvbt(pd);
+       msleep(300);
+       poseidon_set_fe(&pd_dvb->dvb_fe);
+
+       dvb_start_streaming(pd_dvb);
+       return 0;
+}
+#endif
+
+static s32 poseidon_fe_init(struct dvb_frontend *fe)
+{
+       struct poseidon *pd = fe->demodulator_priv;
+       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+#ifdef CONFIG_PM
+       pd->pm_suspend = pm_dvb_suspend;
+       pd->pm_resume  = pm_dvb_resume;
+#endif
+       memset(&pd_dvb->fe_param, 0,
+                       sizeof(struct dtv_frontend_properties));
+       return 0;
+}
+
+static int poseidon_get_fe(struct dvb_frontend *fe)
+{
+       struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
+       struct poseidon *pd = fe->demodulator_priv;
+       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+       memcpy(fep, &pd_dvb->fe_param, sizeof(*fep));
+       return 0;
+}
+
+static int poseidon_fe_get_tune_settings(struct dvb_frontend *fe,
+                               struct dvb_frontend_tune_settings *tune)
+{
+       tune->min_delay_ms = 1000;
+       return 0;
+}
+
+static int poseidon_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+{
+       struct poseidon *pd = fe->demodulator_priv;
+       s32 ret = -1, cmd_status;
+       struct tuner_dtv_sig_stat_s status = {};
+
+       if (in_hibernation(pd))
+               return -EBUSY;
+       mutex_lock(&pd->lock);
+
+       ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T,
+                               &status, &cmd_status, sizeof(status));
+       if (ret | cmd_status) {
+               log("get tuner status error");
+               goto out;
+       }
+
+       if (debug_mode)
+               log("P : %d, L %d, LB :%d", status.sig_present,
+                       status.sig_locked, status.sig_lock_busy);
+
+       if (status.sig_lock_busy) {
+               goto out;
+       } else if (status.sig_present || status.sig_locked) {
+               *stat |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER
+                               | FE_HAS_SYNC | FE_HAS_VITERBI;
+       } else {
+               if (fw_delay_overflow(&pd->dvb_data))
+                       *stat |= FE_TIMEDOUT;
+       }
+out:
+       mutex_unlock(&pd->lock);
+       return ret;
+}
+
+static int poseidon_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+       struct poseidon *pd = fe->demodulator_priv;
+       struct tuner_ber_rate_s tlg_ber = {};
+       s32 ret = -1, cmd_status;
+
+       mutex_lock(&pd->lock);
+       ret = send_get_req(pd, TUNER_BER_RATE, 0,
+                               &tlg_ber, &cmd_status, sizeof(tlg_ber));
+       if (ret | cmd_status)
+               goto out;
+       *ber = tlg_ber.ber_rate;
+out:
+       mutex_unlock(&pd->lock);
+       return ret;
+}
+
+static s32 poseidon_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+       struct poseidon *pd = fe->demodulator_priv;
+       struct tuner_dtv_sig_stat_s status = {};
+       s32 ret = 0, cmd_status;
+
+       mutex_lock(&pd->lock);
+       ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T,
+                               &status, &cmd_status, sizeof(status));
+       if (ret | cmd_status)
+               goto out;
+       if ((status.sig_present || status.sig_locked) && !status.sig_strength)
+               *strength = 0xFFFF;
+       else
+               *strength = status.sig_strength;
+out:
+       mutex_unlock(&pd->lock);
+       return ret;
+}
+
+static int poseidon_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+       return 0;
+}
+
+static int poseidon_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
+{
+       *unc = 0;
+       return 0;
+}
+
+static struct dvb_frontend_ops poseidon_frontend_ops = {
+       .delsys = { SYS_DVBT },
+       .info = {
+               .name           = "Poseidon DVB-T",
+               .frequency_min  = 174000000,
+               .frequency_max  = 862000000,
+               .frequency_stepsize       = 62500,/* FIXME */
+               .caps = FE_CAN_INVERSION_AUTO |
+                       FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+                       FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+                       FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+                       FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
+                       FE_CAN_GUARD_INTERVAL_AUTO |
+                       FE_CAN_RECOVER |
+                       FE_CAN_HIERARCHY_AUTO,
+       },
+
+       .release = poseidon_fe_release,
+
+       .init = poseidon_fe_init,
+       .sleep = poseidon_fe_sleep,
+
+       .set_frontend = poseidon_set_fe,
+       .get_frontend = poseidon_get_fe,
+       .get_tune_settings = poseidon_fe_get_tune_settings,
+
+       .read_status    = poseidon_read_status,
+       .read_ber       = poseidon_read_ber,
+       .read_signal_strength = poseidon_read_signal_strength,
+       .read_snr       = poseidon_read_snr,
+       .read_ucblocks  = poseidon_read_unc_blocks,
+
+       .ts_bus_ctrl = poseidon_ts_bus_ctrl,
+};
+
+static void dvb_urb_irq(struct urb *urb)
+{
+       struct pd_dvb_adapter *pd_dvb = urb->context;
+       int len = urb->transfer_buffer_length;
+       struct dvb_demux *demux = &pd_dvb->demux;
+       s32 ret;
+
+       if (!pd_dvb->is_streaming || urb->status) {
+               if (urb->status == -EPROTO)
+                       goto resend;
+               return;
+       }
+
+       if (urb->actual_length == len)
+               dvb_dmx_swfilter(demux, urb->transfer_buffer, len);
+       else if (urb->actual_length == len - 4) {
+               int offset;
+               u8 *buf = urb->transfer_buffer;
+
+               /*
+                * The packet size is 512,
+                * last packet contains 456 bytes tsp data
+                */
+               for (offset = 456; offset < len; offset += 512) {
+                       if (!strncmp(buf + offset, "DVHS", 4)) {
+                               dvb_dmx_swfilter(demux, buf, offset);
+                               if (len > offset + 52 + 4) {
+                                       /*16 bytes trailer + 36 bytes padding */
+                                       buf += offset + 52;
+                                       len -= offset + 52 + 4;
+                                       dvb_dmx_swfilter(demux, buf, len);
+                               }
+                               break;
+                       }
+               }
+       }
+
+resend:
+       ret = usb_submit_urb(urb, GFP_ATOMIC);
+       if (ret)
+               log(" usb_submit_urb failed: error %d", ret);
+}
+
+static int dvb_urb_init(struct pd_dvb_adapter *pd_dvb)
+{
+       if (pd_dvb->urb_array[0])
+               return 0;
+
+       alloc_bulk_urbs_generic(pd_dvb->urb_array, DVB_SBUF_NUM,
+                       pd_dvb->pd_device->udev, pd_dvb->ep_addr,
+                       DVB_URB_BUF_SIZE, GFP_KERNEL,
+                       dvb_urb_irq, pd_dvb);
+       return 0;
+}
+
+static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb)
+{
+       free_all_urb_generic(pd_dvb->urb_array, DVB_SBUF_NUM);
+}
+
+static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb)
+{
+       struct poseidon *pd = pd_dvb->pd_device;
+       int ret = 0;
+
+       if (pd->state & POSEIDON_STATE_DISCONNECT)
+               return -ENODEV;
+
+       mutex_lock(&pd->lock);
+       if (!pd_dvb->is_streaming) {
+               s32 i, cmd_status = 0;
+               /*
+                * Once upon a time, there was a difficult bug lying here.
+                * ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+                */
+
+               ret = send_set_req(pd, PLAY_SERVICE, 1, &cmd_status);
+               if (ret | cmd_status)
+                       goto out;
+
+               ret = dvb_urb_init(pd_dvb);
+               if (ret < 0)
+                       goto out;
+
+               pd_dvb->is_streaming = 1;
+               for (i = 0; i < DVB_SBUF_NUM; i++) {
+                       ret = usb_submit_urb(pd_dvb->urb_array[i],
+                                                      GFP_KERNEL);
+                       if (ret) {
+                               log(" submit urb error %d", ret);
+                               goto out;
+                       }
+               }
+       }
+out:
+       mutex_unlock(&pd->lock);
+       return ret;
+}
+
+void dvb_stop_streaming(struct pd_dvb_adapter *pd_dvb)
+{
+       struct poseidon *pd = pd_dvb->pd_device;
+
+       mutex_lock(&pd->lock);
+       if (pd_dvb->is_streaming) {
+               s32 i, ret, cmd_status = 0;
+
+               pd_dvb->is_streaming = 0;
+
+               for (i = 0; i < DVB_SBUF_NUM; i++)
+                       if (pd_dvb->urb_array[i])
+                               usb_kill_urb(pd_dvb->urb_array[i]);
+
+               ret = send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
+                                       &cmd_status);
+               if (ret | cmd_status)
+                       log("error");
+       }
+       mutex_unlock(&pd->lock);
+}
+
+static int pd_start_feed(struct dvb_demux_feed *feed)
+{
+       struct pd_dvb_adapter *pd_dvb = feed->demux->priv;
+       int ret = 0;
+
+       if (!pd_dvb)
+               return -1;
+       if (atomic_inc_return(&pd_dvb->active_feed) == 1)
+               ret = dvb_start_streaming(pd_dvb);
+       return ret;
+}
+
+static int pd_stop_feed(struct dvb_demux_feed *feed)
+{
+       struct pd_dvb_adapter *pd_dvb = feed->demux->priv;
+
+       if (!pd_dvb)
+               return -1;
+       if (atomic_dec_and_test(&pd_dvb->active_feed))
+               dvb_stop_streaming(pd_dvb);
+       return 0;
+}
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+int pd_dvb_usb_device_init(struct poseidon *pd)
+{
+       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+       struct dvb_demux *dvbdemux;
+       int ret = 0;
+
+       pd_dvb->ep_addr = 0x82;
+       atomic_set(&pd_dvb->users, 0);
+       atomic_set(&pd_dvb->active_feed, 0);
+       pd_dvb->pd_device = pd;
+
+       ret = dvb_register_adapter(&pd_dvb->dvb_adap,
+                               "Poseidon dvbt adapter",
+                               THIS_MODULE,
+                               NULL /* for hibernation correctly*/,
+                               adapter_nr);
+       if (ret < 0)
+               goto error1;
+
+       /* register frontend */
+       pd_dvb->dvb_fe.demodulator_priv = pd;
+       memcpy(&pd_dvb->dvb_fe.ops, &poseidon_frontend_ops,
+                       sizeof(struct dvb_frontend_ops));
+       ret = dvb_register_frontend(&pd_dvb->dvb_adap, &pd_dvb->dvb_fe);
+       if (ret < 0)
+               goto error2;
+
+       /* register demux device */
+       dvbdemux = &pd_dvb->demux;
+       dvbdemux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
+       dvbdemux->priv = pd_dvb;
+       dvbdemux->feednum = dvbdemux->filternum = 64;
+       dvbdemux->start_feed = pd_start_feed;
+       dvbdemux->stop_feed = pd_stop_feed;
+       dvbdemux->write_to_decoder = NULL;
+
+       ret = dvb_dmx_init(dvbdemux);
+       if (ret < 0)
+               goto error3;
+
+       pd_dvb->dmxdev.filternum = pd_dvb->demux.filternum;
+       pd_dvb->dmxdev.demux = &pd_dvb->demux.dmx;
+       pd_dvb->dmxdev.capabilities = 0;
+
+       ret = dvb_dmxdev_init(&pd_dvb->dmxdev, &pd_dvb->dvb_adap);
+       if (ret < 0)
+               goto error3;
+       return 0;
+
+error3:
+       dvb_unregister_frontend(&pd_dvb->dvb_fe);
+error2:
+       dvb_unregister_adapter(&pd_dvb->dvb_adap);
+error1:
+       return ret;
+}
+
+void pd_dvb_usb_device_exit(struct poseidon *pd)
+{
+       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+       while (atomic_read(&pd_dvb->users) != 0
+               || atomic_read(&pd_dvb->active_feed) != 0) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(HZ);
+       }
+       dvb_dmxdev_release(&pd_dvb->dmxdev);
+       dvb_unregister_frontend(&pd_dvb->dvb_fe);
+       dvb_unregister_adapter(&pd_dvb->dvb_adap);
+       pd_dvb_usb_device_cleanup(pd);
+}
+
+void pd_dvb_usb_device_cleanup(struct poseidon *pd)
+{
+       struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+       dvb_urb_cleanup(pd_dvb);
+}
+
+int pd_dvb_get_adapter_num(struct pd_dvb_adapter *pd_dvb)
+{
+       return pd_dvb->dvb_adap.num;
+}
diff --git a/drivers/staging/media/tlg2300/pd-main.c b/drivers/staging/media/tlg2300/pd-main.c
new file mode 100644 (file)
index 0000000..b31f479
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * device driver for Telegent tlg2300 based TV cards
+ *
+ * Author :
+ *     Kang Yong       <kangyong@telegent.com>
+ *     Zhang Xiaobing  <xbzhang@telegent.com>
+ *     Huang Shijie    <zyziii@telegent.com> or <shijie8@gmail.com>
+ *
+ *     (c) 2009 Telegent Systems
+ *     (c) 2010 Telegent Systems
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/suspend.h>
+#include <linux/usb/quirks.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/firmware.h>
+
+#include "vendorcmds.h"
+#include "pd-common.h"
+
+#define VENDOR_ID      0x1B24
+#define PRODUCT_ID     0x4001
+static struct usb_device_id id_table[] = {
+       { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 0) },
+       { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 1) },
+       { },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+int debug_mode;
+module_param(debug_mode, int, 0644);
+MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose");
+
+#define TLG2300_FIRMWARE "tlg2300_firmware.bin"
+static const char *firmware_name = TLG2300_FIRMWARE;
+static LIST_HEAD(pd_device_list);
+
+/*
+ * send set request to USB firmware.
+ */
+s32 send_set_req(struct poseidon *pd, u8 cmdid, s32 param, s32 *cmd_status)
+{
+       s32 ret;
+       s8  data[32] = {};
+       u16 lower_16, upper_16;
+
+       if (pd->state & POSEIDON_STATE_DISCONNECT)
+               return -ENODEV;
+
+       mdelay(30);
+
+       if (param == 0) {
+               upper_16 = lower_16 = 0;
+       } else {
+               /* send 32 bit param as  two 16 bit param,little endian */
+               lower_16 = (unsigned short)(param & 0xffff);
+               upper_16 = (unsigned short)((param >> 16) & 0xffff);
+       }
+       ret = usb_control_msg(pd->udev,
+                        usb_rcvctrlpipe(pd->udev, 0),
+                        REQ_SET_CMD | cmdid,
+                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                        lower_16,
+                        upper_16,
+                        &data,
+                        sizeof(*cmd_status),
+                        USB_CTRL_GET_TIMEOUT);
+
+       if (!ret) {
+               return -ENXIO;
+       } else {
+               /*  1st 4 bytes into cmd_status   */
+               memcpy((char *)cmd_status, &(data[0]), sizeof(*cmd_status));
+       }
+       return 0;
+}
+
+/*
+ * send get request to Poseidon firmware.
+ */
+s32 send_get_req(struct poseidon *pd, u8 cmdid, s32 param,
+                       void *buf, s32 *cmd_status, s32 datalen)
+{
+       s32 ret;
+       s8 data[128] = {};
+       u16 lower_16, upper_16;
+
+       if (pd->state & POSEIDON_STATE_DISCONNECT)
+               return -ENODEV;
+
+       mdelay(30);
+       if (param == 0) {
+               upper_16 = lower_16 = 0;
+       } else {
+               /*send 32 bit param as two 16 bit param, little endian */
+               lower_16 = (unsigned short)(param & 0xffff);
+               upper_16 = (unsigned short)((param >> 16) & 0xffff);
+       }
+       ret = usb_control_msg(pd->udev,
+                        usb_rcvctrlpipe(pd->udev, 0),
+                        REQ_GET_CMD | cmdid,
+                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                        lower_16,
+                        upper_16,
+                        &data,
+                        (datalen + sizeof(*cmd_status)),
+                        USB_CTRL_GET_TIMEOUT);
+
+       if (ret < 0) {
+               return -ENXIO;
+       } else {
+               /* 1st 4 bytes into cmd_status, remaining data into cmd_data */
+               memcpy((char *)cmd_status, &data[0], sizeof(*cmd_status));
+               memcpy((char *)buf, &data[sizeof(*cmd_status)], datalen);
+       }
+       return 0;
+}
+
+static int pm_notifier_block(struct notifier_block *nb,
+                               unsigned long event, void *dummy)
+{
+       struct poseidon *pd = NULL;
+       struct list_head *node, *next;
+
+       switch (event) {
+       case PM_POST_HIBERNATION:
+               list_for_each_safe(node, next, &pd_device_list) {
+                       struct usb_device *udev;
+                       struct usb_interface *iface;
+                       int rc = 0;
+
+                       pd = container_of(node, struct poseidon, device_list);
+                       udev = pd->udev;
+                       iface = pd->interface;
+
+                       /* It will cause the system to reload the firmware */
+                       rc = usb_lock_device_for_reset(udev, iface);
+                       if (rc >= 0) {
+                               usb_reset_device(udev);
+                               usb_unlock_device(udev);
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+       log("event :%ld\n", event);
+       return 0;
+}
+
+static struct notifier_block pm_notifer = {
+       .notifier_call = pm_notifier_block,
+};
+
+int set_tuner_mode(struct poseidon *pd, unsigned char mode)
+{
+       s32 ret, cmd_status;
+
+       if (pd->state & POSEIDON_STATE_DISCONNECT)
+               return -ENODEV;
+
+       ret = send_set_req(pd, TUNE_MODE_SELECT, mode, &cmd_status);
+       if (ret || cmd_status)
+               return -ENXIO;
+       return 0;
+}
+
+void poseidon_delete(struct kref *kref)
+{
+       struct poseidon *pd = container_of(kref, struct poseidon, kref);
+
+       if (!pd)
+               return;
+       list_del_init(&pd->device_list);
+
+       pd_dvb_usb_device_cleanup(pd);
+       /* clean_audio_data(&pd->audio_data);*/
+
+       if (pd->udev) {
+               usb_put_dev(pd->udev);
+               pd->udev = NULL;
+       }
+       if (pd->interface) {
+               usb_put_intf(pd->interface);
+               pd->interface = NULL;
+       }
+       kfree(pd);
+       log();
+}
+
+static int firmware_download(struct usb_device *udev)
+{
+       int ret = 0, actual_length;
+       const struct firmware *fw = NULL;
+       void *fwbuf = NULL;
+       size_t fwlength = 0, offset;
+       size_t max_packet_size;
+
+       ret = request_firmware(&fw, firmware_name, &udev->dev);
+       if (ret) {
+               log("download err : %d", ret);
+               return ret;
+       }
+
+       fwlength = fw->size;
+
+       fwbuf = kmemdup(fw->data, fwlength, GFP_KERNEL);
+       if (!fwbuf) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       max_packet_size = le16_to_cpu(udev->ep_out[0x1]->desc.wMaxPacketSize);
+       log("\t\t download size : %d", (int)max_packet_size);
+
+       for (offset = 0; offset < fwlength; offset += max_packet_size) {
+               actual_length = 0;
+               ret = usb_bulk_msg(udev,
+                               usb_sndbulkpipe(udev, 0x01), /* ep 1 */
+                               fwbuf + offset,
+                               min(max_packet_size, fwlength - offset),
+                               &actual_length,
+                               HZ * 10);
+               if (ret)
+                       break;
+       }
+       kfree(fwbuf);
+out:
+       release_firmware(fw);
+       return ret;
+}
+
+static inline struct poseidon *get_pd(struct usb_interface *intf)
+{
+       return usb_get_intfdata(intf);
+}
+
+#ifdef CONFIG_PM
+/* one-to-one map : poseidon{} <----> usb_device{}'s port */
+static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
+{
+       pd->portnum = udev->portnum;
+}
+
+static inline int get_autopm_ref(struct poseidon *pd)
+{
+       return  pd->video_data.users + pd->vbi_data.users + pd->audio.users
+               + atomic_read(&pd->dvb_data.users) +
+               !list_empty(&pd->radio_data.fm_dev.fh_list);
+}
+
+/* fixup something for poseidon */
+static inline struct poseidon *fixup(struct poseidon *pd)
+{
+       int count;
+
+       /* old udev and interface have gone, so put back reference . */
+       count = get_autopm_ref(pd);
+       log("count : %d, ref count : %d", count, get_pm_count(pd));
+       while (count--)
+               usb_autopm_put_interface(pd->interface);
+       /*usb_autopm_set_interface(pd->interface); */
+
+       usb_put_dev(pd->udev);
+       usb_put_intf(pd->interface);
+       log("event : %d\n", pd->msg.event);
+       return pd;
+}
+
+static struct poseidon *find_old_poseidon(struct usb_device *udev)
+{
+       struct poseidon *pd;
+
+       list_for_each_entry(pd, &pd_device_list, device_list) {
+               if (pd->portnum == udev->portnum && in_hibernation(pd))
+                       return fixup(pd);
+       }
+       return NULL;
+}
+
+/* Is the card working now ? */
+static inline int is_working(struct poseidon *pd)
+{
+       return get_pm_count(pd) > 0;
+}
+
+static int poseidon_suspend(struct usb_interface *intf, pm_message_t msg)
+{
+       struct poseidon *pd = get_pd(intf);
+
+       if (!pd)
+               return 0;
+       if (!is_working(pd)) {
+               if (get_pm_count(pd) <= 0 && !in_hibernation(pd)) {
+                       pd->msg.event = PM_EVENT_AUTO_SUSPEND;
+                       pd->pm_resume = NULL; /*  a good guard */
+                       printk(KERN_DEBUG "TLG2300 auto suspend\n");
+               }
+               return 0;
+       }
+       pd->msg = msg; /* save it here */
+       logpm(pd);
+       return pd->pm_suspend ? pd->pm_suspend(pd) : 0;
+}
+
+static int poseidon_resume(struct usb_interface *intf)
+{
+       struct poseidon *pd = get_pd(intf);
+
+       if (!pd)
+               return 0;
+       printk(KERN_DEBUG "TLG2300 resume\n");
+
+       if (!is_working(pd)) {
+               if (PM_EVENT_AUTO_SUSPEND == pd->msg.event)
+                       pd->msg = PMSG_ON;
+               return 0;
+       }
+       if (in_hibernation(pd)) {
+               logpm(pd);
+               return 0;
+       }
+       logpm(pd);
+       return pd->pm_resume ? pd->pm_resume(pd) : 0;
+}
+
+static void hibernation_resume(struct work_struct *w)
+{
+       struct poseidon *pd = container_of(w, struct poseidon, pm_work);
+       int count;
+
+       pd->msg.event = 0; /* clear it here */
+       pd->state &= ~POSEIDON_STATE_DISCONNECT;
+
+       /* set the new interface's reference */
+       count = get_autopm_ref(pd);
+       while (count--)
+               usb_autopm_get_interface(pd->interface);
+
+       /* resume the context */
+       logpm(pd);
+       if (pd->pm_resume)
+               pd->pm_resume(pd);
+}
+#else /* CONFIG_PM is not enabled: */
+static inline struct poseidon *find_old_poseidon(struct usb_device *udev)
+{
+       return NULL;
+}
+
+static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
+{
+}
+#endif
+
+static int check_firmware(struct usb_device *udev)
+{
+       void *buf;
+       int ret;
+       struct cmd_firmware_vers_s *cmd_firm;
+
+       buf = kzalloc(sizeof(*cmd_firm) + sizeof(u32), GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       ret = usb_control_msg(udev,
+                        usb_rcvctrlpipe(udev, 0),
+                        REQ_GET_CMD | GET_FW_ID,
+                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                        0,
+                        0,
+                        buf,
+                        sizeof(*cmd_firm) + sizeof(u32),
+                        USB_CTRL_GET_TIMEOUT);
+       kfree(buf);
+
+       if (ret < 0)
+               return firmware_download(udev);
+       return 0;
+}
+
+static int poseidon_probe(struct usb_interface *interface,
+                               const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(interface);
+       struct poseidon *pd = NULL;
+       int ret = 0;
+       int new_one = 0;
+
+       /* download firmware */
+       ret = check_firmware(udev);
+       if (ret)
+               return ret;
+
+       /* Do I recovery from the hibernate ? */
+       pd = find_old_poseidon(udev);
+       if (!pd) {
+               pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+               if (!pd)
+                       return -ENOMEM;
+               kref_init(&pd->kref);
+               set_map_flags(pd, udev);
+               new_one = 1;
+       }
+
+       pd->udev        = usb_get_dev(udev);
+       pd->interface   = usb_get_intf(interface);
+       usb_set_intfdata(interface, pd);
+
+       if (new_one) {
+               logpm(pd);
+               mutex_init(&pd->lock);
+
+               /* register v4l2 device */
+               ret = v4l2_device_register(&interface->dev, &pd->v4l2_dev);
+               if (ret)
+                       goto err_v4l2;
+
+               /* register devices in directory /dev */
+               ret = pd_video_init(pd);
+               if (ret)
+                       goto err_video;
+               ret = poseidon_audio_init(pd);
+               if (ret)
+                       goto err_audio;
+               ret = poseidon_fm_init(pd);
+               if (ret)
+                       goto err_fm;
+               ret = pd_dvb_usb_device_init(pd);
+               if (ret)
+                       goto err_dvb;
+
+               INIT_LIST_HEAD(&pd->device_list);
+               list_add_tail(&pd->device_list, &pd_device_list);
+       }
+
+       device_init_wakeup(&udev->dev, 1);
+#ifdef CONFIG_PM
+       pm_runtime_set_autosuspend_delay(&pd->udev->dev,
+                       1000 * PM_SUSPEND_DELAY);
+       usb_enable_autosuspend(pd->udev);
+
+       if (in_hibernation(pd)) {
+               INIT_WORK(&pd->pm_work, hibernation_resume);
+               schedule_work(&pd->pm_work);
+       }
+#endif
+       return 0;
+err_dvb:
+       poseidon_fm_exit(pd);
+err_fm:
+       poseidon_audio_free(pd);
+err_audio:
+       pd_video_exit(pd);
+err_video:
+       v4l2_device_unregister(&pd->v4l2_dev);
+err_v4l2:
+       usb_put_intf(pd->interface);
+       usb_put_dev(pd->udev);
+       kfree(pd);
+       return ret;
+}
+
+static void poseidon_disconnect(struct usb_interface *interface)
+{
+       struct poseidon *pd = get_pd(interface);
+
+       if (!pd)
+               return;
+       logpm(pd);
+       if (in_hibernation(pd))
+               return;
+
+       mutex_lock(&pd->lock);
+       pd->state |= POSEIDON_STATE_DISCONNECT;
+       mutex_unlock(&pd->lock);
+
+       /* stop urb transferring */
+       stop_all_video_stream(pd);
+       dvb_stop_streaming(&pd->dvb_data);
+
+       /*unregister v4l2 device */
+       v4l2_device_unregister(&pd->v4l2_dev);
+
+       pd_dvb_usb_device_exit(pd);
+       poseidon_fm_exit(pd);
+
+       poseidon_audio_free(pd);
+       pd_video_exit(pd);
+
+       usb_set_intfdata(interface, NULL);
+       kref_put(&pd->kref, poseidon_delete);
+}
+
+static struct usb_driver poseidon_driver = {
+       .name           = "poseidon",
+       .probe          = poseidon_probe,
+       .disconnect     = poseidon_disconnect,
+       .id_table       = id_table,
+#ifdef CONFIG_PM
+       .suspend        = poseidon_suspend,
+       .resume         = poseidon_resume,
+#endif
+       .supports_autosuspend = 1,
+};
+
+static int __init poseidon_init(void)
+{
+       int ret;
+
+       ret = usb_register(&poseidon_driver);
+       if (ret)
+               return ret;
+       register_pm_notifier(&pm_notifer);
+       return ret;
+}
+
+static void __exit poseidon_exit(void)
+{
+       log();
+       unregister_pm_notifier(&pm_notifer);
+       usb_deregister(&poseidon_driver);
+}
+
+module_init(poseidon_init);
+module_exit(poseidon_exit);
+
+MODULE_AUTHOR("Telegent Systems");
+MODULE_DESCRIPTION("For tlg2300-based USB device");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.2");
+MODULE_FIRMWARE(TLG2300_FIRMWARE);
diff --git a/drivers/staging/media/tlg2300/pd-radio.c b/drivers/staging/media/tlg2300/pd-radio.c
new file mode 100644 (file)
index 0000000..b391194
--- /dev/null
@@ -0,0 +1,339 @@
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <media/v4l2-dev.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <linux/sched.h>
+
+#include "pd-common.h"
+#include "vendorcmds.h"
+
+static int set_frequency(struct poseidon *p, __u32 frequency);
+static int poseidon_fm_close(struct file *filp);
+static int poseidon_fm_open(struct file *filp);
+
+#define TUNER_FREQ_MIN_FM 76000000U
+#define TUNER_FREQ_MAX_FM 108000000U
+
+#define MAX_PREEMPHASIS (V4L2_PREEMPHASIS_75_uS + 1)
+static int preemphasis[MAX_PREEMPHASIS] = {
+       TLG_TUNE_ASTD_NONE,   /* V4L2_PREEMPHASIS_DISABLED */
+       TLG_TUNE_ASTD_FM_EUR, /* V4L2_PREEMPHASIS_50_uS    */
+       TLG_TUNE_ASTD_FM_US,  /* V4L2_PREEMPHASIS_75_uS    */
+};
+
+static int poseidon_check_mode_radio(struct poseidon *p)
+{
+       int ret;
+       u32 status;
+
+       set_current_state(TASK_INTERRUPTIBLE);
+       schedule_timeout(HZ/2);
+       ret = usb_set_interface(p->udev, 0, BULK_ALTERNATE_IFACE);
+       if (ret < 0)
+               goto out;
+
+       ret = set_tuner_mode(p, TLG_MODE_FM_RADIO);
+       if (ret != 0)
+               goto out;
+
+       ret = send_set_req(p, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &status);
+       ret = send_set_req(p, TUNER_AUD_ANA_STD,
+                               p->radio_data.pre_emphasis, &status);
+       ret |= send_set_req(p, TUNER_AUD_MODE,
+                               TLG_TUNE_TVAUDIO_MODE_STEREO, &status);
+       ret |= send_set_req(p, AUDIO_SAMPLE_RATE_SEL,
+                               ATV_AUDIO_RATE_48K, &status);
+       ret |= send_set_req(p, TUNE_FREQ_SELECT, TUNER_FREQ_MIN_FM, &status);
+out:
+       return ret;
+}
+
+#ifdef CONFIG_PM
+static int pm_fm_suspend(struct poseidon *p)
+{
+       logpm(p);
+       pm_alsa_suspend(p);
+       usb_set_interface(p->udev, 0, 0);
+       msleep(300);
+       return 0;
+}
+
+static int pm_fm_resume(struct poseidon *p)
+{
+       logpm(p);
+       poseidon_check_mode_radio(p);
+       set_frequency(p, p->radio_data.fm_freq);
+       pm_alsa_resume(p);
+       return 0;
+}
+#endif
+
+static int poseidon_fm_open(struct file *filp)
+{
+       struct poseidon *p = video_drvdata(filp);
+       int ret = 0;
+
+       mutex_lock(&p->lock);
+       if (p->state & POSEIDON_STATE_DISCONNECT) {
+               ret = -ENODEV;
+               goto out;
+       }
+
+       if (p->state && !(p->state & POSEIDON_STATE_FM)) {
+               ret = -EBUSY;
+               goto out;
+       }
+       ret = v4l2_fh_open(filp);
+       if (ret)
+               goto out;
+
+       usb_autopm_get_interface(p->interface);
+       if (0 == p->state) {
+               struct video_device *vfd = &p->radio_data.fm_dev;
+
+               /* default pre-emphasis */
+               if (p->radio_data.pre_emphasis == 0)
+                       p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR;
+               set_debug_mode(vfd, debug_mode);
+
+               ret = poseidon_check_mode_radio(p);
+               if (ret < 0) {
+                       usb_autopm_put_interface(p->interface);
+                       goto out;
+               }
+               p->state |= POSEIDON_STATE_FM;
+       }
+       kref_get(&p->kref);
+out:
+       mutex_unlock(&p->lock);
+       return ret;
+}
+
+static int poseidon_fm_close(struct file *filp)
+{
+       struct poseidon *p = video_drvdata(filp);
+       struct radio_data *fm = &p->radio_data;
+       uint32_t status;
+
+       mutex_lock(&p->lock);
+       if (v4l2_fh_is_singular_file(filp))
+               p->state &= ~POSEIDON_STATE_FM;
+
+       if (fm->is_radio_streaming && filp == p->file_for_stream) {
+               fm->is_radio_streaming = 0;
+               send_set_req(p, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, &status);
+       }
+       usb_autopm_put_interface(p->interface);
+       mutex_unlock(&p->lock);
+
+       kref_put(&p->kref, poseidon_delete);
+       return v4l2_fh_release(filp);
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+                       struct v4l2_capability *v)
+{
+       struct poseidon *p = video_drvdata(file);
+
+       strlcpy(v->driver, "tele-radio", sizeof(v->driver));
+       strlcpy(v->card, "Telegent Poseidon", sizeof(v->card));
+       usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info));
+       v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+       /* Report all capabilities of the USB device */
+       v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS |
+                       V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE |
+                       V4L2_CAP_AUDIO | V4L2_CAP_STREAMING |
+                       V4L2_CAP_READWRITE;
+       return 0;
+}
+
+static const struct v4l2_file_operations poseidon_fm_fops = {
+       .owner         = THIS_MODULE,
+       .open          = poseidon_fm_open,
+       .release       = poseidon_fm_close,
+       .poll           = v4l2_ctrl_poll,
+       .unlocked_ioctl = video_ioctl2,
+};
+
+static int tlg_fm_vidioc_g_tuner(struct file *file, void *priv,
+                                struct v4l2_tuner *vt)
+{
+       struct poseidon *p = video_drvdata(file);
+       struct tuner_fm_sig_stat_s fm_stat = {};
+       int ret, status, count = 5;
+
+       if (vt->index != 0)
+               return -EINVAL;
+
+       vt->type        = V4L2_TUNER_RADIO;
+       vt->capability  = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
+       vt->rangelow    = TUNER_FREQ_MIN_FM * 2 / 125;
+       vt->rangehigh   = TUNER_FREQ_MAX_FM * 2 / 125;
+       vt->rxsubchans  = V4L2_TUNER_SUB_STEREO;
+       vt->audmode     = V4L2_TUNER_MODE_STEREO;
+       vt->signal      = 0;
+       vt->afc         = 0;
+       strlcpy(vt->name, "Radio", sizeof(vt->name));
+
+       mutex_lock(&p->lock);
+       ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
+                             &fm_stat, &status, sizeof(fm_stat));
+
+       while (fm_stat.sig_lock_busy && count-- && !ret) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(HZ);
+
+               ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
+                                 &fm_stat, &status, sizeof(fm_stat));
+       }
+       mutex_unlock(&p->lock);
+
+       if (ret || status) {
+               vt->signal = 0;
+       } else if ((fm_stat.sig_present || fm_stat.sig_locked)
+                       && fm_stat.sig_strength == 0) {
+               vt->signal = 0xffff;
+       } else
+               vt->signal = (fm_stat.sig_strength * 255 / 10) << 8;
+
+       return 0;
+}
+
+static int fm_get_freq(struct file *file, void *priv,
+                      struct v4l2_frequency *argp)
+{
+       struct poseidon *p = video_drvdata(file);
+
+       if (argp->tuner)
+               return -EINVAL;
+       argp->frequency = p->radio_data.fm_freq;
+       return 0;
+}
+
+static int set_frequency(struct poseidon *p, __u32 frequency)
+{
+       __u32 freq ;
+       int ret, status;
+
+       mutex_lock(&p->lock);
+
+       ret = send_set_req(p, TUNER_AUD_ANA_STD,
+                               p->radio_data.pre_emphasis, &status);
+
+       freq = (frequency * 125) / 2; /* Hz */
+       freq = clamp(freq, TUNER_FREQ_MIN_FM, TUNER_FREQ_MAX_FM);
+
+       ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status);
+       if (ret < 0)
+               goto error ;
+       ret = send_set_req(p, TAKE_REQUEST, 0, &status);
+
+       set_current_state(TASK_INTERRUPTIBLE);
+       schedule_timeout(HZ/4);
+       if (!p->radio_data.is_radio_streaming) {
+               ret = send_set_req(p, TAKE_REQUEST, 0, &status);
+               ret = send_set_req(p, PLAY_SERVICE,
+                               TLG_TUNE_PLAY_SVC_START, &status);
+               p->radio_data.is_radio_streaming = 1;
+       }
+       p->radio_data.fm_freq = freq * 2 / 125;
+error:
+       mutex_unlock(&p->lock);
+       return ret;
+}
+
+static int fm_set_freq(struct file *file, void *priv,
+                      const struct v4l2_frequency *argp)
+{
+       struct poseidon *p = video_drvdata(file);
+
+       if (argp->tuner)
+               return -EINVAL;
+       p->file_for_stream = file;
+#ifdef CONFIG_PM
+       p->pm_suspend = pm_fm_suspend;
+       p->pm_resume  = pm_fm_resume;
+#endif
+       return set_frequency(p, argp->frequency);
+}
+
+static int tlg_fm_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct poseidon *p = container_of(ctrl->handler, struct poseidon,
+                                               radio_data.ctrl_handler);
+       int pre_emphasis;
+       u32 status;
+
+       switch (ctrl->id) {
+       case V4L2_CID_TUNE_PREEMPHASIS:
+               pre_emphasis = preemphasis[ctrl->val];
+               send_set_req(p, TUNER_AUD_ANA_STD, pre_emphasis, &status);
+               p->radio_data.pre_emphasis = pre_emphasis;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *vt)
+{
+       return vt->index > 0 ? -EINVAL : 0;
+}
+
+static const struct v4l2_ctrl_ops tlg_fm_ctrl_ops = {
+       .s_ctrl = tlg_fm_s_ctrl,
+};
+
+static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = {
+       .vidioc_querycap    = vidioc_querycap,
+       .vidioc_s_tuner     = vidioc_s_tuner,
+       .vidioc_g_tuner     = tlg_fm_vidioc_g_tuner,
+       .vidioc_g_frequency = fm_get_freq,
+       .vidioc_s_frequency = fm_set_freq,
+       .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static struct video_device poseidon_fm_template = {
+       .name       = "Telegent-Radio",
+       .fops       = &poseidon_fm_fops,
+       .minor      = -1,
+       .release    = video_device_release_empty,
+       .ioctl_ops  = &poseidon_fm_ioctl_ops,
+};
+
+int poseidon_fm_init(struct poseidon *p)
+{
+       struct video_device *vfd = &p->radio_data.fm_dev;
+       struct v4l2_ctrl_handler *hdl = &p->radio_data.ctrl_handler;
+
+       *vfd = poseidon_fm_template;
+
+       set_frequency(p, TUNER_FREQ_MIN_FM);
+       v4l2_ctrl_handler_init(hdl, 1);
+       v4l2_ctrl_new_std_menu(hdl, &tlg_fm_ctrl_ops, V4L2_CID_TUNE_PREEMPHASIS,
+                       V4L2_PREEMPHASIS_75_uS, 0, V4L2_PREEMPHASIS_50_uS);
+       if (hdl->error) {
+               v4l2_ctrl_handler_free(hdl);
+               return hdl->error;
+       }
+       vfd->v4l2_dev = &p->v4l2_dev;
+       vfd->ctrl_handler = hdl;
+       video_set_drvdata(vfd, p);
+       return video_register_device(vfd, VFL_TYPE_RADIO, -1);
+}
+
+int poseidon_fm_exit(struct poseidon *p)
+{
+       video_unregister_device(&p->radio_data.fm_dev);
+       v4l2_ctrl_handler_free(&p->radio_data.ctrl_handler);
+       return 0;
+}
diff --git a/drivers/staging/media/tlg2300/pd-video.c b/drivers/staging/media/tlg2300/pd-video.c
new file mode 100644 (file)
index 0000000..8cd7f02
--- /dev/null
@@ -0,0 +1,1570 @@
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+#include <linux/usb.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ctrls.h>
+
+#include "pd-common.h"
+#include "vendorcmds.h"
+
+#ifdef CONFIG_PM
+static int pm_video_suspend(struct poseidon *pd);
+static int pm_video_resume(struct poseidon *pd);
+#endif
+static void iso_bubble_handler(struct work_struct *w);
+
+static int usb_transfer_mode;
+module_param(usb_transfer_mode, int, 0644);
+MODULE_PARM_DESC(usb_transfer_mode, "0 = Bulk, 1 = Isochronous");
+
+static const struct poseidon_format poseidon_formats[] = {
+       { "YUV 422", V4L2_PIX_FMT_YUYV, 16, 0},
+       { "RGB565", V4L2_PIX_FMT_RGB565, 16, 0},
+};
+
+static const struct poseidon_tvnorm poseidon_tvnorms[] = {
+       { V4L2_STD_PAL_D, "PAL-D",  TLG_TUNE_VSTD_PAL_D },
+       { V4L2_STD_PAL_B, "PAL-B",  TLG_TUNE_VSTD_PAL_B },
+       { V4L2_STD_PAL_G, "PAL-G",  TLG_TUNE_VSTD_PAL_G },
+       { V4L2_STD_PAL_H, "PAL-H",  TLG_TUNE_VSTD_PAL_H },
+       { V4L2_STD_PAL_I, "PAL-I",  TLG_TUNE_VSTD_PAL_I },
+       { V4L2_STD_PAL_M, "PAL-M",  TLG_TUNE_VSTD_PAL_M },
+       { V4L2_STD_PAL_N, "PAL-N",  TLG_TUNE_VSTD_PAL_N_COMBO },
+       { V4L2_STD_PAL_Nc, "PAL-Nc", TLG_TUNE_VSTD_PAL_N_COMBO },
+       { V4L2_STD_NTSC_M, "NTSC-M", TLG_TUNE_VSTD_NTSC_M },
+       { V4L2_STD_NTSC_M_JP, "NTSC-JP", TLG_TUNE_VSTD_NTSC_M_J },
+       { V4L2_STD_SECAM_B, "SECAM-B", TLG_TUNE_VSTD_SECAM_B },
+       { V4L2_STD_SECAM_D, "SECAM-D", TLG_TUNE_VSTD_SECAM_D },
+       { V4L2_STD_SECAM_G, "SECAM-G", TLG_TUNE_VSTD_SECAM_G },
+       { V4L2_STD_SECAM_H, "SECAM-H", TLG_TUNE_VSTD_SECAM_H },
+       { V4L2_STD_SECAM_K, "SECAM-K", TLG_TUNE_VSTD_SECAM_K },
+       { V4L2_STD_SECAM_K1, "SECAM-K1", TLG_TUNE_VSTD_SECAM_K1 },
+       { V4L2_STD_SECAM_L, "SECAM-L", TLG_TUNE_VSTD_SECAM_L },
+       { V4L2_STD_SECAM_LC, "SECAM-LC", TLG_TUNE_VSTD_SECAM_L1 },
+};
+static const unsigned int POSEIDON_TVNORMS = ARRAY_SIZE(poseidon_tvnorms);
+
+struct pd_audio_mode {
+       u32 tlg_audio_mode;
+       u32 v4l2_audio_sub;
+       u32 v4l2_audio_mode;
+};
+
+static const struct pd_audio_mode pd_audio_modes[] = {
+       { TLG_TUNE_TVAUDIO_MODE_MONO, V4L2_TUNER_SUB_MONO,
+               V4L2_TUNER_MODE_MONO },
+       { TLG_TUNE_TVAUDIO_MODE_STEREO, V4L2_TUNER_SUB_STEREO,
+               V4L2_TUNER_MODE_STEREO },
+       { TLG_TUNE_TVAUDIO_MODE_LANG_A, V4L2_TUNER_SUB_LANG1,
+               V4L2_TUNER_MODE_LANG1 },
+       { TLG_TUNE_TVAUDIO_MODE_LANG_B, V4L2_TUNER_SUB_LANG2,
+               V4L2_TUNER_MODE_LANG2 },
+       { TLG_TUNE_TVAUDIO_MODE_LANG_C, V4L2_TUNER_SUB_LANG1,
+               V4L2_TUNER_MODE_LANG1_LANG2 }
+};
+static const unsigned int POSEIDON_AUDIOMODS = ARRAY_SIZE(pd_audio_modes);
+
+struct pd_input {
+       char *name;
+       uint32_t tlg_src;
+};
+
+static const struct pd_input pd_inputs[] = {
+       { "TV Antenna", TLG_SIG_SRC_ANTENNA },
+       { "TV Cable", TLG_SIG_SRC_CABLE },
+       { "TV SVideo", TLG_SIG_SRC_SVIDEO },
+       { "TV Composite", TLG_SIG_SRC_COMPOSITE }
+};
+static const unsigned int POSEIDON_INPUTS = ARRAY_SIZE(pd_inputs);
+
+struct video_std_to_audio_std {
+       v4l2_std_id     video_std;
+       int             audio_std;
+};
+
+static const struct video_std_to_audio_std video_to_audio_map[] = {
+       /* country : { 27, 32, 33, 34, 36, 44, 45, 46, 47, 48, 64,
+                       65, 86, 351, 352, 353, 354, 358, 372, 852, 972 } */
+       { (V4L2_STD_PAL_I | V4L2_STD_PAL_B | V4L2_STD_PAL_D |
+               V4L2_STD_SECAM_L | V4L2_STD_SECAM_D), TLG_TUNE_ASTD_NICAM },
+
+       /* country : { 1, 52, 54, 55, 886 } */
+       {V4L2_STD_NTSC_M | V4L2_STD_PAL_N | V4L2_STD_PAL_M, TLG_TUNE_ASTD_BTSC},
+
+       /* country : { 81 } */
+       { V4L2_STD_NTSC_M_JP, TLG_TUNE_ASTD_EIAJ },
+
+       /* other country : TLG_TUNE_ASTD_A2 */
+};
+static const unsigned int map_size = ARRAY_SIZE(video_to_audio_map);
+
+static int get_audio_std(v4l2_std_id v4l2_std)
+{
+       int i = 0;
+
+       for (; i < map_size; i++) {
+               if (v4l2_std & video_to_audio_map[i].video_std)
+                       return video_to_audio_map[i].audio_std;
+       }
+       return TLG_TUNE_ASTD_A2;
+}
+
+static int vidioc_querycap(struct file *file, void *fh,
+                       struct v4l2_capability *cap)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct poseidon *p = video_get_drvdata(vdev);
+
+       strcpy(cap->driver, "tele-video");
+       strcpy(cap->card, "Telegent Poseidon");
+       usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info));
+       cap->device_caps = V4L2_CAP_TUNER | V4L2_CAP_AUDIO |
+                       V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+       if (vdev->vfl_type == VFL_TYPE_VBI)
+               cap->device_caps |= V4L2_CAP_VBI_CAPTURE;
+       else
+               cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
+       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS |
+               V4L2_CAP_RADIO | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE;
+       return 0;
+}
+
+/*====================================================================*/
+static void init_copy(struct video_data *video, bool index)
+{
+       struct front_face *front = video->front;
+
+       video->field_count      = index;
+       video->lines_copied     = 0;
+       video->prev_left        = 0 ;
+       video->dst              = (char *)videobuf_to_vmalloc(front->curr_frame)
+                                       + index * video->lines_size;
+       video->vbi->copied      = 0; /* set it here */
+}
+
+static bool get_frame(struct front_face *front, int *need_init)
+{
+       struct videobuf_buffer *vb = front->curr_frame;
+
+       if (vb)
+               return true;
+
+       spin_lock(&front->queue_lock);
+       if (!list_empty(&front->active)) {
+               vb = list_entry(front->active.next,
+                              struct videobuf_buffer, queue);
+               if (need_init)
+                       *need_init = 1;
+               front->curr_frame = vb;
+               list_del_init(&vb->queue);
+       }
+       spin_unlock(&front->queue_lock);
+
+       return !!vb;
+}
+
+/* check if the video's buffer is ready */
+static bool get_video_frame(struct front_face *front, struct video_data *video)
+{
+       int need_init = 0;
+       bool ret = true;
+
+       ret = get_frame(front, &need_init);
+       if (ret && need_init)
+               init_copy(video, 0);
+       return ret;
+}
+
+static void submit_frame(struct front_face *front)
+{
+       struct videobuf_buffer *vb = front->curr_frame;
+
+       if (vb == NULL)
+               return;
+
+       front->curr_frame       = NULL;
+       vb->state               = VIDEOBUF_DONE;
+       vb->field_count++;
+       v4l2_get_timestamp(&vb->ts);
+
+       wake_up(&vb->done);
+}
+
+/*
+ * A frame is composed of two fields. If we receive all the two fields,
+ * call the  submit_frame() to submit the whole frame to applications.
+ */
+static void end_field(struct video_data *video)
+{
+       if (1 == video->field_count)
+               submit_frame(video->front);
+       else
+               init_copy(video, 1);
+}
+
+static void copy_video_data(struct video_data *video, char *src,
+                               unsigned int count)
+{
+#define copy_data(len)  \
+       do { \
+               if (++video->lines_copied > video->lines_per_field) \
+                       goto overflow; \
+               memcpy(video->dst, src, len);\
+               video->dst += len + video->lines_size; \
+               src += len; \
+               count -= len; \
+        } while (0)
+
+       while (count && count >= video->lines_size) {
+               if (video->prev_left) {
+                       copy_data(video->prev_left);
+                       video->prev_left = 0;
+                       continue;
+               }
+               copy_data(video->lines_size);
+       }
+       if (count && count < video->lines_size) {
+               memcpy(video->dst, src, count);
+
+               video->prev_left = video->lines_size - count;
+               video->dst += count;
+       }
+       return;
+
+overflow:
+       end_field(video);
+}
+
+static void check_trailer(struct video_data *video, char *src, int count)
+{
+       struct vbi_data *vbi = video->vbi;
+       int offset; /* trailer's offset */
+       char *buf;
+
+       offset = (video->context.pix.sizeimage / 2 + vbi->vbi_size / 2)
+               - (vbi->copied + video->lines_size * video->lines_copied);
+       if (video->prev_left)
+               offset -= (video->lines_size - video->prev_left);
+
+       if (offset > count || offset <= 0)
+               goto short_package;
+
+       buf = src + offset;
+
+       /* trailer : (VFHS) + U32 + U32 + field_num */
+       if (!strncmp(buf, "VFHS", 4)) {
+               int field_num = *((u32 *)(buf + 12));
+
+               if ((field_num & 1) ^ video->field_count) {
+                       init_copy(video, video->field_count);
+                       return;
+               }
+               copy_video_data(video, src, offset);
+       }
+short_package:
+       end_field(video);
+}
+
+/* ==========  Check this more carefully! =========== */
+static inline void copy_vbi_data(struct vbi_data *vbi,
+                               char *src, unsigned int count)
+{
+       struct front_face *front = vbi->front;
+
+       if (front && get_frame(front, NULL)) {
+               char *buf = videobuf_to_vmalloc(front->curr_frame);
+
+               if (vbi->video->field_count)
+                       buf += (vbi->vbi_size / 2);
+               memcpy(buf + vbi->copied, src, count);
+       }
+       vbi->copied += count;
+}
+
+/*
+ * Copy the normal data (VBI or VIDEO) without the trailer.
+ * VBI is not interlaced, while VIDEO is interlaced.
+ */
+static inline void copy_vbi_video_data(struct video_data *video,
+                               char *src, unsigned int count)
+{
+       struct vbi_data *vbi = video->vbi;
+       unsigned int vbi_delta = (vbi->vbi_size / 2) - vbi->copied;
+
+       if (vbi_delta >= count) {
+               copy_vbi_data(vbi, src, count);
+       } else {
+               if (vbi_delta) {
+                       copy_vbi_data(vbi, src, vbi_delta);
+
+                       /* we receive the two fields of the VBI*/
+                       if (vbi->front && video->field_count)
+                               submit_frame(vbi->front);
+               }
+               copy_video_data(video, src + vbi_delta, count - vbi_delta);
+       }
+}
+
+static void urb_complete_bulk(struct urb *urb)
+{
+       struct front_face *front = urb->context;
+       struct video_data *video = &front->pd->video_data;
+       char *src = (char *)urb->transfer_buffer;
+       int count = urb->actual_length;
+       int ret = 0;
+
+       if (!video->is_streaming || urb->status) {
+               if (urb->status == -EPROTO)
+                       goto resend_it;
+               return;
+       }
+       if (!get_video_frame(front, video))
+               goto resend_it;
+
+       if (count == urb->transfer_buffer_length)
+               copy_vbi_video_data(video, src, count);
+       else
+               check_trailer(video, src, count);
+
+resend_it:
+       ret = usb_submit_urb(urb, GFP_ATOMIC);
+       if (ret)
+               log(" submit failed: error %d", ret);
+}
+
+/************************* for ISO *********************/
+#define GET_SUCCESS            (0)
+#define GET_TRAILER            (1)
+#define GET_TOO_MUCH_BUBBLE    (2)
+#define GET_NONE               (3)
+static int get_chunk(int start, struct urb *urb,
+                       int *head, int *tail, int *bubble_err)
+{
+       struct usb_iso_packet_descriptor *pkt = NULL;
+       int ret = GET_SUCCESS;
+
+       for (*head = *tail = -1; start < urb->number_of_packets; start++) {
+               pkt = &urb->iso_frame_desc[start];
+
+               /* handle the bubble of the Hub */
+               if (-EOVERFLOW == pkt->status) {
+                       if (++*bubble_err > urb->number_of_packets / 3)
+                               return GET_TOO_MUCH_BUBBLE;
+                       continue;
+               }
+
+               /* This is the gap */
+               if (pkt->status || pkt->actual_length <= 0
+                               || pkt->actual_length > ISO_PKT_SIZE) {
+                       if (*head != -1)
+                               break;
+                       continue;
+               }
+
+               /* a good isochronous packet */
+               if (pkt->actual_length == ISO_PKT_SIZE) {
+                       if (*head == -1)
+                               *head = start;
+                       *tail = start;
+                       continue;
+               }
+
+               /* trailer is here */
+               if (pkt->actual_length < ISO_PKT_SIZE) {
+                       if (*head == -1) {
+                               *head = start;
+                               *tail = start;
+                               return GET_TRAILER;
+                       }
+                       break;
+               }
+       }
+
+       if (*head == -1 && *tail == -1)
+               ret = GET_NONE;
+       return ret;
+}
+
+/*
+ * |__|------|___|-----|_______|
+ *       ^          ^
+ *       |          |
+ *      gap        gap
+ */
+static void urb_complete_iso(struct urb *urb)
+{
+       struct front_face *front = urb->context;
+       struct video_data *video = &front->pd->video_data;
+       int bubble_err = 0, head = 0, tail = 0;
+       char *src = (char *)urb->transfer_buffer;
+       int ret = 0;
+
+       if (!video->is_streaming)
+               return;
+
+       do {
+               if (!get_video_frame(front, video))
+                       goto out;
+
+               switch (get_chunk(head, urb, &head, &tail, &bubble_err)) {
+               case GET_SUCCESS:
+                       copy_vbi_video_data(video, src + (head * ISO_PKT_SIZE),
+                                       (tail - head + 1) * ISO_PKT_SIZE);
+                       break;
+               case GET_TRAILER:
+                       check_trailer(video, src + (head * ISO_PKT_SIZE),
+                                       ISO_PKT_SIZE);
+                       break;
+               case GET_NONE:
+                       goto out;
+               case GET_TOO_MUCH_BUBBLE:
+                       log("\t We got too much bubble");
+                       schedule_work(&video->bubble_work);
+                       return;
+               }
+       } while (head = tail + 1, head < urb->number_of_packets);
+
+out:
+       ret = usb_submit_urb(urb, GFP_ATOMIC);
+       if (ret)
+               log("usb_submit_urb err : %d", ret);
+}
+/*============================= [  end  ] =====================*/
+
+static int prepare_iso_urb(struct video_data *video)
+{
+       struct usb_device *udev = video->pd->udev;
+       int i;
+
+       if (video->urb_array[0])
+               return 0;
+
+       for (i = 0; i < SBUF_NUM; i++) {
+               struct urb *urb;
+               void *mem;
+               int j;
+
+               urb = usb_alloc_urb(PK_PER_URB, GFP_KERNEL);
+               if (urb == NULL)
+                       goto out;
+
+               video->urb_array[i] = urb;
+               mem = usb_alloc_coherent(udev,
+                                        ISO_PKT_SIZE * PK_PER_URB,
+                                        GFP_KERNEL,
+                                        &urb->transfer_dma);
+
+               urb->complete   = urb_complete_iso;     /* handler */
+               urb->dev        = udev;
+               urb->context    = video->front;
+               urb->pipe       = usb_rcvisocpipe(udev,
+                                               video->endpoint_addr);
+               urb->interval   = 1;
+               urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+               urb->number_of_packets  = PK_PER_URB;
+               urb->transfer_buffer    = mem;
+               urb->transfer_buffer_length = PK_PER_URB * ISO_PKT_SIZE;
+
+               for (j = 0; j < PK_PER_URB; j++) {
+                       urb->iso_frame_desc[j].offset = ISO_PKT_SIZE * j;
+                       urb->iso_frame_desc[j].length = ISO_PKT_SIZE;
+               }
+       }
+       return 0;
+out:
+       for (; i > 0; i--)
+               ;
+       return -ENOMEM;
+}
+
+/* return the succeeded number of the allocation */
+int alloc_bulk_urbs_generic(struct urb **urb_array, int num,
+                       struct usb_device *udev, u8 ep_addr,
+                       int buf_size, gfp_t gfp_flags,
+                       usb_complete_t complete_fn, void *context)
+{
+       int i = 0;
+
+       for (; i < num; i++) {
+               void *mem;
+               struct urb *urb = usb_alloc_urb(0, gfp_flags);
+               if (urb == NULL)
+                       return i;
+
+               mem = usb_alloc_coherent(udev, buf_size, gfp_flags,
+                                        &urb->transfer_dma);
+               if (mem == NULL) {
+                       usb_free_urb(urb);
+                       return i;
+               }
+
+               usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, ep_addr),
+                               mem, buf_size, complete_fn, context);
+               urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+               urb_array[i] = urb;
+       }
+       return i;
+}
+
+void free_all_urb_generic(struct urb **urb_array, int num)
+{
+       int i;
+       struct urb *urb;
+
+       for (i = 0; i < num; i++) {
+               urb = urb_array[i];
+               if (urb) {
+                       usb_free_coherent(urb->dev,
+                                       urb->transfer_buffer_length,
+                                       urb->transfer_buffer,
+                                       urb->transfer_dma);
+                       usb_free_urb(urb);
+                       urb_array[i] = NULL;
+               }
+       }
+}
+
+static int prepare_bulk_urb(struct video_data *video)
+{
+       if (video->urb_array[0])
+               return 0;
+
+       alloc_bulk_urbs_generic(video->urb_array, SBUF_NUM,
+                       video->pd->udev, video->endpoint_addr,
+                       0x2000, GFP_KERNEL,
+                       urb_complete_bulk, video->front);
+       return 0;
+}
+
+/* free the URBs */
+static void free_all_urb(struct video_data *video)
+{
+       free_all_urb_generic(video->urb_array, SBUF_NUM);
+}
+
+static void pd_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+       videobuf_vmalloc_free(vb);
+       vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static void pd_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+       struct front_face *front = q->priv_data;
+       vb->state = VIDEOBUF_QUEUED;
+       list_add_tail(&vb->queue, &front->active);
+}
+
+static int pd_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+                          enum v4l2_field field)
+{
+       struct front_face *front = q->priv_data;
+       int rc;
+
+       switch (front->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (VIDEOBUF_NEEDS_INIT == vb->state) {
+                       struct v4l2_pix_format *pix;
+
+                       pix = &front->pd->video_data.context.pix;
+                       vb->size        = pix->sizeimage; /* real frame size */
+                       vb->width       = pix->width;
+                       vb->height      = pix->height;
+                       rc = videobuf_iolock(q, vb, NULL);
+                       if (rc < 0)
+                               return rc;
+               }
+               break;
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               if (VIDEOBUF_NEEDS_INIT == vb->state) {
+                       vb->size        = front->pd->vbi_data.vbi_size;
+                       rc = videobuf_iolock(q, vb, NULL);
+                       if (rc < 0)
+                               return rc;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+       vb->field = field;
+       vb->state = VIDEOBUF_PREPARED;
+       return 0;
+}
+
+static int fire_all_urb(struct video_data *video)
+{
+       int i, ret;
+
+       video->is_streaming = 1;
+
+       for (i = 0; i < SBUF_NUM; i++) {
+               ret = usb_submit_urb(video->urb_array[i], GFP_KERNEL);
+               if (ret)
+                       log("(%d) failed: error %d", i, ret);
+       }
+       return ret;
+}
+
+static int start_video_stream(struct poseidon *pd)
+{
+       struct video_data *video = &pd->video_data;
+       s32 cmd_status;
+
+       send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+       send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_START, &cmd_status);
+
+       if (pd->cur_transfer_mode) {
+               prepare_iso_urb(video);
+               INIT_WORK(&video->bubble_work, iso_bubble_handler);
+       } else {
+               /* The bulk mode does not need a bubble handler */
+               prepare_bulk_urb(video);
+       }
+       fire_all_urb(video);
+       return 0;
+}
+
+static int pd_buf_setup(struct videobuf_queue *q, unsigned int *count,
+                      unsigned int *size)
+{
+       struct front_face *front = q->priv_data;
+       struct poseidon *pd     = front->pd;
+
+       switch (front->type) {
+       default:
+               return -EINVAL;
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+               struct video_data *video = &pd->video_data;
+               struct v4l2_pix_format *pix = &video->context.pix;
+
+               *size = PAGE_ALIGN(pix->sizeimage);/* page aligned frame size */
+               if (*count < 4)
+                       *count = 4;
+               if (1) {
+                       /* same in different altersetting */
+                       video->endpoint_addr    = 0x82;
+                       video->vbi              = &pd->vbi_data;
+                       video->vbi->video       = video;
+                       video->pd               = pd;
+                       video->lines_per_field  = pix->height / 2;
+                       video->lines_size       = pix->width * 2;
+                       video->front            = front;
+               }
+               return start_video_stream(pd);
+       }
+
+       case V4L2_BUF_TYPE_VBI_CAPTURE: {
+               struct vbi_data *vbi = &pd->vbi_data;
+
+               *size = PAGE_ALIGN(vbi->vbi_size);
+               log("size : %d", *size);
+               if (*count == 0)
+                       *count = 4;
+       }
+               break;
+       }
+       return 0;
+}
+
+static struct videobuf_queue_ops pd_video_qops = {
+       .buf_setup      = pd_buf_setup,
+       .buf_prepare    = pd_buf_prepare,
+       .buf_queue      = pd_buf_queue,
+       .buf_release    = pd_buf_release,
+};
+
+static int vidioc_enum_fmt(struct file *file, void *fh,
+                               struct v4l2_fmtdesc *f)
+{
+       if (ARRAY_SIZE(poseidon_formats) <= f->index)
+               return -EINVAL;
+       f->type         = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       f->flags        = 0;
+       f->pixelformat  = poseidon_formats[f->index].fourcc;
+       strcpy(f->description, poseidon_formats[f->index].name);
+       return 0;
+}
+
+static int vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+       struct front_face *front = fh;
+       struct poseidon *pd = front->pd;
+
+       f->fmt.pix = pd->video_data.context.pix;
+       return 0;
+}
+
+/*
+ * VLC calls VIDIOC_S_STD before VIDIOC_S_FMT, while
+ * Mplayer calls them in the reverse order.
+ */
+static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix)
+{
+       struct video_data *video        = &pd->video_data;
+       struct running_context *context = &video->context;
+       struct v4l2_pix_format *pix_def = &context->pix;
+       s32 ret = 0, cmd_status = 0, vid_resol;
+
+       /* set the pixel format to firmware */
+       if (pix->pixelformat == V4L2_PIX_FMT_RGB565) {
+               vid_resol = TLG_TUNER_VID_FORMAT_RGB_565;
+       } else {
+               pix->pixelformat = V4L2_PIX_FMT_YUYV;
+               vid_resol = TLG_TUNER_VID_FORMAT_YUV;
+       }
+       ret = send_set_req(pd, VIDEO_STREAM_FMT_SEL,
+                               vid_resol, &cmd_status);
+
+       /* set the resolution to firmware */
+       vid_resol = TLG_TUNE_VID_RES_720;
+       switch (pix->width) {
+       case 704:
+               vid_resol = TLG_TUNE_VID_RES_704;
+               break;
+       default:
+               pix->width = 720;
+       case 720:
+               break;
+       }
+       ret |= send_set_req(pd, VIDEO_ROSOLU_SEL,
+                               vid_resol, &cmd_status);
+       if (ret || cmd_status)
+               return -EBUSY;
+
+       pix_def->pixelformat = pix->pixelformat; /* save it */
+       pix->height = (context->tvnormid & V4L2_STD_525_60) ?  480 : 576;
+
+       /* Compare with the default setting */
+       if ((pix_def->width != pix->width)
+               || (pix_def->height != pix->height)) {
+               pix_def->width          = pix->width;
+               pix_def->height         = pix->height;
+               pix_def->bytesperline   = pix->width * 2;
+               pix_def->sizeimage      = pix->width * pix->height * 2;
+       }
+       *pix = *pix_def;
+
+       return 0;
+}
+
+static int vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+       struct front_face *front        = fh;
+       struct poseidon *pd             = front->pd;
+
+       /* stop VBI here */
+       if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type)
+               return -EINVAL;
+
+       mutex_lock(&pd->lock);
+       if (pd->file_for_stream == NULL)
+               pd->file_for_stream = file;
+       else if (file != pd->file_for_stream) {
+               mutex_unlock(&pd->lock);
+               return -EINVAL;
+       }
+
+       pd_vidioc_s_fmt(pd, &f->fmt.pix);
+       mutex_unlock(&pd->lock);
+       return 0;
+}
+
+static int vidioc_g_fmt_vbi(struct file *file, void *fh,
+                              struct v4l2_format *v4l2_f)
+{
+       struct front_face *front        = fh;
+       struct poseidon *pd             = front->pd;
+       struct v4l2_vbi_format *vbi_fmt = &v4l2_f->fmt.vbi;
+
+       vbi_fmt->samples_per_line       = 720 * 2;
+       vbi_fmt->sampling_rate          = 6750000 * 4;
+       vbi_fmt->sample_format          = V4L2_PIX_FMT_GREY;
+       vbi_fmt->offset                 = 64 * 4;  /*FIXME: why offset */
+       if (pd->video_data.context.tvnormid & V4L2_STD_525_60) {
+               vbi_fmt->start[0] = 10;
+               vbi_fmt->start[1] = 264;
+               vbi_fmt->count[0] = V4L_NTSC_VBI_LINES;
+               vbi_fmt->count[1] = V4L_NTSC_VBI_LINES;
+       } else {
+               vbi_fmt->start[0] = 6;
+               vbi_fmt->start[1] = 314;
+               vbi_fmt->count[0] = V4L_PAL_VBI_LINES;
+               vbi_fmt->count[1] = V4L_PAL_VBI_LINES;
+       }
+       vbi_fmt->flags = V4L2_VBI_UNSYNC;
+       return 0;
+}
+
+static int set_std(struct poseidon *pd, v4l2_std_id norm)
+{
+       struct video_data *video = &pd->video_data;
+       struct vbi_data *vbi    = &pd->vbi_data;
+       struct running_context *context;
+       struct v4l2_pix_format *pix;
+       s32 i, ret = 0, cmd_status, param;
+       int height;
+
+       for (i = 0; i < POSEIDON_TVNORMS; i++) {
+               if (norm & poseidon_tvnorms[i].v4l2_id) {
+                       param = poseidon_tvnorms[i].tlg_tvnorm;
+                       log("name : %s", poseidon_tvnorms[i].name);
+                       goto found;
+               }
+       }
+       return -EINVAL;
+found:
+       mutex_lock(&pd->lock);
+       ret = send_set_req(pd, VIDEO_STD_SEL, param, &cmd_status);
+       if (ret || cmd_status)
+               goto out;
+
+       /* Set vbi size and check the height of the frame */
+       context = &video->context;
+       context->tvnormid = poseidon_tvnorms[i].v4l2_id;
+       if (context->tvnormid & V4L2_STD_525_60) {
+               vbi->vbi_size = V4L_NTSC_VBI_FRAMESIZE;
+               height = 480;
+       } else {
+               vbi->vbi_size = V4L_PAL_VBI_FRAMESIZE;
+               height = 576;
+       }
+
+       pix = &context->pix;
+       if (pix->height != height) {
+               pix->height     = height;
+               pix->sizeimage  = pix->width * pix->height * 2;
+       }
+
+out:
+       mutex_unlock(&pd->lock);
+       return ret;
+}
+
+static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id norm)
+{
+       struct front_face *front = fh;
+
+       return set_std(front->pd, norm);
+}
+
+static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+       struct front_face *front = fh;
+
+       *norm = front->pd->video_data.context.tvnormid;
+       return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in)
+{
+       if (in->index >= POSEIDON_INPUTS)
+               return -EINVAL;
+       strcpy(in->name, pd_inputs[in->index].name);
+       in->type  = V4L2_INPUT_TYPE_TUNER;
+
+       /*
+        * the audio input index mixed with this video input,
+        * Poseidon only have one audio/video, set to "0"
+        */
+       in->audioset    = 1;
+       in->tuner       = 0;
+       in->std         = V4L2_STD_ALL;
+       in->status      = 0;
+       return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+       struct front_face *front = fh;
+       struct poseidon *pd = front->pd;
+       struct running_context *context = &pd->video_data.context;
+
+       *i = context->sig_index;
+       return 0;
+}
+
+/* We can support several inputs */
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+       struct front_face *front = fh;
+       struct poseidon *pd = front->pd;
+       s32 ret, cmd_status;
+
+       if (i >= POSEIDON_INPUTS)
+               return -EINVAL;
+       ret = send_set_req(pd, SGNL_SRC_SEL,
+                       pd_inputs[i].tlg_src, &cmd_status);
+       if (ret)
+               return ret;
+
+       pd->video_data.context.sig_index = i;
+       return 0;
+}
+
+static int tlg_s_ctrl(struct v4l2_ctrl *c)
+{
+       struct poseidon *pd = container_of(c->handler, struct poseidon,
+                                               video_data.ctrl_handler);
+       struct tuner_custom_parameter_s param = {0};
+       s32 ret = 0, cmd_status, params;
+
+       switch (c->id) {
+       case V4L2_CID_BRIGHTNESS:
+               param.param_id = CUST_PARM_ID_BRIGHTNESS_CTRL;
+               break;
+       case V4L2_CID_CONTRAST:
+               param.param_id = CUST_PARM_ID_CONTRAST_CTRL;
+               break;
+       case V4L2_CID_HUE:
+               param.param_id = CUST_PARM_ID_HUE_CTRL;
+               break;
+       case V4L2_CID_SATURATION:
+               param.param_id = CUST_PARM_ID_SATURATION_CTRL;
+               break;
+       }
+       param.param_value = c->val;
+       params = *(s32 *)&param; /* temp code */
+
+       mutex_lock(&pd->lock);
+       ret = send_set_req(pd, TUNER_CUSTOM_PARAMETER, params, &cmd_status);
+       ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+       mutex_unlock(&pd->lock);
+
+       set_current_state(TASK_INTERRUPTIBLE);
+       schedule_timeout(HZ/4);
+       return ret;
+}
+
+/* Audio ioctls */
+static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+       if (0 != a->index)
+               return -EINVAL;
+       a->capability = V4L2_AUDCAP_STEREO;
+       strcpy(a->name, "USB audio in");
+       /*Poseidon have no AVL function.*/
+       a->mode = 0;
+       return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+       a->index = 0;
+       a->capability = V4L2_AUDCAP_STEREO;
+       strcpy(a->name, "USB audio in");
+       a->mode = 0;
+       return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a)
+{
+       return (0 == a->index) ? 0 : -EINVAL;
+}
+
+/* Tuner ioctls */
+static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *tuner)
+{
+       struct front_face *front        = fh;
+       struct poseidon *pd             = front->pd;
+       struct tuner_atv_sig_stat_s atv_stat;
+       s32 count = 5, ret, cmd_status;
+       int index;
+
+       if (0 != tuner->index)
+               return -EINVAL;
+
+       mutex_lock(&pd->lock);
+       ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV,
+                               &atv_stat, &cmd_status, sizeof(atv_stat));
+
+       while (atv_stat.sig_lock_busy && count-- && !ret) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(HZ);
+
+               ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV,
+                               &atv_stat, &cmd_status, sizeof(atv_stat));
+       }
+       mutex_unlock(&pd->lock);
+
+       if (debug_mode)
+               log("P:%d,S:%d", atv_stat.sig_present, atv_stat.sig_strength);
+
+       if (ret || cmd_status)
+               tuner->signal = 0;
+       else if (atv_stat.sig_present && !atv_stat.sig_strength)
+               tuner->signal = 0xFFFF;
+       else
+               tuner->signal = (atv_stat.sig_strength * 255 / 10) << 8;
+
+       strcpy(tuner->name, "Telegent Systems");
+       tuner->type = V4L2_TUNER_ANALOG_TV;
+       tuner->rangelow = TUNER_FREQ_MIN / 62500;
+       tuner->rangehigh = TUNER_FREQ_MAX / 62500;
+       tuner->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
+                               V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+       index = pd->video_data.context.audio_idx;
+       tuner->rxsubchans = pd_audio_modes[index].v4l2_audio_sub;
+       tuner->audmode = pd_audio_modes[index].v4l2_audio_mode;
+       tuner->afc = 0;
+       return 0;
+}
+
+static int pd_vidioc_s_tuner(struct poseidon *pd, int index)
+{
+       s32 ret = 0, cmd_status, param, audiomode;
+
+       mutex_lock(&pd->lock);
+       param = pd_audio_modes[index].tlg_audio_mode;
+       ret = send_set_req(pd, TUNER_AUD_MODE, param, &cmd_status);
+       audiomode = get_audio_std(pd->video_data.context.tvnormid);
+       ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode,
+                               &cmd_status);
+       if (!ret)
+               pd->video_data.context.audio_idx = index;
+       mutex_unlock(&pd->lock);
+       return ret;
+}
+
+static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *a)
+{
+       struct front_face *front        = fh;
+       struct poseidon *pd             = front->pd;
+       int index;
+
+       if (0 != a->index)
+               return -EINVAL;
+       for (index = 0; index < POSEIDON_AUDIOMODS; index++)
+               if (a->audmode == pd_audio_modes[index].v4l2_audio_mode)
+                       return pd_vidioc_s_tuner(pd, index);
+       return -EINVAL;
+}
+
+static int vidioc_g_frequency(struct file *file, void *fh,
+                       struct v4l2_frequency *freq)
+{
+       struct front_face *front = fh;
+       struct poseidon *pd = front->pd;
+       struct running_context *context = &pd->video_data.context;
+
+       if (0 != freq->tuner)
+               return -EINVAL;
+       freq->frequency = context->freq;
+       freq->type = V4L2_TUNER_ANALOG_TV;
+       return 0;
+}
+
+static int set_frequency(struct poseidon *pd, u32 *frequency)
+{
+       s32 ret = 0, param, cmd_status;
+       struct running_context *context = &pd->video_data.context;
+
+       *frequency = clamp(*frequency,
+                       TUNER_FREQ_MIN / 62500, TUNER_FREQ_MAX / 62500);
+       param = (*frequency) * 62500 / 1000;
+
+       mutex_lock(&pd->lock);
+       ret = send_set_req(pd, TUNE_FREQ_SELECT, param, &cmd_status);
+       ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+
+       msleep(250); /* wait for a while until the hardware is ready. */
+       context->freq = *frequency;
+       mutex_unlock(&pd->lock);
+       return ret;
+}
+
+static int vidioc_s_frequency(struct file *file, void *fh,
+                               const struct v4l2_frequency *freq)
+{
+       struct front_face *front = fh;
+       struct poseidon *pd = front->pd;
+       u32 frequency = freq->frequency;
+
+       if (freq->tuner)
+               return -EINVAL;
+#ifdef CONFIG_PM
+       pd->pm_suspend = pm_video_suspend;
+       pd->pm_resume = pm_video_resume;
+#endif
+       return set_frequency(pd, &frequency);
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+                               struct v4l2_requestbuffers *b)
+{
+       struct front_face *front = file->private_data;
+       return videobuf_reqbufs(&front->q, b);
+}
+
+static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+       struct front_face *front = file->private_data;
+       return videobuf_querybuf(&front->q, b);
+}
+
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+       struct front_face *front = file->private_data;
+       return videobuf_qbuf(&front->q, b);
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+       struct front_face *front = file->private_data;
+       return videobuf_dqbuf(&front->q, b, file->f_flags & O_NONBLOCK);
+}
+
+/* Just stop the URBs, do not free the URBs */
+static int usb_transfer_stop(struct video_data *video)
+{
+       if (video->is_streaming) {
+               int i;
+               s32 cmd_status;
+               struct poseidon *pd = video->pd;
+
+               video->is_streaming = 0;
+               for (i = 0; i < SBUF_NUM; ++i) {
+                       if (video->urb_array[i])
+                               usb_kill_urb(video->urb_array[i]);
+               }
+
+               send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
+                              &cmd_status);
+       }
+       return 0;
+}
+
+int stop_all_video_stream(struct poseidon *pd)
+{
+       struct video_data *video = &pd->video_data;
+       struct vbi_data *vbi    = &pd->vbi_data;
+
+       mutex_lock(&pd->lock);
+       if (video->is_streaming) {
+               struct front_face *front = video->front;
+
+               /* stop the URBs */
+               usb_transfer_stop(video);
+               free_all_urb(video);
+
+               /* stop the host side of VIDEO */
+               videobuf_stop(&front->q);
+               videobuf_mmap_free(&front->q);
+
+               /* stop the host side of VBI */
+               front = vbi->front;
+               if (front) {
+                       videobuf_stop(&front->q);
+                       videobuf_mmap_free(&front->q);
+               }
+       }
+       mutex_unlock(&pd->lock);
+       return 0;
+}
+
+/*
+ * The bubbles can seriously damage the video's quality,
+ * though it occurs in very rare situation.
+ */
+static void iso_bubble_handler(struct work_struct *w)
+{
+       struct video_data *video;
+       struct poseidon *pd;
+
+       video = container_of(w, struct video_data, bubble_work);
+       pd = video->pd;
+
+       mutex_lock(&pd->lock);
+       usb_transfer_stop(video);
+       msleep(500);
+       start_video_stream(pd);
+       mutex_unlock(&pd->lock);
+}
+
+
+static int vidioc_streamon(struct file *file, void *fh,
+                               enum v4l2_buf_type type)
+{
+       struct front_face *front = fh;
+
+       if (unlikely(type != front->type))
+               return -EINVAL;
+       return videobuf_streamon(&front->q);
+}
+
+static int vidioc_streamoff(struct file *file, void *fh,
+                               enum v4l2_buf_type type)
+{
+       struct front_face *front = file->private_data;
+
+       if (unlikely(type != front->type))
+               return -EINVAL;
+       return videobuf_streamoff(&front->q);
+}
+
+/* Set the firmware's default values : need altersetting */
+static int pd_video_checkmode(struct poseidon *pd)
+{
+       s32 ret = 0, cmd_status, audiomode;
+
+       set_current_state(TASK_INTERRUPTIBLE);
+       schedule_timeout(HZ/2);
+
+       /* choose the altersetting */
+       ret = usb_set_interface(pd->udev, 0,
+                                       (pd->cur_transfer_mode ?
+                                        ISO_3K_BULK_ALTERNATE_IFACE :
+                                        BULK_ALTERNATE_IFACE));
+       if (ret < 0)
+               goto error;
+
+       /* set default parameters for PAL-D , with the VBI enabled*/
+       ret = set_tuner_mode(pd, TLG_MODE_ANALOG_TV);
+       ret |= send_set_req(pd, SGNL_SRC_SEL,
+                               TLG_SIG_SRC_ANTENNA, &cmd_status);
+       ret |= send_set_req(pd, VIDEO_STD_SEL,
+                               TLG_TUNE_VSTD_PAL_D, &cmd_status);
+       ret |= send_set_req(pd, VIDEO_STREAM_FMT_SEL,
+                               TLG_TUNER_VID_FORMAT_YUV, &cmd_status);
+       ret |= send_set_req(pd, VIDEO_ROSOLU_SEL,
+                               TLG_TUNE_VID_RES_720, &cmd_status);
+       ret |= send_set_req(pd, TUNE_FREQ_SELECT, TUNER_FREQ_MIN, &cmd_status);
+       ret |= send_set_req(pd, VBI_DATA_SEL, 1, &cmd_status);/* enable vbi */
+
+       /* set the audio */
+       audiomode = get_audio_std(pd->video_data.context.tvnormid);
+       ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, &cmd_status);
+       ret |= send_set_req(pd, TUNER_AUD_MODE,
+                               TLG_TUNE_TVAUDIO_MODE_STEREO, &cmd_status);
+       ret |= send_set_req(pd, AUDIO_SAMPLE_RATE_SEL,
+                               ATV_AUDIO_RATE_48K, &cmd_status);
+error:
+       return ret;
+}
+
+#ifdef CONFIG_PM
+static int pm_video_suspend(struct poseidon *pd)
+{
+       /* stop audio */
+       pm_alsa_suspend(pd);
+
+       /* stop and free all the URBs */
+       usb_transfer_stop(&pd->video_data);
+       free_all_urb(&pd->video_data);
+
+       /* reset the interface */
+       usb_set_interface(pd->udev, 0, 0);
+       msleep(300);
+       return 0;
+}
+
+static int restore_v4l2_context(struct poseidon *pd,
+                               struct running_context *context)
+{
+       struct front_face *front = pd->video_data.front;
+
+       pd_video_checkmode(pd);
+
+       set_std(pd, context->tvnormid);
+       vidioc_s_input(NULL, front, context->sig_index);
+       pd_vidioc_s_tuner(pd, context->audio_idx);
+       pd_vidioc_s_fmt(pd, &context->pix);
+       set_frequency(pd, &context->freq);
+       return 0;
+}
+
+static int pm_video_resume(struct poseidon *pd)
+{
+       struct video_data *video = &pd->video_data;
+
+       /* resume the video */
+       /* [1] restore the origin V4L2 parameters */
+       restore_v4l2_context(pd, &video->context);
+
+       /* [2] initiate video copy variables */
+       if (video->front->curr_frame)
+               init_copy(video, 0);
+
+       /* [3] fire urbs        */
+       start_video_stream(pd);
+
+       /* resume the audio */
+       pm_alsa_resume(pd);
+       return 0;
+}
+#endif
+
+void set_debug_mode(struct video_device *vfd, int debug_mode)
+{
+       vfd->debug = 0;
+       if (debug_mode & 0x1)
+               vfd->debug = V4L2_DEBUG_IOCTL;
+       if (debug_mode & 0x2)
+               vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
+}
+
+static void init_video_context(struct running_context *context)
+{
+       context->sig_index      = 0;
+       context->audio_idx      = 1; /* stereo */
+       context->tvnormid       = V4L2_STD_PAL_D;
+       context->pix = (struct v4l2_pix_format) {
+                               .width          = 720,
+                               .height         = 576,
+                               .pixelformat    = V4L2_PIX_FMT_YUYV,
+                               .field          = V4L2_FIELD_INTERLACED,
+                               .bytesperline   = 720 * 2,
+                               .sizeimage      = 720 * 576 * 2,
+                               .colorspace     = V4L2_COLORSPACE_SMPTE170M,
+                       };
+}
+
+static int pd_video_open(struct file *file)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct poseidon *pd = video_get_drvdata(vfd);
+       struct front_face *front = NULL;
+       int ret = -ENOMEM;
+
+       mutex_lock(&pd->lock);
+       usb_autopm_get_interface(pd->interface);
+
+       if (pd->state && !(pd->state & POSEIDON_STATE_ANALOG)) {
+               ret = -EBUSY;
+               goto out;
+       }
+       front = kzalloc(sizeof(struct front_face), GFP_KERNEL);
+       if (!front)
+               goto out;
+       if (vfd->vfl_type == VFL_TYPE_GRABBER) {
+               pd->cur_transfer_mode   = usb_transfer_mode;/* bulk or iso */
+               init_video_context(&pd->video_data.context);
+
+               ret = pd_video_checkmode(pd);
+               if (ret < 0) {
+                       kfree(front);
+                       ret = -1;
+                       goto out;
+               }
+
+               front->type             = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               pd->video_data.users++;
+               set_debug_mode(vfd, debug_mode);
+
+               videobuf_queue_vmalloc_init(&front->q, &pd_video_qops,
+                               NULL, &front->queue_lock,
+                               V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                               V4L2_FIELD_INTERLACED,/* video is interlacd */
+                               sizeof(struct videobuf_buffer),/*it's enough*/
+                               front, NULL);
+       } else {
+               front->type     = V4L2_BUF_TYPE_VBI_CAPTURE;
+               pd->vbi_data.front = front;
+               pd->vbi_data.users++;
+
+               videobuf_queue_vmalloc_init(&front->q, &pd_video_qops,
+                               NULL, &front->queue_lock,
+                               V4L2_BUF_TYPE_VBI_CAPTURE,
+                               V4L2_FIELD_NONE, /* vbi is NONE mode */
+                               sizeof(struct videobuf_buffer),
+                               front, NULL);
+       }
+
+       pd->state |= POSEIDON_STATE_ANALOG;
+       front->pd = pd;
+       front->curr_frame = NULL;
+       INIT_LIST_HEAD(&front->active);
+       spin_lock_init(&front->queue_lock);
+
+       file->private_data = front;
+       kref_get(&pd->kref);
+
+       mutex_unlock(&pd->lock);
+       return 0;
+out:
+       usb_autopm_put_interface(pd->interface);
+       mutex_unlock(&pd->lock);
+       return ret;
+}
+
+static int pd_video_release(struct file *file)
+{
+       struct front_face *front = file->private_data;
+       struct poseidon *pd = front->pd;
+       s32 cmd_status = 0;
+
+       mutex_lock(&pd->lock);
+
+       if (front->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               /* stop the device, and free the URBs */
+               usb_transfer_stop(&pd->video_data);
+               free_all_urb(&pd->video_data);
+
+               /* stop the firmware */
+               send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
+                              &cmd_status);
+
+               pd->file_for_stream = NULL;
+               pd->video_data.users--;
+       } else if (front->type  == V4L2_BUF_TYPE_VBI_CAPTURE) {
+               pd->vbi_data.front = NULL;
+               pd->vbi_data.users--;
+       }
+       if (!pd->vbi_data.users && !pd->video_data.users)
+               pd->state &= ~POSEIDON_STATE_ANALOG;
+       videobuf_stop(&front->q);
+       videobuf_mmap_free(&front->q);
+
+       usb_autopm_put_interface(pd->interface);
+       mutex_unlock(&pd->lock);
+
+       kfree(front);
+       file->private_data = NULL;
+       kref_put(&pd->kref, poseidon_delete);
+       return 0;
+}
+
+static int pd_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct front_face *front = file->private_data;
+       return  videobuf_mmap_mapper(&front->q, vma);
+}
+
+static unsigned int pd_video_poll(struct file *file, poll_table *table)
+{
+       struct front_face *front = file->private_data;
+       return videobuf_poll_stream(file, &front->q, table);
+}
+
+static ssize_t pd_video_read(struct file *file, char __user *buffer,
+                       size_t count, loff_t *ppos)
+{
+       struct front_face *front = file->private_data;
+       return videobuf_read_stream(&front->q, buffer, count, ppos,
+                               0, file->f_flags & O_NONBLOCK);
+}
+
+/* This struct works for both VIDEO and VBI */
+static const struct v4l2_file_operations pd_video_fops = {
+       .owner          = THIS_MODULE,
+       .open           = pd_video_open,
+       .release        = pd_video_release,
+       .read           = pd_video_read,
+       .poll           = pd_video_poll,
+       .mmap           = pd_video_mmap,
+       .ioctl          = video_ioctl2, /* maybe changed in future */
+};
+
+static const struct v4l2_ioctl_ops pd_video_ioctl_ops = {
+       .vidioc_querycap        = vidioc_querycap,
+
+       /* Video format */
+       .vidioc_g_fmt_vid_cap   = vidioc_g_fmt,
+       .vidioc_enum_fmt_vid_cap        = vidioc_enum_fmt,
+       .vidioc_s_fmt_vid_cap   = vidioc_s_fmt,
+       .vidioc_g_fmt_vbi_cap   = vidioc_g_fmt_vbi, /* VBI */
+
+       /* Input */
+       .vidioc_g_input         = vidioc_g_input,
+       .vidioc_s_input         = vidioc_s_input,
+       .vidioc_enum_input      = vidioc_enum_input,
+
+       /* Audio ioctls */
+       .vidioc_enumaudio       = vidioc_enumaudio,
+       .vidioc_g_audio         = vidioc_g_audio,
+       .vidioc_s_audio         = vidioc_s_audio,
+
+       /* Tuner ioctls */
+       .vidioc_g_tuner         = vidioc_g_tuner,
+       .vidioc_s_tuner         = vidioc_s_tuner,
+       .vidioc_g_std           = vidioc_g_std,
+       .vidioc_s_std           = vidioc_s_std,
+       .vidioc_g_frequency     = vidioc_g_frequency,
+       .vidioc_s_frequency     = vidioc_s_frequency,
+
+       /* Buffer handlers */
+       .vidioc_reqbufs         = vidioc_reqbufs,
+       .vidioc_querybuf        = vidioc_querybuf,
+       .vidioc_qbuf            = vidioc_qbuf,
+       .vidioc_dqbuf           = vidioc_dqbuf,
+
+       /* Stream on/off */
+       .vidioc_streamon        = vidioc_streamon,
+       .vidioc_streamoff       = vidioc_streamoff,
+};
+
+static struct video_device pd_video_template = {
+       .name = "Telegent-Video",
+       .fops = &pd_video_fops,
+       .minor = -1,
+       .release = video_device_release_empty,
+       .tvnorms = V4L2_STD_ALL,
+       .ioctl_ops = &pd_video_ioctl_ops,
+};
+
+static const struct v4l2_ctrl_ops tlg_ctrl_ops = {
+       .s_ctrl = tlg_s_ctrl,
+};
+
+void pd_video_exit(struct poseidon *pd)
+{
+       struct video_data *video = &pd->video_data;
+       struct vbi_data *vbi = &pd->vbi_data;
+
+       video_unregister_device(&video->v_dev);
+       video_unregister_device(&vbi->v_dev);
+       v4l2_ctrl_handler_free(&video->ctrl_handler);
+       log();
+}
+
+int pd_video_init(struct poseidon *pd)
+{
+       struct video_data *video = &pd->video_data;
+       struct vbi_data *vbi    = &pd->vbi_data;
+       struct v4l2_ctrl_handler *hdl = &video->ctrl_handler;
+       u32 freq = TUNER_FREQ_MIN / 62500;
+       int ret = -ENOMEM;
+
+       v4l2_ctrl_handler_init(hdl, 4);
+       v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_BRIGHTNESS,
+                       0, 10000, 1, 100);
+       v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_CONTRAST,
+                       0, 10000, 1, 100);
+       v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_HUE,
+                       0, 10000, 1, 100);
+       v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_SATURATION,
+                       0, 10000, 1, 100);
+       if (hdl->error) {
+               v4l2_ctrl_handler_free(hdl);
+               return hdl->error;
+       }
+       set_frequency(pd, &freq);
+       video->v_dev = pd_video_template;
+       video->v_dev.v4l2_dev = &pd->v4l2_dev;
+       video->v_dev.ctrl_handler = hdl;
+       video_set_drvdata(&video->v_dev, pd);
+
+       ret = video_register_device(&video->v_dev, VFL_TYPE_GRABBER, -1);
+       if (ret != 0)
+               goto out;
+
+       /* VBI uses the same template as video */
+       vbi->v_dev = pd_video_template;
+       vbi->v_dev.v4l2_dev = &pd->v4l2_dev;
+       vbi->v_dev.ctrl_handler = hdl;
+       video_set_drvdata(&vbi->v_dev, pd);
+       ret = video_register_device(&vbi->v_dev, VFL_TYPE_VBI, -1);
+       if (ret != 0)
+               goto out;
+       log("register VIDEO/VBI devices");
+       return 0;
+out:
+       log("VIDEO/VBI devices register failed, : %d", ret);
+       pd_video_exit(pd);
+       return ret;
+}
diff --git a/drivers/staging/media/tlg2300/vendorcmds.h b/drivers/staging/media/tlg2300/vendorcmds.h
new file mode 100644 (file)
index 0000000..ba6f4ae
--- /dev/null
@@ -0,0 +1,243 @@
+#ifndef VENDOR_CMD_H_
+#define VENDOR_CMD_H_
+
+#define BULK_ALTERNATE_IFACE           (2)
+#define ISO_3K_BULK_ALTERNATE_IFACE     (1)
+#define REQ_SET_CMD                    (0X00)
+#define REQ_GET_CMD                    (0X80)
+
+enum tlg__analog_audio_standard {
+       TLG_TUNE_ASTD_NONE      = 0x00000000,
+       TLG_TUNE_ASTD_A2        = 0x00000001,
+       TLG_TUNE_ASTD_NICAM     = 0x00000002,
+       TLG_TUNE_ASTD_EIAJ      = 0x00000004,
+       TLG_TUNE_ASTD_BTSC      = 0x00000008,
+       TLG_TUNE_ASTD_FM_US     = 0x00000010,
+       TLG_TUNE_ASTD_FM_EUR    = 0x00000020,
+       TLG_TUNE_ASTD_ALL       = 0x0000003f
+};
+
+/*
+ * identifiers for Custom Parameter messages.
+ * @typedef cmd_custom_param_id_t
+ */
+enum cmd_custom_param_id {
+       CUST_PARM_ID_NONE               = 0x00,
+       CUST_PARM_ID_BRIGHTNESS_CTRL    = 0x01,
+       CUST_PARM_ID_CONTRAST_CTRL      = 0x02,
+       CUST_PARM_ID_HUE_CTRL           = 0x03,
+       CUST_PARM_ID_SATURATION_CTRL      = 0x04,
+       CUST_PARM_ID_AUDIO_SNR_THRESHOLD  = 0x10,
+       CUST_PARM_ID_AUDIO_AGC_THRESHOLD  = 0x11,
+       CUST_PARM_ID_MAX
+};
+
+struct  tuner_custom_parameter_s {
+       uint16_t        param_id;        /*  Parameter identifier  */
+       uint16_t        param_value;     /*  Parameter value       */
+};
+
+struct  tuner_ber_rate_s {
+       uint32_t        ber_rate;  /*  BER sample rate in seconds   */
+};
+
+struct tuner_atv_sig_stat_s {
+       uint32_t        sig_present;
+       uint32_t        sig_locked;
+       uint32_t        sig_lock_busy;
+       uint32_t        sig_strength;      /*  milliDb    */
+       uint32_t        tv_audio_chan;    /*  mono/stereo/sap*/
+       uint32_t        mvision_stat;      /*  macrovision status */
+};
+
+struct tuner_dtv_sig_stat_s {
+       uint32_t sig_present;   /*  Boolean*/
+       uint32_t sig_locked;    /*  Boolean */
+       uint32_t sig_lock_busy; /*  Boolean     (Can this time-out?) */
+       uint32_t sig_strength;  /*  milliDb*/
+};
+
+struct tuner_fm_sig_stat_s {
+       uint32_t sig_present;   /* Boolean*/
+       uint32_t sig_locked;     /* Boolean */
+       uint32_t sig_lock_busy;  /* Boolean */
+       uint32_t sig_stereo_mono;/* TBD*/
+       uint32_t sig_strength;   /* milliDb*/
+};
+
+enum _tag_tlg_tune_srv_cmd {
+       TLG_TUNE_PLAY_SVC_START = 1,
+       TLG_TUNE_PLAY_SVC_STOP
+};
+
+enum  _tag_tune_atv_audio_mode_caps {
+       TLG_TUNE_TVAUDIO_MODE_MONO      = 0x00000001,
+       TLG_TUNE_TVAUDIO_MODE_STEREO    = 0x00000002,
+       TLG_TUNE_TVAUDIO_MODE_LANG_A    = 0x00000010,/* Primary language*/
+       TLG_TUNE_TVAUDIO_MODE_LANG_B    = 0x00000020,/* 2nd avail language*/
+       TLG_TUNE_TVAUDIO_MODE_LANG_C    = 0x00000040
+};
+
+
+enum   _tag_tuner_atv_audio_rates {
+       ATV_AUDIO_RATE_NONE     = 0x00,/* Audio not supported*/
+       ATV_AUDIO_RATE_32K      = 0x01,/* Audio rate = 32 KHz*/
+       ATV_AUDIO_RATE_48K      = 0x02, /* Audio rate = 48 KHz*/
+       ATV_AUDIO_RATE_31_25K   = 0x04 /* Audio rate = 31.25KHz */
+};
+
+enum  _tag_tune_atv_vid_res_caps {
+       TLG_TUNE_VID_RES_NONE   = 0x00000000,
+       TLG_TUNE_VID_RES_720    = 0x00000001,
+       TLG_TUNE_VID_RES_704    = 0x00000002,
+       TLG_TUNE_VID_RES_360    = 0x00000004
+};
+
+enum _tag_tuner_analog_video_format {
+       TLG_TUNER_VID_FORMAT_YUV        = 0x00000001,
+       TLG_TUNER_VID_FORMAT_YCRCB      = 0x00000002,
+       TLG_TUNER_VID_FORMAT_RGB_565    = 0x00000004,
+};
+
+enum  tlg_ext_audio_support {
+       TLG_EXT_AUDIO_NONE      = 0x00,/*  No external audio input supported */
+       TLG_EXT_AUDIO_LR        = 0x01/*  LR external audio inputs supported*/
+};
+
+enum {
+       TLG_MODE_NONE                   = 0x00, /* No Mode specified*/
+       TLG_MODE_ANALOG_TV              = 0x01, /* Analog Television mode*/
+       TLG_MODE_ANALOG_TV_UNCOMP       = 0x01, /* Analog Television mode*/
+       TLG_MODE_ANALOG_TV_COMP         = 0x02, /* Analog TV mode (compressed)*/
+       TLG_MODE_FM_RADIO               = 0x04, /* FM Radio mode*/
+       TLG_MODE_DVB_T                  = 0x08, /* Digital TV (DVB-T)*/
+};
+
+enum  tlg_signal_sources_t {
+       TLG_SIG_SRC_NONE        = 0x00,/* Signal source not specified */
+       TLG_SIG_SRC_ANTENNA     = 0x01,/* Signal src is: Antenna */
+       TLG_SIG_SRC_CABLE       = 0x02,/* Signal src is: Coax Cable*/
+       TLG_SIG_SRC_SVIDEO      = 0x04,/* Signal src is: S_VIDEO   */
+       TLG_SIG_SRC_COMPOSITE   = 0x08 /* Signal src is: Composite Video */
+};
+
+enum tuner_analog_video_standard {
+       TLG_TUNE_VSTD_NONE      = 0x00000000,
+       TLG_TUNE_VSTD_NTSC_M    = 0x00000001,
+       TLG_TUNE_VSTD_NTSC_M_J  = 0x00000002,/* Japan   */
+       TLG_TUNE_VSTD_PAL_B     = 0x00000010,
+       TLG_TUNE_VSTD_PAL_D     = 0x00000020,
+       TLG_TUNE_VSTD_PAL_G     = 0x00000040,
+       TLG_TUNE_VSTD_PAL_H     = 0x00000080,
+       TLG_TUNE_VSTD_PAL_I     = 0x00000100,
+       TLG_TUNE_VSTD_PAL_M     = 0x00000200,
+       TLG_TUNE_VSTD_PAL_N     = 0x00000400,
+       TLG_TUNE_VSTD_SECAM_B   = 0x00001000,
+       TLG_TUNE_VSTD_SECAM_D   = 0x00002000,
+       TLG_TUNE_VSTD_SECAM_G   = 0x00004000,
+       TLG_TUNE_VSTD_SECAM_H   = 0x00008000,
+       TLG_TUNE_VSTD_SECAM_K   = 0x00010000,
+       TLG_TUNE_VSTD_SECAM_K1  = 0x00020000,
+       TLG_TUNE_VSTD_SECAM_L   = 0x00040000,
+       TLG_TUNE_VSTD_SECAM_L1  = 0x00080000,
+       TLG_TUNE_VSTD_PAL_N_COMBO = 0x00100000
+};
+
+enum tlg_mode_caps {
+       TLG_MODE_CAPS_NONE              = 0x00,  /*  No Mode specified  */
+       TLG_MODE_CAPS_ANALOG_TV_UNCOMP  = 0x01,  /*  Analog TV mode     */
+       TLG_MODE_CAPS_ANALOG_TV_COMP    = 0x02,  /*  Analog TV (compressed)*/
+       TLG_MODE_CAPS_FM_RADIO          = 0x04,  /*  FM Radio mode      */
+       TLG_MODE_CAPS_DVB_T             = 0x08,  /*  Digital TV (DVB-T) */
+};
+
+enum poseidon_vendor_cmds {
+       LAST_CMD_STAT           = 0x00,
+       GET_CHIP_ID             = 0x01,
+       GET_FW_ID               = 0x02,
+       PRODUCT_CAPS            = 0x03,
+
+       TUNE_MODE_CAP_ATV       = 0x10,
+       TUNE_MODE_CAP_ATVCOMP   = 0X10,
+       TUNE_MODE_CAP_DVBT      = 0x10,
+       TUNE_MODE_CAP_FM        = 0x10,
+       TUNE_MODE_SELECT        = 0x11,
+       TUNE_FREQ_SELECT        = 0x12,
+       SGNL_SRC_SEL            = 0x13,
+
+       VIDEO_STD_SEL           = 0x14,
+       VIDEO_STREAM_FMT_SEL    = 0x15,
+       VIDEO_ROSOLU_AVAIL      = 0x16,
+       VIDEO_ROSOLU_SEL        = 0x17,
+       VIDEO_CONT_PROTECT      = 0x20,
+
+       VCR_TIMING_MODSEL       = 0x21,
+       EXT_AUDIO_CAP           = 0x22,
+       EXT_AUDIO_SEL           = 0x23,
+       TEST_PATTERN_SEL        = 0x24,
+       VBI_DATA_SEL            = 0x25,
+       AUDIO_SAMPLE_RATE_CAP   = 0x28,
+       AUDIO_SAMPLE_RATE_SEL   = 0x29,
+       TUNER_AUD_MODE          = 0x2a,
+       TUNER_AUD_MODE_AVAIL    = 0x2b,
+       TUNER_AUD_ANA_STD       = 0x2c,
+       TUNER_CUSTOM_PARAMETER  = 0x2f,
+
+       DVBT_TUNE_MODE_SEL      = 0x30,
+       DVBT_BANDW_CAP          = 0x31,
+       DVBT_BANDW_SEL          = 0x32,
+       DVBT_GUARD_INTERV_CAP   = 0x33,
+       DVBT_GUARD_INTERV_SEL   = 0x34,
+       DVBT_MODULATION_CAP     = 0x35,
+       DVBT_MODULATION_SEL     = 0x36,
+       DVBT_INNER_FEC_RATE_CAP = 0x37,
+       DVBT_INNER_FEC_RATE_SEL = 0x38,
+       DVBT_TRANS_MODE_CAP     = 0x39,
+       DVBT_TRANS_MODE_SEL     = 0x3a,
+       DVBT_SEARCH_RANG        = 0x3c,
+
+       TUNER_SETUP_ANALOG      = 0x40,
+       TUNER_SETUP_DIGITAL     = 0x41,
+       TUNER_SETUP_FM_RADIO    = 0x42,
+       TAKE_REQUEST            = 0x43, /* Take effect of the command */
+       PLAY_SERVICE            = 0x44, /* Play start or Play stop */
+       TUNER_STATUS            = 0x45,
+       TUNE_PROP_DVBT          = 0x46,
+       ERR_RATE_STATS          = 0x47,
+       TUNER_BER_RATE          = 0x48,
+
+       SCAN_CAPS               = 0x50,
+       SCAN_SETUP              = 0x51,
+       SCAN_SERVICE            = 0x52,
+       SCAN_STATS              = 0x53,
+
+       PID_SET                 = 0x58,
+       PID_UNSET               = 0x59,
+       PID_LIST                = 0x5a,
+
+       IRD_CAP                 = 0x60,
+       IRD_MODE_SEL            = 0x61,
+       IRD_SETUP               = 0x62,
+
+       PTM_MODE_CAP            = 0x70,
+       PTM_MODE_SEL            = 0x71,
+       PTM_SERVICE             = 0x72,
+       TUNER_REG_SCRIPT        = 0x73,
+       CMD_CHIP_RST            = 0x74,
+};
+
+enum tlg_bw {
+       TLG_BW_5 = 5,
+       TLG_BW_6 = 6,
+       TLG_BW_7 = 7,
+       TLG_BW_8 = 8,
+       TLG_BW_12 = 12,
+       TLG_BW_15 = 15
+};
+
+struct cmd_firmware_vers_s {
+       uint8_t  fw_rev_major;
+       uint8_t  fw_rev_minor;
+       uint16_t fw_patch;
+};
+#endif /* VENDOR_CMD_H_ */
diff --git a/drivers/staging/media/vino/Kconfig b/drivers/staging/media/vino/Kconfig
new file mode 100644 (file)
index 0000000..03700da
--- /dev/null
@@ -0,0 +1,24 @@
+config VIDEO_VINO
+       tristate "SGI Vino Video For Linux (Deprecated)"
+       depends on I2C && SGI_IP22 && VIDEO_V4L2
+       select VIDEO_SAA7191 if MEDIA_SUBDRV_AUTOSELECT
+       help
+         Say Y here to build in support for the Vino video input system found
+         on SGI Indy machines.
+
+         This driver is deprecated and will be removed soon. If you have
+         hardware for this and you want to work on this driver, then contact
+         the linux-media mailinglist.
+
+config VIDEO_SAA7191
+       tristate "Philips SAA7191 video decoder (Deprecated)"
+       depends on VIDEO_V4L2 && I2C
+       ---help---
+         Support for the Philips SAA7191 video decoder.
+
+         This driver is deprecated and will be removed soon. If you have
+         hardware for this and you want to work on this driver, then contact
+         the linux-media mailinglist.
+
+         To compile this driver as a module, choose M here: the
+         module will be called saa7191.
diff --git a/drivers/staging/media/vino/Makefile b/drivers/staging/media/vino/Makefile
new file mode 100644 (file)
index 0000000..914c251
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_VIDEO_VINO) += indycam.o
+obj-$(CONFIG_VIDEO_VINO) += vino.o
+obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
diff --git a/drivers/staging/media/vino/indycam.c b/drivers/staging/media/vino/indycam.c
new file mode 100644 (file)
index 0000000..f1d192b
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ *  indycam.c - Silicon Graphics IndyCam digital camera driver
+ *
+ *  Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ *  Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+/* IndyCam decodes stream of photons into digital image representation ;-) */
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+
+#include "indycam.h"
+
+#define INDYCAM_MODULE_VERSION "0.0.5"
+
+MODULE_DESCRIPTION("SGI IndyCam driver");
+MODULE_VERSION(INDYCAM_MODULE_VERSION);
+MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
+MODULE_LICENSE("GPL");
+
+
+// #define INDYCAM_DEBUG
+
+#ifdef INDYCAM_DEBUG
+#define dprintk(x...) printk("IndyCam: " x);
+#define indycam_regdump(client) indycam_regdump_debug(client)
+#else
+#define dprintk(x...)
+#define indycam_regdump(client)
+#endif
+
+struct indycam {
+       struct v4l2_subdev sd;
+       u8 version;
+};
+
+static inline struct indycam *to_indycam(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct indycam, sd);
+}
+
+static const u8 initseq[] = {
+       INDYCAM_CONTROL_AGCENA,         /* INDYCAM_CONTROL */
+       INDYCAM_SHUTTER_60,             /* INDYCAM_SHUTTER */
+       INDYCAM_GAIN_DEFAULT,           /* INDYCAM_GAIN */
+       0x00,                           /* INDYCAM_BRIGHTNESS (read-only) */
+       INDYCAM_RED_BALANCE_DEFAULT,    /* INDYCAM_RED_BALANCE */
+       INDYCAM_BLUE_BALANCE_DEFAULT,   /* INDYCAM_BLUE_BALANCE */
+       INDYCAM_RED_SATURATION_DEFAULT, /* INDYCAM_RED_SATURATION */
+       INDYCAM_BLUE_SATURATION_DEFAULT,/* INDYCAM_BLUE_SATURATION */
+};
+
+/* IndyCam register handling */
+
+static int indycam_read_reg(struct v4l2_subdev *sd, u8 reg, u8 *value)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret;
+
+       if (reg == INDYCAM_REG_RESET) {
+               dprintk("indycam_read_reg(): "
+                       "skipping write-only register %d\n", reg);
+               *value = 0;
+               return 0;
+       }
+
+       ret = i2c_smbus_read_byte_data(client, reg);
+
+       if (ret < 0) {
+               printk(KERN_ERR "IndyCam: indycam_read_reg(): read failed, "
+                      "register = 0x%02x\n", reg);
+               return ret;
+       }
+
+       *value = (u8)ret;
+
+       return 0;
+}
+
+static int indycam_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int err;
+
+       if (reg == INDYCAM_REG_BRIGHTNESS || reg == INDYCAM_REG_VERSION) {
+               dprintk("indycam_write_reg(): "
+                       "skipping read-only register %d\n", reg);
+               return 0;
+       }
+
+       dprintk("Writing Reg %d = 0x%02x\n", reg, value);
+       err = i2c_smbus_write_byte_data(client, reg, value);
+
+       if (err) {
+               printk(KERN_ERR "IndyCam: indycam_write_reg(): write failed, "
+                      "register = 0x%02x, value = 0x%02x\n", reg, value);
+       }
+       return err;
+}
+
+static int indycam_write_block(struct v4l2_subdev *sd, u8 reg,
+                              u8 length, u8 *data)
+{
+       int i, err;
+
+       for (i = 0; i < length; i++) {
+               err = indycam_write_reg(sd, reg + i, data[i]);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+/* Helper functions */
+
+#ifdef INDYCAM_DEBUG
+static void indycam_regdump_debug(struct v4l2_subdev *sd)
+{
+       int i;
+       u8 val;
+
+       for (i = 0; i < 9; i++) {
+               indycam_read_reg(sd, i, &val);
+               dprintk("Reg %d = 0x%02x\n", i, val);
+       }
+}
+#endif
+
+static int indycam_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+       struct indycam *camera = to_indycam(sd);
+       u8 reg;
+       int ret = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_AUTOGAIN:
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, &reg);
+               if (ret)
+                       return -EIO;
+               if (ctrl->id == V4L2_CID_AUTOGAIN)
+                       ctrl->value = (reg & INDYCAM_CONTROL_AGCENA)
+                               ? 1 : 0;
+               else
+                       ctrl->value = (reg & INDYCAM_CONTROL_AWBCTL)
+                               ? 1 : 0;
+               break;
+       case V4L2_CID_EXPOSURE:
+               ret = indycam_read_reg(sd, INDYCAM_REG_SHUTTER, &reg);
+               if (ret)
+                       return -EIO;
+               ctrl->value = ((s32)reg == 0x00) ? 0xff : ((s32)reg - 1);
+               break;
+       case V4L2_CID_GAIN:
+               ret = indycam_read_reg(sd, INDYCAM_REG_GAIN, &reg);
+               if (ret)
+                       return -EIO;
+               ctrl->value = (s32)reg;
+               break;
+       case V4L2_CID_RED_BALANCE:
+               ret = indycam_read_reg(sd, INDYCAM_REG_RED_BALANCE, &reg);
+               if (ret)
+                       return -EIO;
+               ctrl->value = (s32)reg;
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_BALANCE, &reg);
+               if (ret)
+                       return -EIO;
+               ctrl->value = (s32)reg;
+               break;
+       case INDYCAM_CONTROL_RED_SATURATION:
+               ret = indycam_read_reg(sd,
+                                      INDYCAM_REG_RED_SATURATION, &reg);
+               if (ret)
+                       return -EIO;
+               ctrl->value = (s32)reg;
+               break;
+       case INDYCAM_CONTROL_BLUE_SATURATION:
+               ret = indycam_read_reg(sd,
+                                      INDYCAM_REG_BLUE_SATURATION, &reg);
+               if (ret)
+                       return -EIO;
+               ctrl->value = (s32)reg;
+               break;
+       case V4L2_CID_GAMMA:
+               if (camera->version == CAMERA_VERSION_MOOSE) {
+                       ret = indycam_read_reg(sd,
+                                              INDYCAM_REG_GAMMA, &reg);
+                       if (ret)
+                               return -EIO;
+                       ctrl->value = (s32)reg;
+               } else {
+                       ctrl->value = INDYCAM_GAMMA_DEFAULT;
+               }
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int indycam_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+       struct indycam *camera = to_indycam(sd);
+       u8 reg;
+       int ret = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_AUTOGAIN:
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, &reg);
+               if (ret)
+                       break;
+
+               if (ctrl->id == V4L2_CID_AUTOGAIN) {
+                       if (ctrl->value)
+                               reg |= INDYCAM_CONTROL_AGCENA;
+                       else
+                               reg &= ~INDYCAM_CONTROL_AGCENA;
+               } else {
+                       if (ctrl->value)
+                               reg |= INDYCAM_CONTROL_AWBCTL;
+                       else
+                               reg &= ~INDYCAM_CONTROL_AWBCTL;
+               }
+
+               ret = indycam_write_reg(sd, INDYCAM_REG_CONTROL, reg);
+               break;
+       case V4L2_CID_EXPOSURE:
+               reg = (ctrl->value == 0xff) ? 0x00 : (ctrl->value + 1);
+               ret = indycam_write_reg(sd, INDYCAM_REG_SHUTTER, reg);
+               break;
+       case V4L2_CID_GAIN:
+               ret = indycam_write_reg(sd, INDYCAM_REG_GAIN, ctrl->value);
+               break;
+       case V4L2_CID_RED_BALANCE:
+               ret = indycam_write_reg(sd, INDYCAM_REG_RED_BALANCE,
+                                       ctrl->value);
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_BALANCE,
+                                       ctrl->value);
+               break;
+       case INDYCAM_CONTROL_RED_SATURATION:
+               ret = indycam_write_reg(sd, INDYCAM_REG_RED_SATURATION,
+                                       ctrl->value);
+               break;
+       case INDYCAM_CONTROL_BLUE_SATURATION:
+               ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_SATURATION,
+                                       ctrl->value);
+               break;
+       case V4L2_CID_GAMMA:
+               if (camera->version == CAMERA_VERSION_MOOSE) {
+                       ret = indycam_write_reg(sd, INDYCAM_REG_GAMMA,
+                                               ctrl->value);
+               }
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/* I2C-interface */
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops indycam_core_ops = {
+       .g_ctrl = indycam_g_ctrl,
+       .s_ctrl = indycam_s_ctrl,
+};
+
+static const struct v4l2_subdev_ops indycam_ops = {
+       .core = &indycam_core_ops,
+};
+
+static int indycam_probe(struct i2c_client *client,
+                         const struct i2c_device_id *id)
+{
+       int err = 0;
+       struct indycam *camera;
+       struct v4l2_subdev *sd;
+
+       v4l_info(client, "chip found @ 0x%x (%s)\n",
+                       client->addr << 1, client->adapter->name);
+
+       camera = kzalloc(sizeof(struct indycam), GFP_KERNEL);
+       if (!camera)
+               return -ENOMEM;
+
+       sd = &camera->sd;
+       v4l2_i2c_subdev_init(sd, client, &indycam_ops);
+
+       camera->version = i2c_smbus_read_byte_data(client,
+                                                  INDYCAM_REG_VERSION);
+       if (camera->version != CAMERA_VERSION_INDY &&
+           camera->version != CAMERA_VERSION_MOOSE) {
+               kfree(camera);
+               return -ENODEV;
+       }
+
+       printk(KERN_INFO "IndyCam v%d.%d detected\n",
+              INDYCAM_VERSION_MAJOR(camera->version),
+              INDYCAM_VERSION_MINOR(camera->version));
+
+       indycam_regdump(sd);
+
+       // initialize
+       err = indycam_write_block(sd, 0, sizeof(initseq), (u8 *)&initseq);
+       if (err) {
+               printk(KERN_ERR "IndyCam initialization failed\n");
+               kfree(camera);
+               return -EIO;
+       }
+
+       indycam_regdump(sd);
+
+       // white balance
+       err = indycam_write_reg(sd, INDYCAM_REG_CONTROL,
+                         INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL);
+       if (err) {
+               printk(KERN_ERR "IndyCam: White balancing camera failed\n");
+               kfree(camera);
+               return -EIO;
+       }
+
+       indycam_regdump(sd);
+
+       printk(KERN_INFO "IndyCam initialized\n");
+
+       return 0;
+}
+
+static int indycam_remove(struct i2c_client *client)
+{
+       struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+       v4l2_device_unregister_subdev(sd);
+       kfree(to_indycam(sd));
+       return 0;
+}
+
+static const struct i2c_device_id indycam_id[] = {
+       { "indycam", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, indycam_id);
+
+static struct i2c_driver indycam_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "indycam",
+       },
+       .probe          = indycam_probe,
+       .remove         = indycam_remove,
+       .id_table       = indycam_id,
+};
+
+module_i2c_driver(indycam_driver);
diff --git a/drivers/staging/media/vino/indycam.h b/drivers/staging/media/vino/indycam.h
new file mode 100644 (file)
index 0000000..881f21c
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ *  indycam.h - Silicon Graphics IndyCam digital camera driver
+ *
+ *  Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ *  Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#ifndef _INDYCAM_H_
+#define _INDYCAM_H_
+
+/* I2C address for the Guinness Camera */
+#define INDYCAM_ADDR                   0x56
+
+/* Camera version */
+#define CAMERA_VERSION_INDY            0x10    /* v1.0 */
+#define CAMERA_VERSION_MOOSE           0x12    /* v1.2 */
+#define INDYCAM_VERSION_MAJOR(x)       (((x) & 0xf0) >> 4)
+#define INDYCAM_VERSION_MINOR(x)       ((x) & 0x0f)
+
+/* Register bus addresses */
+#define INDYCAM_REG_CONTROL            0x00
+#define INDYCAM_REG_SHUTTER            0x01
+#define INDYCAM_REG_GAIN               0x02
+#define INDYCAM_REG_BRIGHTNESS         0x03 /* read-only */
+#define INDYCAM_REG_RED_BALANCE                0x04
+#define INDYCAM_REG_BLUE_BALANCE       0x05
+#define INDYCAM_REG_RED_SATURATION     0x06
+#define INDYCAM_REG_BLUE_SATURATION    0x07
+#define INDYCAM_REG_GAMMA              0x08
+#define INDYCAM_REG_VERSION            0x0e /* read-only */
+#define INDYCAM_REG_RESET              0x0f /* write-only */
+
+#define INDYCAM_REG_LED                        0x46
+#define INDYCAM_REG_ORIENTATION                0x47
+#define INDYCAM_REG_BUTTON             0x48
+
+/* Field definitions of registers */
+#define INDYCAM_CONTROL_AGCENA         (1<<0) /* automatic gain control */
+#define INDYCAM_CONTROL_AWBCTL         (1<<1) /* automatic white balance */
+                                               /* 2-3 are reserved */
+#define INDYCAM_CONTROL_EVNFLD         (1<<4)  /* read-only */
+
+#define INDYCAM_SHUTTER_10000          0x02    /* 1/10000 second */
+#define INDYCAM_SHUTTER_4000           0x04    /* 1/4000 second */
+#define INDYCAM_SHUTTER_2000           0x08    /* 1/2000 second */
+#define INDYCAM_SHUTTER_1000           0x10    /* 1/1000 second */
+#define INDYCAM_SHUTTER_500            0x20    /* 1/500 second */
+#define INDYCAM_SHUTTER_250            0x3f    /* 1/250 second */
+#define INDYCAM_SHUTTER_125            0x7e    /* 1/125 second */
+#define INDYCAM_SHUTTER_100            0x9e    /* 1/100 second */
+#define INDYCAM_SHUTTER_60             0x00    /* 1/60 second */
+
+#define INDYCAM_LED_ACTIVE                     0x10
+#define INDYCAM_LED_INACTIVE                   0x30
+#define INDYCAM_ORIENTATION_BOTTOM_TO_TOP      0x40
+#define INDYCAM_BUTTON_RELEASED                        0x10
+
+/* Values for controls */
+#define INDYCAM_SHUTTER_MIN            0x00
+#define INDYCAM_SHUTTER_MAX            0xff
+#define INDYCAM_GAIN_MIN                0x00
+#define INDYCAM_GAIN_MAX                0xff
+#define INDYCAM_RED_BALANCE_MIN                0x00
+#define INDYCAM_RED_BALANCE_MAX                0xff
+#define INDYCAM_BLUE_BALANCE_MIN        0x00
+#define INDYCAM_BLUE_BALANCE_MAX        0xff
+#define INDYCAM_RED_SATURATION_MIN      0x00
+#define INDYCAM_RED_SATURATION_MAX      0xff
+#define INDYCAM_BLUE_SATURATION_MIN    0x00
+#define INDYCAM_BLUE_SATURATION_MAX    0xff
+#define INDYCAM_GAMMA_MIN              0x00
+#define INDYCAM_GAMMA_MAX              0xff
+
+#define INDYCAM_AGC_DEFAULT            1
+#define INDYCAM_AWB_DEFAULT            0
+#define INDYCAM_SHUTTER_DEFAULT                0xff
+#define INDYCAM_GAIN_DEFAULT           0x80
+#define INDYCAM_RED_BALANCE_DEFAULT    0x18
+#define INDYCAM_BLUE_BALANCE_DEFAULT   0xa4
+#define INDYCAM_RED_SATURATION_DEFAULT 0x80
+#define INDYCAM_BLUE_SATURATION_DEFAULT        0xc0
+#define INDYCAM_GAMMA_DEFAULT          0x80
+
+/* Driver interface definitions */
+
+#define INDYCAM_CONTROL_RED_SATURATION         (V4L2_CID_PRIVATE_BASE + 0)
+#define INDYCAM_CONTROL_BLUE_SATURATION                (V4L2_CID_PRIVATE_BASE + 1)
+
+#endif
diff --git a/drivers/staging/media/vino/saa7191.c b/drivers/staging/media/vino/saa7191.c
new file mode 100644 (file)
index 0000000..8e96992
--- /dev/null
@@ -0,0 +1,649 @@
+/*
+ *  saa7191.c - Philips SAA7191 video decoder driver
+ *
+ *  Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ *  Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+
+#include "saa7191.h"
+
+#define SAA7191_MODULE_VERSION "0.0.5"
+
+MODULE_DESCRIPTION("Philips SAA7191 video decoder driver");
+MODULE_VERSION(SAA7191_MODULE_VERSION);
+MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
+MODULE_LICENSE("GPL");
+
+
+// #define SAA7191_DEBUG
+
+#ifdef SAA7191_DEBUG
+#define dprintk(x...) printk("SAA7191: " x);
+#else
+#define dprintk(x...)
+#endif
+
+#define SAA7191_SYNC_COUNT     30
+#define SAA7191_SYNC_DELAY     100     /* milliseconds */
+
+struct saa7191 {
+       struct v4l2_subdev sd;
+
+       /* the register values are stored here as the actual
+        * I2C-registers are write-only */
+       u8 reg[25];
+
+       int input;
+       v4l2_std_id norm;
+};
+
+static inline struct saa7191 *to_saa7191(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct saa7191, sd);
+}
+
+static const u8 initseq[] = {
+       0,      /* Subaddress */
+
+       0x50,   /* (0x50) SAA7191_REG_IDEL */
+
+       /* 50 Hz signal timing */
+       0x30,   /* (0x30) SAA7191_REG_HSYB */
+       0x00,   /* (0x00) SAA7191_REG_HSYS */
+       0xe8,   /* (0xe8) SAA7191_REG_HCLB */
+       0xb6,   /* (0xb6) SAA7191_REG_HCLS */
+       0xf4,   /* (0xf4) SAA7191_REG_HPHI */
+
+       /* control */
+       SAA7191_LUMA_APER_1,    /* (0x01) SAA7191_REG_LUMA - CVBS mode */
+       0x00,   /* (0x00) SAA7191_REG_HUEC */
+       0xf8,   /* (0xf8) SAA7191_REG_CKTQ */
+       0xf8,   /* (0xf8) SAA7191_REG_CKTS */
+       0x90,   /* (0x90) SAA7191_REG_PLSE */
+       0x90,   /* (0x90) SAA7191_REG_SESE */
+       0x00,   /* (0x00) SAA7191_REG_GAIN */
+       SAA7191_STDC_NFEN | SAA7191_STDC_HRMV,  /* (0x0c) SAA7191_REG_STDC
+                                                * - not SECAM,
+                                                * slow time constant */
+       SAA7191_IOCK_OEDC | SAA7191_IOCK_OEHS | SAA7191_IOCK_OEVS
+       | SAA7191_IOCK_OEDY,    /* (0x78) SAA7191_REG_IOCK
+                                * - chroma from CVBS, GPSW1 & 2 off */
+       SAA7191_CTL3_AUFD | SAA7191_CTL3_SCEN | SAA7191_CTL3_OFTS
+       | SAA7191_CTL3_YDEL0,   /* (0x99) SAA7191_REG_CTL3
+                                * - automatic field detection */
+       0x00,   /* (0x00) SAA7191_REG_CTL4 */
+       0x2c,   /* (0x2c) SAA7191_REG_CHCV - PAL nominal value */
+       0x00,   /* unused */
+       0x00,   /* unused */
+
+       /* 60 Hz signal timing */
+       0x34,   /* (0x34) SAA7191_REG_HS6B */
+       0x0a,   /* (0x0a) SAA7191_REG_HS6S */
+       0xf4,   /* (0xf4) SAA7191_REG_HC6B */
+       0xce,   /* (0xce) SAA7191_REG_HC6S */
+       0xf4,   /* (0xf4) SAA7191_REG_HP6I */
+};
+
+/* SAA7191 register handling */
+
+static u8 saa7191_read_reg(struct v4l2_subdev *sd, u8 reg)
+{
+       return to_saa7191(sd)->reg[reg];
+}
+
+static int saa7191_read_status(struct v4l2_subdev *sd, u8 *value)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret;
+
+       ret = i2c_master_recv(client, value, 1);
+       if (ret < 0) {
+               printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+
+static int saa7191_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+       to_saa7191(sd)->reg[reg] = value;
+       return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* the first byte of data must be the first subaddress number (register) */
+static int saa7191_write_block(struct v4l2_subdev *sd,
+                              u8 length, const u8 *data)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct saa7191 *decoder = to_saa7191(sd);
+       int i;
+       int ret;
+
+       for (i = 0; i < (length - 1); i++) {
+               decoder->reg[data[0] + i] = data[i + 1];
+       }
+
+       ret = i2c_master_send(client, data, length);
+       if (ret < 0) {
+               printk(KERN_ERR "SAA7191: saa7191_write_block(): "
+                      "write failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+/* Helper functions */
+
+static int saa7191_s_routing(struct v4l2_subdev *sd,
+                            u32 input, u32 output, u32 config)
+{
+       struct saa7191 *decoder = to_saa7191(sd);
+       u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA);
+       u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK);
+       int err;
+
+       switch (input) {
+       case SAA7191_INPUT_COMPOSITE: /* Set Composite input */
+               iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1
+                         | SAA7191_IOCK_GPSW2);
+               /* Chrominance trap active */
+               luma &= ~SAA7191_LUMA_BYPS;
+               break;
+       case SAA7191_INPUT_SVIDEO: /* Set S-Video input */
+               iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2;
+               /* Chrominance trap bypassed */
+               luma |= SAA7191_LUMA_BYPS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       err = saa7191_write_reg(sd, SAA7191_REG_LUMA, luma);
+       if (err)
+               return -EIO;
+       err = saa7191_write_reg(sd, SAA7191_REG_IOCK, iock);
+       if (err)
+               return -EIO;
+
+       decoder->input = input;
+
+       return 0;
+}
+
+static int saa7191_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+       struct saa7191 *decoder = to_saa7191(sd);
+       u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC);
+       u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3);
+       u8 chcv = saa7191_read_reg(sd, SAA7191_REG_CHCV);
+       int err;
+
+       if (norm & V4L2_STD_PAL) {
+               stdc &= ~SAA7191_STDC_SECS;
+               ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);
+               chcv = SAA7191_CHCV_PAL;
+       } else if (norm & V4L2_STD_NTSC) {
+               stdc &= ~SAA7191_STDC_SECS;
+               ctl3 &= ~SAA7191_CTL3_AUFD;
+               ctl3 |= SAA7191_CTL3_FSEL;
+               chcv = SAA7191_CHCV_NTSC;
+       } else if (norm & V4L2_STD_SECAM) {
+               stdc |= SAA7191_STDC_SECS;
+               ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);
+               chcv = SAA7191_CHCV_PAL;
+       } else {
+               return -EINVAL;
+       }
+
+       err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
+       if (err)
+               return -EIO;
+       err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc);
+       if (err)
+               return -EIO;
+       err = saa7191_write_reg(sd, SAA7191_REG_CHCV, chcv);
+       if (err)
+               return -EIO;
+
+       decoder->norm = norm;
+
+       dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3,
+               stdc, chcv);
+       dprintk("norm: %llx\n", norm);
+
+       return 0;
+}
+
+static int saa7191_wait_for_signal(struct v4l2_subdev *sd, u8 *status)
+{
+       int i = 0;
+
+       dprintk("Checking for signal...\n");
+
+       for (i = 0; i < SAA7191_SYNC_COUNT; i++) {
+               if (saa7191_read_status(sd, status))
+                       return -EIO;
+
+               if (((*status) & SAA7191_STATUS_HLCK) == 0) {
+                       dprintk("Signal found\n");
+                       return 0;
+               }
+
+               msleep(SAA7191_SYNC_DELAY);
+       }
+
+       dprintk("No signal\n");
+
+       return -EBUSY;
+}
+
+static int saa7191_querystd(struct v4l2_subdev *sd, v4l2_std_id *norm)
+{
+       struct saa7191 *decoder = to_saa7191(sd);
+       u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC);
+       u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3);
+       u8 status;
+       v4l2_std_id old_norm = decoder->norm;
+       int err = 0;
+
+       dprintk("SAA7191 extended signal auto-detection...\n");
+
+       *norm &= V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
+       stdc &= ~SAA7191_STDC_SECS;
+       ctl3 &= ~(SAA7191_CTL3_FSEL);
+
+       err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc);
+       if (err) {
+               err = -EIO;
+               goto out;
+       }
+       err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
+       if (err) {
+               err = -EIO;
+               goto out;
+       }
+
+       ctl3 |= SAA7191_CTL3_AUFD;
+       err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
+       if (err) {
+               err = -EIO;
+               goto out;
+       }
+
+       msleep(SAA7191_SYNC_DELAY);
+
+       err = saa7191_wait_for_signal(sd, &status);
+       if (err)
+               goto out;
+
+       if (status & SAA7191_STATUS_FIDT) {
+               /* 60Hz signal -> NTSC */
+               dprintk("60Hz signal: NTSC\n");
+               *norm &= V4L2_STD_NTSC;
+               return 0;
+       }
+
+       /* 50Hz signal */
+       dprintk("50Hz signal: Trying PAL...\n");
+
+       /* try PAL first */
+       err = saa7191_s_std(sd, V4L2_STD_PAL);
+       if (err)
+               goto out;
+
+       msleep(SAA7191_SYNC_DELAY);
+
+       err = saa7191_wait_for_signal(sd, &status);
+       if (err)
+               goto out;
+
+       /* not 50Hz ? */
+       if (status & SAA7191_STATUS_FIDT) {
+               dprintk("No 50Hz signal\n");
+               saa7191_s_std(sd, old_norm);
+               *norm = V4L2_STD_UNKNOWN;
+               return 0;
+       }
+
+       if (status & SAA7191_STATUS_CODE) {
+               dprintk("PAL\n");
+               *norm &= V4L2_STD_PAL;
+               return saa7191_s_std(sd, old_norm);
+       }
+
+       dprintk("No color detected with PAL - Trying SECAM...\n");
+
+       /* no color detected ? -> try SECAM */
+       err = saa7191_s_std(sd, V4L2_STD_SECAM);
+       if (err)
+               goto out;
+
+       msleep(SAA7191_SYNC_DELAY);
+
+       err = saa7191_wait_for_signal(sd, &status);
+       if (err)
+               goto out;
+
+       /* not 50Hz ? */
+       if (status & SAA7191_STATUS_FIDT) {
+               dprintk("No 50Hz signal\n");
+               *norm = V4L2_STD_UNKNOWN;
+               goto out;
+       }
+
+       if (status & SAA7191_STATUS_CODE) {
+               /* Color detected -> SECAM */
+               dprintk("SECAM\n");
+               *norm &= V4L2_STD_SECAM;
+               return saa7191_s_std(sd, old_norm);
+       }
+
+       dprintk("No color detected with SECAM - Going back to PAL.\n");
+       *norm = V4L2_STD_UNKNOWN;
+
+out:
+       return saa7191_s_std(sd, old_norm);
+}
+
+static int saa7191_autodetect_norm(struct v4l2_subdev *sd)
+{
+       u8 status;
+
+       dprintk("SAA7191 signal auto-detection...\n");
+
+       dprintk("Reading status...\n");
+
+       if (saa7191_read_status(sd, &status))
+               return -EIO;
+
+       dprintk("Checking for signal...\n");
+
+       /* no signal ? */
+       if (status & SAA7191_STATUS_HLCK) {
+               dprintk("No signal\n");
+               return -EBUSY;
+       }
+
+       dprintk("Signal found\n");
+
+       if (status & SAA7191_STATUS_FIDT) {
+               /* 60hz signal -> NTSC */
+               dprintk("NTSC\n");
+               return saa7191_s_std(sd, V4L2_STD_NTSC);
+       } else {
+               /* 50hz signal -> PAL */
+               dprintk("PAL\n");
+               return saa7191_s_std(sd, V4L2_STD_PAL);
+       }
+}
+
+static int saa7191_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+       u8 reg;
+       int ret = 0;
+
+       switch (ctrl->id) {
+       case SAA7191_CONTROL_BANDPASS:
+       case SAA7191_CONTROL_BANDPASS_WEIGHT:
+       case SAA7191_CONTROL_CORING:
+               reg = saa7191_read_reg(sd, SAA7191_REG_LUMA);
+               switch (ctrl->id) {
+               case SAA7191_CONTROL_BANDPASS:
+                       ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK)
+                               >> SAA7191_LUMA_BPSS_SHIFT;
+                       break;
+               case SAA7191_CONTROL_BANDPASS_WEIGHT:
+                       ctrl->value = ((s32)reg & SAA7191_LUMA_APER_MASK)
+                               >> SAA7191_LUMA_APER_SHIFT;
+                       break;
+               case SAA7191_CONTROL_CORING:
+                       ctrl->value = ((s32)reg & SAA7191_LUMA_CORI_MASK)
+                               >> SAA7191_LUMA_CORI_SHIFT;
+                       break;
+               }
+               break;
+       case SAA7191_CONTROL_FORCE_COLOUR:
+       case SAA7191_CONTROL_CHROMA_GAIN:
+               reg = saa7191_read_reg(sd, SAA7191_REG_GAIN);
+               if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR)
+                       ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0;
+               else
+                       ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK)
+                               >> SAA7191_GAIN_LFIS_SHIFT;
+               break;
+       case V4L2_CID_HUE:
+               reg = saa7191_read_reg(sd, SAA7191_REG_HUEC);
+               if (reg < 0x80)
+                       reg += 0x80;
+               else
+                       reg -= 0x80;
+               ctrl->value = (s32)reg;
+               break;
+       case SAA7191_CONTROL_VTRC:
+               reg = saa7191_read_reg(sd, SAA7191_REG_STDC);
+               ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0;
+               break;
+       case SAA7191_CONTROL_LUMA_DELAY:
+               reg = saa7191_read_reg(sd, SAA7191_REG_CTL3);
+               ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK)
+                       >> SAA7191_CTL3_YDEL_SHIFT;
+               if (ctrl->value >= 4)
+                       ctrl->value -= 8;
+               break;
+       case SAA7191_CONTROL_VNR:
+               reg = saa7191_read_reg(sd, SAA7191_REG_CTL4);
+               ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK)
+                       >> SAA7191_CTL4_VNOI_SHIFT;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int saa7191_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+       u8 reg;
+       int ret = 0;
+
+       switch (ctrl->id) {
+       case SAA7191_CONTROL_BANDPASS:
+       case SAA7191_CONTROL_BANDPASS_WEIGHT:
+       case SAA7191_CONTROL_CORING:
+               reg = saa7191_read_reg(sd, SAA7191_REG_LUMA);
+               switch (ctrl->id) {
+               case SAA7191_CONTROL_BANDPASS:
+                       reg &= ~SAA7191_LUMA_BPSS_MASK;
+                       reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT)
+                               & SAA7191_LUMA_BPSS_MASK;
+                       break;
+               case SAA7191_CONTROL_BANDPASS_WEIGHT:
+                       reg &= ~SAA7191_LUMA_APER_MASK;
+                       reg |= (ctrl->value << SAA7191_LUMA_APER_SHIFT)
+                               & SAA7191_LUMA_APER_MASK;
+                       break;
+               case SAA7191_CONTROL_CORING:
+                       reg &= ~SAA7191_LUMA_CORI_MASK;
+                       reg |= (ctrl->value << SAA7191_LUMA_CORI_SHIFT)
+                               & SAA7191_LUMA_CORI_MASK;
+                       break;
+               }
+               ret = saa7191_write_reg(sd, SAA7191_REG_LUMA, reg);
+               break;
+       case SAA7191_CONTROL_FORCE_COLOUR:
+       case SAA7191_CONTROL_CHROMA_GAIN:
+               reg = saa7191_read_reg(sd, SAA7191_REG_GAIN);
+               if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) {
+                       if (ctrl->value)
+                               reg |= SAA7191_GAIN_COLO;
+                       else
+                               reg &= ~SAA7191_GAIN_COLO;
+               } else {
+                       reg &= ~SAA7191_GAIN_LFIS_MASK;
+                       reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT)
+                               & SAA7191_GAIN_LFIS_MASK;
+               }
+               ret = saa7191_write_reg(sd, SAA7191_REG_GAIN, reg);
+               break;
+       case V4L2_CID_HUE:
+               reg = ctrl->value & 0xff;
+               if (reg < 0x80)
+                       reg += 0x80;
+               else
+                       reg -= 0x80;
+               ret = saa7191_write_reg(sd, SAA7191_REG_HUEC, reg);
+               break;
+       case SAA7191_CONTROL_VTRC:
+               reg = saa7191_read_reg(sd, SAA7191_REG_STDC);
+               if (ctrl->value)
+                       reg |= SAA7191_STDC_VTRC;
+               else
+                       reg &= ~SAA7191_STDC_VTRC;
+               ret = saa7191_write_reg(sd, SAA7191_REG_STDC, reg);
+               break;
+       case SAA7191_CONTROL_LUMA_DELAY: {
+               s32 value = ctrl->value;
+               if (value < 0)
+                       value += 8;
+               reg = saa7191_read_reg(sd, SAA7191_REG_CTL3);
+               reg &= ~SAA7191_CTL3_YDEL_MASK;
+               reg |= (value << SAA7191_CTL3_YDEL_SHIFT)
+                       & SAA7191_CTL3_YDEL_MASK;
+               ret = saa7191_write_reg(sd, SAA7191_REG_CTL3, reg);
+               break;
+       }
+       case SAA7191_CONTROL_VNR:
+               reg = saa7191_read_reg(sd, SAA7191_REG_CTL4);
+               reg &= ~SAA7191_CTL4_VNOI_MASK;
+               reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT)
+                       & SAA7191_CTL4_VNOI_MASK;
+               ret = saa7191_write_reg(sd, SAA7191_REG_CTL4, reg);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/* I2C-interface */
+
+static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+       u8 status_reg;
+       int res = V4L2_IN_ST_NO_SIGNAL;
+
+       if (saa7191_read_status(sd, &status_reg))
+               return -EIO;
+       if ((status_reg & SAA7191_STATUS_HLCK) == 0)
+               res = 0;
+       if (!(status_reg & SAA7191_STATUS_CODE))
+               res |= V4L2_IN_ST_NO_COLOR;
+       *status = res;
+       return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa7191_core_ops = {
+       .g_ctrl = saa7191_g_ctrl,
+       .s_ctrl = saa7191_s_ctrl,
+};
+
+static const struct v4l2_subdev_video_ops saa7191_video_ops = {
+       .s_std = saa7191_s_std,
+       .s_routing = saa7191_s_routing,
+       .querystd = saa7191_querystd,
+       .g_input_status = saa7191_g_input_status,
+};
+
+static const struct v4l2_subdev_ops saa7191_ops = {
+       .core = &saa7191_core_ops,
+       .video = &saa7191_video_ops,
+};
+
+static int saa7191_probe(struct i2c_client *client,
+                         const struct i2c_device_id *id)
+{
+       int err = 0;
+       struct saa7191 *decoder;
+       struct v4l2_subdev *sd;
+
+       v4l_info(client, "chip found @ 0x%x (%s)\n",
+                       client->addr << 1, client->adapter->name);
+
+       decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
+       if (!decoder)
+               return -ENOMEM;
+
+       sd = &decoder->sd;
+       v4l2_i2c_subdev_init(sd, client, &saa7191_ops);
+
+       err = saa7191_write_block(sd, sizeof(initseq), initseq);
+       if (err) {
+               printk(KERN_ERR "SAA7191 initialization failed\n");
+               return err;
+       }
+
+       printk(KERN_INFO "SAA7191 initialized\n");
+
+       decoder->input = SAA7191_INPUT_COMPOSITE;
+       decoder->norm = V4L2_STD_PAL;
+
+       err = saa7191_autodetect_norm(sd);
+       if (err && (err != -EBUSY))
+               printk(KERN_ERR "SAA7191: Signal auto-detection failed\n");
+
+       return 0;
+}
+
+static int saa7191_remove(struct i2c_client *client)
+{
+       struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+       v4l2_device_unregister_subdev(sd);
+       return 0;
+}
+
+static const struct i2c_device_id saa7191_id[] = {
+       { "saa7191", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, saa7191_id);
+
+static struct i2c_driver saa7191_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "saa7191",
+       },
+       .probe          = saa7191_probe,
+       .remove         = saa7191_remove,
+       .id_table       = saa7191_id,
+};
+
+module_i2c_driver(saa7191_driver);
diff --git a/drivers/staging/media/vino/saa7191.h b/drivers/staging/media/vino/saa7191.h
new file mode 100644 (file)
index 0000000..803c74d
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ *  saa7191.h - Philips SAA7191 video decoder driver
+ *
+ *  Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ *  Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#ifndef _SAA7191_H_
+#define _SAA7191_H_
+
+/* Philips SAA7191 DMSD I2C bus address */
+#define SAA7191_ADDR           0x8a
+
+/* Register subaddresses. */
+#define SAA7191_REG_IDEL       0x00
+#define SAA7191_REG_HSYB       0x01
+#define SAA7191_REG_HSYS       0x02
+#define SAA7191_REG_HCLB       0x03
+#define SAA7191_REG_HCLS       0x04
+#define SAA7191_REG_HPHI       0x05
+#define SAA7191_REG_LUMA       0x06
+#define SAA7191_REG_HUEC       0x07
+#define SAA7191_REG_CKTQ       0x08 /* bits 3-7 */
+#define SAA7191_REG_CKTS       0x09 /* bits 3-7 */
+#define SAA7191_REG_PLSE       0x0a
+#define SAA7191_REG_SESE       0x0b
+#define SAA7191_REG_GAIN       0x0c
+#define SAA7191_REG_STDC       0x0d
+#define SAA7191_REG_IOCK       0x0e
+#define SAA7191_REG_CTL3       0x0f
+#define SAA7191_REG_CTL4       0x10
+#define SAA7191_REG_CHCV       0x11
+#define SAA7191_REG_HS6B       0x14
+#define SAA7191_REG_HS6S       0x15
+#define SAA7191_REG_HC6B       0x16
+#define SAA7191_REG_HC6S       0x17
+#define SAA7191_REG_HP6I       0x18
+#define SAA7191_REG_STATUS     0xff    /* not really a subaddress */
+
+/* Status Register definitions */
+#define SAA7191_STATUS_CODE    0x01    /* color detected flag */
+#define SAA7191_STATUS_FIDT    0x20    /* signal type 50/60 Hz */
+#define SAA7191_STATUS_HLCK    0x40    /* PLL unlocked(1)/locked(0) */
+#define SAA7191_STATUS_STTC    0x80    /* tv/vtr time constant */
+
+/* Luminance Control Register definitions */
+/* input mode select bit:
+ * 0=CVBS (chrominance trap active), 1=S-Video (trap bypassed) */
+#define SAA7191_LUMA_BYPS      0x80
+/* pre-filter (only when chrominance trap is active) */
+#define SAA7191_LUMA_PREF      0x40
+/* aperture bandpass to select different characteristics with maximums
+ * (bits 4-5) */
+#define SAA7191_LUMA_BPSS_MASK 0x30
+#define SAA7191_LUMA_BPSS_SHIFT        4
+#define SAA7191_LUMA_BPSS_3    0x30
+#define SAA7191_LUMA_BPSS_2    0x20
+#define SAA7191_LUMA_BPSS_1    0x10
+#define SAA7191_LUMA_BPSS_0    0x00
+/* coring range for high frequency components according to 8-bit luminance
+ * (bits 2-3)
+ * 0=coring off, n= (+-)n LSB */
+#define SAA7191_LUMA_CORI_MASK 0x0c
+#define SAA7191_LUMA_CORI_SHIFT        2
+#define SAA7191_LUMA_CORI_3    0x0c
+#define SAA7191_LUMA_CORI_2    0x08
+#define SAA7191_LUMA_CORI_1    0x04
+#define SAA7191_LUMA_CORI_0    0x00
+/* aperture bandpass filter weights high frequency components of luminance
+ * signal (bits 0-1)
+ * 0=factor 0, 1=0.25, 2=0.5, 3=1 */
+#define SAA7191_LUMA_APER_MASK 0x03
+#define SAA7191_LUMA_APER_SHIFT        0
+#define SAA7191_LUMA_APER_3    0x03
+#define SAA7191_LUMA_APER_2    0x02
+#define SAA7191_LUMA_APER_1    0x01
+#define SAA7191_LUMA_APER_0    0x00
+
+/* Chrominance Gain Control Settings Register definitions */
+/* colour on: 0=automatic colour-killer enabled, 1=forced colour on */
+#define SAA7191_GAIN_COLO      0x80
+/* chrominance gain control (AGC filter)
+ * 0=loop filter time constant slow, 1=medium, 2=fast, 3=actual gain */
+#define SAA7191_GAIN_LFIS_MASK 0x60
+#define SAA7191_GAIN_LFIS_SHIFT        5
+#define SAA7191_GAIN_LFIS_3    0x60
+#define SAA7191_GAIN_LFIS_2    0x40
+#define SAA7191_GAIN_LFIS_1    0x20
+#define SAA7191_GAIN_LFIS_0    0x00
+
+/* Standard/Mode Control Register definitions */
+/* tv/vtr mode bit: 0=TV mode (slow time constant),
+ * 1=VTR mode (fast time constant) */
+#define SAA7191_STDC_VTRC      0x80
+/* SAA7191B-specific functions enable (RTCO, ODD and GPSW0 outputs)
+ * 0=outputs set to high-impedance (circuit equals SAA7191), 1=enabled */
+#define SAA7191_STDC_NFEN      0x08
+/* HREF generation: 0=like SAA7191, 1=HREF is 8xLLC2 clocks earlier */
+#define SAA7191_STDC_HRMV      0x04
+/* general purpose switch 0
+ * (not used with VINO afaik) */
+#define SAA7191_STDC_GPSW0     0x02
+/* SECAM mode bit: 0=other standards, 1=SECAM */
+#define SAA7191_STDC_SECS      0x01
+
+/* I/O and Clock Control Register definitions */
+/* horizontal clock PLL: 0=PLL closed,
+ * 1=PLL circuit open and horizontal freq fixed */
+#define SAA7191_IOCK_HPLL      0x80
+/* colour-difference output enable (outputs UV0-UV7) */
+#define SAA7191_IOCK_OEDC      0x40
+/* H-sync output enable */
+#define SAA7191_IOCK_OEHS      0x20
+/* V-sync output enable */
+#define SAA7191_IOCK_OEVS      0x10
+/* luminance output enable (outputs Y0-Y7) */
+#define SAA7191_IOCK_OEDY      0x08
+/* S-VHS bit (chrominance from CVBS or from chrominance input):
+ * 0=controlled by BYPS-bit, 1=from chrominance input */
+#define SAA7191_IOCK_CHRS      0x04
+/* general purpose switch 2
+ * VINO-specific: 0=used with CVBS, 1=used with S-Video */
+#define SAA7191_IOCK_GPSW2     0x02
+/* general purpose switch 1 */
+/* VINO-specific: 0=always, 1=not used!*/
+#define SAA7191_IOCK_GPSW1     0x01
+
+/* Miscellaneous Control #1 Register definitions */
+/* automatic field detection (50/60Hz standard) */
+#define SAA7191_CTL3_AUFD      0x80
+/* field select: (if AUFD=0)
+ * 0=50Hz (625 lines), 1=60Hz (525 lines) */
+#define SAA7191_CTL3_FSEL      0x40
+/* SECAM cross-colour reduction enable */
+#define SAA7191_CTL3_SXCR      0x20
+/* sync and clamping pulse enable (HCL and HSY outputs) */
+#define SAA7191_CTL3_SCEN      0x10
+/* output format: 0=4:1:1, 1=4:2:2 (4:2:2 for VINO) */
+#define SAA7191_CTL3_OFTS      0x08
+/* luminance delay compensation
+ * 0=0*2/LLC,  1=+1*2/LLC, 2=+2*2/LLC, 3=+3*2/LLC,
+ * 4=-4*2/LLC, 5=-3*2/LLC, 6=-2*2/LLC, 7=-1*2/LLC
+ * step size = 2/LLC = 67.8ns for 50Hz, 81.5ns for 60Hz */
+#define SAA7191_CTL3_YDEL_MASK 0x07
+#define SAA7191_CTL3_YDEL_SHIFT        0
+#define SAA7191_CTL3_YDEL2     0x04
+#define SAA7191_CTL3_YDEL1     0x02
+#define SAA7191_CTL3_YDEL0     0x01
+
+/* Miscellaneous Control #2 Register definitions */
+/* select HREF position
+ * 0=normal, HREF is matched to YUV output port,
+ * 1=HREF is matched to CVBS input port */
+#define SAA7191_CTL4_HRFS      0x04
+/* vertical noise reduction
+ * 0=normal, 1=searching window, 2=auto-deflection, 3=reduction bypassed */
+#define SAA7191_CTL4_VNOI_MASK 0x03
+#define SAA7191_CTL4_VNOI_SHIFT        0
+#define SAA7191_CTL4_VNOI_3    0x03
+#define SAA7191_CTL4_VNOI_2    0x02
+#define SAA7191_CTL4_VNOI_1    0x01
+#define SAA7191_CTL4_VNOI_0    0x00
+
+/* Chrominance Gain Control Register definitions
+ * - for QAM-modulated input signals, effects output amplitude
+ * (SECAM gain fixed)
+ * (nominal values for UV CCIR level) */
+#define SAA7191_CHCV_NTSC      0x2c
+#define SAA7191_CHCV_PAL       0x59
+
+/* Driver interface definitions */
+#define SAA7191_INPUT_COMPOSITE        0
+#define SAA7191_INPUT_SVIDEO   1
+
+#define SAA7191_NORM_PAL       1
+#define SAA7191_NORM_NTSC      2
+#define SAA7191_NORM_SECAM     3
+
+struct saa7191_status {
+       /* 0=no signal, 1=signal detected */
+       int signal;
+       /* 0=50hz (pal) signal, 1=60hz (ntsc) signal */
+       int signal_60hz;
+       /* 0=no color detected, 1=color detected */
+       int color;
+
+       /* current SAA7191_INPUT_ */
+       int input;
+       /* current SAA7191_NORM_ */
+       int norm;
+};
+
+#define SAA7191_BANDPASS_MIN           0x00
+#define SAA7191_BANDPASS_MAX           0x03
+#define SAA7191_BANDPASS_DEFAULT       0x00
+
+#define SAA7191_BANDPASS_WEIGHT_MIN    0x00
+#define SAA7191_BANDPASS_WEIGHT_MAX    0x03
+#define SAA7191_BANDPASS_WEIGHT_DEFAULT        0x01
+
+#define SAA7191_CORING_MIN             0x00
+#define SAA7191_CORING_MAX             0x03
+#define SAA7191_CORING_DEFAULT         0x00
+
+#define SAA7191_HUE_MIN                        0x00
+#define SAA7191_HUE_MAX                        0xff
+#define SAA7191_HUE_DEFAULT            0x80
+
+#define SAA7191_VTRC_MIN               0x00
+#define SAA7191_VTRC_MAX               0x01
+#define SAA7191_VTRC_DEFAULT           0x00
+
+#define SAA7191_FORCE_COLOUR_MIN       0x00
+#define SAA7191_FORCE_COLOUR_MAX       0x01
+#define SAA7191_FORCE_COLOUR_DEFAULT   0x00
+
+#define SAA7191_CHROMA_GAIN_MIN                0x00
+#define SAA7191_CHROMA_GAIN_MAX                0x03
+#define SAA7191_CHROMA_GAIN_DEFAULT    0x00
+
+#define SAA7191_LUMA_DELAY_MIN         -0x04
+#define SAA7191_LUMA_DELAY_MAX         0x03
+#define SAA7191_LUMA_DELAY_DEFAULT     0x01
+
+#define SAA7191_VNR_MIN                        0x00
+#define SAA7191_VNR_MAX                        0x03
+#define SAA7191_VNR_DEFAULT            0x00
+
+#define SAA7191_CONTROL_BANDPASS       (V4L2_CID_PRIVATE_BASE + 0)
+#define SAA7191_CONTROL_BANDPASS_WEIGHT        (V4L2_CID_PRIVATE_BASE + 1)
+#define SAA7191_CONTROL_CORING         (V4L2_CID_PRIVATE_BASE + 2)
+#define SAA7191_CONTROL_FORCE_COLOUR   (V4L2_CID_PRIVATE_BASE + 3)
+#define SAA7191_CONTROL_CHROMA_GAIN    (V4L2_CID_PRIVATE_BASE + 4)
+#define SAA7191_CONTROL_VTRC           (V4L2_CID_PRIVATE_BASE + 5)
+#define SAA7191_CONTROL_LUMA_DELAY     (V4L2_CID_PRIVATE_BASE + 6)
+#define SAA7191_CONTROL_VNR            (V4L2_CID_PRIVATE_BASE + 7)
+
+#define        DECODER_SAA7191_GET_STATUS      _IOR('d', 195, struct saa7191_status)
+#define        DECODER_SAA7191_SET_NORM        _IOW('d', 196, int)
+
+#endif
diff --git a/drivers/staging/media/vino/vino.c b/drivers/staging/media/vino/vino.c
new file mode 100644 (file)
index 0000000..2c85357
--- /dev/null
@@ -0,0 +1,4345 @@
+/*
+ * Driver for the VINO (Video In No Out) system found in SGI Indys.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ * Based on the previous version of the driver for 2.4 kernels by:
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ *
+ * v4l2_device/v4l2_subdev conversion by:
+ * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * Note: this conversion is untested! Please contact the linux-media
+ * mailinglist if you can test this, together with the test results.
+ */
+
+/*
+ * TODO:
+ * - remove "mark pages reserved-hacks" from memory allocation code
+ *   and implement fault()
+ * - check decimation, calculating and reporting image size when
+ *   using decimation
+ * - implement read(), user mode buffers and overlay (?)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/time.h>
+#include <linux/kmod.h>
+
+#include <linux/i2c.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/mutex.h>
+
+#include <asm/paccess.h>
+#include <asm/io.h>
+#include <asm/sgi/ip22.h>
+#include <asm/sgi/mc.h>
+
+#include "vino.h"
+#include "saa7191.h"
+#include "indycam.h"
+
+/* Uncomment the following line to get lots and lots of (mostly useless)
+ * debug info.
+ * Note that the debug output also slows down the driver significantly */
+// #define VINO_DEBUG
+// #define VINO_DEBUG_INT
+
+#define VINO_MODULE_VERSION "0.0.7"
+
+MODULE_DESCRIPTION("SGI VINO Video4Linux2 driver");
+MODULE_VERSION(VINO_MODULE_VERSION);
+MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
+MODULE_LICENSE("GPL");
+
+#ifdef VINO_DEBUG
+#define dprintk(x...) printk("VINO: " x);
+#else
+#define dprintk(x...)
+#endif
+
+#define VINO_NO_CHANNEL                        0
+#define VINO_CHANNEL_A                 1
+#define VINO_CHANNEL_B                 2
+
+#define VINO_PAL_WIDTH                 768
+#define VINO_PAL_HEIGHT                        576
+#define VINO_NTSC_WIDTH                        640
+#define VINO_NTSC_HEIGHT               480
+
+#define VINO_MIN_WIDTH                 32
+#define VINO_MIN_HEIGHT                        32
+
+#define VINO_CLIPPING_START_ODD_D1     1
+#define VINO_CLIPPING_START_ODD_PAL    15
+#define VINO_CLIPPING_START_ODD_NTSC   12
+
+#define VINO_CLIPPING_START_EVEN_D1    2
+#define VINO_CLIPPING_START_EVEN_PAL   15
+#define VINO_CLIPPING_START_EVEN_NTSC  12
+
+#define VINO_INPUT_CHANNEL_COUNT       3
+
+/* the number is the index for vino_inputs */
+#define VINO_INPUT_NONE                        -1
+#define VINO_INPUT_COMPOSITE           0
+#define VINO_INPUT_SVIDEO              1
+#define VINO_INPUT_D1                  2
+
+#define VINO_PAGE_RATIO                        (PAGE_SIZE / VINO_PAGE_SIZE)
+
+#define VINO_FIFO_THRESHOLD_DEFAULT    16
+
+#define VINO_FRAMEBUFFER_SIZE          ((VINO_PAL_WIDTH \
+                                         * VINO_PAL_HEIGHT * 4 \
+                                         + 3 * PAGE_SIZE) & ~(PAGE_SIZE - 1))
+
+#define VINO_FRAMEBUFFER_COUNT_MAX     8
+
+#define VINO_FRAMEBUFFER_UNUSED                0
+#define VINO_FRAMEBUFFER_IN_USE                1
+#define VINO_FRAMEBUFFER_READY         2
+
+#define VINO_QUEUE_ERROR               -1
+#define VINO_QUEUE_MAGIC               0x20050125
+
+#define VINO_MEMORY_NONE               0
+#define VINO_MEMORY_MMAP               1
+#define VINO_MEMORY_USERPTR            2
+
+#define VINO_DUMMY_DESC_COUNT          4
+#define VINO_DESC_FETCH_DELAY          5       /* microseconds */
+
+#define VINO_MAX_FRAME_SKIP_COUNT      128
+
+/* the number is the index for vino_data_formats */
+#define VINO_DATA_FMT_NONE             -1
+#define VINO_DATA_FMT_GREY             0
+#define VINO_DATA_FMT_RGB332           1
+#define VINO_DATA_FMT_RGB32            2
+#define VINO_DATA_FMT_YUV              3
+
+#define VINO_DATA_FMT_COUNT            4
+
+/* the number is the index for vino_data_norms */
+#define VINO_DATA_NORM_NONE            -1
+#define VINO_DATA_NORM_NTSC            0
+#define VINO_DATA_NORM_PAL             1
+#define VINO_DATA_NORM_SECAM           2
+#define VINO_DATA_NORM_D1              3
+
+#define VINO_DATA_NORM_COUNT           4
+
+/* I2C controller flags */
+#define SGI_I2C_FORCE_IDLE             (0 << 0)
+#define SGI_I2C_NOT_IDLE               (1 << 0)
+#define SGI_I2C_WRITE                  (0 << 1)
+#define SGI_I2C_READ                   (1 << 1)
+#define SGI_I2C_RELEASE_BUS            (0 << 2)
+#define SGI_I2C_HOLD_BUS               (1 << 2)
+#define SGI_I2C_XFER_DONE              (0 << 4)
+#define SGI_I2C_XFER_BUSY              (1 << 4)
+#define SGI_I2C_ACK                    (0 << 5)
+#define SGI_I2C_NACK                   (1 << 5)
+#define SGI_I2C_BUS_OK                 (0 << 7)
+#define SGI_I2C_BUS_ERR                        (1 << 7)
+
+/* Internal data structure definitions */
+
+struct vino_input {
+       char *name;
+       v4l2_std_id std;
+};
+
+struct vino_clipping {
+       unsigned int left, right, top, bottom;
+};
+
+struct vino_data_format {
+       /* the description */
+       char *description;
+       /* bytes per pixel */
+       unsigned int bpp;
+       /* V4L2 fourcc code */
+       __u32 pixelformat;
+       /* V4L2 colorspace (duh!) */
+       enum v4l2_colorspace colorspace;
+};
+
+struct vino_data_norm {
+       char *description;
+       unsigned int width, height;
+       struct vino_clipping odd;
+       struct vino_clipping even;
+
+       v4l2_std_id std;
+       unsigned int fps_min, fps_max;
+       __u32 framelines;
+};
+
+struct vino_descriptor_table {
+       /* the number of PAGE_SIZE sized pages in the buffer */
+       unsigned int page_count;
+       /* virtual (kmalloc'd) pointers to the actual data
+        * (in PAGE_SIZE chunks, used with mmap streaming) */
+       unsigned long *virtual;
+
+       /* cpu address for the VINO descriptor table
+        * (contains DMA addresses, VINO_PAGE_SIZE chunks) */
+       unsigned long *dma_cpu;
+       /* dma address for the VINO descriptor table
+        * (contains DMA addresses, VINO_PAGE_SIZE chunks) */
+       dma_addr_t dma;
+};
+
+struct vino_framebuffer {
+       /* identifier nubmer */
+       unsigned int id;
+       /* the length of the whole buffer */
+       unsigned int size;
+       /* the length of actual data in buffer */
+       unsigned int data_size;
+       /* the data format */
+       unsigned int data_format;
+       /* the state of buffer data */
+       unsigned int state;
+       /* is the buffer mapped in user space? */
+       unsigned int map_count;
+       /* memory offset for mmap() */
+       unsigned int offset;
+       /* frame counter */
+       unsigned int frame_counter;
+       /* timestamp (written when image capture finishes) */
+       struct timeval timestamp;
+
+       struct vino_descriptor_table desc_table;
+
+       spinlock_t state_lock;
+};
+
+struct vino_framebuffer_fifo {
+       unsigned int length;
+
+       unsigned int used;
+       unsigned int head;
+       unsigned int tail;
+
+       unsigned int data[VINO_FRAMEBUFFER_COUNT_MAX];
+};
+
+struct vino_framebuffer_queue {
+       unsigned int magic;
+
+       /* VINO_MEMORY_NONE, VINO_MEMORY_MMAP or VINO_MEMORY_USERPTR */
+       unsigned int type;
+       unsigned int length;
+
+       /* data field of in and out contain index numbers for buffer */
+       struct vino_framebuffer_fifo in;
+       struct vino_framebuffer_fifo out;
+
+       struct vino_framebuffer *buffer[VINO_FRAMEBUFFER_COUNT_MAX];
+
+       spinlock_t queue_lock;
+       struct mutex queue_mutex;
+       wait_queue_head_t frame_wait_queue;
+};
+
+struct vino_interrupt_data {
+       struct timeval timestamp;
+       unsigned int frame_counter;
+       unsigned int skip_count;
+       unsigned int skip;
+};
+
+struct vino_channel_settings {
+       unsigned int channel;
+
+       int input;
+       unsigned int data_format;
+       unsigned int data_norm;
+       struct vino_clipping clipping;
+       unsigned int decimation;
+       unsigned int line_size;
+       unsigned int alpha;
+       unsigned int fps;
+       unsigned int framert_reg;
+
+       unsigned int fifo_threshold;
+
+       struct vino_framebuffer_queue fb_queue;
+
+       /* number of the current field */
+       unsigned int field;
+
+       /* read in progress */
+       int reading;
+       /* streaming is active */
+       int streaming;
+       /* the driver is currently processing the queue */
+       int capturing;
+
+       struct mutex mutex;
+       spinlock_t capture_lock;
+
+       unsigned int users;
+
+       struct vino_interrupt_data int_data;
+
+       /* V4L support */
+       struct video_device *vdev;
+};
+
+struct vino_settings {
+       struct v4l2_device v4l2_dev;
+       struct vino_channel_settings a;
+       struct vino_channel_settings b;
+
+       /* the channel which owns this client:
+        * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */
+       unsigned int decoder_owner;
+       struct v4l2_subdev *decoder;
+       unsigned int camera_owner;
+       struct v4l2_subdev *camera;
+
+       /* a lock for vino register access */
+       spinlock_t vino_lock;
+       /* a lock for channel input changes */
+       spinlock_t input_lock;
+
+       unsigned long dummy_page;
+       struct vino_descriptor_table dummy_desc_table;
+};
+
+/* Module parameters */
+
+/*
+ * Using vino_pixel_conversion the ABGR32-format pixels supplied
+ * by the VINO chip can be converted to more common formats
+ * like RGBA32 (or probably RGB24 in the future). This way we
+ * can give out data that can be specified correctly with
+ * the V4L2-definitions.
+ *
+ * The pixel format is specified as RGBA32 when no conversion
+ * is used.
+ *
+ * Note that this only affects the 32-bit bit depth.
+ *
+ * Use non-zero value to enable conversion.
+ */
+static int vino_pixel_conversion;
+
+module_param_named(pixelconv, vino_pixel_conversion, int, 0);
+
+MODULE_PARM_DESC(pixelconv,
+                "enable pixel conversion (non-zero value enables)");
+
+/* Internal data structures */
+
+static struct sgi_vino *vino;
+
+static struct vino_settings *vino_drvdata;
+
+#define camera_call(o, f, args...) \
+       v4l2_subdev_call(vino_drvdata->camera, o, f, ##args)
+#define decoder_call(o, f, args...) \
+       v4l2_subdev_call(vino_drvdata->decoder, o, f, ##args)
+
+static const char *vino_driver_name = "vino";
+static const char *vino_driver_description = "SGI VINO";
+static const char *vino_bus_name = "GIO64 bus";
+static const char *vino_vdev_name_a = "SGI VINO Channel A";
+static const char *vino_vdev_name_b = "SGI VINO Channel B";
+
+static void vino_capture_tasklet(unsigned long channel);
+
+DECLARE_TASKLET(vino_tasklet_a, vino_capture_tasklet, VINO_CHANNEL_A);
+DECLARE_TASKLET(vino_tasklet_b, vino_capture_tasklet, VINO_CHANNEL_B);
+
+static const struct vino_input vino_inputs[] = {
+       {
+               .name           = "Composite",
+               .std            = V4L2_STD_NTSC | V4L2_STD_PAL
+               | V4L2_STD_SECAM,
+       }, {
+               .name           = "S-Video",
+               .std            = V4L2_STD_NTSC | V4L2_STD_PAL
+               | V4L2_STD_SECAM,
+       }, {
+               .name           = "D1/IndyCam",
+               .std            = V4L2_STD_NTSC,
+       }
+};
+
+static const struct vino_data_format vino_data_formats[] = {
+       {
+               .description    = "8-bit greyscale",
+               .bpp            = 1,
+               .pixelformat    = V4L2_PIX_FMT_GREY,
+               .colorspace     = V4L2_COLORSPACE_SMPTE170M,
+       }, {
+               .description    = "8-bit dithered RGB 3-3-2",
+               .bpp            = 1,
+               .pixelformat    = V4L2_PIX_FMT_RGB332,
+               .colorspace     = V4L2_COLORSPACE_SRGB,
+       }, {
+               .description    = "32-bit RGB",
+               .bpp            = 4,
+               .pixelformat    = V4L2_PIX_FMT_RGB32,
+               .colorspace     = V4L2_COLORSPACE_SRGB,
+       }, {
+               .description    = "YUV 4:2:2",
+               .bpp            = 2,
+               .pixelformat    = V4L2_PIX_FMT_YUYV, // XXX: swapped?
+               .colorspace     = V4L2_COLORSPACE_SMPTE170M,
+       }
+};
+
+static const struct vino_data_norm vino_data_norms[] = {
+       {
+               .description    = "NTSC",
+               .std            = V4L2_STD_NTSC,
+               .fps_min        = 6,
+               .fps_max        = 30,
+               .framelines     = 525,
+               .width          = VINO_NTSC_WIDTH,
+               .height         = VINO_NTSC_HEIGHT,
+               .odd            = {
+                       .top    = VINO_CLIPPING_START_ODD_NTSC,
+                       .left   = 0,
+                       .bottom = VINO_CLIPPING_START_ODD_NTSC
+                       + VINO_NTSC_HEIGHT / 2 - 1,
+                       .right  = VINO_NTSC_WIDTH,
+               },
+               .even           = {
+                       .top    = VINO_CLIPPING_START_EVEN_NTSC,
+                       .left   = 0,
+                       .bottom = VINO_CLIPPING_START_EVEN_NTSC
+                       + VINO_NTSC_HEIGHT / 2 - 1,
+                       .right  = VINO_NTSC_WIDTH,
+               },
+       }, {
+               .description    = "PAL",
+               .std            = V4L2_STD_PAL,
+               .fps_min        = 5,
+               .fps_max        = 25,
+               .framelines     = 625,
+               .width          = VINO_PAL_WIDTH,
+               .height         = VINO_PAL_HEIGHT,
+               .odd            = {
+                       .top    = VINO_CLIPPING_START_ODD_PAL,
+                       .left   = 0,
+                       .bottom = VINO_CLIPPING_START_ODD_PAL
+                       + VINO_PAL_HEIGHT / 2 - 1,
+                       .right  = VINO_PAL_WIDTH,
+               },
+               .even           = {
+                       .top    = VINO_CLIPPING_START_EVEN_PAL,
+                       .left   = 0,
+                       .bottom = VINO_CLIPPING_START_EVEN_PAL
+                       + VINO_PAL_HEIGHT / 2 - 1,
+                       .right  = VINO_PAL_WIDTH,
+               },
+       }, {
+               .description    = "SECAM",
+               .std            = V4L2_STD_SECAM,
+               .fps_min        = 5,
+               .fps_max        = 25,
+               .framelines     = 625,
+               .width          = VINO_PAL_WIDTH,
+               .height         = VINO_PAL_HEIGHT,
+               .odd            = {
+                       .top    = VINO_CLIPPING_START_ODD_PAL,
+                       .left   = 0,
+                       .bottom = VINO_CLIPPING_START_ODD_PAL
+                       + VINO_PAL_HEIGHT / 2 - 1,
+                       .right  = VINO_PAL_WIDTH,
+               },
+               .even           = {
+                       .top    = VINO_CLIPPING_START_EVEN_PAL,
+                       .left   = 0,
+                       .bottom = VINO_CLIPPING_START_EVEN_PAL
+                       + VINO_PAL_HEIGHT / 2 - 1,
+                       .right  = VINO_PAL_WIDTH,
+               },
+       }, {
+               .description    = "NTSC/D1",
+               .std            = V4L2_STD_NTSC,
+               .fps_min        = 6,
+               .fps_max        = 30,
+               .framelines     = 525,
+               .width          = VINO_NTSC_WIDTH,
+               .height         = VINO_NTSC_HEIGHT,
+               .odd            = {
+                       .top    = VINO_CLIPPING_START_ODD_D1,
+                       .left   = 0,
+                       .bottom = VINO_CLIPPING_START_ODD_D1
+                       + VINO_NTSC_HEIGHT / 2 - 1,
+                       .right  = VINO_NTSC_WIDTH,
+               },
+               .even           = {
+                       .top    = VINO_CLIPPING_START_EVEN_D1,
+                       .left   = 0,
+                       .bottom = VINO_CLIPPING_START_EVEN_D1
+                       + VINO_NTSC_HEIGHT / 2 - 1,
+                       .right  = VINO_NTSC_WIDTH,
+               },
+       }
+};
+
+#define VINO_INDYCAM_V4L2_CONTROL_COUNT                9
+
+struct v4l2_queryctrl vino_indycam_v4l2_controls[] = {
+       {
+               .id = V4L2_CID_AUTOGAIN,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Automatic Gain Control",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = INDYCAM_AGC_DEFAULT,
+       }, {
+               .id = V4L2_CID_AUTO_WHITE_BALANCE,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Automatic White Balance",
+               .minimum = 0,
+               .maximum = 1,
+               .step = 1,
+               .default_value = INDYCAM_AWB_DEFAULT,
+       }, {
+               .id = V4L2_CID_GAIN,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Gain",
+               .minimum = INDYCAM_GAIN_MIN,
+               .maximum = INDYCAM_GAIN_MAX,
+               .step = 1,
+               .default_value = INDYCAM_GAIN_DEFAULT,
+       }, {
+               .id = INDYCAM_CONTROL_RED_SATURATION,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Red Saturation",
+               .minimum = INDYCAM_RED_SATURATION_MIN,
+               .maximum = INDYCAM_RED_SATURATION_MAX,
+               .step = 1,
+               .default_value = INDYCAM_RED_SATURATION_DEFAULT,
+       }, {
+               .id = INDYCAM_CONTROL_BLUE_SATURATION,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Blue Saturation",
+               .minimum = INDYCAM_BLUE_SATURATION_MIN,
+               .maximum = INDYCAM_BLUE_SATURATION_MAX,
+               .step = 1,
+               .default_value = INDYCAM_BLUE_SATURATION_DEFAULT,
+       }, {
+               .id = V4L2_CID_RED_BALANCE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Red Balance",
+               .minimum = INDYCAM_RED_BALANCE_MIN,
+               .maximum = INDYCAM_RED_BALANCE_MAX,
+               .step = 1,
+               .default_value = INDYCAM_RED_BALANCE_DEFAULT,
+       }, {
+               .id = V4L2_CID_BLUE_BALANCE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Blue Balance",
+               .minimum = INDYCAM_BLUE_BALANCE_MIN,
+               .maximum = INDYCAM_BLUE_BALANCE_MAX,
+               .step = 1,
+               .default_value = INDYCAM_BLUE_BALANCE_DEFAULT,
+       }, {
+               .id = V4L2_CID_EXPOSURE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Shutter Control",
+               .minimum = INDYCAM_SHUTTER_MIN,
+               .maximum = INDYCAM_SHUTTER_MAX,
+               .step = 1,
+               .default_value = INDYCAM_SHUTTER_DEFAULT,
+       }, {
+               .id = V4L2_CID_GAMMA,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Gamma",
+               .minimum = INDYCAM_GAMMA_MIN,
+               .maximum = INDYCAM_GAMMA_MAX,
+               .step = 1,
+               .default_value = INDYCAM_GAMMA_DEFAULT,
+       }
+};
+
+#define VINO_SAA7191_V4L2_CONTROL_COUNT                9
+
+struct v4l2_queryctrl vino_saa7191_v4l2_controls[] = {
+       {
+               .id = V4L2_CID_HUE,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Hue",
+               .minimum = SAA7191_HUE_MIN,
+               .maximum = SAA7191_HUE_MAX,
+               .step = 1,
+               .default_value = SAA7191_HUE_DEFAULT,
+       }, {
+               .id = SAA7191_CONTROL_BANDPASS,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Luminance Bandpass",
+               .minimum = SAA7191_BANDPASS_MIN,
+               .maximum = SAA7191_BANDPASS_MAX,
+               .step = 1,
+               .default_value = SAA7191_BANDPASS_DEFAULT,
+       }, {
+               .id = SAA7191_CONTROL_BANDPASS_WEIGHT,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Luminance Bandpass Weight",
+               .minimum = SAA7191_BANDPASS_WEIGHT_MIN,
+               .maximum = SAA7191_BANDPASS_WEIGHT_MAX,
+               .step = 1,
+               .default_value = SAA7191_BANDPASS_WEIGHT_DEFAULT,
+       }, {
+               .id = SAA7191_CONTROL_CORING,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "HF Luminance Coring",
+               .minimum = SAA7191_CORING_MIN,
+               .maximum = SAA7191_CORING_MAX,
+               .step = 1,
+               .default_value = SAA7191_CORING_DEFAULT,
+       }, {
+               .id = SAA7191_CONTROL_FORCE_COLOUR,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "Force Colour",
+               .minimum = SAA7191_FORCE_COLOUR_MIN,
+               .maximum = SAA7191_FORCE_COLOUR_MAX,
+               .step = 1,
+               .default_value = SAA7191_FORCE_COLOUR_DEFAULT,
+       }, {
+               .id = SAA7191_CONTROL_CHROMA_GAIN,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Chrominance Gain Control",
+               .minimum = SAA7191_CHROMA_GAIN_MIN,
+               .maximum = SAA7191_CHROMA_GAIN_MAX,
+               .step = 1,
+               .default_value = SAA7191_CHROMA_GAIN_DEFAULT,
+       }, {
+               .id = SAA7191_CONTROL_VTRC,
+               .type = V4L2_CTRL_TYPE_BOOLEAN,
+               .name = "VTR Time Constant",
+               .minimum = SAA7191_VTRC_MIN,
+               .maximum = SAA7191_VTRC_MAX,
+               .step = 1,
+               .default_value = SAA7191_VTRC_DEFAULT,
+       }, {
+               .id = SAA7191_CONTROL_LUMA_DELAY,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Luminance Delay Compensation",
+               .minimum = SAA7191_LUMA_DELAY_MIN,
+               .maximum = SAA7191_LUMA_DELAY_MAX,
+               .step = 1,
+               .default_value = SAA7191_LUMA_DELAY_DEFAULT,
+       }, {
+               .id = SAA7191_CONTROL_VNR,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Vertical Noise Reduction",
+               .minimum = SAA7191_VNR_MIN,
+               .maximum = SAA7191_VNR_MAX,
+               .step = 1,
+               .default_value = SAA7191_VNR_DEFAULT,
+       }
+};
+
+/* VINO framebuffer/DMA descriptor management */
+
+static void vino_free_buffer_with_count(struct vino_framebuffer *fb,
+                                              unsigned int count)
+{
+       unsigned int i;
+
+       dprintk("vino_free_buffer_with_count(): count = %d\n", count);
+
+       for (i = 0; i < count; i++) {
+               ClearPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
+               dma_unmap_single(NULL,
+                                fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
+                                PAGE_SIZE, DMA_FROM_DEVICE);
+               free_page(fb->desc_table.virtual[i]);
+       }
+
+       dma_free_coherent(NULL,
+                         VINO_PAGE_RATIO * (fb->desc_table.page_count + 4) *
+                         sizeof(dma_addr_t), (void *)fb->desc_table.dma_cpu,
+                         fb->desc_table.dma);
+       kfree(fb->desc_table.virtual);
+
+       memset(fb, 0, sizeof(struct vino_framebuffer));
+}
+
+static void vino_free_buffer(struct vino_framebuffer *fb)
+{
+       vino_free_buffer_with_count(fb, fb->desc_table.page_count);
+}
+
+static int vino_allocate_buffer(struct vino_framebuffer *fb,
+                               unsigned int size)
+{
+       unsigned int count, i, j;
+       int ret = 0;
+
+       dprintk("vino_allocate_buffer():\n");
+
+       if (size < 1)
+               return -EINVAL;
+
+       memset(fb, 0, sizeof(struct vino_framebuffer));
+
+       count = ((size / PAGE_SIZE) + 4) & ~3;
+
+       dprintk("vino_allocate_buffer(): size = %d, count = %d\n",
+               size, count);
+
+       /* allocate memory for table with virtual (page) addresses */
+       fb->desc_table.virtual =
+               kmalloc(count * sizeof(unsigned long), GFP_KERNEL);
+       if (!fb->desc_table.virtual)
+               return -ENOMEM;
+
+       /* allocate memory for table with dma addresses
+        * (has space for four extra descriptors) */
+       fb->desc_table.dma_cpu =
+               dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) *
+                                  sizeof(dma_addr_t), &fb->desc_table.dma,
+                                  GFP_KERNEL | GFP_DMA);
+       if (!fb->desc_table.dma_cpu) {
+               ret = -ENOMEM;
+               goto out_free_virtual;
+       }
+
+       /* allocate pages for the buffer and acquire the according
+        * dma addresses */
+       for (i = 0; i < count; i++) {
+               dma_addr_t dma_data_addr;
+
+               fb->desc_table.virtual[i] =
+                       get_zeroed_page(GFP_KERNEL | GFP_DMA);
+               if (!fb->desc_table.virtual[i]) {
+                       ret = -ENOBUFS;
+                       break;
+               }
+
+               dma_data_addr =
+                       dma_map_single(NULL,
+                                      (void *)fb->desc_table.virtual[i],
+                                      PAGE_SIZE, DMA_FROM_DEVICE);
+
+               for (j = 0; j < VINO_PAGE_RATIO; j++) {
+                       fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] =
+                               dma_data_addr + VINO_PAGE_SIZE * j;
+               }
+
+               SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
+       }
+
+       /* page_count needs to be set anyway, because the descriptor table has
+        * been allocated according to this number */
+       fb->desc_table.page_count = count;
+
+       if (ret) {
+               /* the descriptor with index i doesn't contain
+                * a valid address yet */
+               vino_free_buffer_with_count(fb, i);
+               return ret;
+       }
+
+       //fb->size = size;
+       fb->size = count * PAGE_SIZE;
+       fb->data_format = VINO_DATA_FMT_NONE;
+
+       /* set the dma stop-bit for the last (count+1)th descriptor */
+       fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP;
+       return 0;
+
+ out_free_virtual:
+       kfree(fb->desc_table.virtual);
+       return ret;
+}
+
+#if 0
+/* user buffers not fully implemented yet */
+static int vino_prepare_user_buffer(struct vino_framebuffer *fb,
+                                    void *user,
+                                    unsigned int size)
+{
+       unsigned int count, i, j;
+       int ret = 0;
+
+       dprintk("vino_prepare_user_buffer():\n");
+
+       if (size < 1)
+               return -EINVAL;
+
+       memset(fb, 0, sizeof(struct vino_framebuffer));
+
+       count = ((size / PAGE_SIZE)) & ~3;
+
+       dprintk("vino_prepare_user_buffer(): size = %d, count = %d\n",
+               size, count);
+
+       /* allocate memory for table with virtual (page) addresses */
+       fb->desc_table.virtual = (unsigned long *)
+               kmalloc(count * sizeof(unsigned long), GFP_KERNEL);
+       if (!fb->desc_table.virtual)
+               return -ENOMEM;
+
+       /* allocate memory for table with dma addresses
+        * (has space for four extra descriptors) */
+       fb->desc_table.dma_cpu =
+               dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) *
+                                  sizeof(dma_addr_t), &fb->desc_table.dma,
+                                  GFP_KERNEL | GFP_DMA);
+       if (!fb->desc_table.dma_cpu) {
+               ret = -ENOMEM;
+               goto out_free_virtual;
+       }
+
+       /* allocate pages for the buffer and acquire the according
+        * dma addresses */
+       for (i = 0; i < count; i++) {
+               dma_addr_t dma_data_addr;
+
+               fb->desc_table.virtual[i] =
+                       get_zeroed_page(GFP_KERNEL | GFP_DMA);
+               if (!fb->desc_table.virtual[i]) {
+                       ret = -ENOBUFS;
+                       break;
+               }
+
+               dma_data_addr =
+                       dma_map_single(NULL,
+                                      (void *)fb->desc_table.virtual[i],
+                                      PAGE_SIZE, DMA_FROM_DEVICE);
+
+               for (j = 0; j < VINO_PAGE_RATIO; j++) {
+                       fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] =
+                               dma_data_addr + VINO_PAGE_SIZE * j;
+               }
+
+               SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
+       }
+
+       /* page_count needs to be set anyway, because the descriptor table has
+        * been allocated according to this number */
+       fb->desc_table.page_count = count;
+
+       if (ret) {
+               /* the descriptor with index i doesn't contain
+                * a valid address yet */
+               vino_free_buffer_with_count(fb, i);
+               return ret;
+       }
+
+       //fb->size = size;
+       fb->size = count * PAGE_SIZE;
+
+       /* set the dma stop-bit for the last (count+1)th descriptor */
+       fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP;
+       return 0;
+
+ out_free_virtual:
+       kfree(fb->desc_table.virtual);
+       return ret;
+}
+#endif
+
+static void vino_sync_buffer(struct vino_framebuffer *fb)
+{
+       int i;
+
+       dprintk("vino_sync_buffer():\n");
+
+       for (i = 0; i < fb->desc_table.page_count; i++)
+               dma_sync_single_for_cpu(NULL,
+                                       fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
+                                       PAGE_SIZE, DMA_FROM_DEVICE);
+}
+
+/* Framebuffer fifo functions (need to be locked externally) */
+
+static inline void vino_fifo_init(struct vino_framebuffer_fifo *f,
+                          unsigned int length)
+{
+       f->length = 0;
+       f->used = 0;
+       f->head = 0;
+       f->tail = 0;
+
+       if (length > VINO_FRAMEBUFFER_COUNT_MAX)
+               length = VINO_FRAMEBUFFER_COUNT_MAX;
+
+       f->length = length;
+}
+
+/* returns true/false */
+static inline int vino_fifo_has_id(struct vino_framebuffer_fifo *f,
+                                  unsigned int id)
+{
+       unsigned int i;
+
+       for (i = f->head; i == (f->tail - 1); i = (i + 1) % f->length) {
+               if (f->data[i] == id)
+                       return 1;
+       }
+
+       return 0;
+}
+
+#if 0
+/* returns true/false */
+static inline int vino_fifo_full(struct vino_framebuffer_fifo *f)
+{
+       return (f->used == f->length);
+}
+#endif
+
+static inline unsigned int vino_fifo_get_used(struct vino_framebuffer_fifo *f)
+{
+       return f->used;
+}
+
+static int vino_fifo_enqueue(struct vino_framebuffer_fifo *f, unsigned int id)
+{
+       if (id >= f->length) {
+               return VINO_QUEUE_ERROR;
+       }
+
+       if (vino_fifo_has_id(f, id)) {
+               return VINO_QUEUE_ERROR;
+       }
+
+       if (f->used < f->length) {
+               f->data[f->tail] = id;
+               f->tail = (f->tail + 1) % f->length;
+               f->used++;
+       } else {
+               return VINO_QUEUE_ERROR;
+       }
+
+       return 0;
+}
+
+static int vino_fifo_peek(struct vino_framebuffer_fifo *f, unsigned int *id)
+{
+       if (f->used > 0) {
+               *id = f->data[f->head];
+       } else {
+               return VINO_QUEUE_ERROR;
+       }
+
+       return 0;
+}
+
+static int vino_fifo_dequeue(struct vino_framebuffer_fifo *f, unsigned int *id)
+{
+       if (f->used > 0) {
+               *id = f->data[f->head];
+               f->head = (f->head + 1) % f->length;
+               f->used--;
+       } else {
+               return VINO_QUEUE_ERROR;
+       }
+
+       return 0;
+}
+
+/* Framebuffer queue functions */
+
+/* execute with queue_lock locked */
+static void vino_queue_free_with_count(struct vino_framebuffer_queue *q,
+                                      unsigned int length)
+{
+       unsigned int i;
+
+       q->length = 0;
+       memset(&q->in, 0, sizeof(struct vino_framebuffer_fifo));
+       memset(&q->out, 0, sizeof(struct vino_framebuffer_fifo));
+       for (i = 0; i < length; i++) {
+               dprintk("vino_queue_free_with_count(): freeing buffer %d\n",
+                       i);
+               vino_free_buffer(q->buffer[i]);
+               kfree(q->buffer[i]);
+       }
+
+       q->type = VINO_MEMORY_NONE;
+       q->magic = 0;
+}
+
+static void vino_queue_free(struct vino_framebuffer_queue *q)
+{
+       dprintk("vino_queue_free():\n");
+
+       if (q->magic != VINO_QUEUE_MAGIC)
+               return;
+       if (q->type != VINO_MEMORY_MMAP)
+               return;
+
+       mutex_lock(&q->queue_mutex);
+
+       vino_queue_free_with_count(q, q->length);
+
+       mutex_unlock(&q->queue_mutex);
+}
+
+static int vino_queue_init(struct vino_framebuffer_queue *q,
+                          unsigned int *length)
+{
+       unsigned int i;
+       int ret = 0;
+
+       dprintk("vino_queue_init(): length = %d\n", *length);
+
+       if (q->magic == VINO_QUEUE_MAGIC) {
+               dprintk("vino_queue_init(): queue already initialized!\n");
+               return -EINVAL;
+       }
+
+       if (q->type != VINO_MEMORY_NONE) {
+               dprintk("vino_queue_init(): queue already initialized!\n");
+               return -EINVAL;
+       }
+
+       if (*length < 1)
+               return -EINVAL;
+
+       mutex_lock(&q->queue_mutex);
+
+       if (*length > VINO_FRAMEBUFFER_COUNT_MAX)
+               *length = VINO_FRAMEBUFFER_COUNT_MAX;
+
+       q->length = 0;
+
+       for (i = 0; i < *length; i++) {
+               dprintk("vino_queue_init(): allocating buffer %d\n", i);
+               q->buffer[i] = kmalloc(sizeof(struct vino_framebuffer),
+                                      GFP_KERNEL);
+               if (!q->buffer[i]) {
+                       dprintk("vino_queue_init(): kmalloc() failed\n");
+                       ret = -ENOMEM;
+                       break;
+               }
+
+               ret = vino_allocate_buffer(q->buffer[i],
+                                          VINO_FRAMEBUFFER_SIZE);
+               if (ret) {
+                       kfree(q->buffer[i]);
+                       dprintk("vino_queue_init(): "
+                               "vino_allocate_buffer() failed\n");
+                       break;
+               }
+
+               q->buffer[i]->id = i;
+               if (i > 0) {
+                       q->buffer[i]->offset = q->buffer[i - 1]->offset +
+                               q->buffer[i - 1]->size;
+               } else {
+                       q->buffer[i]->offset = 0;
+               }
+
+               spin_lock_init(&q->buffer[i]->state_lock);
+
+               dprintk("vino_queue_init(): buffer = %d, offset = %d, "
+                       "size = %d\n", i, q->buffer[i]->offset,
+                       q->buffer[i]->size);
+       }
+
+       if (ret) {
+               vino_queue_free_with_count(q, i);
+               *length = 0;
+       } else {
+               q->length = *length;
+               vino_fifo_init(&q->in, q->length);
+               vino_fifo_init(&q->out, q->length);
+               q->type = VINO_MEMORY_MMAP;
+               q->magic = VINO_QUEUE_MAGIC;
+       }
+
+       mutex_unlock(&q->queue_mutex);
+
+       return ret;
+}
+
+static struct vino_framebuffer *vino_queue_add(struct
+                                              vino_framebuffer_queue *q,
+                                              unsigned int id)
+{
+       struct vino_framebuffer *ret = NULL;
+       unsigned int total;
+       unsigned long flags;
+
+       dprintk("vino_queue_add(): id = %d\n", id);
+
+       if (q->magic != VINO_QUEUE_MAGIC) {
+               return ret;
+       }
+
+       spin_lock_irqsave(&q->queue_lock, flags);
+
+       if (q->length == 0)
+               goto out;
+
+       if (id >= q->length)
+               goto out;
+
+       /* not needed?: if (vino_fifo_full(&q->out)) {
+               goto out;
+               }*/
+       /* check that outgoing queue isn't already full
+        * (or that it won't become full) */
+       total = vino_fifo_get_used(&q->in) +
+               vino_fifo_get_used(&q->out);
+       if (total >= q->length)
+               goto out;
+
+       if (vino_fifo_enqueue(&q->in, id))
+               goto out;
+
+       ret = q->buffer[id];
+
+out:
+       spin_unlock_irqrestore(&q->queue_lock, flags);
+
+       return ret;
+}
+
+static struct vino_framebuffer *vino_queue_transfer(struct
+                                                   vino_framebuffer_queue *q)
+{
+       struct vino_framebuffer *ret = NULL;
+       struct vino_framebuffer *fb;
+       int id;
+       unsigned long flags;
+
+       dprintk("vino_queue_transfer():\n");
+
+       if (q->magic != VINO_QUEUE_MAGIC) {
+               return ret;
+       }
+
+       spin_lock_irqsave(&q->queue_lock, flags);
+
+       if (q->length == 0)
+               goto out;
+
+       // now this actually removes an entry from the incoming queue
+       if (vino_fifo_dequeue(&q->in, &id)) {
+               goto out;
+       }
+
+       dprintk("vino_queue_transfer(): id = %d\n", id);
+       fb = q->buffer[id];
+
+       // we have already checked that the outgoing queue is not full, but...
+       if (vino_fifo_enqueue(&q->out, id)) {
+               printk(KERN_ERR "vino_queue_transfer(): "
+                      "outgoing queue is full, this shouldn't happen!\n");
+               goto out;
+       }
+
+       ret = fb;
+out:
+       spin_unlock_irqrestore(&q->queue_lock, flags);
+
+       return ret;
+}
+
+/* returns true/false */
+static int vino_queue_incoming_contains(struct vino_framebuffer_queue *q,
+                                       unsigned int id)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       if (q->magic != VINO_QUEUE_MAGIC) {
+               return ret;
+       }
+
+       spin_lock_irqsave(&q->queue_lock, flags);
+
+       if (q->length == 0)
+               goto out;
+
+       ret = vino_fifo_has_id(&q->in, id);
+
+out:
+       spin_unlock_irqrestore(&q->queue_lock, flags);
+
+       return ret;
+}
+
+/* returns true/false */
+static int vino_queue_outgoing_contains(struct vino_framebuffer_queue *q,
+                                       unsigned int id)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       if (q->magic != VINO_QUEUE_MAGIC) {
+               return ret;
+       }
+
+       spin_lock_irqsave(&q->queue_lock, flags);
+
+       if (q->length == 0)
+               goto out;
+
+       ret = vino_fifo_has_id(&q->out, id);
+
+out:
+       spin_unlock_irqrestore(&q->queue_lock, flags);
+
+       return ret;
+}
+
+static int vino_queue_get_incoming(struct vino_framebuffer_queue *q,
+                                  unsigned int *used)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       if (q->magic != VINO_QUEUE_MAGIC) {
+               return VINO_QUEUE_ERROR;
+       }
+
+       spin_lock_irqsave(&q->queue_lock, flags);
+
+       if (q->length == 0) {
+               ret = VINO_QUEUE_ERROR;
+               goto out;
+       }
+
+       *used = vino_fifo_get_used(&q->in);
+
+out:
+       spin_unlock_irqrestore(&q->queue_lock, flags);
+
+       return ret;
+}
+
+static int vino_queue_get_outgoing(struct vino_framebuffer_queue *q,
+                                  unsigned int *used)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       if (q->magic != VINO_QUEUE_MAGIC) {
+               return VINO_QUEUE_ERROR;
+       }
+
+       spin_lock_irqsave(&q->queue_lock, flags);
+
+       if (q->length == 0) {
+               ret = VINO_QUEUE_ERROR;
+               goto out;
+       }
+
+       *used = vino_fifo_get_used(&q->out);
+
+out:
+       spin_unlock_irqrestore(&q->queue_lock, flags);
+
+       return ret;
+}
+
+#if 0
+static int vino_queue_get_total(struct vino_framebuffer_queue *q,
+                               unsigned int *total)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       if (q->magic != VINO_QUEUE_MAGIC) {
+               return VINO_QUEUE_ERROR;
+       }
+
+       spin_lock_irqsave(&q->queue_lock, flags);
+
+       if (q->length == 0) {
+               ret = VINO_QUEUE_ERROR;
+               goto out;
+       }
+
+       *total = vino_fifo_get_used(&q->in) +
+               vino_fifo_get_used(&q->out);
+
+out:
+       spin_unlock_irqrestore(&q->queue_lock, flags);
+
+       return ret;
+}
+#endif
+
+static struct vino_framebuffer *vino_queue_peek(struct
+                                               vino_framebuffer_queue *q,
+                                               unsigned int *id)
+{
+       struct vino_framebuffer *ret = NULL;
+       unsigned long flags;
+
+       if (q->magic != VINO_QUEUE_MAGIC) {
+               return ret;
+       }
+
+       spin_lock_irqsave(&q->queue_lock, flags);
+
+       if (q->length == 0)
+               goto out;
+
+       if (vino_fifo_peek(&q->in, id)) {
+               goto out;
+       }
+
+       ret = q->buffer[*id];
+out:
+       spin_unlock_irqrestore(&q->queue_lock, flags);
+
+       return ret;
+}
+
+static struct vino_framebuffer *vino_queue_remove(struct
+                                                 vino_framebuffer_queue *q,
+                                                 unsigned int *id)
+{
+       struct vino_framebuffer *ret = NULL;
+       unsigned long flags;
+       dprintk("vino_queue_remove():\n");
+
+       if (q->magic != VINO_QUEUE_MAGIC) {
+               return ret;
+       }
+
+       spin_lock_irqsave(&q->queue_lock, flags);
+
+       if (q->length == 0)
+               goto out;
+
+       if (vino_fifo_dequeue(&q->out, id)) {
+               goto out;
+       }
+
+       dprintk("vino_queue_remove(): id = %d\n", *id);
+       ret = q->buffer[*id];
+out:
+       spin_unlock_irqrestore(&q->queue_lock, flags);
+
+       return ret;
+}
+
+static struct
+vino_framebuffer *vino_queue_get_buffer(struct vino_framebuffer_queue *q,
+                                       unsigned int id)
+{
+       struct vino_framebuffer *ret = NULL;
+       unsigned long flags;
+
+       if (q->magic != VINO_QUEUE_MAGIC) {
+               return ret;
+       }
+
+       spin_lock_irqsave(&q->queue_lock, flags);
+
+       if (q->length == 0)
+               goto out;
+
+       if (id >= q->length)
+               goto out;
+
+       ret = q->buffer[id];
+ out:
+       spin_unlock_irqrestore(&q->queue_lock, flags);
+
+       return ret;
+}
+
+static unsigned int vino_queue_get_length(struct vino_framebuffer_queue *q)
+{
+       unsigned int length = 0;
+       unsigned long flags;
+
+       if (q->magic != VINO_QUEUE_MAGIC) {
+               return length;
+       }
+
+       spin_lock_irqsave(&q->queue_lock, flags);
+       length = q->length;
+       spin_unlock_irqrestore(&q->queue_lock, flags);
+
+       return length;
+}
+
+static int vino_queue_has_mapped_buffers(struct vino_framebuffer_queue *q)
+{
+       unsigned int i;
+       int ret = 0;
+       unsigned long flags;
+
+       if (q->magic != VINO_QUEUE_MAGIC) {
+               return ret;
+       }
+
+       spin_lock_irqsave(&q->queue_lock, flags);
+       for (i = 0; i < q->length; i++) {
+               if (q->buffer[i]->map_count > 0) {
+                       ret = 1;
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&q->queue_lock, flags);
+
+       return ret;
+}
+
+/* VINO functions */
+
+/* execute with input_lock locked */
+static void vino_update_line_size(struct vino_channel_settings *vcs)
+{
+       unsigned int w = vcs->clipping.right - vcs->clipping.left;
+       unsigned int d = vcs->decimation;
+       unsigned int bpp = vino_data_formats[vcs->data_format].bpp;
+       unsigned int lsize;
+
+       dprintk("update_line_size(): before: w = %d, d = %d, "
+               "line_size = %d\n", w, d, vcs->line_size);
+
+       /* line size must be multiple of 8 bytes */
+       lsize = (bpp * (w / d)) & ~7;
+       w = (lsize / bpp) * d;
+
+       vcs->clipping.right = vcs->clipping.left + w;
+       vcs->line_size = lsize;
+
+       dprintk("update_line_size(): after: w = %d, d = %d, "
+               "line_size = %d\n", w, d, vcs->line_size);
+}
+
+/* execute with input_lock locked */
+static void vino_set_clipping(struct vino_channel_settings *vcs,
+                             unsigned int x, unsigned int y,
+                             unsigned int w, unsigned int h)
+{
+       unsigned int maxwidth, maxheight;
+       unsigned int d;
+
+       maxwidth = vino_data_norms[vcs->data_norm].width;
+       maxheight = vino_data_norms[vcs->data_norm].height;
+       d = vcs->decimation;
+
+       y &= ~1;        /* odd/even fields */
+
+       if (x > maxwidth) {
+               x = 0;
+       }
+       if (y > maxheight) {
+               y = 0;
+       }
+
+       if (((w / d) < VINO_MIN_WIDTH)
+           || ((h / d) < VINO_MIN_HEIGHT)) {
+               w = VINO_MIN_WIDTH * d;
+               h = VINO_MIN_HEIGHT * d;
+       }
+
+       if ((x + w) > maxwidth) {
+               w = maxwidth - x;
+               if ((w / d) < VINO_MIN_WIDTH)
+                       x = maxwidth - VINO_MIN_WIDTH * d;
+       }
+       if ((y + h) > maxheight) {
+               h = maxheight - y;
+               if ((h / d) < VINO_MIN_HEIGHT)
+                       y = maxheight - VINO_MIN_HEIGHT * d;
+       }
+
+       vcs->clipping.left = x;
+       vcs->clipping.top = y;
+       vcs->clipping.right = x + w;
+       vcs->clipping.bottom = y + h;
+
+       vino_update_line_size(vcs);
+
+       dprintk("clipping %d, %d, %d, %d / %d - %d\n",
+               vcs->clipping.left, vcs->clipping.top, vcs->clipping.right,
+               vcs->clipping.bottom, vcs->decimation, vcs->line_size);
+}
+
+/* execute with input_lock locked */
+static inline void vino_set_default_clipping(struct vino_channel_settings *vcs)
+{
+       vino_set_clipping(vcs, 0, 0, vino_data_norms[vcs->data_norm].width,
+                         vino_data_norms[vcs->data_norm].height);
+}
+
+/* execute with input_lock locked */
+static void vino_set_scaling(struct vino_channel_settings *vcs,
+                            unsigned int w, unsigned int h)
+{
+       unsigned int x, y, curw, curh, d;
+
+       x = vcs->clipping.left;
+       y = vcs->clipping.top;
+       curw = vcs->clipping.right - vcs->clipping.left;
+       curh = vcs->clipping.bottom - vcs->clipping.top;
+
+       d = max(curw / w, curh / h);
+
+       dprintk("scaling w: %d, h: %d, curw: %d, curh: %d, d: %d\n",
+               w, h, curw, curh, d);
+
+       if (d < 1) {
+               d = 1;
+       } else if (d > 8) {
+               d = 8;
+       }
+
+       vcs->decimation = d;
+       vino_set_clipping(vcs, x, y, w * d, h * d);
+
+       dprintk("scaling %d, %d, %d, %d / %d - %d\n", vcs->clipping.left,
+               vcs->clipping.top, vcs->clipping.right, vcs->clipping.bottom,
+               vcs->decimation, vcs->line_size);
+}
+
+/* execute with input_lock locked */
+static inline void vino_set_default_scaling(struct vino_channel_settings *vcs)
+{
+       vino_set_scaling(vcs, vcs->clipping.right - vcs->clipping.left,
+                        vcs->clipping.bottom - vcs->clipping.top);
+}
+
+/* execute with input_lock locked */
+static void vino_set_framerate(struct vino_channel_settings *vcs,
+                              unsigned int fps)
+{
+       unsigned int mask;
+
+       switch (vcs->data_norm) {
+       case VINO_DATA_NORM_NTSC:
+       case VINO_DATA_NORM_D1:
+               fps = (unsigned int)(fps / 6) * 6; // FIXME: round!
+
+               if (fps < vino_data_norms[vcs->data_norm].fps_min)
+                       fps = vino_data_norms[vcs->data_norm].fps_min;
+               if (fps > vino_data_norms[vcs->data_norm].fps_max)
+                       fps = vino_data_norms[vcs->data_norm].fps_max;
+
+               switch (fps) {
+               case 6:
+                       mask = 0x003;
+                       break;
+               case 12:
+                       mask = 0x0c3;
+                       break;
+               case 18:
+                       mask = 0x333;
+                       break;
+               case 24:
+                       mask = 0x3ff;
+                       break;
+               case 30:
+                       mask = 0xfff;
+                       break;
+               default:
+                       mask = VINO_FRAMERT_FULL;
+               }
+               vcs->framert_reg = VINO_FRAMERT_RT(mask);
+               break;
+       case VINO_DATA_NORM_PAL:
+       case VINO_DATA_NORM_SECAM:
+               fps = (unsigned int)(fps / 5) * 5; // FIXME: round!
+
+               if (fps < vino_data_norms[vcs->data_norm].fps_min)
+                       fps = vino_data_norms[vcs->data_norm].fps_min;
+               if (fps > vino_data_norms[vcs->data_norm].fps_max)
+                       fps = vino_data_norms[vcs->data_norm].fps_max;
+
+               switch (fps) {
+               case 5:
+                       mask = 0x003;
+                       break;
+               case 10:
+                       mask = 0x0c3;
+                       break;
+               case 15:
+                       mask = 0x333;
+                       break;
+               case 20:
+                       mask = 0x0ff;
+                       break;
+               case 25:
+                       mask = 0x3ff;
+                       break;
+               default:
+                       mask = VINO_FRAMERT_FULL;
+               }
+               vcs->framert_reg = VINO_FRAMERT_RT(mask) | VINO_FRAMERT_PAL;
+               break;
+       }
+
+       vcs->fps = fps;
+}
+
+/* execute with input_lock locked */
+static inline void vino_set_default_framerate(struct
+                                             vino_channel_settings *vcs)
+{
+       vino_set_framerate(vcs, vino_data_norms[vcs->data_norm].fps_max);
+}
+
+/* VINO I2C bus functions */
+
+struct i2c_algo_sgi_data {
+       void *data;     /* private data for lowlevel routines */
+       unsigned (*getctrl)(void *data);
+       void (*setctrl)(void *data, unsigned val);
+       unsigned (*rdata)(void *data);
+       void (*wdata)(void *data, unsigned val);
+
+       int xfer_timeout;
+       int ack_timeout;
+};
+
+static int wait_xfer_done(struct i2c_algo_sgi_data *adap)
+{
+       int i;
+
+       for (i = 0; i < adap->xfer_timeout; i++) {
+               if ((adap->getctrl(adap->data) & SGI_I2C_XFER_BUSY) == 0)
+                       return 0;
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int wait_ack(struct i2c_algo_sgi_data *adap)
+{
+       int i;
+
+       if (wait_xfer_done(adap))
+               return -ETIMEDOUT;
+       for (i = 0; i < adap->ack_timeout; i++) {
+               if ((adap->getctrl(adap->data) & SGI_I2C_NACK) == 0)
+                       return 0;
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int force_idle(struct i2c_algo_sgi_data *adap)
+{
+       int i;
+
+       adap->setctrl(adap->data, SGI_I2C_FORCE_IDLE);
+       for (i = 0; i < adap->xfer_timeout; i++) {
+               if ((adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE) == 0)
+                       goto out;
+               udelay(1);
+       }
+       return -ETIMEDOUT;
+out:
+       if (adap->getctrl(adap->data) & SGI_I2C_BUS_ERR)
+               return -EIO;
+       return 0;
+}
+
+static int do_address(struct i2c_algo_sgi_data *adap, unsigned int addr,
+                     int rd)
+{
+       if (rd)
+               adap->setctrl(adap->data, SGI_I2C_NOT_IDLE);
+       /* Check if bus is idle, eventually force it to do so */
+       if (adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE)
+               if (force_idle(adap))
+                       return -EIO;
+       /* Write out the i2c chip address and specify operation */
+       adap->setctrl(adap->data,
+                     SGI_I2C_HOLD_BUS | SGI_I2C_WRITE | SGI_I2C_NOT_IDLE);
+       if (rd)
+               addr |= 1;
+       adap->wdata(adap->data, addr);
+       if (wait_ack(adap))
+               return -EIO;
+       return 0;
+}
+
+static int i2c_read(struct i2c_algo_sgi_data *adap, unsigned char *buf,
+                   unsigned int len)
+{
+       int i;
+
+       adap->setctrl(adap->data,
+                     SGI_I2C_HOLD_BUS | SGI_I2C_READ | SGI_I2C_NOT_IDLE);
+       for (i = 0; i < len; i++) {
+               if (wait_xfer_done(adap))
+                       return -EIO;
+               buf[i] = adap->rdata(adap->data);
+       }
+       adap->setctrl(adap->data, SGI_I2C_RELEASE_BUS | SGI_I2C_FORCE_IDLE);
+
+       return 0;
+
+}
+
+static int i2c_write(struct i2c_algo_sgi_data *adap, unsigned char *buf,
+                    unsigned int len)
+{
+       int i;
+
+       /* We are already in write state */
+       for (i = 0; i < len; i++) {
+               adap->wdata(adap->data, buf[i]);
+               if (wait_ack(adap))
+                       return -EIO;
+       }
+       return 0;
+}
+
+static int sgi_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
+                   int num)
+{
+       struct i2c_algo_sgi_data *adap = i2c_adap->algo_data;
+       struct i2c_msg *p;
+       int i, err = 0;
+
+       for (i = 0; !err && i < num; i++) {
+               p = &msgs[i];
+               err = do_address(adap, p->addr, p->flags & I2C_M_RD);
+               if (err || !p->len)
+                       continue;
+               if (p->flags & I2C_M_RD)
+                       err = i2c_read(adap, p->buf, p->len);
+               else
+                       err = i2c_write(adap, p->buf, p->len);
+       }
+
+       return (err < 0) ? err : i;
+}
+
+static u32 sgi_func(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm sgi_algo = {
+       .master_xfer    = sgi_xfer,
+       .functionality  = sgi_func,
+};
+
+static unsigned i2c_vino_getctrl(void *data)
+{
+       return vino->i2c_control;
+}
+
+static void i2c_vino_setctrl(void *data, unsigned val)
+{
+       vino->i2c_control = val;
+}
+
+static unsigned i2c_vino_rdata(void *data)
+{
+       return vino->i2c_data;
+}
+
+static void i2c_vino_wdata(void *data, unsigned val)
+{
+       vino->i2c_data = val;
+}
+
+static struct i2c_algo_sgi_data i2c_sgi_vino_data = {
+       .getctrl = &i2c_vino_getctrl,
+       .setctrl = &i2c_vino_setctrl,
+       .rdata   = &i2c_vino_rdata,
+       .wdata   = &i2c_vino_wdata,
+       .xfer_timeout = 200,
+       .ack_timeout  = 1000,
+};
+
+static struct i2c_adapter vino_i2c_adapter = {
+       .name                   = "VINO I2C bus",
+       .algo                   = &sgi_algo,
+       .algo_data              = &i2c_sgi_vino_data,
+       .owner                  = THIS_MODULE,
+};
+
+/*
+ * Prepare VINO for DMA transfer...
+ * (execute only with vino_lock and input_lock locked)
+ */
+static int vino_dma_setup(struct vino_channel_settings *vcs,
+                         struct vino_framebuffer *fb)
+{
+       u32 ctrl, intr;
+       struct sgi_vino_channel *ch;
+       const struct vino_data_norm *norm;
+
+       dprintk("vino_dma_setup():\n");
+
+       vcs->field = 0;
+       fb->frame_counter = 0;
+
+       ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b;
+       norm = &vino_data_norms[vcs->data_norm];
+
+       ch->page_index = 0;
+       ch->line_count = 0;
+
+       /* VINO line size register is set 8 bytes less than actual */
+       ch->line_size = vcs->line_size - 8;
+
+       /* let VINO know where to transfer data */
+       ch->start_desc_tbl = fb->desc_table.dma;
+       ch->next_4_desc = fb->desc_table.dma;
+
+       /* give vino time to fetch the first four descriptors, 5 usec
+        * should be more than enough time */
+       udelay(VINO_DESC_FETCH_DELAY);
+
+       dprintk("vino_dma_setup(): start desc = %08x, next 4 desc = %08x\n",
+               ch->start_desc_tbl, ch->next_4_desc);
+
+       /* set the alpha register */
+       ch->alpha = vcs->alpha;
+
+       /* set clipping registers */
+       ch->clip_start = VINO_CLIP_ODD(norm->odd.top + vcs->clipping.top / 2) |
+               VINO_CLIP_EVEN(norm->even.top +
+                              vcs->clipping.top / 2) |
+               VINO_CLIP_X(vcs->clipping.left);
+       ch->clip_end = VINO_CLIP_ODD(norm->odd.top +
+                                    vcs->clipping.bottom / 2 - 1) |
+               VINO_CLIP_EVEN(norm->even.top +
+                              vcs->clipping.bottom / 2 - 1) |
+               VINO_CLIP_X(vcs->clipping.right);
+
+       /* set the size of actual content in the buffer (DECIMATION !) */
+       fb->data_size = ((vcs->clipping.right - vcs->clipping.left) /
+                        vcs->decimation) *
+               ((vcs->clipping.bottom - vcs->clipping.top) /
+                vcs->decimation) *
+               vino_data_formats[vcs->data_format].bpp;
+
+       ch->frame_rate = vcs->framert_reg;
+
+       ctrl = vino->control;
+       intr = vino->intr_status;
+
+       if (vcs->channel == VINO_CHANNEL_A) {
+               /* All interrupt conditions for this channel was cleared
+                * so clear the interrupt status register and enable
+                * interrupts */
+               intr &= ~VINO_INTSTAT_A;
+               ctrl |= VINO_CTRL_A_INT;
+
+               /* enable synchronization */
+               ctrl |= VINO_CTRL_A_SYNC_ENBL;
+
+               /* enable frame assembly */
+               ctrl |= VINO_CTRL_A_INTERLEAVE_ENBL;
+
+               /* set decimation used */
+               if (vcs->decimation < 2)
+                       ctrl &= ~VINO_CTRL_A_DEC_ENBL;
+               else {
+                       ctrl |= VINO_CTRL_A_DEC_ENBL;
+                       ctrl &= ~VINO_CTRL_A_DEC_SCALE_MASK;
+                       ctrl |= (vcs->decimation - 1) <<
+                               VINO_CTRL_A_DEC_SCALE_SHIFT;
+               }
+
+               /* select input interface */
+               if (vcs->input == VINO_INPUT_D1)
+                       ctrl |= VINO_CTRL_A_SELECT;
+               else
+                       ctrl &= ~VINO_CTRL_A_SELECT;
+
+               /* palette */
+               ctrl &= ~(VINO_CTRL_A_LUMA_ONLY | VINO_CTRL_A_RGB |
+                         VINO_CTRL_A_DITHER);
+       } else {
+               intr &= ~VINO_INTSTAT_B;
+               ctrl |= VINO_CTRL_B_INT;
+
+               ctrl |= VINO_CTRL_B_SYNC_ENBL;
+               ctrl |= VINO_CTRL_B_INTERLEAVE_ENBL;
+
+               if (vcs->decimation < 2)
+                       ctrl &= ~VINO_CTRL_B_DEC_ENBL;
+               else {
+                       ctrl |= VINO_CTRL_B_DEC_ENBL;
+                       ctrl &= ~VINO_CTRL_B_DEC_SCALE_MASK;
+                       ctrl |= (vcs->decimation - 1) <<
+                               VINO_CTRL_B_DEC_SCALE_SHIFT;
+
+               }
+               if (vcs->input == VINO_INPUT_D1)
+                       ctrl |= VINO_CTRL_B_SELECT;
+               else
+                       ctrl &= ~VINO_CTRL_B_SELECT;
+
+               ctrl &= ~(VINO_CTRL_B_LUMA_ONLY | VINO_CTRL_B_RGB |
+                         VINO_CTRL_B_DITHER);
+       }
+
+       /* set palette */
+       fb->data_format = vcs->data_format;
+
+       switch (vcs->data_format) {
+               case VINO_DATA_FMT_GREY:
+                       ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+                               VINO_CTRL_A_LUMA_ONLY : VINO_CTRL_B_LUMA_ONLY;
+                       break;
+               case VINO_DATA_FMT_RGB32:
+                       ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+                               VINO_CTRL_A_RGB : VINO_CTRL_B_RGB;
+                       break;
+               case VINO_DATA_FMT_YUV:
+                       /* nothing needs to be done */
+                       break;
+               case VINO_DATA_FMT_RGB332:
+                       ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+                               VINO_CTRL_A_RGB | VINO_CTRL_A_DITHER :
+                               VINO_CTRL_B_RGB | VINO_CTRL_B_DITHER;
+                       break;
+       }
+
+       vino->intr_status = intr;
+       vino->control = ctrl;
+
+       return 0;
+}
+
+/* (execute only with vino_lock locked) */
+static inline void vino_dma_start(struct vino_channel_settings *vcs)
+{
+       u32 ctrl = vino->control;
+
+       dprintk("vino_dma_start():\n");
+       ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+               VINO_CTRL_A_DMA_ENBL : VINO_CTRL_B_DMA_ENBL;
+       vino->control = ctrl;
+}
+
+/* (execute only with vino_lock locked) */
+static inline void vino_dma_stop(struct vino_channel_settings *vcs)
+{
+       u32 ctrl = vino->control;
+
+       ctrl &= (vcs->channel == VINO_CHANNEL_A) ?
+               ~VINO_CTRL_A_DMA_ENBL : ~VINO_CTRL_B_DMA_ENBL;
+       ctrl &= (vcs->channel == VINO_CHANNEL_A) ?
+               ~VINO_CTRL_A_INT : ~VINO_CTRL_B_INT;
+       vino->control = ctrl;
+       dprintk("vino_dma_stop():\n");
+}
+
+/*
+ * Load dummy page to descriptor registers. This prevents generating of
+ * spurious interrupts. (execute only with vino_lock locked)
+ */
+static void vino_clear_interrupt(struct vino_channel_settings *vcs)
+{
+       struct sgi_vino_channel *ch;
+
+       ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b;
+
+       ch->page_index = 0;
+       ch->line_count = 0;
+
+       ch->start_desc_tbl = vino_drvdata->dummy_desc_table.dma;
+       ch->next_4_desc = vino_drvdata->dummy_desc_table.dma;
+
+       udelay(VINO_DESC_FETCH_DELAY);
+       dprintk("channel %c clear interrupt condition\n",
+              (vcs->channel == VINO_CHANNEL_A) ? 'A':'B');
+}
+
+static int vino_capture(struct vino_channel_settings *vcs,
+                       struct vino_framebuffer *fb)
+{
+       int err = 0;
+       unsigned long flags, flags2;
+
+       spin_lock_irqsave(&fb->state_lock, flags);
+
+       if (fb->state == VINO_FRAMEBUFFER_IN_USE)
+               err = -EBUSY;
+       fb->state = VINO_FRAMEBUFFER_IN_USE;
+
+       spin_unlock_irqrestore(&fb->state_lock, flags);
+
+       if (err)
+               return err;
+
+       spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags2);
+
+       vino_dma_setup(vcs, fb);
+       vino_dma_start(vcs);
+
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags2);
+       spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
+
+       return err;
+}
+
+static
+struct vino_framebuffer *vino_capture_enqueue(struct
+                                             vino_channel_settings *vcs,
+                                             unsigned int index)
+{
+       struct vino_framebuffer *fb;
+       unsigned long flags;
+
+       dprintk("vino_capture_enqueue():\n");
+
+       spin_lock_irqsave(&vcs->capture_lock, flags);
+
+       fb = vino_queue_add(&vcs->fb_queue, index);
+       if (fb == NULL) {
+               dprintk("vino_capture_enqueue(): vino_queue_add() failed, "
+                       "queue full?\n");
+               goto out;
+       }
+out:
+       spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+       return fb;
+}
+
+static int vino_capture_next(struct vino_channel_settings *vcs, int start)
+{
+       struct vino_framebuffer *fb;
+       unsigned int incoming, id;
+       int err = 0;
+       unsigned long flags;
+
+       dprintk("vino_capture_next():\n");
+
+       spin_lock_irqsave(&vcs->capture_lock, flags);
+
+       if (start) {
+               /* start capture only if capture isn't in progress already */
+               if (vcs->capturing) {
+                       spin_unlock_irqrestore(&vcs->capture_lock, flags);
+                       return 0;
+               }
+
+       } else {
+               /* capture next frame:
+                * stop capture if capturing is not set */
+               if (!vcs->capturing) {
+                       spin_unlock_irqrestore(&vcs->capture_lock, flags);
+                       return 0;
+               }
+       }
+
+       err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
+       if (err) {
+               dprintk("vino_capture_next(): vino_queue_get_incoming() "
+                       "failed\n");
+               err = -EINVAL;
+               goto out;
+       }
+       if (incoming == 0) {
+               dprintk("vino_capture_next(): no buffers available\n");
+               goto out;
+       }
+
+       fb = vino_queue_peek(&vcs->fb_queue, &id);
+       if (fb == NULL) {
+               dprintk("vino_capture_next(): vino_queue_peek() failed\n");
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (start) {
+               vcs->capturing = 1;
+       }
+
+       spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+       err = vino_capture(vcs, fb);
+
+       return err;
+
+out:
+       vcs->capturing = 0;
+       spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+       return err;
+}
+
+static inline int vino_is_capturing(struct vino_channel_settings *vcs)
+{
+       int ret;
+       unsigned long flags;
+
+       spin_lock_irqsave(&vcs->capture_lock, flags);
+
+       ret = vcs->capturing;
+
+       spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+       return ret;
+}
+
+/* waits until a frame is captured */
+static int vino_wait_for_frame(struct vino_channel_settings *vcs)
+{
+       wait_queue_t wait;
+       int err = 0;
+
+       dprintk("vino_wait_for_frame():\n");
+
+       init_waitqueue_entry(&wait, current);
+       /* add ourselves into wait queue */
+       add_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait);
+
+       /* to ensure that schedule_timeout will return immediately
+        * if VINO interrupt was triggered meanwhile */
+       schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+       if (signal_pending(current))
+               err = -EINTR;
+
+       remove_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait);
+
+       dprintk("vino_wait_for_frame(): waiting for frame %s\n",
+               err ? "failed" : "ok");
+
+       return err;
+}
+
+/* the function assumes that PAGE_SIZE % 4 == 0 */
+static void vino_convert_to_rgba(struct vino_framebuffer *fb) {
+       unsigned char *pageptr;
+       unsigned int page, i;
+       unsigned char a;
+
+       for (page = 0; page < fb->desc_table.page_count; page++) {
+               pageptr = (unsigned char *)fb->desc_table.virtual[page];
+
+               for (i = 0; i < PAGE_SIZE; i += 4) {
+                       a = pageptr[0];
+                       pageptr[0] = pageptr[3];
+                       pageptr[1] = pageptr[2];
+                       pageptr[2] = pageptr[1];
+                       pageptr[3] = a;
+                       pageptr += 4;
+               }
+       }
+}
+
+/* checks if the buffer is in correct state and syncs data */
+static int vino_check_buffer(struct vino_channel_settings *vcs,
+                            struct vino_framebuffer *fb)
+{
+       int err = 0;
+       unsigned long flags;
+
+       dprintk("vino_check_buffer():\n");
+
+       spin_lock_irqsave(&fb->state_lock, flags);
+       switch (fb->state) {
+       case VINO_FRAMEBUFFER_IN_USE:
+               err = -EIO;
+               break;
+       case VINO_FRAMEBUFFER_READY:
+               vino_sync_buffer(fb);
+               fb->state = VINO_FRAMEBUFFER_UNUSED;
+               break;
+       default:
+               err = -EINVAL;
+       }
+       spin_unlock_irqrestore(&fb->state_lock, flags);
+
+       if (!err) {
+               if (vino_pixel_conversion
+                   && (fb->data_format == VINO_DATA_FMT_RGB32)) {
+                       vino_convert_to_rgba(fb);
+               }
+       } else if (err && (err != -EINVAL)) {
+               dprintk("vino_check_buffer(): buffer not ready\n");
+
+               spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
+               vino_dma_stop(vcs);
+               vino_clear_interrupt(vcs);
+               spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
+       }
+
+       return err;
+}
+
+/* forcefully terminates capture */
+static void vino_capture_stop(struct vino_channel_settings *vcs)
+{
+       unsigned int incoming = 0, outgoing = 0, id;
+       unsigned long flags, flags2;
+
+       dprintk("vino_capture_stop():\n");
+
+       spin_lock_irqsave(&vcs->capture_lock, flags);
+
+       /* unset capturing to stop queue processing */
+       vcs->capturing = 0;
+
+       spin_lock_irqsave(&vino_drvdata->vino_lock, flags2);
+
+       vino_dma_stop(vcs);
+       vino_clear_interrupt(vcs);
+
+       spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags2);
+
+       /* remove all items from the queue */
+       if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) {
+               dprintk("vino_capture_stop(): "
+                       "vino_queue_get_incoming() failed\n");
+               goto out;
+       }
+       while (incoming > 0) {
+               vino_queue_transfer(&vcs->fb_queue);
+
+               if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) {
+                       dprintk("vino_capture_stop(): "
+                               "vino_queue_get_incoming() failed\n");
+                       goto out;
+               }
+       }
+
+       if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+               dprintk("vino_capture_stop(): "
+                       "vino_queue_get_outgoing() failed\n");
+               goto out;
+       }
+       while (outgoing > 0) {
+               vino_queue_remove(&vcs->fb_queue, &id);
+
+               if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+                       dprintk("vino_capture_stop(): "
+                               "vino_queue_get_outgoing() failed\n");
+                       goto out;
+               }
+       }
+
+out:
+       spin_unlock_irqrestore(&vcs->capture_lock, flags);
+}
+
+#if 0
+static int vino_capture_failed(struct vino_channel_settings *vcs)
+{
+       struct vino_framebuffer *fb;
+       unsigned long flags;
+       unsigned int i;
+       int ret;
+
+       dprintk("vino_capture_failed():\n");
+
+       spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
+
+       vino_dma_stop(vcs);
+       vino_clear_interrupt(vcs);
+
+       spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
+
+       ret = vino_queue_get_incoming(&vcs->fb_queue, &i);
+       if (ret == VINO_QUEUE_ERROR) {
+               dprintk("vino_queue_get_incoming() failed\n");
+               return -EINVAL;
+       }
+       if (i == 0) {
+               /* no buffers to process */
+               return 0;
+       }
+
+       fb = vino_queue_peek(&vcs->fb_queue, &i);
+       if (fb == NULL) {
+               dprintk("vino_queue_peek() failed\n");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&fb->state_lock, flags);
+       if (fb->state == VINO_FRAMEBUFFER_IN_USE) {
+               fb->state = VINO_FRAMEBUFFER_UNUSED;
+               vino_queue_transfer(&vcs->fb_queue);
+               vino_queue_remove(&vcs->fb_queue, &i);
+               /* we should actually discard the newest frame,
+                * but who cares ... */
+       }
+       spin_unlock_irqrestore(&fb->state_lock, flags);
+
+       return 0;
+}
+#endif
+
+static void vino_skip_frame(struct vino_channel_settings *vcs)
+{
+       struct vino_framebuffer *fb;
+       unsigned long flags;
+       unsigned int id;
+
+       spin_lock_irqsave(&vcs->capture_lock, flags);
+       fb = vino_queue_peek(&vcs->fb_queue, &id);
+       if (!fb) {
+               spin_unlock_irqrestore(&vcs->capture_lock, flags);
+               dprintk("vino_skip_frame(): vino_queue_peek() failed!\n");
+               return;
+       }
+       spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+       spin_lock_irqsave(&fb->state_lock, flags);
+       fb->state = VINO_FRAMEBUFFER_UNUSED;
+       spin_unlock_irqrestore(&fb->state_lock, flags);
+
+       vino_capture_next(vcs, 0);
+}
+
+static void vino_frame_done(struct vino_channel_settings *vcs)
+{
+       struct vino_framebuffer *fb;
+       unsigned long flags;
+
+       spin_lock_irqsave(&vcs->capture_lock, flags);
+       fb = vino_queue_transfer(&vcs->fb_queue);
+       if (!fb) {
+               spin_unlock_irqrestore(&vcs->capture_lock, flags);
+               dprintk("vino_frame_done(): vino_queue_transfer() failed!\n");
+               return;
+       }
+       spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+       fb->frame_counter = vcs->int_data.frame_counter;
+       memcpy(&fb->timestamp, &vcs->int_data.timestamp,
+              sizeof(struct timeval));
+
+       spin_lock_irqsave(&fb->state_lock, flags);
+       if (fb->state == VINO_FRAMEBUFFER_IN_USE)
+               fb->state = VINO_FRAMEBUFFER_READY;
+       spin_unlock_irqrestore(&fb->state_lock, flags);
+
+       wake_up(&vcs->fb_queue.frame_wait_queue);
+
+       vino_capture_next(vcs, 0);
+}
+
+static void vino_capture_tasklet(unsigned long channel) {
+       struct vino_channel_settings *vcs;
+
+       vcs = (channel == VINO_CHANNEL_A)
+               ? &vino_drvdata->a : &vino_drvdata->b;
+
+       if (vcs->int_data.skip)
+               vcs->int_data.skip_count++;
+
+       if (vcs->int_data.skip && (vcs->int_data.skip_count
+                                  <= VINO_MAX_FRAME_SKIP_COUNT)) {
+               vino_skip_frame(vcs);
+       } else {
+               vcs->int_data.skip_count = 0;
+               vino_frame_done(vcs);
+       }
+}
+
+static irqreturn_t vino_interrupt(int irq, void *dev_id)
+{
+       u32 ctrl, intr;
+       unsigned int fc_a, fc_b;
+       int handled_a = 0, skip_a = 0, done_a = 0;
+       int handled_b = 0, skip_b = 0, done_b = 0;
+
+#ifdef VINO_DEBUG_INT
+       int loop = 0;
+       unsigned int line_count = vino->a.line_count,
+               page_index = vino->a.page_index,
+               field_counter = vino->a.field_counter,
+               start_desc_tbl = vino->a.start_desc_tbl,
+               next_4_desc = vino->a.next_4_desc;
+       unsigned int line_count_2,
+               page_index_2,
+               field_counter_2,
+               start_desc_tbl_2,
+               next_4_desc_2;
+#endif
+
+       spin_lock(&vino_drvdata->vino_lock);
+
+       while ((intr = vino->intr_status)) {
+               fc_a = vino->a.field_counter >> 1;
+               fc_b = vino->b.field_counter >> 1;
+
+               /* handle error-interrupts in some special way ?
+                * --> skips frames */
+               if (intr & VINO_INTSTAT_A) {
+                       if (intr & VINO_INTSTAT_A_EOF) {
+                               vino_drvdata->a.field++;
+                               if (vino_drvdata->a.field > 1) {
+                                       vino_dma_stop(&vino_drvdata->a);
+                                       vino_clear_interrupt(&vino_drvdata->a);
+                                       vino_drvdata->a.field = 0;
+                                       done_a = 1;
+                               } else {
+                                       if (vino->a.page_index
+                                           != vino_drvdata->a.line_size) {
+                                               vino->a.line_count = 0;
+                                               vino->a.page_index =
+                                                       vino_drvdata->
+                                                       a.line_size;
+                                               vino->a.next_4_desc =
+                                                       vino->a.start_desc_tbl;
+                                       }
+                               }
+                               dprintk("channel A end-of-field "
+                                       "interrupt: %04x\n", intr);
+                       } else {
+                               vino_dma_stop(&vino_drvdata->a);
+                               vino_clear_interrupt(&vino_drvdata->a);
+                               vino_drvdata->a.field = 0;
+                               skip_a = 1;
+                               dprintk("channel A error interrupt: %04x\n",
+                                       intr);
+                       }
+
+#ifdef VINO_DEBUG_INT
+                       line_count_2 = vino->a.line_count;
+                       page_index_2 = vino->a.page_index;
+                       field_counter_2 = vino->a.field_counter;
+                       start_desc_tbl_2 = vino->a.start_desc_tbl;
+                       next_4_desc_2 = vino->a.next_4_desc;
+
+                       printk("intr = %04x, loop = %d, field = %d\n",
+                              intr, loop, vino_drvdata->a.field);
+                       printk("1- line count = %04d, page index = %04d, "
+                              "start = %08x, next = %08x\n"
+                              "   fieldc = %d, framec = %d\n",
+                              line_count, page_index, start_desc_tbl,
+                              next_4_desc, field_counter, fc_a);
+                       printk("12-line count = %04d, page index = %04d, "
+                              "   start = %08x, next = %08x\n",
+                              line_count_2, page_index_2, start_desc_tbl_2,
+                              next_4_desc_2);
+
+                       if (done_a)
+                               printk("\n");
+#endif
+               }
+
+               if (intr & VINO_INTSTAT_B) {
+                       if (intr & VINO_INTSTAT_B_EOF) {
+                               vino_drvdata->b.field++;
+                               if (vino_drvdata->b.field > 1) {
+                                       vino_dma_stop(&vino_drvdata->b);
+                                       vino_clear_interrupt(&vino_drvdata->b);
+                                       vino_drvdata->b.field = 0;
+                                       done_b = 1;
+                               }
+                               dprintk("channel B end-of-field "
+                                       "interrupt: %04x\n", intr);
+                       } else {
+                               vino_dma_stop(&vino_drvdata->b);
+                               vino_clear_interrupt(&vino_drvdata->b);
+                               vino_drvdata->b.field = 0;
+                               skip_b = 1;
+                               dprintk("channel B error interrupt: %04x\n",
+                                       intr);
+                       }
+               }
+
+               /* Always remember to clear interrupt status.
+                * Disable VINO interrupts while we do this. */
+               ctrl = vino->control;
+               vino->control = ctrl & ~(VINO_CTRL_A_INT | VINO_CTRL_B_INT);
+               vino->intr_status = ~intr;
+               vino->control = ctrl;
+
+               spin_unlock(&vino_drvdata->vino_lock);
+
+               if ((!handled_a) && (done_a || skip_a)) {
+                       if (!skip_a) {
+                               v4l2_get_timestamp(
+                                       &vino_drvdata->a.int_data.timestamp);
+                               vino_drvdata->a.int_data.frame_counter = fc_a;
+                       }
+                       vino_drvdata->a.int_data.skip = skip_a;
+
+                       dprintk("channel A %s, interrupt: %d\n",
+                               skip_a ? "skipping frame" : "frame done",
+                               intr);
+                       tasklet_hi_schedule(&vino_tasklet_a);
+                       handled_a = 1;
+               }
+
+               if ((!handled_b) && (done_b || skip_b)) {
+                       if (!skip_b) {
+                               v4l2_get_timestamp(
+                                       &vino_drvdata->b.int_data.timestamp);
+                               vino_drvdata->b.int_data.frame_counter = fc_b;
+                       }
+                       vino_drvdata->b.int_data.skip = skip_b;
+
+                       dprintk("channel B %s, interrupt: %d\n",
+                               skip_b ? "skipping frame" : "frame done",
+                               intr);
+                       tasklet_hi_schedule(&vino_tasklet_b);
+                       handled_b = 1;
+               }
+
+#ifdef VINO_DEBUG_INT
+               loop++;
+#endif
+               spin_lock(&vino_drvdata->vino_lock);
+       }
+
+       spin_unlock(&vino_drvdata->vino_lock);
+
+       return IRQ_HANDLED;
+}
+
+/* VINO video input management */
+
+static int vino_get_saa7191_input(int input)
+{
+       switch (input) {
+       case VINO_INPUT_COMPOSITE:
+               return SAA7191_INPUT_COMPOSITE;
+       case VINO_INPUT_SVIDEO:
+               return SAA7191_INPUT_SVIDEO;
+       default:
+               printk(KERN_ERR "VINO: vino_get_saa7191_input(): "
+                      "invalid input!\n");
+               return -1;
+       }
+}
+
+/* execute with input_lock locked */
+static int vino_is_input_owner(struct vino_channel_settings *vcs)
+{
+       switch(vcs->input) {
+       case VINO_INPUT_COMPOSITE:
+       case VINO_INPUT_SVIDEO:
+               return vino_drvdata->decoder_owner == vcs->channel;
+       case VINO_INPUT_D1:
+               return vino_drvdata->camera_owner == vcs->channel;
+       default:
+               return 0;
+       }
+}
+
+static int vino_acquire_input(struct vino_channel_settings *vcs)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       dprintk("vino_acquire_input():\n");
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+       /* First try D1 and then SAA7191 */
+       if (vino_drvdata->camera
+           && (vino_drvdata->camera_owner == VINO_NO_CHANNEL)) {
+               vino_drvdata->camera_owner = vcs->channel;
+               vcs->input = VINO_INPUT_D1;
+               vcs->data_norm = VINO_DATA_NORM_D1;
+       } else if (vino_drvdata->decoder
+                  && (vino_drvdata->decoder_owner == VINO_NO_CHANNEL)) {
+               int input;
+               int data_norm = 0;
+               v4l2_std_id norm;
+
+               input = VINO_INPUT_COMPOSITE;
+
+               ret = decoder_call(video, s_routing,
+                               vino_get_saa7191_input(input), 0, 0);
+               if (ret) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+               /* Don't hold spinlocks while auto-detecting norm
+                * as it may take a while... */
+
+               ret = decoder_call(video, querystd, &norm);
+               if (!ret) {
+                       for (data_norm = 0; data_norm < 3; data_norm++) {
+                               if (vino_data_norms[data_norm].std & norm)
+                                       break;
+                       }
+                       if (data_norm == 3)
+                               data_norm = VINO_DATA_NORM_PAL;
+                       ret = decoder_call(video, s_std, norm);
+               }
+
+               spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+               if (ret) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               vino_drvdata->decoder_owner = vcs->channel;
+
+               vcs->input = input;
+               vcs->data_norm = data_norm;
+       } else {
+               vcs->input = (vcs->channel == VINO_CHANNEL_A) ?
+                       vino_drvdata->b.input : vino_drvdata->a.input;
+               vcs->data_norm = (vcs->channel == VINO_CHANNEL_A) ?
+                       vino_drvdata->b.data_norm : vino_drvdata->a.data_norm;
+       }
+
+       if (vcs->input == VINO_INPUT_NONE) {
+               ret = -ENODEV;
+               goto out;
+       }
+
+       vino_set_default_clipping(vcs);
+       vino_set_default_scaling(vcs);
+       vino_set_default_framerate(vcs);
+
+       dprintk("vino_acquire_input(): %s\n", vino_inputs[vcs->input].name);
+
+out:
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+       return ret;
+}
+
+static int vino_set_input(struct vino_channel_settings *vcs, int input)
+{
+       struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ?
+               &vino_drvdata->b : &vino_drvdata->a;
+       unsigned long flags;
+       int ret = 0;
+
+       dprintk("vino_set_input():\n");
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+       if (vcs->input == input)
+               goto out;
+
+       switch (input) {
+       case VINO_INPUT_COMPOSITE:
+       case VINO_INPUT_SVIDEO:
+               if (!vino_drvdata->decoder) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               if (vino_drvdata->decoder_owner == VINO_NO_CHANNEL) {
+                       vino_drvdata->decoder_owner = vcs->channel;
+               }
+
+               if (vino_drvdata->decoder_owner == vcs->channel) {
+                       int data_norm = 0;
+                       v4l2_std_id norm;
+
+                       ret = decoder_call(video, s_routing,
+                                       vino_get_saa7191_input(input), 0, 0);
+                       if (ret) {
+                               vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+                               ret = -EINVAL;
+                               goto out;
+                       }
+
+                       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+                       /* Don't hold spinlocks while auto-detecting norm
+                        * as it may take a while... */
+
+                       ret = decoder_call(video, querystd, &norm);
+                       if (!ret) {
+                               for (data_norm = 0; data_norm < 3; data_norm++) {
+                                       if (vino_data_norms[data_norm].std & norm)
+                                               break;
+                               }
+                               if (data_norm == 3)
+                                       data_norm = VINO_DATA_NORM_PAL;
+                               ret = decoder_call(video, s_std, norm);
+                       }
+
+                       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+                       if (ret) {
+                               vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+                               ret = -EINVAL;
+                               goto out;
+                       }
+
+                       vcs->input = input;
+                       vcs->data_norm = data_norm;
+               } else {
+                       if (input != vcs2->input) {
+                               ret = -EBUSY;
+                               goto out;
+                       }
+
+                       vcs->input = input;
+                       vcs->data_norm = vcs2->data_norm;
+               }
+
+               if (vino_drvdata->camera_owner == vcs->channel) {
+                       /* Transfer the ownership or release the input */
+                       if (vcs2->input == VINO_INPUT_D1) {
+                               vino_drvdata->camera_owner = vcs2->channel;
+                       } else {
+                               vino_drvdata->camera_owner = VINO_NO_CHANNEL;
+                       }
+               }
+               break;
+       case VINO_INPUT_D1:
+               if (!vino_drvdata->camera) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               if (vino_drvdata->camera_owner == VINO_NO_CHANNEL)
+                       vino_drvdata->camera_owner = vcs->channel;
+
+               if (vino_drvdata->decoder_owner == vcs->channel) {
+                       /* Transfer the ownership or release the input */
+                       if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
+                                (vcs2->input == VINO_INPUT_SVIDEO)) {
+                               vino_drvdata->decoder_owner = vcs2->channel;
+                       } else {
+                               vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+                       }
+               }
+
+               vcs->input = input;
+               vcs->data_norm = VINO_DATA_NORM_D1;
+               break;
+       default:
+               ret = -EINVAL;
+               goto out;
+       }
+
+       vino_set_default_clipping(vcs);
+       vino_set_default_scaling(vcs);
+       vino_set_default_framerate(vcs);
+
+       dprintk("vino_set_input(): %s\n", vino_inputs[vcs->input].name);
+
+out:
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+       return ret;
+}
+
+static void vino_release_input(struct vino_channel_settings *vcs)
+{
+       struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ?
+               &vino_drvdata->b : &vino_drvdata->a;
+       unsigned long flags;
+
+       dprintk("vino_release_input():\n");
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+       /* Release ownership of the channel
+        * and if the other channel takes input from
+        * the same source, transfer the ownership */
+       if (vino_drvdata->camera_owner == vcs->channel) {
+               if (vcs2->input == VINO_INPUT_D1) {
+                       vino_drvdata->camera_owner = vcs2->channel;
+               } else {
+                       vino_drvdata->camera_owner = VINO_NO_CHANNEL;
+               }
+       } else if (vino_drvdata->decoder_owner == vcs->channel) {
+               if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
+                        (vcs2->input == VINO_INPUT_SVIDEO)) {
+                       vino_drvdata->decoder_owner = vcs2->channel;
+               } else {
+                       vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+               }
+       }
+       vcs->input = VINO_INPUT_NONE;
+
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+}
+
+/* execute with input_lock locked */
+static int vino_set_data_norm(struct vino_channel_settings *vcs,
+                             unsigned int data_norm,
+                             unsigned long *flags)
+{
+       int err = 0;
+
+       if (data_norm == vcs->data_norm)
+               return 0;
+
+       switch (vcs->input) {
+       case VINO_INPUT_D1:
+               /* only one "norm" supported */
+               if (data_norm != VINO_DATA_NORM_D1)
+                       return -EINVAL;
+               break;
+       case VINO_INPUT_COMPOSITE:
+       case VINO_INPUT_SVIDEO: {
+               v4l2_std_id norm;
+
+               if ((data_norm != VINO_DATA_NORM_PAL)
+                   && (data_norm != VINO_DATA_NORM_NTSC)
+                   && (data_norm != VINO_DATA_NORM_SECAM))
+                       return -EINVAL;
+
+               spin_unlock_irqrestore(&vino_drvdata->input_lock, *flags);
+
+               /* Don't hold spinlocks while setting norm
+                * as it may take a while... */
+
+               norm = vino_data_norms[data_norm].std;
+               err = decoder_call(video, s_std, norm);
+
+               spin_lock_irqsave(&vino_drvdata->input_lock, *flags);
+
+               if (err)
+                       goto out;
+
+               vcs->data_norm = data_norm;
+
+               vino_set_default_clipping(vcs);
+               vino_set_default_scaling(vcs);
+               vino_set_default_framerate(vcs);
+               break;
+       }
+       default:
+               return -EINVAL;
+       }
+
+out:
+       return err;
+}
+
+/* V4L2 helper functions */
+
+static int vino_find_data_format(__u32 pixelformat)
+{
+       int i;
+
+       for (i = 0; i < VINO_DATA_FMT_COUNT; i++) {
+               if (vino_data_formats[i].pixelformat == pixelformat)
+                       return i;
+       }
+
+       return VINO_DATA_FMT_NONE;
+}
+
+static int vino_int_enum_input(struct vino_channel_settings *vcs, __u32 index)
+{
+       int input = VINO_INPUT_NONE;
+       unsigned long flags;
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+       if (vino_drvdata->decoder && vino_drvdata->camera) {
+               switch (index) {
+               case 0:
+                       input = VINO_INPUT_COMPOSITE;
+                       break;
+               case 1:
+                       input = VINO_INPUT_SVIDEO;
+                       break;
+               case 2:
+                       input = VINO_INPUT_D1;
+                       break;
+               }
+       } else if (vino_drvdata->decoder) {
+               switch (index) {
+               case 0:
+                       input = VINO_INPUT_COMPOSITE;
+                       break;
+               case 1:
+                       input = VINO_INPUT_SVIDEO;
+                       break;
+               }
+       } else if (vino_drvdata->camera) {
+               switch (index) {
+               case 0:
+                       input = VINO_INPUT_D1;
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+       return input;
+}
+
+/* execute with input_lock locked */
+static __u32 vino_find_input_index(struct vino_channel_settings *vcs)
+{
+       __u32 index = 0;
+       // FIXME: detect when no inputs available
+
+       if (vino_drvdata->decoder && vino_drvdata->camera) {
+               switch (vcs->input) {
+               case VINO_INPUT_COMPOSITE:
+                       index = 0;
+                       break;
+               case VINO_INPUT_SVIDEO:
+                       index = 1;
+                       break;
+               case VINO_INPUT_D1:
+                       index = 2;
+                       break;
+               }
+       } else if (vino_drvdata->decoder) {
+               switch (vcs->input) {
+               case VINO_INPUT_COMPOSITE:
+                       index = 0;
+                       break;
+               case VINO_INPUT_SVIDEO:
+                       index = 1;
+                       break;
+               }
+       } else if (vino_drvdata->camera) {
+               switch (vcs->input) {
+               case VINO_INPUT_D1:
+                       index = 0;
+                       break;
+               }
+       }
+
+       return index;
+}
+
+/* V4L2 ioctls */
+
+static int vino_querycap(struct file *file, void *__fh,
+               struct v4l2_capability *cap)
+{
+       memset(cap, 0, sizeof(struct v4l2_capability));
+
+       strcpy(cap->driver, vino_driver_name);
+       strcpy(cap->card, vino_driver_description);
+       strcpy(cap->bus_info, vino_bus_name);
+       cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+       return 0;
+}
+
+static int vino_enum_input(struct file *file, void *__fh,
+                              struct v4l2_input *i)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       __u32 index = i->index;
+       int input;
+       dprintk("requested index = %d\n", index);
+
+       input = vino_int_enum_input(vcs, index);
+       if (input == VINO_INPUT_NONE)
+               return -EINVAL;
+
+       i->type = V4L2_INPUT_TYPE_CAMERA;
+       i->std = vino_inputs[input].std;
+       strcpy(i->name, vino_inputs[input].name);
+
+       if (input == VINO_INPUT_COMPOSITE || input == VINO_INPUT_SVIDEO)
+               decoder_call(video, g_input_status, &i->status);
+       return 0;
+}
+
+static int vino_g_input(struct file *file, void *__fh,
+                            unsigned int *i)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       __u32 index;
+       int input;
+       unsigned long flags;
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+       input = vcs->input;
+       index = vino_find_input_index(vcs);
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+       dprintk("input = %d\n", input);
+
+       if (input == VINO_INPUT_NONE) {
+               return -EINVAL;
+       }
+
+       *i = index;
+
+       return 0;
+}
+
+static int vino_s_input(struct file *file, void *__fh,
+                            unsigned int i)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       int input;
+       dprintk("requested input = %d\n", i);
+
+       input = vino_int_enum_input(vcs, i);
+       if (input == VINO_INPUT_NONE)
+               return -EINVAL;
+
+       return vino_set_input(vcs, input);
+}
+
+static int vino_querystd(struct file *file, void *__fh,
+                             v4l2_std_id *std)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       unsigned long flags;
+       int err = 0;
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+       switch (vcs->input) {
+       case VINO_INPUT_D1:
+               *std = vino_inputs[vcs->input].std;
+               break;
+       case VINO_INPUT_COMPOSITE:
+       case VINO_INPUT_SVIDEO: {
+               decoder_call(video, querystd, std);
+               break;
+       }
+       default:
+               err = -EINVAL;
+       }
+
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+       return err;
+}
+
+static int vino_g_std(struct file *file, void *__fh,
+                          v4l2_std_id *std)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       unsigned long flags;
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+       *std = vino_data_norms[vcs->data_norm].std;
+       dprintk("current standard = %d\n", vcs->data_norm);
+
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+       return 0;
+}
+
+static int vino_s_std(struct file *file, void *__fh,
+                          v4l2_std_id std)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+       if (!vino_is_input_owner(vcs)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       /* check if the standard is valid for the current input */
+       if (std & vino_inputs[vcs->input].std) {
+               dprintk("standard accepted\n");
+
+               /* change the video norm for SAA7191
+                * and accept NTSC for D1 (do nothing) */
+
+               if (vcs->input == VINO_INPUT_D1)
+                       goto out;
+
+               if (std & V4L2_STD_PAL) {
+                       ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL,
+                                                &flags);
+               } else if (std & V4L2_STD_NTSC) {
+                       ret = vino_set_data_norm(vcs, VINO_DATA_NORM_NTSC,
+                                                &flags);
+               } else if (std & V4L2_STD_SECAM) {
+                       ret = vino_set_data_norm(vcs, VINO_DATA_NORM_SECAM,
+                                                &flags);
+               } else {
+                       ret = -EINVAL;
+               }
+
+               if (ret) {
+                       ret = -EINVAL;
+               }
+       } else {
+               ret = -EINVAL;
+       }
+
+out:
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+       return ret;
+}
+
+static int vino_enum_fmt_vid_cap(struct file *file, void *__fh,
+                             struct v4l2_fmtdesc *fd)
+{
+       dprintk("format index = %d\n", fd->index);
+
+       if (fd->index >= VINO_DATA_FMT_COUNT)
+               return -EINVAL;
+       dprintk("format name = %s\n", vino_data_formats[fd->index].description);
+
+       fd->pixelformat = vino_data_formats[fd->index].pixelformat;
+       strcpy(fd->description, vino_data_formats[fd->index].description);
+       return 0;
+}
+
+static int vino_try_fmt_vid_cap(struct file *file, void *__fh,
+                            struct v4l2_format *f)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       struct vino_channel_settings tempvcs;
+       unsigned long flags;
+       struct v4l2_pix_format *pf = &f->fmt.pix;
+
+       dprintk("requested: w = %d, h = %d\n",
+                       pf->width, pf->height);
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+       memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings));
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+       tempvcs.data_format = vino_find_data_format(pf->pixelformat);
+       if (tempvcs.data_format == VINO_DATA_FMT_NONE) {
+               tempvcs.data_format = VINO_DATA_FMT_GREY;
+               pf->pixelformat =
+                       vino_data_formats[tempvcs.data_format].
+                       pixelformat;
+       }
+
+       /* data format must be set before clipping/scaling */
+       vino_set_scaling(&tempvcs, pf->width, pf->height);
+
+       dprintk("data format = %s\n",
+                       vino_data_formats[tempvcs.data_format].description);
+
+       pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) /
+               tempvcs.decimation;
+       pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) /
+               tempvcs.decimation;
+
+       pf->field = V4L2_FIELD_INTERLACED;
+       pf->bytesperline = tempvcs.line_size;
+       pf->sizeimage = tempvcs.line_size *
+               (tempvcs.clipping.bottom - tempvcs.clipping.top) /
+               tempvcs.decimation;
+       pf->colorspace =
+               vino_data_formats[tempvcs.data_format].colorspace;
+
+       return 0;
+}
+
+static int vino_g_fmt_vid_cap(struct file *file, void *__fh,
+                          struct v4l2_format *f)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       unsigned long flags;
+       struct v4l2_pix_format *pf = &f->fmt.pix;
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+       pf->width = (vcs->clipping.right - vcs->clipping.left) /
+               vcs->decimation;
+       pf->height = (vcs->clipping.bottom - vcs->clipping.top) /
+               vcs->decimation;
+       pf->pixelformat =
+               vino_data_formats[vcs->data_format].pixelformat;
+
+       pf->field = V4L2_FIELD_INTERLACED;
+       pf->bytesperline = vcs->line_size;
+       pf->sizeimage = vcs->line_size *
+               (vcs->clipping.bottom - vcs->clipping.top) /
+               vcs->decimation;
+       pf->colorspace =
+               vino_data_formats[vcs->data_format].colorspace;
+
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+       return 0;
+}
+
+static int vino_s_fmt_vid_cap(struct file *file, void *__fh,
+                          struct v4l2_format *f)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       int data_format;
+       unsigned long flags;
+       struct v4l2_pix_format *pf = &f->fmt.pix;
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+       data_format = vino_find_data_format(pf->pixelformat);
+
+       if (data_format == VINO_DATA_FMT_NONE) {
+               vcs->data_format = VINO_DATA_FMT_GREY;
+               pf->pixelformat =
+                       vino_data_formats[vcs->data_format].
+                       pixelformat;
+       } else {
+               vcs->data_format = data_format;
+       }
+
+       /* data format must be set before clipping/scaling */
+       vino_set_scaling(vcs, pf->width, pf->height);
+
+       dprintk("data format = %s\n",
+              vino_data_formats[vcs->data_format].description);
+
+       pf->width = vcs->clipping.right - vcs->clipping.left;
+       pf->height = vcs->clipping.bottom - vcs->clipping.top;
+
+       pf->field = V4L2_FIELD_INTERLACED;
+       pf->bytesperline = vcs->line_size;
+       pf->sizeimage = vcs->line_size *
+               (vcs->clipping.bottom - vcs->clipping.top) /
+               vcs->decimation;
+       pf->colorspace =
+               vino_data_formats[vcs->data_format].colorspace;
+
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+       return 0;
+}
+
+static int vino_cropcap(struct file *file, void *__fh,
+                            struct v4l2_cropcap *ccap)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       const struct vino_data_norm *norm;
+       unsigned long flags;
+
+       switch (ccap->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+               norm = &vino_data_norms[vcs->data_norm];
+
+               spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+               ccap->bounds.left = 0;
+               ccap->bounds.top = 0;
+               ccap->bounds.width = norm->width;
+               ccap->bounds.height = norm->height;
+               memcpy(&ccap->defrect, &ccap->bounds,
+                      sizeof(struct v4l2_rect));
+
+               ccap->pixelaspect.numerator = 1;
+               ccap->pixelaspect.denominator = 1;
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int vino_g_crop(struct file *file, void *__fh,
+                           struct v4l2_crop *c)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       unsigned long flags;
+
+       switch (c->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+               c->c.left = vcs->clipping.left;
+               c->c.top = vcs->clipping.top;
+               c->c.width = vcs->clipping.right - vcs->clipping.left;
+               c->c.height = vcs->clipping.bottom - vcs->clipping.top;
+
+               spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int vino_s_crop(struct file *file, void *__fh,
+                           const struct v4l2_crop *c)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       unsigned long flags;
+
+       switch (c->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+               vino_set_clipping(vcs, c->c.left, c->c.top,
+                                 c->c.width, c->c.height);
+
+               spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int vino_g_parm(struct file *file, void *__fh,
+                           struct v4l2_streamparm *sp)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       unsigned long flags;
+       struct v4l2_captureparm *cp = &sp->parm.capture;
+
+       cp->capability = V4L2_CAP_TIMEPERFRAME;
+       cp->timeperframe.numerator = 1;
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+       cp->timeperframe.denominator = vcs->fps;
+
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+       /* TODO: cp->readbuffers = xxx; */
+
+       return 0;
+}
+
+static int vino_s_parm(struct file *file, void *__fh,
+                           struct v4l2_streamparm *sp)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       unsigned long flags;
+       struct v4l2_captureparm *cp = &sp->parm.capture;
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+       if ((cp->timeperframe.numerator == 0) ||
+           (cp->timeperframe.denominator == 0)) {
+               /* reset framerate */
+               vino_set_default_framerate(vcs);
+       } else {
+               vino_set_framerate(vcs, cp->timeperframe.denominator /
+                                  cp->timeperframe.numerator);
+       }
+
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+       return 0;
+}
+
+static int vino_reqbufs(struct file *file, void *__fh,
+                            struct v4l2_requestbuffers *rb)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+
+       if (vcs->reading)
+               return -EBUSY;
+
+       /* TODO: check queue type */
+       if (rb->memory != V4L2_MEMORY_MMAP) {
+               dprintk("type not mmap\n");
+               return -EINVAL;
+       }
+
+       dprintk("count = %d\n", rb->count);
+       if (rb->count > 0) {
+               if (vino_is_capturing(vcs)) {
+                       dprintk("busy, capturing\n");
+                       return -EBUSY;
+               }
+
+               if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) {
+                       dprintk("busy, buffers still mapped\n");
+                       return -EBUSY;
+               } else {
+                       vcs->streaming = 0;
+                       vino_queue_free(&vcs->fb_queue);
+                       vino_queue_init(&vcs->fb_queue, &rb->count);
+               }
+       } else {
+               vcs->streaming = 0;
+               vino_capture_stop(vcs);
+               vino_queue_free(&vcs->fb_queue);
+       }
+
+       return 0;
+}
+
+static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs,
+                                       struct vino_framebuffer *fb,
+                                       struct v4l2_buffer *b)
+{
+       if (vino_queue_outgoing_contains(&vcs->fb_queue,
+                                        fb->id)) {
+               b->flags &= ~V4L2_BUF_FLAG_QUEUED;
+               b->flags |= V4L2_BUF_FLAG_DONE;
+       } else if (vino_queue_incoming_contains(&vcs->fb_queue,
+                                      fb->id)) {
+               b->flags &= ~V4L2_BUF_FLAG_DONE;
+               b->flags |= V4L2_BUF_FLAG_QUEUED;
+       } else {
+               b->flags &= ~(V4L2_BUF_FLAG_DONE |
+                             V4L2_BUF_FLAG_QUEUED);
+       }
+
+       b->flags &= ~(V4L2_BUF_FLAG_TIMECODE);
+
+       if (fb->map_count > 0)
+               b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+       b->flags &= ~V4L2_BUF_FLAG_TIMESTAMP_MASK;
+       b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+       b->index = fb->id;
+       b->memory = (vcs->fb_queue.type == VINO_MEMORY_MMAP) ?
+               V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR;
+       b->m.offset = fb->offset;
+       b->bytesused = fb->data_size;
+       b->length = fb->size;
+       b->field = V4L2_FIELD_INTERLACED;
+       b->sequence = fb->frame_counter;
+       memcpy(&b->timestamp, &fb->timestamp,
+              sizeof(struct timeval));
+       // b->input ?
+
+       dprintk("buffer %d: length = %d, bytesused = %d, offset = %d\n",
+               fb->id, fb->size, fb->data_size, fb->offset);
+}
+
+static int vino_querybuf(struct file *file, void *__fh,
+                             struct v4l2_buffer *b)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       struct vino_framebuffer *fb;
+
+       if (vcs->reading)
+               return -EBUSY;
+
+       /* TODO: check queue type */
+       if (b->index >= vino_queue_get_length(&vcs->fb_queue)) {
+               dprintk("invalid index = %d\n",
+                      b->index);
+               return -EINVAL;
+       }
+
+       fb = vino_queue_get_buffer(&vcs->fb_queue,
+                                  b->index);
+       if (fb == NULL) {
+               dprintk("vino_queue_get_buffer() failed");
+               return -EINVAL;
+       }
+
+       vino_v4l2_get_buffer_status(vcs, fb, b);
+
+       return 0;
+}
+
+static int vino_qbuf(struct file *file, void *__fh,
+                         struct v4l2_buffer *b)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       struct vino_framebuffer *fb;
+       int ret;
+
+       if (vcs->reading)
+               return -EBUSY;
+
+       /* TODO: check queue type */
+       if (b->memory != V4L2_MEMORY_MMAP) {
+               dprintk("type not mmap\n");
+               return -EINVAL;
+       }
+
+       fb = vino_capture_enqueue(vcs, b->index);
+       if (fb == NULL)
+               return -EINVAL;
+
+       vino_v4l2_get_buffer_status(vcs, fb, b);
+
+       if (vcs->streaming) {
+               ret = vino_capture_next(vcs, 1);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int vino_dqbuf(struct file *file, void *__fh,
+                          struct v4l2_buffer *b)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       unsigned int nonblocking = file->f_flags & O_NONBLOCK;
+       struct vino_framebuffer *fb;
+       unsigned int incoming, outgoing;
+       int err;
+
+       if (vcs->reading)
+               return -EBUSY;
+
+       /* TODO: check queue type */
+
+       err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
+       if (err) {
+               dprintk("vino_queue_get_incoming() failed\n");
+               return -EINVAL;
+       }
+       err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing);
+       if (err) {
+               dprintk("vino_queue_get_outgoing() failed\n");
+               return -EINVAL;
+       }
+
+       dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing);
+
+       if (outgoing == 0) {
+               if (incoming == 0) {
+                       dprintk("no incoming or outgoing buffers\n");
+                       return -EINVAL;
+               }
+               if (nonblocking) {
+                       dprintk("non-blocking I/O was selected and "
+                               "there are no buffers to dequeue\n");
+                       return -EAGAIN;
+               }
+
+               err = vino_wait_for_frame(vcs);
+               if (err) {
+                       err = vino_wait_for_frame(vcs);
+                       if (err) {
+                               /* interrupted or no frames captured because of
+                                * frame skipping */
+                               /* vino_capture_failed(vcs); */
+                               return -EIO;
+                       }
+               }
+       }
+
+       fb = vino_queue_remove(&vcs->fb_queue, &b->index);
+       if (fb == NULL) {
+               dprintk("vino_queue_remove() failed\n");
+               return -EINVAL;
+       }
+
+       err = vino_check_buffer(vcs, fb);
+
+       vino_v4l2_get_buffer_status(vcs, fb, b);
+
+       if (err)
+               return -EIO;
+
+       return 0;
+}
+
+static int vino_streamon(struct file *file, void *__fh,
+               enum v4l2_buf_type i)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       unsigned int incoming;
+       int ret;
+       if (vcs->reading)
+               return -EBUSY;
+
+       if (vcs->streaming)
+               return 0;
+
+       // TODO: check queue type
+
+       if (vino_queue_get_length(&vcs->fb_queue) < 1) {
+               dprintk("no buffers allocated\n");
+               return -EINVAL;
+       }
+
+       ret = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
+       if (ret) {
+               dprintk("vino_queue_get_incoming() failed\n");
+               return -EINVAL;
+       }
+
+       vcs->streaming = 1;
+
+       if (incoming > 0) {
+               ret = vino_capture_next(vcs, 1);
+               if (ret) {
+                       vcs->streaming = 0;
+
+                       dprintk("couldn't start capture\n");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int vino_streamoff(struct file *file, void *__fh,
+               enum v4l2_buf_type i)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       if (vcs->reading)
+               return -EBUSY;
+
+       if (!vcs->streaming)
+               return 0;
+
+       vcs->streaming = 0;
+       vino_capture_stop(vcs);
+
+       return 0;
+}
+
+static int vino_queryctrl(struct file *file, void *__fh,
+                              struct v4l2_queryctrl *queryctrl)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       unsigned long flags;
+       int i;
+       int err = 0;
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+       switch (vcs->input) {
+       case VINO_INPUT_D1:
+               for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
+                       if (vino_indycam_v4l2_controls[i].id ==
+                           queryctrl->id) {
+                               memcpy(queryctrl,
+                                      &vino_indycam_v4l2_controls[i],
+                                      sizeof(struct v4l2_queryctrl));
+                               queryctrl->reserved[0] = 0;
+                               goto found;
+                       }
+               }
+
+               err =  -EINVAL;
+               break;
+       case VINO_INPUT_COMPOSITE:
+       case VINO_INPUT_SVIDEO:
+               for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
+                       if (vino_saa7191_v4l2_controls[i].id ==
+                           queryctrl->id) {
+                               memcpy(queryctrl,
+                                      &vino_saa7191_v4l2_controls[i],
+                                      sizeof(struct v4l2_queryctrl));
+                               queryctrl->reserved[0] = 0;
+                               goto found;
+                       }
+               }
+
+               err =  -EINVAL;
+               break;
+       default:
+               err =  -EINVAL;
+       }
+
+ found:
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+       return err;
+}
+
+static int vino_g_ctrl(struct file *file, void *__fh,
+                           struct v4l2_control *control)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       unsigned long flags;
+       int i;
+       int err = 0;
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+       switch (vcs->input) {
+       case VINO_INPUT_D1: {
+               err = -EINVAL;
+               for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
+                       if (vino_indycam_v4l2_controls[i].id == control->id) {
+                               err = 0;
+                               break;
+                       }
+               }
+
+               if (err)
+                       goto out;
+
+               err = camera_call(core, g_ctrl, control);
+               if (err)
+                       err = -EINVAL;
+               break;
+       }
+       case VINO_INPUT_COMPOSITE:
+       case VINO_INPUT_SVIDEO: {
+               err = -EINVAL;
+               for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
+                       if (vino_saa7191_v4l2_controls[i].id == control->id) {
+                               err = 0;
+                               break;
+                       }
+               }
+
+               if (err)
+                       goto out;
+
+               err = decoder_call(core, g_ctrl, control);
+               if (err)
+                       err = -EINVAL;
+               break;
+       }
+       default:
+               err =  -EINVAL;
+       }
+
+out:
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+       return err;
+}
+
+static int vino_s_ctrl(struct file *file, void *__fh,
+                           struct v4l2_control *control)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       unsigned long flags;
+       int i;
+       int err = 0;
+
+       spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+       if (!vino_is_input_owner(vcs)) {
+               err = -EBUSY;
+               goto out;
+       }
+
+       switch (vcs->input) {
+       case VINO_INPUT_D1: {
+               err = -EINVAL;
+               for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
+                       if (vino_indycam_v4l2_controls[i].id == control->id) {
+                               err = 0;
+                               break;
+                       }
+               }
+               if (err)
+                       goto out;
+               if (control->value < vino_indycam_v4l2_controls[i].minimum ||
+                   control->value > vino_indycam_v4l2_controls[i].maximum) {
+                       err = -ERANGE;
+                       goto out;
+               }
+               err = camera_call(core, s_ctrl, control);
+               if (err)
+                       err = -EINVAL;
+               break;
+       }
+       case VINO_INPUT_COMPOSITE:
+       case VINO_INPUT_SVIDEO: {
+               err = -EINVAL;
+               for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
+                       if (vino_saa7191_v4l2_controls[i].id == control->id) {
+                               err = 0;
+                               break;
+                       }
+               }
+               if (err)
+                       goto out;
+               if (control->value < vino_saa7191_v4l2_controls[i].minimum ||
+                   control->value > vino_saa7191_v4l2_controls[i].maximum) {
+                       err = -ERANGE;
+                       goto out;
+               }
+
+               err = decoder_call(core, s_ctrl, control);
+               if (err)
+                       err = -EINVAL;
+               break;
+       }
+       default:
+               err =  -EINVAL;
+       }
+
+out:
+       spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+       return err;
+}
+
+/* File operations */
+
+static int vino_open(struct file *file)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       int ret = 0;
+       dprintk("open(): channel = %c\n",
+              (vcs->channel == VINO_CHANNEL_A) ? 'A' : 'B');
+
+       mutex_lock(&vcs->mutex);
+
+       if (vcs->users) {
+               dprintk("open(): driver busy\n");
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ret = vino_acquire_input(vcs);
+       if (ret) {
+               dprintk("open(): vino_acquire_input() failed\n");
+               goto out;
+       }
+
+       vcs->users++;
+
+ out:
+       mutex_unlock(&vcs->mutex);
+
+       dprintk("open(): %s!\n", ret ? "failed" : "complete");
+
+       return ret;
+}
+
+static int vino_close(struct file *file)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       dprintk("close():\n");
+
+       mutex_lock(&vcs->mutex);
+
+       vcs->users--;
+
+       if (!vcs->users) {
+               vino_release_input(vcs);
+
+               /* stop DMA and free buffers */
+               vino_capture_stop(vcs);
+               vino_queue_free(&vcs->fb_queue);
+       }
+
+       mutex_unlock(&vcs->mutex);
+
+       return 0;
+}
+
+static void vino_vm_open(struct vm_area_struct *vma)
+{
+       struct vino_framebuffer *fb = vma->vm_private_data;
+
+       fb->map_count++;
+       dprintk("vino_vm_open(): count = %d\n", fb->map_count);
+}
+
+static void vino_vm_close(struct vm_area_struct *vma)
+{
+       struct vino_framebuffer *fb = vma->vm_private_data;
+
+       fb->map_count--;
+       dprintk("vino_vm_close(): count = %d\n", fb->map_count);
+}
+
+static const struct vm_operations_struct vino_vm_ops = {
+       .open   = vino_vm_open,
+       .close  = vino_vm_close,
+};
+
+static int vino_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+
+       unsigned long start = vma->vm_start;
+       unsigned long size = vma->vm_end - vma->vm_start;
+       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+
+       struct vino_framebuffer *fb = NULL;
+       unsigned int i, length;
+       int ret = 0;
+
+       dprintk("mmap():\n");
+
+       // TODO: reject mmap if already mapped
+
+       if (mutex_lock_interruptible(&vcs->mutex))
+               return -EINTR;
+
+       if (vcs->reading) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       // TODO: check queue type
+
+       if (!(vma->vm_flags & VM_WRITE)) {
+               dprintk("mmap(): app bug: PROT_WRITE please\n");
+               ret = -EINVAL;
+               goto out;
+       }
+       if (!(vma->vm_flags & VM_SHARED)) {
+               dprintk("mmap(): app bug: MAP_SHARED please\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* find the correct buffer using offset */
+       length = vino_queue_get_length(&vcs->fb_queue);
+       if (length == 0) {
+               dprintk("mmap(): queue not initialized\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       for (i = 0; i < length; i++) {
+               fb = vino_queue_get_buffer(&vcs->fb_queue, i);
+               if (fb == NULL) {
+                       dprintk("mmap(): vino_queue_get_buffer() failed\n");
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               if (fb->offset == offset)
+                       goto found;
+       }
+
+       dprintk("mmap(): invalid offset = %lu\n", offset);
+       ret = -EINVAL;
+       goto out;
+
+found:
+       dprintk("mmap(): buffer = %d\n", i);
+
+       if (size > (fb->desc_table.page_count * PAGE_SIZE)) {
+               dprintk("mmap(): failed: size = %lu > %lu\n",
+                       size, fb->desc_table.page_count * PAGE_SIZE);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       for (i = 0; i < fb->desc_table.page_count; i++) {
+               unsigned long pfn =
+                       virt_to_phys((void *)fb->desc_table.virtual[i]) >>
+                       PAGE_SHIFT;
+
+               if (size < PAGE_SIZE)
+                       break;
+
+               // protection was: PAGE_READONLY
+               if (remap_pfn_range(vma, start, pfn, PAGE_SIZE,
+                                   vma->vm_page_prot)) {
+                       dprintk("mmap(): remap_pfn_range() failed\n");
+                       ret = -EAGAIN;
+                       goto out;
+               }
+
+               start += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+
+       fb->map_count = 1;
+
+       vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+       vma->vm_flags &= ~VM_IO;
+       vma->vm_private_data = fb;
+       vma->vm_file = file;
+       vma->vm_ops = &vino_vm_ops;
+
+out:
+       mutex_unlock(&vcs->mutex);
+
+       return ret;
+}
+
+static unsigned int vino_poll(struct file *file, poll_table *pt)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       unsigned int outgoing;
+       unsigned int ret = 0;
+
+       // lock mutex (?)
+       // TODO: this has to be corrected for different read modes
+
+       dprintk("poll():\n");
+
+       if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+               dprintk("poll(): vino_queue_get_outgoing() failed\n");
+               ret = POLLERR;
+               goto error;
+       }
+       if (outgoing > 0)
+               goto over;
+
+       poll_wait(file, &vcs->fb_queue.frame_wait_queue, pt);
+
+       if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+               dprintk("poll(): vino_queue_get_outgoing() failed\n");
+               ret = POLLERR;
+               goto error;
+       }
+
+over:
+       dprintk("poll(): data %savailable\n",
+               (outgoing > 0) ? "" : "not ");
+
+       if (outgoing > 0)
+               ret = POLLIN | POLLRDNORM;
+
+error:
+       return ret;
+}
+
+static long vino_ioctl(struct file *file,
+                     unsigned int cmd, unsigned long arg)
+{
+       struct vino_channel_settings *vcs = video_drvdata(file);
+       long ret;
+
+       if (mutex_lock_interruptible(&vcs->mutex))
+               return -EINTR;
+
+       ret = video_ioctl2(file, cmd, arg);
+
+       mutex_unlock(&vcs->mutex);
+
+       return ret;
+}
+
+/* Initialization and cleanup */
+
+/* __initdata */
+static int vino_init_stage;
+
+const struct v4l2_ioctl_ops vino_ioctl_ops = {
+       .vidioc_enum_fmt_vid_cap     = vino_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap        = vino_g_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap        = vino_s_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap      = vino_try_fmt_vid_cap,
+       .vidioc_querycap             = vino_querycap,
+       .vidioc_enum_input           = vino_enum_input,
+       .vidioc_g_input              = vino_g_input,
+       .vidioc_s_input              = vino_s_input,
+       .vidioc_g_std                = vino_g_std,
+       .vidioc_s_std                = vino_s_std,
+       .vidioc_querystd             = vino_querystd,
+       .vidioc_cropcap              = vino_cropcap,
+       .vidioc_s_crop               = vino_s_crop,
+       .vidioc_g_crop               = vino_g_crop,
+       .vidioc_s_parm               = vino_s_parm,
+       .vidioc_g_parm               = vino_g_parm,
+       .vidioc_reqbufs              = vino_reqbufs,
+       .vidioc_querybuf             = vino_querybuf,
+       .vidioc_qbuf                 = vino_qbuf,
+       .vidioc_dqbuf                = vino_dqbuf,
+       .vidioc_streamon             = vino_streamon,
+       .vidioc_streamoff            = vino_streamoff,
+       .vidioc_queryctrl            = vino_queryctrl,
+       .vidioc_g_ctrl               = vino_g_ctrl,
+       .vidioc_s_ctrl               = vino_s_ctrl,
+};
+
+static const struct v4l2_file_operations vino_fops = {
+       .owner          = THIS_MODULE,
+       .open           = vino_open,
+       .release        = vino_close,
+       .unlocked_ioctl = vino_ioctl,
+       .mmap           = vino_mmap,
+       .poll           = vino_poll,
+};
+
+static struct video_device vdev_template = {
+       .name           = "NOT SET",
+       .fops           = &vino_fops,
+       .ioctl_ops      = &vino_ioctl_ops,
+       .tvnorms        = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+};
+
+static void vino_module_cleanup(int stage)
+{
+       switch(stage) {
+       case 11:
+               video_unregister_device(vino_drvdata->b.vdev);
+               vino_drvdata->b.vdev = NULL;
+       case 10:
+               video_unregister_device(vino_drvdata->a.vdev);
+               vino_drvdata->a.vdev = NULL;
+       case 9:
+               i2c_del_adapter(&vino_i2c_adapter);
+       case 8:
+               free_irq(SGI_VINO_IRQ, NULL);
+       case 7:
+               if (vino_drvdata->b.vdev) {
+                       video_device_release(vino_drvdata->b.vdev);
+                       vino_drvdata->b.vdev = NULL;
+               }
+       case 6:
+               if (vino_drvdata->a.vdev) {
+                       video_device_release(vino_drvdata->a.vdev);
+                       vino_drvdata->a.vdev = NULL;
+               }
+       case 5:
+               /* all entries in dma_cpu dummy table have the same address */
+               dma_unmap_single(NULL,
+                                vino_drvdata->dummy_desc_table.dma_cpu[0],
+                                PAGE_SIZE, DMA_FROM_DEVICE);
+               dma_free_coherent(NULL, VINO_DUMMY_DESC_COUNT
+                                 * sizeof(dma_addr_t),
+                                 (void *)vino_drvdata->
+                                 dummy_desc_table.dma_cpu,
+                                 vino_drvdata->dummy_desc_table.dma);
+       case 4:
+               free_page(vino_drvdata->dummy_page);
+       case 3:
+               v4l2_device_unregister(&vino_drvdata->v4l2_dev);
+       case 2:
+               kfree(vino_drvdata);
+       case 1:
+               iounmap(vino);
+       case 0:
+               break;
+       default:
+               dprintk("vino_module_cleanup(): invalid cleanup stage = %d\n",
+                       stage);
+       }
+}
+
+static int vino_probe(void)
+{
+       unsigned long rev_id;
+
+       if (ip22_is_fullhouse()) {
+               printk(KERN_ERR "VINO doesn't exist in IP22 Fullhouse\n");
+               return -ENODEV;
+       }
+
+       if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) {
+               printk(KERN_ERR "VINO is not found (EISA BUS not present)\n");
+               return -ENODEV;
+       }
+
+       vino = (struct sgi_vino *)ioremap(VINO_BASE, sizeof(struct sgi_vino));
+       if (!vino) {
+               printk(KERN_ERR "VINO: ioremap() failed\n");
+               return -EIO;
+       }
+       vino_init_stage++;
+
+       if (get_dbe(rev_id, &(vino->rev_id))) {
+               printk(KERN_ERR "Failed to read VINO revision register\n");
+               vino_module_cleanup(vino_init_stage);
+               return -ENODEV;
+       }
+
+       if (VINO_ID_VALUE(rev_id) != VINO_CHIP_ID) {
+               printk(KERN_ERR "Unknown VINO chip ID (Rev/ID: 0x%02lx)\n",
+                      rev_id);
+               vino_module_cleanup(vino_init_stage);
+               return -ENODEV;
+       }
+
+       printk(KERN_INFO "VINO revision %ld found\n", VINO_REV_NUM(rev_id));
+
+       return 0;
+}
+
+static int vino_init(void)
+{
+       dma_addr_t dma_dummy_address;
+       int err;
+       int i;
+
+       vino_drvdata = kzalloc(sizeof(struct vino_settings), GFP_KERNEL);
+       if (!vino_drvdata) {
+               vino_module_cleanup(vino_init_stage);
+               return -ENOMEM;
+       }
+       vino_init_stage++;
+       strlcpy(vino_drvdata->v4l2_dev.name, "vino",
+                       sizeof(vino_drvdata->v4l2_dev.name));
+       err = v4l2_device_register(NULL, &vino_drvdata->v4l2_dev);
+       if (err)
+               return err;
+       vino_init_stage++;
+
+       /* create a dummy dma descriptor */
+       vino_drvdata->dummy_page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!vino_drvdata->dummy_page) {
+               vino_module_cleanup(vino_init_stage);
+               return -ENOMEM;
+       }
+       vino_init_stage++;
+
+       // TODO: use page_count in dummy_desc_table
+
+       vino_drvdata->dummy_desc_table.dma_cpu =
+               dma_alloc_coherent(NULL,
+               VINO_DUMMY_DESC_COUNT * sizeof(dma_addr_t),
+               &vino_drvdata->dummy_desc_table.dma,
+               GFP_KERNEL | GFP_DMA);
+       if (!vino_drvdata->dummy_desc_table.dma_cpu) {
+               vino_module_cleanup(vino_init_stage);
+               return -ENOMEM;
+       }
+       vino_init_stage++;
+
+       dma_dummy_address = dma_map_single(NULL,
+                                          (void *)vino_drvdata->dummy_page,
+                                       PAGE_SIZE, DMA_FROM_DEVICE);
+       for (i = 0; i < VINO_DUMMY_DESC_COUNT; i++) {
+               vino_drvdata->dummy_desc_table.dma_cpu[i] = dma_dummy_address;
+       }
+
+       /* initialize VINO */
+
+       vino->control = 0;
+       vino->a.next_4_desc = vino_drvdata->dummy_desc_table.dma;
+       vino->b.next_4_desc = vino_drvdata->dummy_desc_table.dma;
+       udelay(VINO_DESC_FETCH_DELAY);
+
+       vino->intr_status = 0;
+
+       vino->a.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT;
+       vino->b.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT;
+
+       return 0;
+}
+
+static int vino_init_channel_settings(struct vino_channel_settings *vcs,
+                                unsigned int channel, const char *name)
+{
+       vcs->channel = channel;
+       vcs->input = VINO_INPUT_NONE;
+       vcs->alpha = 0;
+       vcs->users = 0;
+       vcs->data_format = VINO_DATA_FMT_GREY;
+       vcs->data_norm = VINO_DATA_NORM_NTSC;
+       vcs->decimation = 1;
+       vino_set_default_clipping(vcs);
+       vino_set_default_framerate(vcs);
+
+       vcs->capturing = 0;
+
+       mutex_init(&vcs->mutex);
+       spin_lock_init(&vcs->capture_lock);
+
+       mutex_init(&vcs->fb_queue.queue_mutex);
+       spin_lock_init(&vcs->fb_queue.queue_lock);
+       init_waitqueue_head(&vcs->fb_queue.frame_wait_queue);
+
+       vcs->vdev = video_device_alloc();
+       if (!vcs->vdev) {
+               vino_module_cleanup(vino_init_stage);
+               return -ENOMEM;
+       }
+       vino_init_stage++;
+
+       memcpy(vcs->vdev, &vdev_template,
+              sizeof(struct video_device));
+       strcpy(vcs->vdev->name, name);
+       vcs->vdev->release = video_device_release;
+       vcs->vdev->v4l2_dev = &vino_drvdata->v4l2_dev;
+
+       video_set_drvdata(vcs->vdev, vcs);
+
+       return 0;
+}
+
+static int __init vino_module_init(void)
+{
+       int ret;
+
+       printk(KERN_INFO "SGI VINO driver version %s\n",
+              VINO_MODULE_VERSION);
+
+       ret = vino_probe();
+       if (ret)
+               return ret;
+
+       ret = vino_init();
+       if (ret)
+               return ret;
+
+       /* initialize data structures */
+
+       spin_lock_init(&vino_drvdata->vino_lock);
+       spin_lock_init(&vino_drvdata->input_lock);
+
+       ret = vino_init_channel_settings(&vino_drvdata->a, VINO_CHANNEL_A,
+                                   vino_vdev_name_a);
+       if (ret)
+               return ret;
+
+       ret = vino_init_channel_settings(&vino_drvdata->b, VINO_CHANNEL_B,
+                                   vino_vdev_name_b);
+       if (ret)
+               return ret;
+
+       /* initialize hardware and register V4L devices */
+
+       ret = request_irq(SGI_VINO_IRQ, vino_interrupt, 0,
+               vino_driver_description, NULL);
+       if (ret) {
+               printk(KERN_ERR "VINO: requesting IRQ %02d failed\n",
+                      SGI_VINO_IRQ);
+               vino_module_cleanup(vino_init_stage);
+               return -EAGAIN;
+       }
+       vino_init_stage++;
+
+       ret = i2c_add_adapter(&vino_i2c_adapter);
+       if (ret) {
+               printk(KERN_ERR "VINO I2C bus registration failed\n");
+               vino_module_cleanup(vino_init_stage);
+               return ret;
+       }
+       i2c_set_adapdata(&vino_i2c_adapter, &vino_drvdata->v4l2_dev);
+       vino_init_stage++;
+
+       ret = video_register_device(vino_drvdata->a.vdev,
+                                   VFL_TYPE_GRABBER, -1);
+       if (ret < 0) {
+               printk(KERN_ERR "VINO channel A Video4Linux-device "
+                      "registration failed\n");
+               vino_module_cleanup(vino_init_stage);
+               return -EINVAL;
+       }
+       vino_init_stage++;
+
+       ret = video_register_device(vino_drvdata->b.vdev,
+                                   VFL_TYPE_GRABBER, -1);
+       if (ret < 0) {
+               printk(KERN_ERR "VINO channel B Video4Linux-device "
+                      "registration failed\n");
+               vino_module_cleanup(vino_init_stage);
+               return -EINVAL;
+       }
+       vino_init_stage++;
+
+       vino_drvdata->decoder =
+               v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter,
+                              "saa7191", 0, I2C_ADDRS(0x45));
+       vino_drvdata->camera =
+               v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter,
+                              "indycam", 0, I2C_ADDRS(0x2b));
+
+       dprintk("init complete!\n");
+
+       return 0;
+}
+
+static void __exit vino_module_exit(void)
+{
+       dprintk("exiting, stage = %d ...\n", vino_init_stage);
+       vino_module_cleanup(vino_init_stage);
+       dprintk("cleanup complete, exit!\n");
+}
+
+module_init(vino_module_init);
+module_exit(vino_module_exit);
diff --git a/drivers/staging/media/vino/vino.h b/drivers/staging/media/vino/vino.h
new file mode 100644 (file)
index 0000000..de2d615
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Driver for the VINO (Video In No Out) system found in SGI Indys.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * Copyright (C) 1999 Ulf Karlsson <ulfc@bun.falkenberg.se>
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ */
+
+#ifndef _VINO_H_
+#define _VINO_H_
+
+#define VINO_BASE      0x00080000      /* Vino is in the EISA address space,
+                                        * but it is not an EISA bus card */
+#define VINO_PAGE_SIZE 4096
+
+struct sgi_vino_channel {
+       u32 _pad_alpha;
+       volatile u32 alpha;
+
+#define VINO_CLIP_X(x)         ((x) & 0x3ff)           /* bits 0:9 */
+#define VINO_CLIP_ODD(x)       (((x) & 0x1ff) << 10)   /* bits 10:18 */
+#define VINO_CLIP_EVEN(x)      (((x) & 0x1ff) << 19)   /* bits 19:27 */
+       u32 _pad_clip_start;
+       volatile u32 clip_start;
+       u32 _pad_clip_end;
+       volatile u32 clip_end;
+
+#define VINO_FRAMERT_FULL      0xfff
+#define VINO_FRAMERT_PAL       (1<<0)                  /* 0=NTSC 1=PAL */
+#define VINO_FRAMERT_RT(x)     (((x) & 0xfff) << 1)    /* bits 1:12 */
+       u32 _pad_frame_rate;
+       volatile u32 frame_rate;
+
+       u32 _pad_field_counter;
+       volatile u32 field_counter;
+       u32 _pad_line_size;
+       volatile u32 line_size;
+       u32 _pad_line_count;
+       volatile u32 line_count;
+       u32 _pad_page_index;
+       volatile u32 page_index;
+       u32 _pad_next_4_desc;
+       volatile u32 next_4_desc;
+       u32 _pad_start_desc_tbl;
+       volatile u32 start_desc_tbl;
+
+#define VINO_DESC_JUMP         (1<<30)
+#define VINO_DESC_STOP         (1<<31)
+#define VINO_DESC_VALID                (1<<32)
+       u32 _pad_desc_0;
+       volatile u32 desc_0;
+       u32 _pad_desc_1;
+       volatile u32 desc_1;
+       u32 _pad_desc_2;
+       volatile u32 desc_2;
+       u32 _pad_Bdesc_3;
+       volatile u32 desc_3;
+
+       u32 _pad_fifo_thres;
+       volatile u32 fifo_thres;
+       u32 _pad_fifo_read;
+       volatile u32 fifo_read;
+       u32 _pad_fifo_write;
+       volatile u32 fifo_write;
+};
+
+struct sgi_vino {
+#define VINO_CHIP_ID           0xb
+#define VINO_REV_NUM(x)                ((x) & 0x0f)
+#define VINO_ID_VALUE(x)       (((x) & 0xf0) >> 4)
+       u32 _pad_rev_id;
+       volatile u32 rev_id;
+
+#define VINO_CTRL_LITTLE_ENDIAN                (1<<0)
+#define VINO_CTRL_A_EOF_INT            (1<<1)  /* Field transferred int */
+#define VINO_CTRL_A_FIFO_INT           (1<<2)  /* FIFO overflow int */
+#define VINO_CTRL_A_EOD_INT            (1<<3)  /* End of desc table int */
+#define VINO_CTRL_A_INT                        (VINO_CTRL_A_EOF_INT | \
+                                        VINO_CTRL_A_FIFO_INT | \
+                                        VINO_CTRL_A_EOD_INT)
+#define VINO_CTRL_B_EOF_INT            (1<<4)  /* Field transferred int */
+#define VINO_CTRL_B_FIFO_INT           (1<<5)  /* FIFO overflow int */
+#define VINO_CTRL_B_EOD_INT            (1<<6)  /* End of desc table int */
+#define VINO_CTRL_B_INT                        (VINO_CTRL_B_EOF_INT | \
+                                        VINO_CTRL_B_FIFO_INT | \
+                                        VINO_CTRL_B_EOD_INT)
+#define VINO_CTRL_A_DMA_ENBL           (1<<7)
+#define VINO_CTRL_A_INTERLEAVE_ENBL    (1<<8)
+#define VINO_CTRL_A_SYNC_ENBL          (1<<9)
+#define VINO_CTRL_A_SELECT             (1<<10) /* 1=D1 0=Philips */
+#define VINO_CTRL_A_RGB                        (1<<11) /* 1=RGB 0=YUV */
+#define VINO_CTRL_A_LUMA_ONLY          (1<<12)
+#define VINO_CTRL_A_DEC_ENBL           (1<<13) /* Decimation */
+#define VINO_CTRL_A_DEC_SCALE_MASK     0x1c000 /* bits 14:17 */
+#define VINO_CTRL_A_DEC_SCALE_SHIFT    (14)
+#define VINO_CTRL_A_DEC_HOR_ONLY       (1<<17) /* Horizontal only */
+#define VINO_CTRL_A_DITHER             (1<<18) /* 24 -> 8 bit dither */
+#define VINO_CTRL_B_DMA_ENBL           (1<<19)
+#define VINO_CTRL_B_INTERLEAVE_ENBL    (1<<20)
+#define VINO_CTRL_B_SYNC_ENBL          (1<<21)
+#define VINO_CTRL_B_SELECT             (1<<22) /* 1=D1 0=Philips */
+#define VINO_CTRL_B_RGB                        (1<<23) /* 1=RGB 0=YUV */
+#define VINO_CTRL_B_LUMA_ONLY          (1<<24)
+#define VINO_CTRL_B_DEC_ENBL           (1<<25) /* Decimation */
+#define VINO_CTRL_B_DEC_SCALE_MASK     0x1c000000      /* bits 26:28 */
+#define VINO_CTRL_B_DEC_SCALE_SHIFT    (26)
+#define VINO_CTRL_B_DEC_HOR_ONLY       (1<<29) /* Decimation horizontal only */
+#define VINO_CTRL_B_DITHER             (1<<30) /* ChanB 24 -> 8 bit dither */
+       u32 _pad_control;
+       volatile u32 control;
+
+#define VINO_INTSTAT_A_EOF             (1<<0)  /* Field transferred int */
+#define VINO_INTSTAT_A_FIFO            (1<<1)  /* FIFO overflow int */
+#define VINO_INTSTAT_A_EOD             (1<<2)  /* End of desc table int */
+#define VINO_INTSTAT_A                 (VINO_INTSTAT_A_EOF | \
+                                        VINO_INTSTAT_A_FIFO | \
+                                        VINO_INTSTAT_A_EOD)
+#define VINO_INTSTAT_B_EOF             (1<<3)  /* Field transferred int */
+#define VINO_INTSTAT_B_FIFO            (1<<4)  /* FIFO overflow int */
+#define VINO_INTSTAT_B_EOD             (1<<5)  /* End of desc table int */
+#define VINO_INTSTAT_B                 (VINO_INTSTAT_B_EOF | \
+                                        VINO_INTSTAT_B_FIFO | \
+                                        VINO_INTSTAT_B_EOD)
+       u32 _pad_intr_status;
+       volatile u32 intr_status;
+
+       u32 _pad_i2c_control;
+       volatile u32 i2c_control;
+       u32 _pad_i2c_data;
+       volatile u32 i2c_data;
+
+       struct sgi_vino_channel a;
+       struct sgi_vino_channel b;
+};
+
+#endif
index b4b58ae..555de07 100644 (file)
@@ -530,7 +530,7 @@ static int dw8250_resume(struct device *dev)
 }
 #endif /* CONFIG_PM_SLEEP */
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int dw8250_runtime_suspend(struct device *dev)
 {
        struct dw8250_data *data = dev_get_drvdata(dev);
index 6f93123..7a11fac 100644 (file)
@@ -244,7 +244,7 @@ static int mtk8250_resume(struct device *dev)
 }
 #endif /* CONFIG_PM_SLEEP */
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int mtk8250_runtime_suspend(struct device *dev)
 {
        struct mtk8250_data *data = dev_get_drvdata(dev);
index e1f4fda..8fe4501 100644 (file)
@@ -1252,12 +1252,7 @@ static int serial_hsu_resume(struct pci_dev *pdev)
        }
        return 0;
 }
-#else
-#define serial_hsu_suspend     NULL
-#define serial_hsu_resume      NULL
-#endif
 
-#ifdef CONFIG_PM_RUNTIME
 static int serial_hsu_runtime_idle(struct device *dev)
 {
        pm_schedule_suspend(dev, 500);
@@ -1274,6 +1269,8 @@ static int serial_hsu_runtime_resume(struct device *dev)
        return 0;
 }
 #else
+#define serial_hsu_suspend             NULL
+#define serial_hsu_resume              NULL
 #define serial_hsu_runtime_idle                NULL
 #define serial_hsu_runtime_suspend     NULL
 #define serial_hsu_runtime_resume      NULL
index 8abe8ea..62da853 100644 (file)
@@ -1792,7 +1792,7 @@ static void __exit msm_serial_hs_exit(void)
 }
 module_exit(msm_serial_hs_exit);
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int msm_hs_runtime_idle(struct device *dev)
 {
        /*
index 435478a..2e1073d 100644 (file)
@@ -1776,7 +1776,7 @@ static void serial_omap_mdr1_errataset(struct uart_omap_port *up, u8 mdr1)
        }
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static void serial_omap_restore_context(struct uart_omap_port *up)
 {
        if (up->errata & UART_ERRATA_i202_MDR1_ACCESS)
index 9cfda6a..cc0ced0 100644 (file)
@@ -43,7 +43,7 @@ config USB_DYNAMIC_MINORS
 
 config USB_OTG
        bool "OTG support"
-       depends on PM_RUNTIME
+       depends on PM
        default n
        help
          The most notable feature of USB OTG is support for a
index 0cd1f44..c6d0c8e 100644 (file)
@@ -20,7 +20,7 @@ config AB8500_USB
 
 config FSL_USB2_OTG
        bool "Freescale USB OTG Transceiver Driver"
-       depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM_RUNTIME
+       depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM
        select USB_OTG
        select USB_PHY
        help
@@ -153,7 +153,7 @@ config USB_MSM_OTG
 
 config USB_MV_OTG
        tristate "Marvell USB OTG support"
-       depends on USB_EHCI_MV && USB_MV_UDC && PM_RUNTIME
+       depends on USB_EHCI_MV && USB_MV_UDC && PM
        select USB_OTG
        select USB_PHY
        help
index 715f299..ec84758 100644 (file)
@@ -41,7 +41,7 @@ config USB_STORAGE_REALTEK
 
 config REALTEK_AUTOPM
        bool "Realtek Card Reader autosuspend support"
-       depends on USB_STORAGE_REALTEK && PM_RUNTIME
+       depends on USB_STORAGE_REALTEK && PM
        default y
 
 config USB_STORAGE_DATAFAB
index 5174eba..3bb02c6 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/uaccess.h>
 #include <linux/slab.h>
 #include <linux/export.h>
+#include <uapi/linux/virtio_config.h>
 
 static __printf(1,2) __cold void vringh_bad(const char *fmt, ...)
 {
@@ -28,13 +29,14 @@ static __printf(1,2) __cold void vringh_bad(const char *fmt, ...)
 
 /* Returns vring->num if empty, -ve on error. */
 static inline int __vringh_get_head(const struct vringh *vrh,
-                                   int (*getu16)(u16 *val, const u16 *p),
+                                   int (*getu16)(const struct vringh *vrh,
+                                                 u16 *val, const __virtio16 *p),
                                    u16 *last_avail_idx)
 {
        u16 avail_idx, i, head;
        int err;
 
-       err = getu16(&avail_idx, &vrh->vring.avail->idx);
+       err = getu16(vrh, &avail_idx, &vrh->vring.avail->idx);
        if (err) {
                vringh_bad("Failed to access avail idx at %p",
                           &vrh->vring.avail->idx);
@@ -49,7 +51,7 @@ static inline int __vringh_get_head(const struct vringh *vrh,
 
        i = *last_avail_idx & (vrh->vring.num - 1);
 
-       err = getu16(&head, &vrh->vring.avail->ring[i]);
+       err = getu16(vrh, &head, &vrh->vring.avail->ring[i]);
        if (err) {
                vringh_bad("Failed to read head: idx %d address %p",
                           *last_avail_idx, &vrh->vring.avail->ring[i]);
@@ -144,28 +146,32 @@ static inline bool no_range_check(struct vringh *vrh, u64 addr, size_t *len,
 }
 
 /* No reason for this code to be inline. */
-static int move_to_indirect(int *up_next, u16 *i, void *addr,
+static int move_to_indirect(const struct vringh *vrh,
+                           int *up_next, u16 *i, void *addr,
                            const struct vring_desc *desc,
                            struct vring_desc **descs, int *desc_max)
 {
+       u32 len;
+
        /* Indirect tables can't have indirect. */
        if (*up_next != -1) {
                vringh_bad("Multilevel indirect %u->%u", *up_next, *i);
                return -EINVAL;
        }
 
-       if (unlikely(desc->len % sizeof(struct vring_desc))) {
+       len = vringh32_to_cpu(vrh, desc->len);
+       if (unlikely(len % sizeof(struct vring_desc))) {
                vringh_bad("Strange indirect len %u", desc->len);
                return -EINVAL;
        }
 
        /* We will check this when we follow it! */
-       if (desc->flags & VRING_DESC_F_NEXT)
-               *up_next = desc->next;
+       if (desc->flags & cpu_to_vringh16(vrh, VRING_DESC_F_NEXT))
+               *up_next = vringh16_to_cpu(vrh, desc->next);
        else
                *up_next = -2;
        *descs = addr;
-       *desc_max = desc->len / sizeof(struct vring_desc);
+       *desc_max = len / sizeof(struct vring_desc);
 
        /* Now, start at the first indirect. */
        *i = 0;
@@ -287,22 +293,25 @@ __vringh_iov(struct vringh *vrh, u16 i,
                if (unlikely(err))
                        goto fail;
 
-               if (unlikely(desc.flags & VRING_DESC_F_INDIRECT)) {
+               if (unlikely(desc.flags &
+                            cpu_to_vringh16(vrh, VRING_DESC_F_INDIRECT))) {
+                       u64 a = vringh64_to_cpu(vrh, desc.addr);
+
                        /* Make sure it's OK, and get offset. */
-                       len = desc.len;
-                       if (!rcheck(vrh, desc.addr, &len, &range, getrange)) {
+                       len = vringh32_to_cpu(vrh, desc.len);
+                       if (!rcheck(vrh, a, &len, &range, getrange)) {
                                err = -EINVAL;
                                goto fail;
                        }
 
-                       if (unlikely(len != desc.len)) {
+                       if (unlikely(len != vringh32_to_cpu(vrh, desc.len))) {
                                slow = true;
                                /* We need to save this range to use offset */
                                slowrange = range;
                        }
 
-                       addr = (void *)(long)(desc.addr + range.offset);
-                       err = move_to_indirect(&up_next, &i, addr, &desc,
+                       addr = (void *)(long)(a + range.offset);
+                       err = move_to_indirect(vrh, &up_next, &i, addr, &desc,
                                               &descs, &desc_max);
                        if (err)
                                goto fail;
@@ -315,7 +324,7 @@ __vringh_iov(struct vringh *vrh, u16 i,
                        goto fail;
                }
 
-               if (desc.flags & VRING_DESC_F_WRITE)
+               if (desc.flags & cpu_to_vringh16(vrh, VRING_DESC_F_WRITE))
                        iov = wiov;
                else {
                        iov = riov;
@@ -336,12 +345,14 @@ __vringh_iov(struct vringh *vrh, u16 i,
 
        again:
                /* Make sure it's OK, and get offset. */
-               len = desc.len;
-               if (!rcheck(vrh, desc.addr, &len, &range, getrange)) {
+               len = vringh32_to_cpu(vrh, desc.len);
+               if (!rcheck(vrh, vringh64_to_cpu(vrh, desc.addr), &len, &range,
+                           getrange)) {
                        err = -EINVAL;
                        goto fail;
                }
-               addr = (void *)(unsigned long)(desc.addr + range.offset);
+               addr = (void *)(unsigned long)(vringh64_to_cpu(vrh, desc.addr) +
+                                              range.offset);
 
                if (unlikely(iov->used == (iov->max_num & ~VRINGH_IOV_ALLOCATED))) {
                        err = resize_iovec(iov, gfp);
@@ -353,14 +364,16 @@ __vringh_iov(struct vringh *vrh, u16 i,
                iov->iov[iov->used].iov_len = len;
                iov->used++;
 
-               if (unlikely(len != desc.len)) {
-                       desc.len -= len;
-                       desc.addr += len;
+               if (unlikely(len != vringh32_to_cpu(vrh, desc.len))) {
+                       desc.len = cpu_to_vringh32(vrh,
+                                  vringh32_to_cpu(vrh, desc.len) - len);
+                       desc.addr = cpu_to_vringh64(vrh,
+                                   vringh64_to_cpu(vrh, desc.addr) + len);
                        goto again;
                }
 
-               if (desc.flags & VRING_DESC_F_NEXT) {
-                       i = desc.next;
+               if (desc.flags & cpu_to_vringh16(vrh, VRING_DESC_F_NEXT)) {
+                       i = vringh16_to_cpu(vrh, desc.next);
                } else {
                        /* Just in case we need to finish traversing above. */
                        if (unlikely(up_next > 0)) {
@@ -387,7 +400,8 @@ fail:
 static inline int __vringh_complete(struct vringh *vrh,
                                    const struct vring_used_elem *used,
                                    unsigned int num_used,
-                                   int (*putu16)(u16 *p, u16 val),
+                                   int (*putu16)(const struct vringh *vrh,
+                                                 __virtio16 *p, u16 val),
                                    int (*putused)(struct vring_used_elem *dst,
                                                   const struct vring_used_elem
                                                   *src, unsigned num))
@@ -420,7 +434,7 @@ static inline int __vringh_complete(struct vringh *vrh,
        /* Make sure buffer is written before we update index. */
        virtio_wmb(vrh->weak_barriers);
 
-       err = putu16(&vrh->vring.used->idx, used_idx + num_used);
+       err = putu16(vrh, &vrh->vring.used->idx, used_idx + num_used);
        if (err) {
                vringh_bad("Failed to update used index at %p",
                           &vrh->vring.used->idx);
@@ -433,7 +447,9 @@ static inline int __vringh_complete(struct vringh *vrh,
 
 
 static inline int __vringh_need_notify(struct vringh *vrh,
-                                      int (*getu16)(u16 *val, const u16 *p))
+                                      int (*getu16)(const struct vringh *vrh,
+                                                    u16 *val,
+                                                    const __virtio16 *p))
 {
        bool notify;
        u16 used_event;
@@ -447,7 +463,7 @@ static inline int __vringh_need_notify(struct vringh *vrh,
        /* Old-style, without event indices. */
        if (!vrh->event_indices) {
                u16 flags;
-               err = getu16(&flags, &vrh->vring.avail->flags);
+               err = getu16(vrh, &flags, &vrh->vring.avail->flags);
                if (err) {
                        vringh_bad("Failed to get flags at %p",
                                   &vrh->vring.avail->flags);
@@ -457,7 +473,7 @@ static inline int __vringh_need_notify(struct vringh *vrh,
        }
 
        /* Modern: we know when other side wants to know. */
-       err = getu16(&used_event, &vring_used_event(&vrh->vring));
+       err = getu16(vrh, &used_event, &vring_used_event(&vrh->vring));
        if (err) {
                vringh_bad("Failed to get used event idx at %p",
                           &vring_used_event(&vrh->vring));
@@ -478,20 +494,22 @@ static inline int __vringh_need_notify(struct vringh *vrh,
 }
 
 static inline bool __vringh_notify_enable(struct vringh *vrh,
-                                         int (*getu16)(u16 *val, const u16 *p),
-                                         int (*putu16)(u16 *p, u16 val))
+                                         int (*getu16)(const struct vringh *vrh,
+                                                       u16 *val, const __virtio16 *p),
+                                         int (*putu16)(const struct vringh *vrh,
+                                                       __virtio16 *p, u16 val))
 {
        u16 avail;
 
        if (!vrh->event_indices) {
                /* Old-school; update flags. */
-               if (putu16(&vrh->vring.used->flags, 0) != 0) {
+               if (putu16(vrh, &vrh->vring.used->flags, 0) != 0) {
                        vringh_bad("Clearing used flags %p",
                                   &vrh->vring.used->flags);
                        return true;
                }
        } else {
-               if (putu16(&vring_avail_event(&vrh->vring),
+               if (putu16(vrh, &vring_avail_event(&vrh->vring),
                           vrh->last_avail_idx) != 0) {
                        vringh_bad("Updating avail event index %p",
                                   &vring_avail_event(&vrh->vring));
@@ -503,7 +521,7 @@ static inline bool __vringh_notify_enable(struct vringh *vrh,
         * sure it's written, then check again. */
        virtio_mb(vrh->weak_barriers);
 
-       if (getu16(&avail, &vrh->vring.avail->idx) != 0) {
+       if (getu16(vrh, &avail, &vrh->vring.avail->idx) != 0) {
                vringh_bad("Failed to check avail idx at %p",
                           &vrh->vring.avail->idx);
                return true;
@@ -516,11 +534,13 @@ static inline bool __vringh_notify_enable(struct vringh *vrh,
 }
 
 static inline void __vringh_notify_disable(struct vringh *vrh,
-                                          int (*putu16)(u16 *p, u16 val))
+                                          int (*putu16)(const struct vringh *vrh,
+                                                        __virtio16 *p, u16 val))
 {
        if (!vrh->event_indices) {
                /* Old-school; update flags. */
-               if (putu16(&vrh->vring.used->flags, VRING_USED_F_NO_NOTIFY)) {
+               if (putu16(vrh, &vrh->vring.used->flags,
+                          VRING_USED_F_NO_NOTIFY)) {
                        vringh_bad("Setting used flags %p",
                                   &vrh->vring.used->flags);
                }
@@ -528,14 +548,18 @@ static inline void __vringh_notify_disable(struct vringh *vrh,
 }
 
 /* Userspace access helpers: in this case, addresses are really userspace. */
-static inline int getu16_user(u16 *val, const u16 *p)
+static inline int getu16_user(const struct vringh *vrh, u16 *val, const __virtio16 *p)
 {
-       return get_user(*val, (__force u16 __user *)p);
+       __virtio16 v = 0;
+       int rc = get_user(v, (__force __virtio16 __user *)p);
+       *val = vringh16_to_cpu(vrh, v);
+       return rc;
 }
 
-static inline int putu16_user(u16 *p, u16 val)
+static inline int putu16_user(const struct vringh *vrh, __virtio16 *p, u16 val)
 {
-       return put_user(val, (__force u16 __user *)p);
+       __virtio16 v = cpu_to_vringh16(vrh, val);
+       return put_user(v, (__force __virtio16 __user *)p);
 }
 
 static inline int copydesc_user(void *dst, const void *src, size_t len)
@@ -577,7 +601,7 @@ static inline int xfer_to_user(void *dst, void *src, size_t len)
  * Returns an error if num is invalid: you should check pointers
  * yourself!
  */
-int vringh_init_user(struct vringh *vrh, u32 features,
+int vringh_init_user(struct vringh *vrh, u64 features,
                     unsigned int num, bool weak_barriers,
                     struct vring_desc __user *desc,
                     struct vring_avail __user *avail,
@@ -589,6 +613,7 @@ int vringh_init_user(struct vringh *vrh, u32 features,
                return -EINVAL;
        }
 
+       vrh->little_endian = (features & (1ULL << VIRTIO_F_VERSION_1));
        vrh->event_indices = (features & (1 << VIRTIO_RING_F_EVENT_IDX));
        vrh->weak_barriers = weak_barriers;
        vrh->completed = 0;
@@ -729,8 +754,8 @@ int vringh_complete_user(struct vringh *vrh, u16 head, u32 len)
 {
        struct vring_used_elem used;
 
-       used.id = head;
-       used.len = len;
+       used.id = cpu_to_vringh32(vrh, head);
+       used.len = cpu_to_vringh32(vrh, len);
        return __vringh_complete(vrh, &used, 1, putu16_user, putused_user);
 }
 EXPORT_SYMBOL(vringh_complete_user);
@@ -792,15 +817,16 @@ int vringh_need_notify_user(struct vringh *vrh)
 EXPORT_SYMBOL(vringh_need_notify_user);
 
 /* Kernelspace access helpers. */
-static inline int getu16_kern(u16 *val, const u16 *p)
+static inline int getu16_kern(const struct vringh *vrh,
+                             u16 *val, const __virtio16 *p)
 {
-       *val = ACCESS_ONCE(*p);
+       *val = vringh16_to_cpu(vrh, ACCESS_ONCE(*p));
        return 0;
 }
 
-static inline int putu16_kern(u16 *p, u16 val)
+static inline int putu16_kern(const struct vringh *vrh, __virtio16 *p, u16 val)
 {
-       ACCESS_ONCE(*p) = val;
+       ACCESS_ONCE(*p) = cpu_to_vringh16(vrh, val);
        return 0;
 }
 
@@ -836,7 +862,7 @@ static inline int xfer_kern(void *src, void *dst, size_t len)
  *
  * Returns an error if num is invalid.
  */
-int vringh_init_kern(struct vringh *vrh, u32 features,
+int vringh_init_kern(struct vringh *vrh, u64 features,
                     unsigned int num, bool weak_barriers,
                     struct vring_desc *desc,
                     struct vring_avail *avail,
@@ -848,6 +874,7 @@ int vringh_init_kern(struct vringh *vrh, u32 features,
                return -EINVAL;
        }
 
+       vrh->little_endian = (features & (1ULL << VIRTIO_F_VERSION_1));
        vrh->event_indices = (features & (1 << VIRTIO_RING_F_EVENT_IDX));
        vrh->weak_barriers = weak_barriers;
        vrh->completed = 0;
@@ -962,8 +989,8 @@ int vringh_complete_kern(struct vringh *vrh, u16 head, u32 len)
 {
        struct vring_used_elem used;
 
-       used.id = head;
-       used.len = len;
+       used.id = cpu_to_vringh32(vrh, head);
+       used.len = cpu_to_vringh32(vrh, len);
 
        return __vringh_complete(vrh, &used, 1, putu16_kern, putused_kern);
 }
index a623a4d..7e3a05f 100644 (file)
@@ -1630,7 +1630,7 @@ static int s3c_fb_resume(struct device *dev)
 }
 #endif
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int s3c_fb_runtime_suspend(struct device *dev)
 {
        struct s3c_fb *sfb = dev_get_drvdata(dev);
index 1d56108..baadfb2 100644 (file)
@@ -569,7 +569,7 @@ EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_update);
  * Power management
  */
 
-#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME)
+#ifdef CONFIG_PM
 static int sh_mobile_meram_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
@@ -612,7 +612,7 @@ static int sh_mobile_meram_resume(struct device *dev)
                meram_write_reg(priv->base, common_regs[i], priv->regs[i]);
        return 0;
 }
-#endif /* CONFIG_PM_SLEEP || CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM */
 
 static UNIVERSAL_DEV_PM_OPS(sh_mobile_meram_dev_pm_ops,
                            sh_mobile_meram_suspend,
index f226658..b9f70df 100644 (file)
@@ -162,6 +162,27 @@ static void virtio_config_enable(struct virtio_device *dev)
        spin_unlock_irq(&dev->config_lock);
 }
 
+static int virtio_finalize_features(struct virtio_device *dev)
+{
+       int ret = dev->config->finalize_features(dev);
+       unsigned status;
+
+       if (ret)
+               return ret;
+
+       if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1))
+               return 0;
+
+       add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK);
+       status = dev->config->get_status(dev);
+       if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) {
+               dev_err(&dev->dev, "virtio: device refuses features: %x\n",
+                       status);
+               return -ENODEV;
+       }
+       return 0;
+}
+
 static int virtio_dev_probe(struct device *_d)
 {
        int err, i;
@@ -170,7 +191,6 @@ static int virtio_dev_probe(struct device *_d)
        u64 device_features;
        u64 driver_features;
        u64 driver_features_legacy;
-       unsigned status;
 
        /* We have a driver! */
        add_status(dev, VIRTIO_CONFIG_S_DRIVER);
@@ -208,21 +228,10 @@ static int virtio_dev_probe(struct device *_d)
                if (device_features & (1ULL << i))
                        __virtio_set_bit(dev, i);
 
-       err = dev->config->finalize_features(dev);
+       err = virtio_finalize_features(dev);
        if (err)
                goto err;
 
-       if (virtio_has_feature(dev, VIRTIO_F_VERSION_1)) {
-               add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK);
-               status = dev->config->get_status(dev);
-               if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) {
-                       dev_err(_d, "virtio: device refuses features: %x\n",
-                              status);
-                       err = -ENODEV;
-                       goto err;
-               }
-       }
-
        err = drv->probe(dev);
        if (err)
                goto err;
@@ -372,7 +381,7 @@ int virtio_device_restore(struct virtio_device *dev)
        /* We have a driver! */
        add_status(dev, VIRTIO_CONFIG_S_DRIVER);
 
-       ret = dev->config->finalize_features(dev);
+       ret = virtio_finalize_features(dev);
        if (ret)
                goto err;
 
index 953057d..2ef9529 100644 (file)
@@ -458,7 +458,44 @@ static int virtio_pci_restore(struct device *dev)
        return virtio_device_restore(&vp_dev->vdev);
 }
 
-const struct dev_pm_ops virtio_pci_pm_ops = {
+static const struct dev_pm_ops virtio_pci_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(virtio_pci_freeze, virtio_pci_restore)
 };
 #endif
+
+
+/* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */
+static const struct pci_device_id virtio_pci_id_table[] = {
+       { PCI_DEVICE(0x1af4, PCI_ANY_ID) },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, virtio_pci_id_table);
+
+static int virtio_pci_probe(struct pci_dev *pci_dev,
+                           const struct pci_device_id *id)
+{
+       return virtio_pci_legacy_probe(pci_dev, id);
+}
+
+static void virtio_pci_remove(struct pci_dev *pci_dev)
+{
+     virtio_pci_legacy_remove(pci_dev);
+}
+
+static struct pci_driver virtio_pci_driver = {
+       .name           = "virtio-pci",
+       .id_table       = virtio_pci_id_table,
+       .probe          = virtio_pci_probe,
+       .remove         = virtio_pci_remove,
+#ifdef CONFIG_PM_SLEEP
+       .driver.pm      = &virtio_pci_pm_ops,
+#endif
+};
+
+module_pci_driver(virtio_pci_driver);
+
+MODULE_AUTHOR("Anthony Liguori <aliguori@us.ibm.com>");
+MODULE_DESCRIPTION("virtio-pci");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1");
index d840dad..adddb64 100644 (file)
@@ -27,7 +27,6 @@
 #include <linux/virtio.h>
 #include <linux/virtio_config.h>
 #include <linux/virtio_ring.h>
-#define VIRTIO_PCI_NO_LEGACY
 #include <linux/virtio_pci.h>
 #include <linux/highmem.h>
 #include <linux/spinlock.h>
@@ -129,8 +128,8 @@ const char *vp_bus_name(struct virtio_device *vdev);
 int vp_set_vq_affinity(struct virtqueue *vq, int cpu);
 void virtio_pci_release_dev(struct device *);
 
-#ifdef CONFIG_PM_SLEEP
-extern const struct dev_pm_ops virtio_pci_pm_ops;
-#endif
+int virtio_pci_legacy_probe(struct pci_dev *pci_dev,
+                           const struct pci_device_id *id);
+void virtio_pci_legacy_remove(struct pci_dev *pci_dev);
 
 #endif
index 2588252..6c76f0f 100644 (file)
 
 #include "virtio_pci_common.h"
 
-/* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */
-static const struct pci_device_id virtio_pci_id_table[] = {
-       { PCI_DEVICE(0x1af4, PCI_ANY_ID) },
-       { 0 }
-};
-
-MODULE_DEVICE_TABLE(pci, virtio_pci_id_table);
-
 /* virtio config->get_features() implementation */
 static u64 vp_get_features(struct virtio_device *vdev)
 {
@@ -220,7 +212,7 @@ static const struct virtio_config_ops virtio_pci_config_ops = {
 };
 
 /* the PCI probing function */
-static int virtio_pci_probe(struct pci_dev *pci_dev,
+int virtio_pci_legacy_probe(struct pci_dev *pci_dev,
                            const struct pci_device_id *id)
 {
        struct virtio_pci_device *vp_dev;
@@ -300,7 +292,7 @@ out:
        return err;
 }
 
-static void virtio_pci_remove(struct pci_dev *pci_dev)
+void virtio_pci_legacy_remove(struct pci_dev *pci_dev)
 {
        struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
 
@@ -312,15 +304,3 @@ static void virtio_pci_remove(struct pci_dev *pci_dev)
        pci_disable_device(pci_dev);
        kfree(vp_dev);
 }
-
-static struct pci_driver virtio_pci_driver = {
-       .name           = "virtio-pci",
-       .id_table       = virtio_pci_id_table,
-       .probe          = virtio_pci_probe,
-       .remove         = virtio_pci_remove,
-#ifdef CONFIG_PM_SLEEP
-       .driver.pm      = &virtio_pci_pm_ops,
-#endif
-};
-
-module_pci_driver(virtio_pci_driver);
index 65b84d8..d6add51 100644 (file)
@@ -326,6 +326,52 @@ static void imx2_wdt_shutdown(struct platform_device *pdev)
        }
 }
 
+#ifdef CONFIG_PM_SLEEP
+/* Disable watchdog if it is active during suspend */
+static int imx2_wdt_suspend(struct device *dev)
+{
+       struct watchdog_device *wdog = dev_get_drvdata(dev);
+       struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+       imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
+       imx2_wdt_ping(wdog);
+
+       /* Watchdog has been stopped but IP block is still running */
+       if (!watchdog_active(wdog) && imx2_wdt_is_running(wdev))
+               del_timer_sync(&wdev->timer);
+
+       clk_disable_unprepare(wdev->clk);
+
+       return 0;
+}
+
+/* Enable watchdog and configure it if necessary */
+static int imx2_wdt_resume(struct device *dev)
+{
+       struct watchdog_device *wdog = dev_get_drvdata(dev);
+       struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+       clk_prepare_enable(wdev->clk);
+
+       if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) {
+               /* Resumes from deep sleep we need restart
+                * the watchdog again.
+                */
+               imx2_wdt_setup(wdog);
+               imx2_wdt_set_timeout(wdog, wdog->timeout);
+               imx2_wdt_ping(wdog);
+       } else if (imx2_wdt_is_running(wdev)) {
+               imx2_wdt_ping(wdog);
+               mod_timer(&wdev->timer, jiffies + wdog->timeout * HZ / 2);
+       }
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend,
+                        imx2_wdt_resume);
+
 static const struct of_device_id imx2_wdt_dt_ids[] = {
        { .compatible = "fsl,imx21-wdt", },
        { /* sentinel */ }
@@ -337,6 +383,7 @@ static struct platform_driver imx2_wdt_driver = {
        .shutdown       = imx2_wdt_shutdown,
        .driver         = {
                .name   = DRIVER_NAME,
+               .pm     = &imx2_wdt_pm_ops,
                .of_match_table = imx2_wdt_dt_ids,
        },
 };
index 32602c6..7892e6f 100644 (file)
@@ -38,21 +38,30 @@ int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *k1,
        return hfsplus_strcmp(&k1->cat.name, &k2->cat.name);
 }
 
-void hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *key,
-                          u32 parent, struct qstr *str)
+/* Generates key for catalog file/folders record. */
+int hfsplus_cat_build_key(struct super_block *sb,
+               hfsplus_btree_key *key, u32 parent, struct qstr *str)
 {
-       int len;
+       int len, err;
 
        key->cat.parent = cpu_to_be32(parent);
-       if (str) {
-               hfsplus_asc2uni(sb, &key->cat.name, HFSPLUS_MAX_STRLEN,
-                                       str->name, str->len);
-               len = be16_to_cpu(key->cat.name.length);
-       } else {
-               key->cat.name.length = 0;
-               len = 0;
-       }
+       err = hfsplus_asc2uni(sb, &key->cat.name, HFSPLUS_MAX_STRLEN,
+                       str->name, str->len);
+       if (unlikely(err < 0))
+               return err;
+
+       len = be16_to_cpu(key->cat.name.length);
        key->key_len = cpu_to_be16(6 + 2 * len);
+       return 0;
+}
+
+/* Generates key for catalog thread record. */
+void hfsplus_cat_build_key_with_cnid(struct super_block *sb,
+                       hfsplus_btree_key *key, u32 parent)
+{
+       key->cat.parent = cpu_to_be32(parent);
+       key->cat.name.length = 0;
+       key->key_len = cpu_to_be16(6);
 }
 
 static void hfsplus_cat_build_key_uni(hfsplus_btree_key *key, u32 parent,
@@ -167,11 +176,16 @@ static int hfsplus_fill_cat_thread(struct super_block *sb,
                                   hfsplus_cat_entry *entry, int type,
                                   u32 parentid, struct qstr *str)
 {
+       int err;
+
        entry->type = cpu_to_be16(type);
        entry->thread.reserved = 0;
        entry->thread.parentID = cpu_to_be32(parentid);
-       hfsplus_asc2uni(sb, &entry->thread.nodeName, HFSPLUS_MAX_STRLEN,
+       err = hfsplus_asc2uni(sb, &entry->thread.nodeName, HFSPLUS_MAX_STRLEN,
                                str->name, str->len);
+       if (unlikely(err < 0))
+               return err;
+
        return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2;
 }
 
@@ -183,7 +197,7 @@ int hfsplus_find_cat(struct super_block *sb, u32 cnid,
        int err;
        u16 type;
 
-       hfsplus_cat_build_key(sb, fd->search_key, cnid, NULL);
+       hfsplus_cat_build_key_with_cnid(sb, fd->search_key, cnid);
        err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry));
        if (err)
                return err;
@@ -250,11 +264,16 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,
        if (err)
                return err;
 
-       hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
+       hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
        entry_size = hfsplus_fill_cat_thread(sb, &entry,
                S_ISDIR(inode->i_mode) ?
                        HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD,
                dir->i_ino, str);
+       if (unlikely(entry_size < 0)) {
+               err = entry_size;
+               goto err2;
+       }
+
        err = hfs_brec_find(&fd, hfs_find_rec_by_key);
        if (err != -ENOENT) {
                if (!err)
@@ -265,7 +284,10 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,
        if (err)
                goto err2;
 
-       hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
+       err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
+       if (unlikely(err))
+               goto err1;
+
        entry_size = hfsplus_cat_build_record(&entry, cnid, inode);
        err = hfs_brec_find(&fd, hfs_find_rec_by_key);
        if (err != -ENOENT) {
@@ -288,7 +310,7 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,
        return 0;
 
 err1:
-       hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
+       hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
        if (!hfs_brec_find(&fd, hfs_find_rec_by_key))
                hfs_brec_remove(&fd);
 err2:
@@ -313,7 +335,7 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
        if (!str) {
                int len;
 
-               hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
+               hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
                err = hfs_brec_find(&fd, hfs_find_rec_by_key);
                if (err)
                        goto out;
@@ -329,7 +351,9 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
                        off + 2, len);
                fd.search_key->key_len = cpu_to_be16(6 + len);
        } else
-               hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
+               err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
+               if (unlikely(err))
+                       goto out;
 
        err = hfs_brec_find(&fd, hfs_find_rec_by_key);
        if (err)
@@ -360,7 +384,7 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
        if (err)
                goto out;
 
-       hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
+       hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
        err = hfs_brec_find(&fd, hfs_find_rec_by_key);
        if (err)
                goto out;
@@ -405,7 +429,11 @@ int hfsplus_rename_cat(u32 cnid,
        dst_fd = src_fd;
 
        /* find the old dir entry and read the data */
-       hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
+       err = hfsplus_cat_build_key(sb, src_fd.search_key,
+                       src_dir->i_ino, src_name);
+       if (unlikely(err))
+               goto out;
+
        err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
        if (err)
                goto out;
@@ -419,7 +447,11 @@ int hfsplus_rename_cat(u32 cnid,
        type = be16_to_cpu(entry.type);
 
        /* create new dir entry with the data from the old entry */
-       hfsplus_cat_build_key(sb, dst_fd.search_key, dst_dir->i_ino, dst_name);
+       err = hfsplus_cat_build_key(sb, dst_fd.search_key,
+                       dst_dir->i_ino, dst_name);
+       if (unlikely(err))
+               goto out;
+
        err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
        if (err != -ENOENT) {
                if (!err)
@@ -436,7 +468,11 @@ int hfsplus_rename_cat(u32 cnid,
        dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC;
 
        /* finally remove the old entry */
-       hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
+       err = hfsplus_cat_build_key(sb, src_fd.search_key,
+                       src_dir->i_ino, src_name);
+       if (unlikely(err))
+               goto out;
+
        err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
        if (err)
                goto out;
@@ -449,7 +485,7 @@ int hfsplus_rename_cat(u32 cnid,
        src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC;
 
        /* remove old thread entry */
-       hfsplus_cat_build_key(sb, src_fd.search_key, cnid, NULL);
+       hfsplus_cat_build_key_with_cnid(sb, src_fd.search_key, cnid);
        err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
        if (err)
                goto out;
@@ -459,9 +495,14 @@ int hfsplus_rename_cat(u32 cnid,
                goto out;
 
        /* create new thread entry */
-       hfsplus_cat_build_key(sb, dst_fd.search_key, cnid, NULL);
+       hfsplus_cat_build_key_with_cnid(sb, dst_fd.search_key, cnid);
        entry_size = hfsplus_fill_cat_thread(sb, &entry, type,
                dst_dir->i_ino, dst_name);
+       if (unlikely(entry_size < 0)) {
+               err = entry_size;
+               goto out;
+       }
+
        err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
        if (err != -ENOENT) {
                if (!err)
index 610a326..435bea2 100644 (file)
@@ -44,7 +44,10 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
        err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
        if (err)
                return ERR_PTR(err);
-       hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name);
+       err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino,
+                       &dentry->d_name);
+       if (unlikely(err < 0))
+               goto fail;
 again:
        err = hfs_brec_read(&fd, &entry, sizeof(entry));
        if (err) {
@@ -97,9 +100,11 @@ again:
                                        be32_to_cpu(entry.file.permissions.dev);
                                str.len = sprintf(name, "iNode%d", linkid);
                                str.name = name;
-                               hfsplus_cat_build_key(sb, fd.search_key,
+                               err = hfsplus_cat_build_key(sb, fd.search_key,
                                        HFSPLUS_SB(sb)->hidden_dir->i_ino,
                                        &str);
+                               if (unlikely(err < 0))
+                                       goto fail;
                                goto again;
                        }
                } else if (!dentry->d_fsdata)
@@ -145,7 +150,7 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx)
                err = -ENOMEM;
                goto out;
        }
-       hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
+       hfsplus_cat_build_key_with_cnid(sb, fd.search_key, inode->i_ino);
        err = hfs_brec_find(&fd, hfs_find_rec_by_key);
        if (err)
                goto out;
index eb5e059..b0441d6 100644 (file)
@@ -443,8 +443,10 @@ int hfsplus_cat_case_cmp_key(const hfsplus_btree_key *k1,
                             const hfsplus_btree_key *k2);
 int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *k1,
                            const hfsplus_btree_key *k2);
-void hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *key,
+int hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *key,
                           u32 parent, struct qstr *str);
+void hfsplus_cat_build_key_with_cnid(struct super_block *sb,
+                                    hfsplus_btree_key *key, u32 parent);
 void hfsplus_cat_set_perms(struct inode *inode, struct hfsplus_perm *perms);
 int hfsplus_find_cat(struct super_block *sb, u32 cnid,
                     struct hfs_find_data *fd);
index 4cf2024..593af2f 100644 (file)
@@ -515,7 +515,9 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
        err = hfs_find_init(sbi->cat_tree, &fd);
        if (err)
                goto out_put_root;
-       hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str);
+       err = hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str);
+       if (unlikely(err < 0))
+               goto out_put_root;
        if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
                hfs_find_exit(&fd);
                if (entry.type != cpu_to_be16(HFSPLUS_FOLDER))
index a93bf98..fcae9ef 100644 (file)
@@ -5662,7 +5662,7 @@ int ocfs2_remove_btree_range(struct inode *inode,
                             struct ocfs2_extent_tree *et,
                             u32 cpos, u32 phys_cpos, u32 len, int flags,
                             struct ocfs2_cached_dealloc_ctxt *dealloc,
-                            u64 refcount_loc)
+                            u64 refcount_loc, bool refcount_tree_locked)
 {
        int ret, credits = 0, extra_blocks = 0;
        u64 phys_blkno = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos);
@@ -5676,11 +5676,13 @@ int ocfs2_remove_btree_range(struct inode *inode,
                BUG_ON(!(OCFS2_I(inode)->ip_dyn_features &
                         OCFS2_HAS_REFCOUNT_FL));
 
-               ret = ocfs2_lock_refcount_tree(osb, refcount_loc, 1,
-                                              &ref_tree, NULL);
-               if (ret) {
-                       mlog_errno(ret);
-                       goto bail;
+               if (!refcount_tree_locked) {
+                       ret = ocfs2_lock_refcount_tree(osb, refcount_loc, 1,
+                                                      &ref_tree, NULL);
+                       if (ret) {
+                               mlog_errno(ret);
+                               goto bail;
+                       }
                }
 
                ret = ocfs2_prepare_refcount_change_for_del(inode,
@@ -7021,6 +7023,7 @@ int ocfs2_commit_truncate(struct ocfs2_super *osb,
        u64 refcount_loc = le64_to_cpu(di->i_refcount_loc);
        struct ocfs2_extent_tree et;
        struct ocfs2_cached_dealloc_ctxt dealloc;
+       struct ocfs2_refcount_tree *ref_tree = NULL;
 
        ocfs2_init_dinode_extent_tree(&et, INODE_CACHE(inode), di_bh);
        ocfs2_init_dealloc_ctxt(&dealloc);
@@ -7130,9 +7133,18 @@ start:
 
        phys_cpos = ocfs2_blocks_to_clusters(inode->i_sb, blkno);
 
+       if ((flags & OCFS2_EXT_REFCOUNTED) && trunc_len && !ref_tree) {
+               status = ocfs2_lock_refcount_tree(osb, refcount_loc, 1,
+                               &ref_tree, NULL);
+               if (status) {
+                       mlog_errno(status);
+                       goto bail;
+               }
+       }
+
        status = ocfs2_remove_btree_range(inode, &et, trunc_cpos,
                                          phys_cpos, trunc_len, flags, &dealloc,
-                                         refcount_loc);
+                                         refcount_loc, true);
        if (status < 0) {
                mlog_errno(status);
                goto bail;
@@ -7147,6 +7159,8 @@ start:
        goto start;
 
 bail:
+       if (ref_tree)
+               ocfs2_unlock_refcount_tree(osb, ref_tree, 1);
 
        ocfs2_schedule_truncate_log_flush(osb, 1);
 
index ca381c5..fb09b97 100644 (file)
@@ -142,7 +142,7 @@ int ocfs2_remove_btree_range(struct inode *inode,
                             struct ocfs2_extent_tree *et,
                             u32 cpos, u32 phys_cpos, u32 len, int flags,
                             struct ocfs2_cached_dealloc_ctxt *dealloc,
-                            u64 refcount_loc);
+                            u64 refcount_loc, bool refcount_tree_locked);
 
 int ocfs2_num_free_extents(struct ocfs2_super *osb,
                           struct ocfs2_extent_tree *et);
index d9f2229..46d93e9 100644 (file)
@@ -894,7 +894,7 @@ void ocfs2_unlock_and_free_pages(struct page **pages, int num_pages)
        }
 }
 
-static void ocfs2_free_write_ctxt(struct ocfs2_write_ctxt *wc)
+static void ocfs2_unlock_pages(struct ocfs2_write_ctxt *wc)
 {
        int i;
 
@@ -915,7 +915,11 @@ static void ocfs2_free_write_ctxt(struct ocfs2_write_ctxt *wc)
                page_cache_release(wc->w_target_page);
        }
        ocfs2_unlock_and_free_pages(wc->w_pages, wc->w_num_pages);
+}
 
+static void ocfs2_free_write_ctxt(struct ocfs2_write_ctxt *wc)
+{
+       ocfs2_unlock_pages(wc);
        brelse(wc->w_di_bh);
        kfree(wc);
 }
@@ -2042,11 +2046,19 @@ out_write_size:
        ocfs2_update_inode_fsync_trans(handle, inode, 1);
        ocfs2_journal_dirty(handle, wc->w_di_bh);
 
+       /* unlock pages before dealloc since it needs acquiring j_trans_barrier
+        * lock, or it will cause a deadlock since journal commit threads holds
+        * this lock and will ask for the page lock when flushing the data.
+        * put it here to preserve the unlock order.
+        */
+       ocfs2_unlock_pages(wc);
+
        ocfs2_commit_trans(osb, handle);
 
        ocfs2_run_deallocs(osb, &wc->w_dealloc);
 
-       ocfs2_free_write_ctxt(wc);
+       brelse(wc->w_di_bh);
+       kfree(wc);
 
        return copied;
 }
index 79d56dc..319e786 100644 (file)
@@ -4479,7 +4479,7 @@ int ocfs2_dx_dir_truncate(struct inode *dir, struct buffer_head *di_bh)
                p_cpos = ocfs2_blocks_to_clusters(dir->i_sb, blkno);
 
                ret = ocfs2_remove_btree_range(dir, &et, cpos, p_cpos, clen, 0,
-                                              &dealloc, 0);
+                                              &dealloc, 0, false);
                if (ret) {
                        mlog_errno(ret);
                        goto out;
index 3689b35..a6944b2 100644 (file)
@@ -695,14 +695,6 @@ void __dlm_lockres_grab_inflight_worker(struct dlm_ctxt *dlm,
                        res->inflight_assert_workers);
 }
 
-static void dlm_lockres_grab_inflight_worker(struct dlm_ctxt *dlm,
-               struct dlm_lock_resource *res)
-{
-       spin_lock(&res->spinlock);
-       __dlm_lockres_grab_inflight_worker(dlm, res);
-       spin_unlock(&res->spinlock);
-}
-
 static void __dlm_lockres_drop_inflight_worker(struct dlm_ctxt *dlm,
                struct dlm_lock_resource *res)
 {
@@ -1646,6 +1638,7 @@ send_response:
                }
                mlog(0, "%u is the owner of %.*s, cleaning everyone else\n",
                             dlm->node_num, res->lockname.len, res->lockname.name);
+               spin_lock(&res->spinlock);
                ret = dlm_dispatch_assert_master(dlm, res, 0, request->node_idx,
                                                 DLM_ASSERT_MASTER_MLE_CLEANUP);
                if (ret < 0) {
@@ -1653,7 +1646,8 @@ send_response:
                        response = DLM_MASTER_RESP_ERROR;
                        dlm_lockres_put(res);
                } else
-                       dlm_lockres_grab_inflight_worker(dlm, res);
+                       __dlm_lockres_grab_inflight_worker(dlm, res);
+               spin_unlock(&res->spinlock);
        } else {
                if (res)
                        dlm_lockres_put(res);
index 69fb9f7..3950693 100644 (file)
@@ -1803,7 +1803,7 @@ static int ocfs2_remove_inode_range(struct inode *inode,
 
                ret = ocfs2_remove_btree_range(inode, &et, trunc_cpos,
                                               phys_cpos, trunc_len, flags,
-                                              &dealloc, refcount_loc);
+                                              &dealloc, refcount_loc, false);
                if (ret < 0) {
                        mlog_errno(ret);
                        goto out;
index aa1eee0..d3ebf2e 100644 (file)
@@ -12,6 +12,9 @@
 #include <linux/vmstat.h>
 #include <linux/atomic.h>
 #include <linux/vmalloc.h>
+#ifdef CONFIG_CMA
+#include <linux/cma.h>
+#endif
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include "internal.h"
@@ -137,6 +140,10 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
 #endif
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
                "AnonHugePages:  %8lu kB\n"
+#endif
+#ifdef CONFIG_CMA
+               "CmaTotal:       %8lu kB\n"
+               "CmaFree:        %8lu kB\n"
 #endif
                ,
                K(i.totalram),
@@ -187,11 +194,15 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
                vmi.used >> 10,
                vmi.largest_chunk >> 10
 #ifdef CONFIG_MEMORY_FAILURE
-               ,atomic_long_read(&num_poisoned_pages) << (PAGE_SHIFT - 10)
+               , atomic_long_read(&num_poisoned_pages) << (PAGE_SHIFT - 10)
 #endif
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
-               ,K(global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) *
+               , K(global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) *
                   HPAGE_PMD_NR)
+#endif
+#ifdef CONFIG_CMA
+               , K(totalcma_pages)
+               , K(global_page_state(NR_FREE_CMA_PAGES))
 #endif
                );
 
index 7581518..61e32ec 100644 (file)
@@ -313,6 +313,7 @@ struct acpi_device_wakeup_flags {
        u8 valid:1;             /* Can successfully enable wakeup? */
        u8 run_wake:1;          /* Run-Wake GPE devices */
        u8 notifier_present:1;  /* Wake-up notify handler has been installed */
+       u8 enabled:1;           /* Enabled for wakeup */
 };
 
 struct acpi_device_wakeup_context {
index a93438b..9384ba6 100644 (file)
@@ -15,6 +15,7 @@
 
 struct cma;
 
+extern unsigned long totalcma_pages;
 extern phys_addr_t cma_get_base(struct cma *cma);
 extern unsigned long cma_get_size(struct cma *cma);
 
index ea4f1c4..4e5bd81 100644 (file)
@@ -119,6 +119,15 @@ enum {
        MLX5_MAX_LOG_PKEY_TABLE  = 5,
 };
 
+enum {
+       MLX5_MKEY_INBOX_PG_ACCESS = 1 << 31
+};
+
+enum {
+       MLX5_PFAULT_SUBTYPE_WQE = 0,
+       MLX5_PFAULT_SUBTYPE_RDMA = 1,
+};
+
 enum {
        MLX5_PERM_LOCAL_READ    = 1 << 2,
        MLX5_PERM_LOCAL_WRITE   = 1 << 3,
@@ -180,6 +189,19 @@ enum {
        MLX5_MKEY_MASK_FREE             = 1ull << 29,
 };
 
+enum {
+       MLX5_UMR_TRANSLATION_OFFSET_EN  = (1 << 4),
+
+       MLX5_UMR_CHECK_NOT_FREE         = (1 << 5),
+       MLX5_UMR_CHECK_FREE             = (2 << 5),
+
+       MLX5_UMR_INLINE                 = (1 << 7),
+};
+
+#define MLX5_UMR_MTT_ALIGNMENT 0x40
+#define MLX5_UMR_MTT_MASK      (MLX5_UMR_MTT_ALIGNMENT - 1)
+#define MLX5_UMR_MTT_MIN_CHUNK_SIZE MLX5_UMR_MTT_ALIGNMENT
+
 enum mlx5_event {
        MLX5_EVENT_TYPE_COMP               = 0x0,
 
@@ -206,6 +228,8 @@ enum mlx5_event {
 
        MLX5_EVENT_TYPE_CMD                = 0x0a,
        MLX5_EVENT_TYPE_PAGE_REQUEST       = 0xb,
+
+       MLX5_EVENT_TYPE_PAGE_FAULT         = 0xc,
 };
 
 enum {
@@ -225,6 +249,7 @@ enum {
        MLX5_DEV_CAP_FLAG_APM           = 1LL << 17,
        MLX5_DEV_CAP_FLAG_ATOMIC        = 1LL << 18,
        MLX5_DEV_CAP_FLAG_BLOCK_MCAST   = 1LL << 23,
+       MLX5_DEV_CAP_FLAG_ON_DMND_PG    = 1LL << 24,
        MLX5_DEV_CAP_FLAG_CQ_MODER      = 1LL << 29,
        MLX5_DEV_CAP_FLAG_RESIZE_CQ     = 1LL << 30,
        MLX5_DEV_CAP_FLAG_DCT           = 1LL << 37,
@@ -290,6 +315,8 @@ enum {
 enum {
        HCA_CAP_OPMOD_GET_MAX   = 0,
        HCA_CAP_OPMOD_GET_CUR   = 1,
+       HCA_CAP_OPMOD_GET_ODP_MAX = 4,
+       HCA_CAP_OPMOD_GET_ODP_CUR = 5
 };
 
 struct mlx5_inbox_hdr {
@@ -319,6 +346,23 @@ struct mlx5_cmd_query_adapter_mbox_out {
        u8                      vsd_psid[16];
 };
 
+enum mlx5_odp_transport_cap_bits {
+       MLX5_ODP_SUPPORT_SEND    = 1 << 31,
+       MLX5_ODP_SUPPORT_RECV    = 1 << 30,
+       MLX5_ODP_SUPPORT_WRITE   = 1 << 29,
+       MLX5_ODP_SUPPORT_READ    = 1 << 28,
+};
+
+struct mlx5_odp_caps {
+       char reserved[0x10];
+       struct {
+               __be32                  rc_odp_caps;
+               __be32                  uc_odp_caps;
+               __be32                  ud_odp_caps;
+       } per_transport_caps;
+       char reserved2[0xe4];
+};
+
 struct mlx5_cmd_init_hca_mbox_in {
        struct mlx5_inbox_hdr   hdr;
        u8                      rsvd0[2];
@@ -439,6 +483,27 @@ struct mlx5_eqe_page_req {
        __be32          rsvd1[5];
 };
 
+struct mlx5_eqe_page_fault {
+       __be32 bytes_committed;
+       union {
+               struct {
+                       u16     reserved1;
+                       __be16  wqe_index;
+                       u16     reserved2;
+                       __be16  packet_length;
+                       u8      reserved3[12];
+               } __packed wqe;
+               struct {
+                       __be32  r_key;
+                       u16     reserved1;
+                       __be16  packet_length;
+                       __be32  rdma_op_len;
+                       __be64  rdma_va;
+               } __packed rdma;
+       } __packed;
+       __be32 flags_qpn;
+} __packed;
+
 union ev_data {
        __be32                          raw[7];
        struct mlx5_eqe_cmd             cmd;
@@ -450,6 +515,7 @@ union ev_data {
        struct mlx5_eqe_congestion      cong;
        struct mlx5_eqe_stall_vl        stall_vl;
        struct mlx5_eqe_page_req        req_pages;
+       struct mlx5_eqe_page_fault      page_fault;
 } __packed;
 
 struct mlx5_eqe {
@@ -776,6 +842,10 @@ struct mlx5_query_eq_mbox_out {
        struct mlx5_eq_context  ctx;
 };
 
+enum {
+       MLX5_MKEY_STATUS_FREE = 1 << 6,
+};
+
 struct mlx5_mkey_seg {
        /* This is a two bit field occupying bits 31-30.
         * bit 31 is always 0,
@@ -812,7 +882,7 @@ struct mlx5_query_special_ctxs_mbox_out {
 struct mlx5_create_mkey_mbox_in {
        struct mlx5_inbox_hdr   hdr;
        __be32                  input_mkey_index;
-       u8                      rsvd0[4];
+       __be32                  flags;
        struct mlx5_mkey_seg    seg;
        u8                      rsvd1[16];
        __be32                  xlat_oct_act_size;
index b1bf415..166d931 100644 (file)
@@ -113,6 +113,13 @@ enum {
        MLX5_REG_HOST_ENDIANNESS = 0x7004,
 };
 
+enum mlx5_page_fault_resume_flags {
+       MLX5_PAGE_FAULT_RESUME_REQUESTOR = 1 << 0,
+       MLX5_PAGE_FAULT_RESUME_WRITE     = 1 << 1,
+       MLX5_PAGE_FAULT_RESUME_RDMA      = 1 << 2,
+       MLX5_PAGE_FAULT_RESUME_ERROR     = 1 << 7,
+};
+
 enum dbg_rsc_type {
        MLX5_DBG_RSC_QP,
        MLX5_DBG_RSC_EQ,
@@ -467,7 +474,7 @@ struct mlx5_priv {
        struct workqueue_struct *pg_wq;
        struct rb_root          page_root;
        int                     fw_pages;
-       int                     reg_pages;
+       atomic_t                reg_pages;
        struct list_head        free_list;
 
        struct mlx5_core_health health;
@@ -703,6 +710,9 @@ void mlx5_eq_cleanup(struct mlx5_core_dev *dev);
 void mlx5_fill_page_array(struct mlx5_buf *buf, __be64 *pas);
 void mlx5_cq_completion(struct mlx5_core_dev *dev, u32 cqn);
 void mlx5_rsc_event(struct mlx5_core_dev *dev, u32 rsn, int event_type);
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+void mlx5_eq_pagefault(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe);
+#endif
 void mlx5_srq_event(struct mlx5_core_dev *dev, u32 srqn, int event_type);
 struct mlx5_core_srq *mlx5_core_get_srq(struct mlx5_core_dev *dev, u32 srqn);
 void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector);
@@ -740,6 +750,8 @@ int mlx5_core_create_psv(struct mlx5_core_dev *dev, u32 pdn,
                         int npsvs, u32 *sig_index);
 int mlx5_core_destroy_psv(struct mlx5_core_dev *dev, int psv_num);
 void mlx5_core_put_rsc(struct mlx5_core_rsc_common *common);
+int mlx5_query_odp_caps(struct mlx5_core_dev *dev,
+                       struct mlx5_odp_caps *odp_caps);
 
 static inline u32 mlx5_mkey_to_idx(u32 mkey)
 {
index 3fa075d..61f7a34 100644 (file)
@@ -50,6 +50,9 @@
 #define MLX5_BSF_APPTAG_ESCAPE 0x1
 #define MLX5_BSF_APPREF_ESCAPE 0x2
 
+#define MLX5_QPN_BITS          24
+#define MLX5_QPN_MASK          ((1 << MLX5_QPN_BITS) - 1)
+
 enum mlx5_qp_optpar {
        MLX5_QP_OPTPAR_ALT_ADDR_PATH            = 1 << 0,
        MLX5_QP_OPTPAR_RRE                      = 1 << 1,
@@ -189,6 +192,14 @@ struct mlx5_wqe_ctrl_seg {
        __be32                  imm;
 };
 
+#define MLX5_WQE_CTRL_DS_MASK 0x3f
+#define MLX5_WQE_CTRL_QPN_MASK 0xffffff00
+#define MLX5_WQE_CTRL_QPN_SHIFT 8
+#define MLX5_WQE_DS_UNITS 16
+#define MLX5_WQE_CTRL_OPCODE_MASK 0xff
+#define MLX5_WQE_CTRL_WQE_INDEX_MASK 0x00ffff00
+#define MLX5_WQE_CTRL_WQE_INDEX_SHIFT 8
+
 struct mlx5_wqe_xrc_seg {
        __be32                  xrc_srqn;
        u8                      rsvd[12];
@@ -292,6 +303,8 @@ struct mlx5_wqe_signature_seg {
        u8      rsvd1[11];
 };
 
+#define MLX5_WQE_INLINE_SEG_BYTE_COUNT_MASK 0x3ff
+
 struct mlx5_wqe_inline_seg {
        __be32  byte_count;
 };
@@ -360,9 +373,46 @@ struct mlx5_stride_block_ctrl_seg {
        __be16          num_entries;
 };
 
+enum mlx5_pagefault_flags {
+       MLX5_PFAULT_REQUESTOR = 1 << 0,
+       MLX5_PFAULT_WRITE     = 1 << 1,
+       MLX5_PFAULT_RDMA      = 1 << 2,
+};
+
+/* Contains the details of a pagefault. */
+struct mlx5_pagefault {
+       u32                     bytes_committed;
+       u8                      event_subtype;
+       enum mlx5_pagefault_flags flags;
+       union {
+               /* Initiator or send message responder pagefault details. */
+               struct {
+                       /* Received packet size, only valid for responders. */
+                       u32     packet_size;
+                       /*
+                        * WQE index. Refers to either the send queue or
+                        * receive queue, according to event_subtype.
+                        */
+                       u16     wqe_index;
+               } wqe;
+               /* RDMA responder pagefault details */
+               struct {
+                       u32     r_key;
+                       /*
+                        * Received packet size, minimal size page fault
+                        * resolution required for forward progress.
+                        */
+                       u32     packet_size;
+                       u32     rdma_op_len;
+                       u64     rdma_va;
+               } rdma;
+       };
+};
+
 struct mlx5_core_qp {
        struct mlx5_core_rsc_common     common; /* must be first */
        void (*event)           (struct mlx5_core_qp *, int);
+       void (*pfault_handler)(struct mlx5_core_qp *, struct mlx5_pagefault *);
        int                     qpn;
        struct mlx5_rsc_debug   *dbg;
        int                     pid;
@@ -530,6 +580,17 @@ static inline struct mlx5_core_mr *__mlx5_mr_lookup(struct mlx5_core_dev *dev, u
        return radix_tree_lookup(&dev->priv.mr_table.tree, key);
 }
 
+struct mlx5_page_fault_resume_mbox_in {
+       struct mlx5_inbox_hdr   hdr;
+       __be32                  flags_qpn;
+       u8                      reserved[4];
+};
+
+struct mlx5_page_fault_resume_mbox_out {
+       struct mlx5_outbox_hdr  hdr;
+       u8                      rsvd[8];
+};
+
 int mlx5_core_create_qp(struct mlx5_core_dev *dev,
                        struct mlx5_core_qp *qp,
                        struct mlx5_create_qp_mbox_in *in,
@@ -549,6 +610,10 @@ void mlx5_init_qp_table(struct mlx5_core_dev *dev);
 void mlx5_cleanup_qp_table(struct mlx5_core_dev *dev);
 int mlx5_debug_qp_add(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp);
 void mlx5_debug_qp_remove(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp);
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+int mlx5_core_page_fault_resume(struct mlx5_core_dev *dev, u32 qpn,
+                               u8 context, int error);
+#endif
 
 static inline const char *mlx5_qp_type_str(int type)
 {
index 71f282a..ebfb0e1 100644 (file)
@@ -210,20 +210,6 @@ enum module_state {
        MODULE_STATE_UNFORMED,  /* Still setting it up. */
 };
 
-/**
- * struct module_ref - per cpu module reference counts
- * @incs: number of module get on this cpu
- * @decs: number of module put on this cpu
- *
- * We force an alignment on 8 or 16 bytes, so that alloc_percpu()
- * put @incs/@decs in same cache line, with no extra memory cost,
- * since alloc_percpu() is fine grained.
- */
-struct module_ref {
-       unsigned long incs;
-       unsigned long decs;
-} __attribute((aligned(2 * sizeof(unsigned long))));
-
 struct module {
        enum module_state state;
 
@@ -367,7 +353,7 @@ struct module {
        /* Destruction function. */
        void (*exit)(void);
 
-       struct module_ref __percpu *refptr;
+       atomic_t refcnt;
 #endif
 
 #ifdef CONFIG_CONSTRUCTORS
index 66a656e..8b59763 100644 (file)
@@ -351,8 +351,6 @@ struct dev_pm_ops {
 #define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn)
 #endif
 
-#define SET_PM_RUNTIME_PM_OPS  SET_RUNTIME_PM_OPS
-
 /*
  * Use this if you want to use the same suspend and resume callbacks for suspend
  * to RAM and hibernation.
index 7979f85..ca3ed78 100644 (file)
@@ -19,6 +19,9 @@
  *     offset: the offset of the configuration field
  *     buf: the buffer to read the field value from.
  *     len: the length of the buffer
+ * @generation: config generation counter
+ *     vdev: the virtio_device
+ *     Returns the config generation counter
  * @get_status: read the status byte
  *     vdev: the virtio_device
  *     Returns the status byte
@@ -60,6 +63,7 @@ struct virtio_config_ops {
                    void *buf, unsigned len);
        void (*set)(struct virtio_device *vdev, unsigned offset,
                    const void *buf, unsigned len);
+       u32 (*generation)(struct virtio_device *vdev);
        u8 (*get_status)(struct virtio_device *vdev);
        void (*set_status)(struct virtio_device *vdev, u8 status);
        void (*reset)(struct virtio_device *vdev);
@@ -301,11 +305,33 @@ static inline u8 virtio_cread8(struct virtio_device *vdev, unsigned int offset)
        return ret;
 }
 
+/* Read @count fields, @bytes each. */
+static inline void __virtio_cread_many(struct virtio_device *vdev,
+                                      unsigned int offset,
+                                      void *buf, size_t count, size_t bytes)
+{
+       u32 old, gen = vdev->config->generation ?
+               vdev->config->generation(vdev) : 0;
+       int i;
+
+       do {
+               old = gen;
+
+               for (i = 0; i < count; i++)
+                       vdev->config->get(vdev, offset + bytes * i,
+                                         buf + i * bytes, bytes);
+
+               gen = vdev->config->generation ?
+                       vdev->config->generation(vdev) : 0;
+       } while (gen != old);
+}
+
+
 static inline void virtio_cread_bytes(struct virtio_device *vdev,
                                      unsigned int offset,
                                      void *buf, size_t len)
 {
-       vdev->config->get(vdev, offset, buf, len);
+       __virtio_cread_many(vdev, offset, buf, len, 1);
 }
 
 static inline void virtio_cwrite8(struct virtio_device *vdev,
@@ -349,6 +375,7 @@ static inline u64 virtio_cread64(struct virtio_device *vdev,
 {
        u64 ret;
        vdev->config->get(vdev, offset, &ret, sizeof(ret));
+       __virtio_cread_many(vdev, offset, &ret, 1, sizeof(ret));
        return virtio64_to_cpu(vdev, (__force __virtio64)ret);
 }
 
index 749cde2..a3fa537 100644 (file)
 #ifndef _LINUX_VRINGH_H
 #define _LINUX_VRINGH_H
 #include <uapi/linux/virtio_ring.h>
+#include <linux/virtio_byteorder.h>
 #include <linux/uio.h>
 #include <linux/slab.h>
 #include <asm/barrier.h>
 
 /* virtio_ring with information needed for host access. */
 struct vringh {
+       /* Everything is little endian */
+       bool little_endian;
+
        /* Guest publishes used event idx (note: we always do). */
        bool event_indices;
 
@@ -105,7 +109,7 @@ struct vringh_kiov {
 #define VRINGH_IOV_ALLOCATED 0x8000000
 
 /* Helpers for userspace vrings. */
-int vringh_init_user(struct vringh *vrh, u32 features,
+int vringh_init_user(struct vringh *vrh, u64 features,
                     unsigned int num, bool weak_barriers,
                     struct vring_desc __user *desc,
                     struct vring_avail __user *avail,
@@ -167,7 +171,7 @@ bool vringh_notify_enable_user(struct vringh *vrh);
 void vringh_notify_disable_user(struct vringh *vrh);
 
 /* Helpers for kernelspace vrings. */
-int vringh_init_kern(struct vringh *vrh, u32 features,
+int vringh_init_kern(struct vringh *vrh, u64 features,
                     unsigned int num, bool weak_barriers,
                     struct vring_desc *desc,
                     struct vring_avail *avail,
@@ -222,4 +226,33 @@ static inline void vringh_notify(struct vringh *vrh)
                vrh->notify(vrh);
 }
 
+static inline u16 vringh16_to_cpu(const struct vringh *vrh, __virtio16 val)
+{
+       return __virtio16_to_cpu(vrh->little_endian, val);
+}
+
+static inline __virtio16 cpu_to_vringh16(const struct vringh *vrh, u16 val)
+{
+       return __cpu_to_virtio16(vrh->little_endian, val);
+}
+
+static inline u32 vringh32_to_cpu(const struct vringh *vrh, __virtio32 val)
+{
+       return __virtio32_to_cpu(vrh->little_endian, val);
+}
+
+static inline __virtio32 cpu_to_vringh32(const struct vringh *vrh, u32 val)
+{
+       return __cpu_to_virtio32(vrh->little_endian, val);
+}
+
+static inline u64 vringh64_to_cpu(const struct vringh *vrh, __virtio64 val)
+{
+       return __virtio64_to_cpu(vrh->little_endian, val);
+}
+
+static inline __virtio64 cpu_to_vringh64(const struct vringh *vrh, u64 val)
+{
+       return __cpu_to_virtio64(vrh->little_endian, val);
+}
 #endif /* _LINUX_VRINGH_H */
index a2bf41e..2d83cfd 100644 (file)
 #include <linux/workqueue.h>
 
 struct ib_ucontext;
+struct ib_umem_odp;
 
 struct ib_umem {
        struct ib_ucontext     *context;
        size_t                  length;
-       int                     offset;
+       unsigned long           address;
        int                     page_size;
        int                     writable;
        int                     hugetlb;
@@ -50,17 +51,43 @@ struct ib_umem {
        struct pid             *pid;
        struct mm_struct       *mm;
        unsigned long           diff;
+       struct ib_umem_odp     *odp_data;
        struct sg_table sg_head;
        int             nmap;
        int             npages;
 };
 
+/* Returns the offset of the umem start relative to the first page. */
+static inline int ib_umem_offset(struct ib_umem *umem)
+{
+       return umem->address & ((unsigned long)umem->page_size - 1);
+}
+
+/* Returns the first page of an ODP umem. */
+static inline unsigned long ib_umem_start(struct ib_umem *umem)
+{
+       return umem->address - ib_umem_offset(umem);
+}
+
+/* Returns the address of the page after the last one of an ODP umem. */
+static inline unsigned long ib_umem_end(struct ib_umem *umem)
+{
+       return PAGE_ALIGN(umem->address + umem->length);
+}
+
+static inline size_t ib_umem_num_pages(struct ib_umem *umem)
+{
+       return (ib_umem_end(umem) - ib_umem_start(umem)) >> PAGE_SHIFT;
+}
+
 #ifdef CONFIG_INFINIBAND_USER_MEM
 
 struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
                            size_t size, int access, int dmasync);
 void ib_umem_release(struct ib_umem *umem);
 int ib_umem_page_count(struct ib_umem *umem);
+int ib_umem_copy_from(void *dst, struct ib_umem *umem, size_t offset,
+                     size_t length);
 
 #else /* CONFIG_INFINIBAND_USER_MEM */
 
@@ -73,7 +100,10 @@ static inline struct ib_umem *ib_umem_get(struct ib_ucontext *context,
 }
 static inline void ib_umem_release(struct ib_umem *umem) { }
 static inline int ib_umem_page_count(struct ib_umem *umem) { return 0; }
-
+static inline int ib_umem_copy_from(void *dst, struct ib_umem *umem, size_t offset,
+                                   size_t length) {
+       return -EINVAL;
+}
 #endif /* CONFIG_INFINIBAND_USER_MEM */
 
 #endif /* IB_UMEM_H */
diff --git a/include/rdma/ib_umem_odp.h b/include/rdma/ib_umem_odp.h
new file mode 100644 (file)
index 0000000..3da0b16
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2014 Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef IB_UMEM_ODP_H
+#define IB_UMEM_ODP_H
+
+#include <rdma/ib_umem.h>
+#include <rdma/ib_verbs.h>
+#include <linux/interval_tree.h>
+
+struct umem_odp_node {
+       u64 __subtree_last;
+       struct rb_node rb;
+};
+
+struct ib_umem_odp {
+       /*
+        * An array of the pages included in the on-demand paging umem.
+        * Indices of pages that are currently not mapped into the device will
+        * contain NULL.
+        */
+       struct page             **page_list;
+       /*
+        * An array of the same size as page_list, with DMA addresses mapped
+        * for pages the pages in page_list. The lower two bits designate
+        * access permissions. See ODP_READ_ALLOWED_BIT and
+        * ODP_WRITE_ALLOWED_BIT.
+        */
+       dma_addr_t              *dma_list;
+       /*
+        * The umem_mutex protects the page_list and dma_list fields of an ODP
+        * umem, allowing only a single thread to map/unmap pages. The mutex
+        * also protects access to the mmu notifier counters.
+        */
+       struct mutex            umem_mutex;
+       void                    *private; /* for the HW driver to use. */
+
+       /* When false, use the notifier counter in the ucontext struct. */
+       bool mn_counters_active;
+       int notifiers_seq;
+       int notifiers_count;
+
+       /* A linked list of umems that don't have private mmu notifier
+        * counters yet. */
+       struct list_head no_private_counters;
+       struct ib_umem          *umem;
+
+       /* Tree tracking */
+       struct umem_odp_node    interval_tree;
+
+       struct completion       notifier_completion;
+       int                     dying;
+};
+
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+
+int ib_umem_odp_get(struct ib_ucontext *context, struct ib_umem *umem);
+
+void ib_umem_odp_release(struct ib_umem *umem);
+
+/*
+ * The lower 2 bits of the DMA address signal the R/W permissions for
+ * the entry. To upgrade the permissions, provide the appropriate
+ * bitmask to the map_dma_pages function.
+ *
+ * Be aware that upgrading a mapped address might result in change of
+ * the DMA address for the page.
+ */
+#define ODP_READ_ALLOWED_BIT  (1<<0ULL)
+#define ODP_WRITE_ALLOWED_BIT (1<<1ULL)
+
+#define ODP_DMA_ADDR_MASK (~(ODP_READ_ALLOWED_BIT | ODP_WRITE_ALLOWED_BIT))
+
+int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 start_offset, u64 bcnt,
+                             u64 access_mask, unsigned long current_seq);
+
+void ib_umem_odp_unmap_dma_pages(struct ib_umem *umem, u64 start_offset,
+                                u64 bound);
+
+void rbt_ib_umem_insert(struct umem_odp_node *node, struct rb_root *root);
+void rbt_ib_umem_remove(struct umem_odp_node *node, struct rb_root *root);
+typedef int (*umem_call_back)(struct ib_umem *item, u64 start, u64 end,
+                             void *cookie);
+/*
+ * Call the callback on each ib_umem in the range. Returns the logical or of
+ * the return values of the functions called.
+ */
+int rbt_ib_umem_for_each_in_range(struct rb_root *root, u64 start, u64 end,
+                                 umem_call_back cb, void *cookie);
+
+struct umem_odp_node *rbt_ib_umem_iter_first(struct rb_root *root,
+                                            u64 start, u64 last);
+struct umem_odp_node *rbt_ib_umem_iter_next(struct umem_odp_node *node,
+                                           u64 start, u64 last);
+
+static inline int ib_umem_mmu_notifier_retry(struct ib_umem *item,
+                                            unsigned long mmu_seq)
+{
+       /*
+        * This code is strongly based on the KVM code from
+        * mmu_notifier_retry. Should be called with
+        * the relevant locks taken (item->odp_data->umem_mutex
+        * and the ucontext umem_mutex semaphore locked for read).
+        */
+
+       /* Do not allow page faults while the new ib_umem hasn't seen a state
+        * with zero notifiers yet, and doesn't have its own valid set of
+        * private counters. */
+       if (!item->odp_data->mn_counters_active)
+               return 1;
+
+       if (unlikely(item->odp_data->notifiers_count))
+               return 1;
+       if (item->odp_data->notifiers_seq != mmu_seq)
+               return 1;
+       return 0;
+}
+
+#else /* CONFIG_INFINIBAND_ON_DEMAND_PAGING */
+
+static inline int ib_umem_odp_get(struct ib_ucontext *context,
+                                 struct ib_umem *umem)
+{
+       return -EINVAL;
+}
+
+static inline void ib_umem_odp_release(struct ib_umem *umem) {}
+
+#endif /* CONFIG_INFINIBAND_ON_DEMAND_PAGING */
+
+#endif /* IB_UMEM_ODP_H */
index 470a011..0d74f1d 100644 (file)
@@ -51,6 +51,7 @@
 #include <uapi/linux/if_ether.h>
 
 #include <linux/atomic.h>
+#include <linux/mmu_notifier.h>
 #include <asm/uaccess.h>
 
 extern struct workqueue_struct *ib_wq;
@@ -123,7 +124,8 @@ enum ib_device_cap_flags {
        IB_DEVICE_MEM_WINDOW_TYPE_2A    = (1<<23),
        IB_DEVICE_MEM_WINDOW_TYPE_2B    = (1<<24),
        IB_DEVICE_MANAGED_FLOW_STEERING = (1<<29),
-       IB_DEVICE_SIGNATURE_HANDOVER    = (1<<30)
+       IB_DEVICE_SIGNATURE_HANDOVER    = (1<<30),
+       IB_DEVICE_ON_DEMAND_PAGING      = (1<<31),
 };
 
 enum ib_signature_prot_cap {
@@ -143,6 +145,27 @@ enum ib_atomic_cap {
        IB_ATOMIC_GLOB
 };
 
+enum ib_odp_general_cap_bits {
+       IB_ODP_SUPPORT = 1 << 0,
+};
+
+enum ib_odp_transport_cap_bits {
+       IB_ODP_SUPPORT_SEND     = 1 << 0,
+       IB_ODP_SUPPORT_RECV     = 1 << 1,
+       IB_ODP_SUPPORT_WRITE    = 1 << 2,
+       IB_ODP_SUPPORT_READ     = 1 << 3,
+       IB_ODP_SUPPORT_ATOMIC   = 1 << 4,
+};
+
+struct ib_odp_caps {
+       uint64_t general_caps;
+       struct {
+               uint32_t  rc_odp_caps;
+               uint32_t  uc_odp_caps;
+               uint32_t  ud_odp_caps;
+       } per_transport_caps;
+};
+
 struct ib_device_attr {
        u64                     fw_ver;
        __be64                  sys_image_guid;
@@ -186,6 +209,7 @@ struct ib_device_attr {
        u8                      local_ca_ack_delay;
        int                     sig_prot_cap;
        int                     sig_guard_cap;
+       struct ib_odp_caps      odp_caps;
 };
 
 enum ib_mtu {
@@ -1073,7 +1097,8 @@ enum ib_access_flags {
        IB_ACCESS_REMOTE_READ   = (1<<2),
        IB_ACCESS_REMOTE_ATOMIC = (1<<3),
        IB_ACCESS_MW_BIND       = (1<<4),
-       IB_ZERO_BASED           = (1<<5)
+       IB_ZERO_BASED           = (1<<5),
+       IB_ACCESS_ON_DEMAND     = (1<<6),
 };
 
 struct ib_phys_buf {
@@ -1115,6 +1140,8 @@ struct ib_fmr_attr {
        u8      page_shift;
 };
 
+struct ib_umem;
+
 struct ib_ucontext {
        struct ib_device       *device;
        struct list_head        pd_list;
@@ -1127,6 +1154,24 @@ struct ib_ucontext {
        struct list_head        xrcd_list;
        struct list_head        rule_list;
        int                     closing;
+
+       struct pid             *tgid;
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+       struct rb_root      umem_tree;
+       /*
+        * Protects .umem_rbroot and tree, as well as odp_mrs_count and
+        * mmu notifiers registration.
+        */
+       struct rw_semaphore     umem_rwsem;
+       void (*invalidate_range)(struct ib_umem *umem,
+                                unsigned long start, unsigned long end);
+
+       struct mmu_notifier     mn;
+       atomic_t                notifier_count;
+       /* A list of umems that don't have private mmu notifier counters yet. */
+       struct list_head        no_private_counters;
+       int                     odp_mrs_count;
+#endif
 };
 
 struct ib_uobject {
@@ -1662,7 +1707,10 @@ static inline int ib_copy_from_udata(void *dest, struct ib_udata *udata, size_t
 
 static inline int ib_copy_to_udata(struct ib_udata *udata, void *src, size_t len)
 {
-       return copy_to_user(udata->outbuf, src, len) ? -EFAULT : 0;
+       size_t copy_sz;
+
+       copy_sz = min_t(size_t, len, udata->outlen);
+       return copy_to_user(udata->outbuf, src, copy_sz) ? -EFAULT : 0;
 }
 
 /**
index 6364e23..3a4edd1 100644 (file)
@@ -441,13 +441,13 @@ static inline int scsi_execute_req(struct scsi_device *sdev,
 extern void sdev_disable_disk_events(struct scsi_device *sdev);
 extern void sdev_enable_disk_events(struct scsi_device *sdev);
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 extern int scsi_autopm_get_device(struct scsi_device *);
 extern void scsi_autopm_put_device(struct scsi_device *);
 #else
 static inline int scsi_autopm_get_device(struct scsi_device *d) { return 0; }
 static inline void scsi_autopm_put_device(struct scsi_device *d) {}
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM */
 
 static inline int __must_check scsi_device_reprobe(struct scsi_device *sdev)
 {
index 7c5cbfe..81c4c18 100644 (file)
@@ -80,7 +80,7 @@ DECLARE_EVENT_CLASS(module_refcnt,
 
        TP_fast_assign(
                __entry->ip     = ip;
-               __entry->refcnt = __this_cpu_read(mod->refptr->incs) - __this_cpu_read(mod->refptr->decs);
+               __entry->refcnt = atomic_read(&mod->refcnt);
                __assign_str(name, mod->name);
        ),
 
index 5a86d8e..26db206 100644 (file)
@@ -31,9 +31,9 @@ struct v4l2_mbus_framefmt {
        __u32                   code;
        __u32                   field;
        __u32                   colorspace;
-       __u32                   ycbcr_enc;
-       __u32                   quantization;
-       __u32                   reserved[5];
+       __u16                   ycbcr_enc;
+       __u16                   quantization;
+       __u32                   reserved[6];
 };
 
 #ifndef __KERNEL__
index e5ec1ca..35b552c 100644 (file)
@@ -41,6 +41,8 @@
 
 #include <linux/virtio_config.h>
 
+#ifndef VIRTIO_PCI_NO_LEGACY
+
 /* A 32-bit r/o bitmask of the features supported by the host */
 #define VIRTIO_PCI_HOST_FEATURES       0
 
  * a read-and-acknowledge. */
 #define VIRTIO_PCI_ISR                 19
 
-/* The bit of the ISR which indicates a device configuration change. */
-#define VIRTIO_PCI_ISR_CONFIG          0x2
-
 /* MSI-X registers: only enabled if MSI-X is enabled. */
 /* A 16-bit vector for configuration changes. */
 #define VIRTIO_MSI_CONFIG_VECTOR        20
 /* A 16-bit vector for selected queue notifications. */
 #define VIRTIO_MSI_QUEUE_VECTOR         22
-/* Vector value used to disable MSI for queue */
-#define VIRTIO_MSI_NO_VECTOR            0xffff
 
 /* The remaining space is defined by each driver as the per-driver
  * configuration space */
 /* The alignment to use between consumer and producer parts of vring.
  * x86 pagesize again. */
 #define VIRTIO_PCI_VRING_ALIGN         4096
+
+#endif /* VIRTIO_PCI_NO_LEGACY */
+
+/* The bit of the ISR which indicates a device configuration change. */
+#define VIRTIO_PCI_ISR_CONFIG          0x2
+/* Vector value used to disable MSI for queue */
+#define VIRTIO_MSI_NO_VECTOR            0xffff
+
 #endif
index 26daf55..4275b96 100644 (file)
@@ -90,8 +90,9 @@ enum {
 };
 
 enum {
+       IB_USER_VERBS_EX_CMD_QUERY_DEVICE = IB_USER_VERBS_CMD_QUERY_DEVICE,
        IB_USER_VERBS_EX_CMD_CREATE_FLOW = IB_USER_VERBS_CMD_THRESHOLD,
-       IB_USER_VERBS_EX_CMD_DESTROY_FLOW
+       IB_USER_VERBS_EX_CMD_DESTROY_FLOW,
 };
 
 /*
@@ -201,6 +202,32 @@ struct ib_uverbs_query_device_resp {
        __u8  reserved[4];
 };
 
+enum {
+       IB_USER_VERBS_EX_QUERY_DEVICE_ODP =             1ULL << 0,
+};
+
+struct ib_uverbs_ex_query_device {
+       __u32 comp_mask;
+       __u32 reserved;
+};
+
+struct ib_uverbs_odp_caps {
+       __u64 general_caps;
+       struct {
+               __u32 rc_odp_caps;
+               __u32 uc_odp_caps;
+               __u32 ud_odp_caps;
+       } per_transport_caps;
+       __u32 reserved;
+};
+
+struct ib_uverbs_ex_query_device_resp {
+       struct ib_uverbs_query_device_resp base;
+       __u32 comp_mask;
+       __u32 reserved;
+       struct ib_uverbs_odp_caps odp_caps;
+};
+
 struct ib_uverbs_query_port {
        __u64 response;
        __u8  port_num;
index e52a873..3965511 100644 (file)
@@ -42,7 +42,6 @@
 #include <linux/vermagic.h>
 #include <linux/notifier.h>
 #include <linux/sched.h>
-#include <linux/stop_machine.h>
 #include <linux/device.h>
 #include <linux/string.h>
 #include <linux/mutex.h>
@@ -98,7 +97,7 @@
  * 1) List of modules (also safely readable with preempt_disable),
  * 2) module_use links,
  * 3) module_addr_min/module_addr_max.
- * (delete uses stop_machine/add uses RCU list operations). */
+ * (delete and add uses RCU list operations). */
 DEFINE_MUTEX(module_mutex);
 EXPORT_SYMBOL_GPL(module_mutex);
 static LIST_HEAD(modules);
@@ -158,13 +157,13 @@ static BLOCKING_NOTIFIER_HEAD(module_notify_list);
  * Protected by module_mutex. */
 static unsigned long module_addr_min = -1UL, module_addr_max = 0;
 
-int register_module_notifier(struct notifier_block * nb)
+int register_module_notifier(struct notifier_block *nb)
 {
        return blocking_notifier_chain_register(&module_notify_list, nb);
 }
 EXPORT_SYMBOL(register_module_notifier);
 
-int unregister_module_notifier(struct notifier_block * nb)
+int unregister_module_notifier(struct notifier_block *nb)
 {
        return blocking_notifier_chain_unregister(&module_notify_list, nb);
 }
@@ -628,18 +627,23 @@ static char last_unloaded_module[MODULE_NAME_LEN+1];
 
 EXPORT_TRACEPOINT_SYMBOL(module_get);
 
+/* MODULE_REF_BASE is the base reference count by kmodule loader. */
+#define MODULE_REF_BASE        1
+
 /* Init the unload section of the module. */
 static int module_unload_init(struct module *mod)
 {
-       mod->refptr = alloc_percpu(struct module_ref);
-       if (!mod->refptr)
-               return -ENOMEM;
+       /*
+        * Initialize reference counter to MODULE_REF_BASE.
+        * refcnt == 0 means module is going.
+        */
+       atomic_set(&mod->refcnt, MODULE_REF_BASE);
 
        INIT_LIST_HEAD(&mod->source_list);
        INIT_LIST_HEAD(&mod->target_list);
 
        /* Hold reference count during initialization. */
-       raw_cpu_write(mod->refptr->incs, 1);
+       atomic_inc(&mod->refcnt);
 
        return 0;
 }
@@ -721,8 +725,6 @@ static void module_unload_free(struct module *mod)
                kfree(use);
        }
        mutex_unlock(&module_mutex);
-
-       free_percpu(mod->refptr);
 }
 
 #ifdef CONFIG_MODULE_FORCE_UNLOAD
@@ -740,60 +742,39 @@ static inline int try_force_unload(unsigned int flags)
 }
 #endif /* CONFIG_MODULE_FORCE_UNLOAD */
 
-struct stopref
+/* Try to release refcount of module, 0 means success. */
+static int try_release_module_ref(struct module *mod)
 {
-       struct module *mod;
-       int flags;
-       int *forced;
-};
+       int ret;
 
-/* Whole machine is stopped with interrupts off when this runs. */
-static int __try_stop_module(void *_sref)
-{
-       struct stopref *sref = _sref;
+       /* Try to decrement refcnt which we set at loading */
+       ret = atomic_sub_return(MODULE_REF_BASE, &mod->refcnt);
+       BUG_ON(ret < 0);
+       if (ret)
+               /* Someone can put this right now, recover with checking */
+               ret = atomic_add_unless(&mod->refcnt, MODULE_REF_BASE, 0);
+
+       return ret;
+}
 
+static int try_stop_module(struct module *mod, int flags, int *forced)
+{
        /* If it's not unused, quit unless we're forcing. */
-       if (module_refcount(sref->mod) != 0) {
-               if (!(*sref->forced = try_force_unload(sref->flags)))
+       if (try_release_module_ref(mod) != 0) {
+               *forced = try_force_unload(flags);
+               if (!(*forced))
                        return -EWOULDBLOCK;
        }
 
        /* Mark it as dying. */
-       sref->mod->state = MODULE_STATE_GOING;
-       return 0;
-}
-
-static int try_stop_module(struct module *mod, int flags, int *forced)
-{
-       struct stopref sref = { mod, flags, forced };
+       mod->state = MODULE_STATE_GOING;
 
-       return stop_machine(__try_stop_module, &sref, NULL);
+       return 0;
 }
 
 unsigned long module_refcount(struct module *mod)
 {
-       unsigned long incs = 0, decs = 0;
-       int cpu;
-
-       for_each_possible_cpu(cpu)
-               decs += per_cpu_ptr(mod->refptr, cpu)->decs;
-       /*
-        * ensure the incs are added up after the decs.
-        * module_put ensures incs are visible before decs with smp_wmb.
-        *
-        * This 2-count scheme avoids the situation where the refcount
-        * for CPU0 is read, then CPU0 increments the module refcount,
-        * then CPU1 drops that refcount, then the refcount for CPU1 is
-        * read. We would record a decrement but not its corresponding
-        * increment so we would see a low count (disaster).
-        *
-        * Rare situation? But module_refcount can be preempted, and we
-        * might be tallying up 4096+ CPUs. So it is not impossible.
-        */
-       smp_rmb();
-       for_each_possible_cpu(cpu)
-               incs += per_cpu_ptr(mod->refptr, cpu)->incs;
-       return incs - decs;
+       return (unsigned long)atomic_read(&mod->refcnt) - MODULE_REF_BASE;
 }
 EXPORT_SYMBOL(module_refcount);
 
@@ -877,8 +858,10 @@ static inline void print_unload_info(struct seq_file *m, struct module *mod)
 
        seq_printf(m, " %lu ", module_refcount(mod));
 
-       /* Always include a trailing , so userspace can differentiate
-           between this and the old multi-field proc format. */
+       /*
+        * Always include a trailing , so userspace can differentiate
+        * between this and the old multi-field proc format.
+        */
        list_for_each_entry(use, &mod->source_list, source_list) {
                printed_something = 1;
                seq_printf(m, "%s,", use->source->name);
@@ -886,11 +869,11 @@ static inline void print_unload_info(struct seq_file *m, struct module *mod)
 
        if (mod->init != NULL && mod->exit == NULL) {
                printed_something = 1;
-               seq_printf(m, "[permanent],");
+               seq_puts(m, "[permanent],");
        }
 
        if (!printed_something)
-               seq_printf(m, "-");
+               seq_puts(m, "-");
 }
 
 void __symbol_put(const char *symbol)
@@ -935,7 +918,7 @@ void __module_get(struct module *module)
 {
        if (module) {
                preempt_disable();
-               __this_cpu_inc(module->refptr->incs);
+               atomic_inc(&module->refcnt);
                trace_module_get(module, _RET_IP_);
                preempt_enable();
        }
@@ -948,11 +931,11 @@ bool try_module_get(struct module *module)
 
        if (module) {
                preempt_disable();
-
-               if (likely(module_is_live(module))) {
-                       __this_cpu_inc(module->refptr->incs);
+               /* Note: here, we can fail to get a reference */
+               if (likely(module_is_live(module) &&
+                          atomic_inc_not_zero(&module->refcnt) != 0))
                        trace_module_get(module, _RET_IP_);
-               else
+               else
                        ret = false;
 
                preempt_enable();
@@ -963,11 +946,12 @@ EXPORT_SYMBOL(try_module_get);
 
 void module_put(struct module *module)
 {
+       int ret;
+
        if (module) {
                preempt_disable();
-               smp_wmb(); /* see comment in module_refcount */
-               __this_cpu_inc(module->refptr->decs);
-
+               ret = atomic_dec_if_positive(&module->refcnt);
+               WARN_ON(ret < 0);       /* Failed to put refcount */
                trace_module_put(module, _RET_IP_);
                preempt_enable();
        }
@@ -978,7 +962,7 @@ EXPORT_SYMBOL(module_put);
 static inline void print_unload_info(struct seq_file *m, struct module *mod)
 {
        /* We don't know the usage count, or what modules are using. */
-       seq_printf(m, " - -");
+       seq_puts(m, " - -");
 }
 
 static inline void module_unload_free(struct module *mod)
@@ -1131,7 +1115,7 @@ static unsigned long maybe_relocated(unsigned long crc,
 static int check_version(Elf_Shdr *sechdrs,
                         unsigned int versindex,
                         const char *symname,
-                        struct module *mod, 
+                        struct module *mod,
                         const unsigned long *crc,
                         const struct module *crc_owner)
 {
@@ -1165,7 +1149,7 @@ static int check_version(Elf_Shdr *sechdrs,
        return 0;
 
 bad_version:
-       printk("%s: disagrees about version of symbol %s\n",
+       pr_warn("%s: disagrees about version of symbol %s\n",
               mod->name, symname);
        return 0;
 }
@@ -1200,7 +1184,7 @@ static inline int same_magic(const char *amagic, const char *bmagic,
 static inline int check_version(Elf_Shdr *sechdrs,
                                unsigned int versindex,
                                const char *symname,
-                               struct module *mod, 
+                               struct module *mod,
                                const unsigned long *crc,
                                const struct module *crc_owner)
 {
@@ -1288,15 +1272,13 @@ static inline bool sect_empty(const Elf_Shdr *sect)
        return !(sect->sh_flags & SHF_ALLOC) || sect->sh_size == 0;
 }
 
-struct module_sect_attr
-{
+struct module_sect_attr {
        struct module_attribute mattr;
        char *name;
        unsigned long address;
 };
 
-struct module_sect_attrs
-{
+struct module_sect_attrs {
        struct attribute_group grp;
        unsigned int nsections;
        struct module_sect_attr attrs[0];
@@ -1550,7 +1532,8 @@ static int module_add_modinfo_attrs(struct module *mod)
                    (attr->test && attr->test(mod))) {
                        memcpy(temp_attr, attr, sizeof(*temp_attr));
                        sysfs_attr_init(&temp_attr->attr);
-                       error = sysfs_create_file(&mod->mkobj.kobj,&temp_attr->attr);
+                       error = sysfs_create_file(&mod->mkobj.kobj,
+                                       &temp_attr->attr);
                        ++temp_attr;
                }
        }
@@ -1566,7 +1549,7 @@ static void module_remove_modinfo_attrs(struct module *mod)
                /* pick a field to test for end of list */
                if (!attr->attr.name)
                        break;
-               sysfs_remove_file(&mod->mkobj.kobj,&attr->attr);
+               sysfs_remove_file(&mod->mkobj.kobj, &attr->attr);
                if (attr->free)
                        attr->free(mod);
        }
@@ -1697,18 +1680,6 @@ static void mod_sysfs_teardown(struct module *mod)
        mod_sysfs_fini(mod);
 }
 
-/*
- * unlink the module with the whole machine is stopped with interrupts off
- * - this defends against kallsyms not taking locks
- */
-static int __unlink_module(void *_mod)
-{
-       struct module *mod = _mod;
-       list_del(&mod->list);
-       module_bug_cleanup(mod);
-       return 0;
-}
-
 #ifdef CONFIG_DEBUG_SET_MODULE_RONX
 /*
  * LKM RO/NX protection: protect module's text/ro-data
@@ -1860,7 +1831,12 @@ static void free_module(struct module *mod)
 
        /* Now we can delete it from the lists */
        mutex_lock(&module_mutex);
-       stop_machine(__unlink_module, mod, NULL);
+       /* Unlink carefully: kallsyms could be walking list. */
+       list_del_rcu(&mod->list);
+       /* Remove this module from bug list, this uses list_del_rcu */
+       module_bug_cleanup(mod);
+       /* Wait for RCU synchronizing before releasing mod->list and buglist. */
+       synchronize_rcu();
        mutex_unlock(&module_mutex);
 
        /* This may be NULL, but that's OK */
@@ -1955,7 +1931,7 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
                        /* We compiled with -fno-common.  These are not
                           supposed to happen.  */
                        pr_debug("Common symbol: %s\n", name);
-                       printk("%s: please compile with -fno-common\n",
+                       pr_warn("%s: please compile with -fno-common\n",
                               mod->name);
                        ret = -ENOEXEC;
                        break;
@@ -2259,7 +2235,7 @@ static char elf_type(const Elf_Sym *sym, const struct load_info *info)
 }
 
 static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs,
-                           unsigned int shnum)
+                       unsigned int shnum)
 {
        const Elf_Shdr *sec;
 
@@ -2735,7 +2711,7 @@ static int find_module_sections(struct module *mod, struct load_info *info)
                 * This shouldn't happen with same compiler and binutils
                 * building all parts of the module.
                 */
-               printk(KERN_WARNING "%s: has both .ctors and .init_array.\n",
+               pr_warn("%s: has both .ctors and .init_array.\n",
                       mod->name);
                return -EINVAL;
        }
@@ -3023,8 +2999,10 @@ static int do_init_module(struct module *mod)
        if (mod->init != NULL)
                ret = do_one_initcall(mod->init);
        if (ret < 0) {
-               /* Init routine failed: abort.  Try to protect us from
-                   buggy refcounters. */
+               /*
+                * Init routine failed: abort.  Try to protect us from
+                * buggy refcounters.
+                */
                mod->state = MODULE_STATE_GOING;
                synchronize_sched();
                module_put(mod);
@@ -3202,7 +3180,7 @@ out:
 
 static int unknown_module_param_cb(char *param, char *val, const char *modname)
 {
-       /* Check for magic 'dyndbg' arg */ 
+       /* Check for magic 'dyndbg' arg */
        int ret = ddebug_dyndbg_module_param_cb(param, val, modname);
        if (ret != 0)
                pr_warn("%s: unknown parameter '%s' ignored\n", modname, param);
@@ -3352,6 +3330,8 @@ static int load_module(struct load_info *info, const char __user *uargs,
        /* Unlink carefully: kallsyms could be walking list. */
        list_del_rcu(&mod->list);
        wake_up_all(&module_wq);
+       /* Wait for RCU synchronizing before releasing mod->list. */
+       synchronize_rcu();
        mutex_unlock(&module_mutex);
  free_module:
        module_deallocate(mod, info);
@@ -3685,8 +3665,8 @@ static int m_show(struct seq_file *m, void *p)
 
        /* Informative for users. */
        seq_printf(m, " %s",
-                  mod->state == MODULE_STATE_GOING ? "Unloading":
-                  mod->state == MODULE_STATE_COMING ? "Loading":
+                  mod->state == MODULE_STATE_GOING ? "Unloading" :
+                  mod->state == MODULE_STATE_COMING ? "Loading" :
                   "Live");
        /* Used by oprofile and other similar tools. */
        seq_printf(m, " 0x%pK", mod->module_core);
@@ -3695,7 +3675,7 @@ static int m_show(struct seq_file *m, void *p)
        if (mod->taints)
                seq_printf(m, " %s", module_flags(mod, buf));
 
-       seq_printf(m, "\n");
+       seq_puts(m, "\n");
        return 0;
 }
 
index db97b79..0af9b2c 100644 (file)
@@ -603,74 +603,67 @@ static __modinit int add_sysfs_param(struct module_kobject *mk,
                                     const struct kernel_param *kp,
                                     const char *name)
 {
-       struct module_param_attrs *new;
-       struct attribute **attrs;
-       int err, num;
+       struct module_param_attrs *new_mp;
+       struct attribute **new_attrs;
+       unsigned int i;
 
        /* We don't bother calling this with invisible parameters. */
        BUG_ON(!kp->perm);
 
        if (!mk->mp) {
-               num = 0;
-               attrs = NULL;
-       } else {
-               num = mk->mp->num;
-               attrs = mk->mp->grp.attrs;
+               /* First allocation. */
+               mk->mp = kzalloc(sizeof(*mk->mp), GFP_KERNEL);
+               if (!mk->mp)
+                       return -ENOMEM;
+               mk->mp->grp.name = "parameters";
+               /* NULL-terminated attribute array. */
+               mk->mp->grp.attrs = kzalloc(sizeof(mk->mp->grp.attrs[0]),
+                                           GFP_KERNEL);
+               /* Caller will cleanup via free_module_param_attrs */
+               if (!mk->mp->grp.attrs)
+                       return -ENOMEM;
        }
 
-       /* Enlarge. */
-       new = krealloc(mk->mp,
-                      sizeof(*mk->mp) + sizeof(mk->mp->attrs[0]) * (num+1),
-                      GFP_KERNEL);
-       if (!new) {
-               kfree(attrs);
-               err = -ENOMEM;
-               goto fail;
-       }
-       /* Despite looking like the typical realloc() bug, this is safe.
-        * We *want* the old 'attrs' to be freed either way, and we'll store
-        * the new one in the success case. */
-       attrs = krealloc(attrs, sizeof(new->grp.attrs[0])*(num+2), GFP_KERNEL);
-       if (!attrs) {
-               err = -ENOMEM;
-               goto fail_free_new;
-       }
+       /* Enlarge allocations. */
+       new_mp = krealloc(mk->mp,
+                         sizeof(*mk->mp) +
+                         sizeof(mk->mp->attrs[0]) * (mk->mp->num + 1),
+                         GFP_KERNEL);
+       if (!new_mp)
+               return -ENOMEM;
+       mk->mp = new_mp;
 
-       /* Sysfs wants everything zeroed. */
-       memset(new, 0, sizeof(*new));
-       memset(&new->attrs[num], 0, sizeof(new->attrs[num]));
-       memset(&attrs[num], 0, sizeof(attrs[num]));
-       new->grp.name = "parameters";
-       new->grp.attrs = attrs;
+       /* Extra pointer for NULL terminator */
+       new_attrs = krealloc(mk->mp->grp.attrs,
+                            sizeof(mk->mp->grp.attrs[0]) * (mk->mp->num + 2),
+                            GFP_KERNEL);
+       if (!new_attrs)
+               return -ENOMEM;
+       mk->mp->grp.attrs = new_attrs;
 
        /* Tack new one on the end. */
-       sysfs_attr_init(&new->attrs[num].mattr.attr);
-       new->attrs[num].param = kp;
-       new->attrs[num].mattr.show = param_attr_show;
-       new->attrs[num].mattr.store = param_attr_store;
-       new->attrs[num].mattr.attr.name = (char *)name;
-       new->attrs[num].mattr.attr.mode = kp->perm;
-       new->num = num+1;
+       sysfs_attr_init(&mk->mp->attrs[mk->mp->num].mattr.attr);
+       mk->mp->attrs[mk->mp->num].param = kp;
+       mk->mp->attrs[mk->mp->num].mattr.show = param_attr_show;
+       /* Do not allow runtime DAC changes to make param writable. */
+       if ((kp->perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
+               mk->mp->attrs[mk->mp->num].mattr.store = param_attr_store;
+       mk->mp->attrs[mk->mp->num].mattr.attr.name = (char *)name;
+       mk->mp->attrs[mk->mp->num].mattr.attr.mode = kp->perm;
+       mk->mp->num++;
 
        /* Fix up all the pointers, since krealloc can move us */
-       for (num = 0; num < new->num; num++)
-               new->grp.attrs[num] = &new->attrs[num].mattr.attr;
-       new->grp.attrs[num] = NULL;
-
-       mk->mp = new;
+       for (i = 0; i < mk->mp->num; i++)
+               mk->mp->grp.attrs[i] = &mk->mp->attrs[i].mattr.attr;
+       mk->mp->grp.attrs[mk->mp->num] = NULL;
        return 0;
-
-fail_free_new:
-       kfree(new);
-fail:
-       mk->mp = NULL;
-       return err;
 }
 
 #ifdef CONFIG_MODULES
 static void free_module_param_attrs(struct module_kobject *mk)
 {
-       kfree(mk->mp->grp.attrs);
+       if (mk->mp)
+               kfree(mk->mp->grp.attrs);
        kfree(mk->mp);
        mk->mp = NULL;
 }
@@ -695,8 +688,10 @@ int module_param_sysfs_setup(struct module *mod,
                if (kparam[i].perm == 0)
                        continue;
                err = add_sysfs_param(&mod->mkobj, &kparam[i], kparam[i].name);
-               if (err)
+               if (err) {
+                       free_module_param_attrs(&mod->mkobj);
                        return err;
+               }
                params = true;
        }
 
index 67d6369..979ccde 100644 (file)
@@ -55,7 +55,7 @@ obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o
 obj-$(CONFIG_EVENT_TRACING) += trace_events_trigger.o
 obj-$(CONFIG_KPROBE_EVENT) += trace_kprobe.o
 obj-$(CONFIG_TRACEPOINTS) += power-traces.o
-ifeq ($(CONFIG_PM_RUNTIME),y)
+ifeq ($(CONFIG_PM),y)
 obj-$(CONFIG_TRACEPOINTS) += rpm-traces.o
 endif
 ifeq ($(CONFIG_TRACING),y)
index d1d7c78..0c3bd95 100644 (file)
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -64,16 +64,22 @@ static LIST_HEAD(module_bug_list);
 static const struct bug_entry *module_find_bug(unsigned long bugaddr)
 {
        struct module *mod;
+       const struct bug_entry *bug = NULL;
 
-       list_for_each_entry(mod, &module_bug_list, bug_list) {
-               const struct bug_entry *bug = mod->bug_table;
+       rcu_read_lock();
+       list_for_each_entry_rcu(mod, &module_bug_list, bug_list) {
                unsigned i;
 
+               bug = mod->bug_table;
                for (i = 0; i < mod->num_bugs; ++i, ++bug)
                        if (bugaddr == bug_addr(bug))
-                               return bug;
+                               goto out;
        }
-       return NULL;
+       bug = NULL;
+out:
+       rcu_read_unlock();
+
+       return bug;
 }
 
 void module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
@@ -99,13 +105,15 @@ void module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
         * Strictly speaking this should have a spinlock to protect against
         * traversals, but since we only traverse on BUG()s, a spinlock
         * could potentially lead to deadlock and thus be counter-productive.
+        * Thus, this uses RCU to safely manipulate the bug list, since BUG
+        * must run in non-interruptive state.
         */
-       list_add(&mod->bug_list, &module_bug_list);
+       list_add_rcu(&mod->bug_list, &module_bug_list);
 }
 
 void module_bug_cleanup(struct module *mod)
 {
-       list_del(&mod->bug_list);
+       list_del_rcu(&mod->bug_list);
 }
 
 #else
index 5e25627..7de89f4 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/mm.h>
 #include <linux/nmi.h>
 #include <linux/quicklist.h>
+#include <linux/cma.h>
 
 void show_mem(unsigned int filter)
 {
@@ -38,7 +39,12 @@ void show_mem(unsigned int filter)
 
        printk("%lu pages RAM\n", total);
        printk("%lu pages HighMem/MovableOnly\n", highmem);
+#ifdef CONFIG_CMA
+       printk("%lu pages reserved\n", (reserved - totalcma_pages));
+       printk("%lu pages cma reserved\n", totalcma_pages);
+#else
        printk("%lu pages reserved\n", reserved);
+#endif
 #ifdef CONFIG_QUICKLIST
        printk("%lu pages in pagetable cache\n",
                quicklist_total_size());
index f891762..a85ae28 100644 (file)
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -337,6 +337,7 @@ int __init cma_declare_contiguous(phys_addr_t base,
        if (ret)
                goto err;
 
+       totalcma_pages += (size / PAGE_SIZE);
        pr_info("Reserved %ld MiB at %pa\n", (unsigned long)size / SZ_1M,
                &base);
        return 0;
index 6efe36a..d8aebc5 100644 (file)
@@ -2996,6 +2996,12 @@ static int do_shared_fault(struct mm_struct *mm, struct vm_area_struct *vma,
 
        if (set_page_dirty(fault_page))
                dirtied = 1;
+       /*
+        * Take a local copy of the address_space - page.mapping may be zeroed
+        * by truncate after unlock_page().   The address_space itself remains
+        * pinned by vma->vm_file's reference.  We rely on unlock_page()'s
+        * release semantics to prevent the compiler from undoing this copying.
+        */
        mapping = fault_page->mapping;
        unlock_page(fault_page);
        if ((dirtied || vma->vm_ops->page_mkwrite) && mapping) {
index e58725a..f22c559 100644 (file)
@@ -162,12 +162,6 @@ static const struct mempolicy_operations {
                        enum mpol_rebind_step step);
 } mpol_ops[MPOL_MAX];
 
-/* Check that the nodemask contains at least one populated zone */
-static int is_valid_nodemask(const nodemask_t *nodemask)
-{
-       return nodes_intersects(*nodemask, node_states[N_MEMORY]);
-}
-
 static inline int mpol_store_user_nodemask(const struct mempolicy *pol)
 {
        return pol->flags & MPOL_MODE_FLAGS;
@@ -202,7 +196,7 @@ static int mpol_new_preferred(struct mempolicy *pol, const nodemask_t *nodes)
 
 static int mpol_new_bind(struct mempolicy *pol, const nodemask_t *nodes)
 {
-       if (!is_valid_nodemask(nodes))
+       if (nodes_empty(*nodes))
                return -EINVAL;
        pol->v.nodes = *nodes;
        return 0;
@@ -234,7 +228,7 @@ static int mpol_set_nodemask(struct mempolicy *pol,
                nodes = NULL;   /* explicit local allocation */
        else {
                if (pol->flags & MPOL_F_RELATIVE_NODES)
-                       mpol_relative_nodemask(&nsc->mask2, nodes,&nsc->mask1);
+                       mpol_relative_nodemask(&nsc->mask2, nodes, &nsc->mask1);
                else
                        nodes_and(nsc->mask2, *nodes, nsc->mask1);
 
index fa974d8..7633c50 100644 (file)
@@ -111,6 +111,7 @@ static DEFINE_SPINLOCK(managed_page_count_lock);
 
 unsigned long totalram_pages __read_mostly;
 unsigned long totalreserve_pages __read_mostly;
+unsigned long totalcma_pages __read_mostly;
 /*
  * When calculating the number of globally allowed dirty pages, there
  * is a certain number of per-zone reserves that should not be
@@ -5586,7 +5587,7 @@ void __init mem_init_print_info(const char *str)
 
        pr_info("Memory: %luK/%luK available "
               "(%luK kernel code, %luK rwdata, %luK rodata, "
-              "%luK init, %luK bss, %luK reserved"
+              "%luK init, %luK bss, %luK reserved, %luK cma-reserved"
 #ifdef CONFIG_HIGHMEM
               ", %luK highmem"
 #endif
@@ -5594,7 +5595,8 @@ void __init mem_init_print_info(const char *str)
               nr_free_pages() << (PAGE_SHIFT-10), physpages << (PAGE_SHIFT-10),
               codesize >> 10, datasize >> 10, rosize >> 10,
               (init_data_size + init_code_size) >> 10, bss_size >> 10,
-              (physpages - totalram_pages) << (PAGE_SHIFT-10),
+              (physpages - totalram_pages - totalcma_pages) << (PAGE_SHIFT-10),
+              totalcma_pages << (PAGE_SHIFT-10),
 #ifdef CONFIG_HIGHMEM
               totalhigh_pages << (PAGE_SHIFT-10),
 #endif
index 4d0a063..b724039 100644 (file)
@@ -884,19 +884,6 @@ static struct notifier_block zs_cpu_nb = {
        .notifier_call = zs_cpu_notifier
 };
 
-static void zs_unregister_cpu_notifier(void)
-{
-       int cpu;
-
-       cpu_notifier_register_begin();
-
-       for_each_online_cpu(cpu)
-               zs_cpu_notifier(NULL, CPU_DEAD, (void *)(long)cpu);
-       __unregister_cpu_notifier(&zs_cpu_nb);
-
-       cpu_notifier_register_done();
-}
-
 static int zs_register_cpu_notifier(void)
 {
        int cpu, uninitialized_var(ret);
@@ -914,40 +901,28 @@ static int zs_register_cpu_notifier(void)
        return notifier_to_errno(ret);
 }
 
-static void init_zs_size_classes(void)
+static void zs_unregister_cpu_notifier(void)
 {
-       int nr;
+       int cpu;
 
-       nr = (ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) / ZS_SIZE_CLASS_DELTA + 1;
-       if ((ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) % ZS_SIZE_CLASS_DELTA)
-               nr += 1;
+       cpu_notifier_register_begin();
 
-       zs_size_classes = nr;
-}
+       for_each_online_cpu(cpu)
+               zs_cpu_notifier(NULL, CPU_DEAD, (void *)(long)cpu);
+       __unregister_cpu_notifier(&zs_cpu_nb);
 
-static void __exit zs_exit(void)
-{
-#ifdef CONFIG_ZPOOL
-       zpool_unregister_driver(&zs_zpool_driver);
-#endif
-       zs_unregister_cpu_notifier();
+       cpu_notifier_register_done();
 }
 
-static int __init zs_init(void)
+static void init_zs_size_classes(void)
 {
-       int ret = zs_register_cpu_notifier();
-
-       if (ret) {
-               zs_unregister_cpu_notifier();
-               return ret;
-       }
+       int nr;
 
-       init_zs_size_classes();
+       nr = (ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) / ZS_SIZE_CLASS_DELTA + 1;
+       if ((ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) % ZS_SIZE_CLASS_DELTA)
+               nr += 1;
 
-#ifdef CONFIG_ZPOOL
-       zpool_register_driver(&zs_zpool_driver);
-#endif
-       return 0;
+       zs_size_classes = nr;
 }
 
 static unsigned int get_maxobj_per_zspage(int size, int pages_per_zspage)
@@ -967,113 +942,101 @@ static bool can_merge(struct size_class *prev, int size, int pages_per_zspage)
        return true;
 }
 
+unsigned long zs_get_total_pages(struct zs_pool *pool)
+{
+       return atomic_long_read(&pool->pages_allocated);
+}
+EXPORT_SYMBOL_GPL(zs_get_total_pages);
+
 /**
- * zs_create_pool - Creates an allocation pool to work from.
- * @flags: allocation flags used to allocate pool metadata
+ * zs_map_object - get address of allocated object from handle.
+ * @pool: pool from which the object was allocated
+ * @handle: handle returned from zs_malloc
  *
- * This function must be called before anything when using
- * the zsmalloc allocator.
+ * Before using an object allocated from zs_malloc, it must be mapped using
+ * this function. When done with the object, it must be unmapped using
+ * zs_unmap_object.
  *
- * On success, a pointer to the newly created pool is returned,
- * otherwise NULL.
+ * Only one object can be mapped per cpu at a time. There is no protection
+ * against nested mappings.
+ *
+ * This function returns with preemption and page faults disabled.
  */
-struct zs_pool *zs_create_pool(gfp_t flags)
+void *zs_map_object(struct zs_pool *pool, unsigned long handle,
+                       enum zs_mapmode mm)
 {
-       int i;
-       struct zs_pool *pool;
-       struct size_class *prev_class = NULL;
+       struct page *page;
+       unsigned long obj_idx, off;
 
-       pool = kzalloc(sizeof(*pool), GFP_KERNEL);
-       if (!pool)
-               return NULL;
+       unsigned int class_idx;
+       enum fullness_group fg;
+       struct size_class *class;
+       struct mapping_area *area;
+       struct page *pages[2];
 
-       pool->size_class = kcalloc(zs_size_classes, sizeof(struct size_class *),
-                       GFP_KERNEL);
-       if (!pool->size_class) {
-               kfree(pool);
-               return NULL;
-       }
+       BUG_ON(!handle);
 
        /*
-        * Iterate reversly, because, size of size_class that we want to use
-        * for merging should be larger or equal to current size.
+        * Because we use per-cpu mapping areas shared among the
+        * pools/users, we can't allow mapping in interrupt context
+        * because it can corrupt another users mappings.
         */
-       for (i = zs_size_classes - 1; i >= 0; i--) {
-               int size;
-               int pages_per_zspage;
-               struct size_class *class;
-
-               size = ZS_MIN_ALLOC_SIZE + i * ZS_SIZE_CLASS_DELTA;
-               if (size > ZS_MAX_ALLOC_SIZE)
-                       size = ZS_MAX_ALLOC_SIZE;
-               pages_per_zspage = get_pages_per_zspage(size);
-
-               /*
-                * size_class is used for normal zsmalloc operation such
-                * as alloc/free for that size. Although it is natural that we
-                * have one size_class for each size, there is a chance that we
-                * can get more memory utilization if we use one size_class for
-                * many different sizes whose size_class have same
-                * characteristics. So, we makes size_class point to
-                * previous size_class if possible.
-                */
-               if (prev_class) {
-                       if (can_merge(prev_class, size, pages_per_zspage)) {
-                               pool->size_class[i] = prev_class;
-                               continue;
-                       }
-               }
-
-               class = kzalloc(sizeof(struct size_class), GFP_KERNEL);
-               if (!class)
-                       goto err;
+       BUG_ON(in_interrupt());
 
-               class->size = size;
-               class->index = i;
-               class->pages_per_zspage = pages_per_zspage;
-               spin_lock_init(&class->lock);
-               pool->size_class[i] = class;
+       obj_handle_to_location(handle, &page, &obj_idx);
+       get_zspage_mapping(get_first_page(page), &class_idx, &fg);
+       class = pool->size_class[class_idx];
+       off = obj_idx_to_offset(page, obj_idx, class->size);
 
-               prev_class = class;
+       area = &get_cpu_var(zs_map_area);
+       area->vm_mm = mm;
+       if (off + class->size <= PAGE_SIZE) {
+               /* this object is contained entirely within a page */
+               area->vm_addr = kmap_atomic(page);
+               return area->vm_addr + off;
        }
 
-       pool->flags = flags;
-
-       return pool;
+       /* this object spans two pages */
+       pages[0] = page;
+       pages[1] = get_next_page(page);
+       BUG_ON(!pages[1]);
 
-err:
-       zs_destroy_pool(pool);
-       return NULL;
+       return __zs_map_object(area, pages, off, class->size);
 }
-EXPORT_SYMBOL_GPL(zs_create_pool);
+EXPORT_SYMBOL_GPL(zs_map_object);
 
-void zs_destroy_pool(struct zs_pool *pool)
+void zs_unmap_object(struct zs_pool *pool, unsigned long handle)
 {
-       int i;
+       struct page *page;
+       unsigned long obj_idx, off;
 
-       for (i = 0; i < zs_size_classes; i++) {
-               int fg;
-               struct size_class *class = pool->size_class[i];
+       unsigned int class_idx;
+       enum fullness_group fg;
+       struct size_class *class;
+       struct mapping_area *area;
 
-               if (!class)
-                       continue;
+       BUG_ON(!handle);
 
-               if (class->index != i)
-                       continue;
+       obj_handle_to_location(handle, &page, &obj_idx);
+       get_zspage_mapping(get_first_page(page), &class_idx, &fg);
+       class = pool->size_class[class_idx];
+       off = obj_idx_to_offset(page, obj_idx, class->size);
 
-               for (fg = 0; fg < _ZS_NR_FULLNESS_GROUPS; fg++) {
-                       if (class->fullness_list[fg]) {
-                               pr_info("Freeing non-empty class with size %db, fullness group %d\n",
-                                       class->size, fg);
-                       }
-               }
-               kfree(class);
-       }
+       area = this_cpu_ptr(&zs_map_area);
+       if (off + class->size <= PAGE_SIZE)
+               kunmap_atomic(area->vm_addr);
+       else {
+               struct page *pages[2];
 
-       kfree(pool->size_class);
-       kfree(pool);
+               pages[0] = page;
+               pages[1] = get_next_page(page);
+               BUG_ON(!pages[1]);
+
+               __zs_unmap_object(area, pages, off, class->size);
+       }
+       put_cpu_var(zs_map_area);
 }
-EXPORT_SYMBOL_GPL(zs_destroy_pool);
+EXPORT_SYMBOL_GPL(zs_unmap_object);
 
 /**
  * zs_malloc - Allocate block of given size from pool.
@@ -1176,100 +1139,137 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
 EXPORT_SYMBOL_GPL(zs_free);
 
 /**
- * zs_map_object - get address of allocated object from handle.
- * @pool: pool from which the object was allocated
- * @handle: handle returned from zs_malloc
- *
- * Before using an object allocated from zs_malloc, it must be mapped using
- * this function. When done with the object, it must be unmapped using
- * zs_unmap_object.
+ * zs_create_pool - Creates an allocation pool to work from.
+ * @flags: allocation flags used to allocate pool metadata
  *
- * Only one object can be mapped per cpu at a time. There is no protection
- * against nested mappings.
+ * This function must be called before anything when using
+ * the zsmalloc allocator.
  *
- * This function returns with preemption and page faults disabled.
+ * On success, a pointer to the newly created pool is returned,
+ * otherwise NULL.
  */
-void *zs_map_object(struct zs_pool *pool, unsigned long handle,
-                       enum zs_mapmode mm)
+struct zs_pool *zs_create_pool(gfp_t flags)
 {
-       struct page *page;
-       unsigned long obj_idx, off;
+       int i;
+       struct zs_pool *pool;
+       struct size_class *prev_class = NULL;
 
-       unsigned int class_idx;
-       enum fullness_group fg;
-       struct size_class *class;
-       struct mapping_area *area;
-       struct page *pages[2];
+       pool = kzalloc(sizeof(*pool), GFP_KERNEL);
+       if (!pool)
+               return NULL;
 
-       BUG_ON(!handle);
+       pool->size_class = kcalloc(zs_size_classes, sizeof(struct size_class *),
+                       GFP_KERNEL);
+       if (!pool->size_class) {
+               kfree(pool);
+               return NULL;
+       }
 
        /*
-        * Because we use per-cpu mapping areas shared among the
-        * pools/users, we can't allow mapping in interrupt context
-        * because it can corrupt another users mappings.
+        * Iterate reversly, because, size of size_class that we want to use
+        * for merging should be larger or equal to current size.
         */
-       BUG_ON(in_interrupt());
+       for (i = zs_size_classes - 1; i >= 0; i--) {
+               int size;
+               int pages_per_zspage;
+               struct size_class *class;
 
-       obj_handle_to_location(handle, &page, &obj_idx);
-       get_zspage_mapping(get_first_page(page), &class_idx, &fg);
-       class = pool->size_class[class_idx];
-       off = obj_idx_to_offset(page, obj_idx, class->size);
+               size = ZS_MIN_ALLOC_SIZE + i * ZS_SIZE_CLASS_DELTA;
+               if (size > ZS_MAX_ALLOC_SIZE)
+                       size = ZS_MAX_ALLOC_SIZE;
+               pages_per_zspage = get_pages_per_zspage(size);
 
-       area = &get_cpu_var(zs_map_area);
-       area->vm_mm = mm;
-       if (off + class->size <= PAGE_SIZE) {
-               /* this object is contained entirely within a page */
-               area->vm_addr = kmap_atomic(page);
-               return area->vm_addr + off;
+               /*
+                * size_class is used for normal zsmalloc operation such
+                * as alloc/free for that size. Although it is natural that we
+                * have one size_class for each size, there is a chance that we
+                * can get more memory utilization if we use one size_class for
+                * many different sizes whose size_class have same
+                * characteristics. So, we makes size_class point to
+                * previous size_class if possible.
+                */
+               if (prev_class) {
+                       if (can_merge(prev_class, size, pages_per_zspage)) {
+                               pool->size_class[i] = prev_class;
+                               continue;
+                       }
+               }
+
+               class = kzalloc(sizeof(struct size_class), GFP_KERNEL);
+               if (!class)
+                       goto err;
+
+               class->size = size;
+               class->index = i;
+               class->pages_per_zspage = pages_per_zspage;
+               spin_lock_init(&class->lock);
+               pool->size_class[i] = class;
+
+               prev_class = class;
        }
 
-       /* this object spans two pages */
-       pages[0] = page;
-       pages[1] = get_next_page(page);
-       BUG_ON(!pages[1]);
+       pool->flags = flags;
 
-       return __zs_map_object(area, pages, off, class->size);
+       return pool;
+
+err:
+       zs_destroy_pool(pool);
+       return NULL;
 }
-EXPORT_SYMBOL_GPL(zs_map_object);
+EXPORT_SYMBOL_GPL(zs_create_pool);
 
-void zs_unmap_object(struct zs_pool *pool, unsigned long handle)
+void zs_destroy_pool(struct zs_pool *pool)
 {
-       struct page *page;
-       unsigned long obj_idx, off;
+       int i;
 
-       unsigned int class_idx;
-       enum fullness_group fg;
-       struct size_class *class;
-       struct mapping_area *area;
+       for (i = 0; i < zs_size_classes; i++) {
+               int fg;
+               struct size_class *class = pool->size_class[i];
 
-       BUG_ON(!handle);
+               if (!class)
+                       continue;
 
-       obj_handle_to_location(handle, &page, &obj_idx);
-       get_zspage_mapping(get_first_page(page), &class_idx, &fg);
-       class = pool->size_class[class_idx];
-       off = obj_idx_to_offset(page, obj_idx, class->size);
+               if (class->index != i)
+                       continue;
 
-       area = this_cpu_ptr(&zs_map_area);
-       if (off + class->size <= PAGE_SIZE)
-               kunmap_atomic(area->vm_addr);
-       else {
-               struct page *pages[2];
+               for (fg = 0; fg < _ZS_NR_FULLNESS_GROUPS; fg++) {
+                       if (class->fullness_list[fg]) {
+                               pr_info("Freeing non-empty class with size %db, fullness group %d\n",
+                                       class->size, fg);
+                       }
+               }
+               kfree(class);
+       }
 
-               pages[0] = page;
-               pages[1] = get_next_page(page);
-               BUG_ON(!pages[1]);
+       kfree(pool->size_class);
+       kfree(pool);
+}
+EXPORT_SYMBOL_GPL(zs_destroy_pool);
 
-               __zs_unmap_object(area, pages, off, class->size);
+static int __init zs_init(void)
+{
+       int ret = zs_register_cpu_notifier();
+
+       if (ret) {
+               zs_unregister_cpu_notifier();
+               return ret;
        }
-       put_cpu_var(zs_map_area);
+
+       init_zs_size_classes();
+
+#ifdef CONFIG_ZPOOL
+       zpool_register_driver(&zs_zpool_driver);
+#endif
+       return 0;
 }
-EXPORT_SYMBOL_GPL(zs_unmap_object);
 
-unsigned long zs_get_total_pages(struct zs_pool *pool)
+static void __exit zs_exit(void)
 {
-       return atomic_long_read(&pool->pages_allocated);
+#ifdef CONFIG_ZPOOL
+       zpool_unregister_driver(&zs_zpool_driver);
+#endif
+       zs_unregister_cpu_notifier();
 }
-EXPORT_SYMBOL_GPL(zs_get_total_pages);
 
 module_init(zs_init);
 module_exit(zs_exit);
index 8337645..8276a74 100644 (file)
@@ -1676,7 +1676,7 @@ irqreturn_t azx_interrupt(int irq, void *dev_id)
        u8 sd_status;
        int i;
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
        if (chip->driver_caps & AZX_DCAPS_PM_RUNTIME)
                if (!pm_runtime_active(chip->card->dev))
                        return IRQ_NONE;
index 5ac0d39..2bf0b56 100644 (file)
@@ -872,7 +872,7 @@ static int azx_resume(struct device *dev)
 }
 #endif /* CONFIG_PM_SLEEP || SUPPORT_VGA_SWITCHEROO */
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int azx_runtime_suspend(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
@@ -970,9 +970,6 @@ static int azx_runtime_idle(struct device *dev)
        return 0;
 }
 
-#endif /* CONFIG_PM_RUNTIME */
-
-#ifdef CONFIG_PM
 static const struct dev_pm_ops azx_pm = {
        SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
        SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle)
index c125925..ec55c59 100644 (file)
@@ -550,7 +550,7 @@ static int cs35l32_i2c_remove(struct i2c_client *i2c_client)
        return 0;
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int cs35l32_runtime_suspend(struct device *dev)
 {
        struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
index 02b1520..670ebfe 100644 (file)
@@ -537,7 +537,7 @@ err_enable:
 }
 EXPORT_SYMBOL_GPL(cs42xx8_probe);
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int cs42xx8_runtime_resume(struct device *dev)
 {
        struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev);
index 151f718..b112b1c 100644 (file)
@@ -2611,7 +2611,7 @@ static int max98090_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int max98090_runtime_resume(struct device *dev)
 {
        struct max98090_priv *max98090 = dev_get_drvdata(dev);
index 0c8aefa..e5f2fb8 100644 (file)
@@ -517,7 +517,7 @@ void pcm512x_remove(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(pcm512x_remove);
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int pcm512x_suspend(struct device *dev)
 {
        struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
index b505212..ae23acd 100644 (file)
@@ -115,7 +115,7 @@ static const struct snd_soc_dapm_route tas2552_audio_map[] = {
        {"ClassD", NULL, "PLL"},
 };
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
 {
        u8 cfg1_reg;
@@ -264,7 +264,7 @@ static int tas2552_mute(struct snd_soc_dai *dai, int mute)
        return 0;
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int tas2552_runtime_suspend(struct device *dev)
 {
        struct tas2552_data *tas2552 = dev_get_drvdata(dev);
index cdea9d9..1559984 100644 (file)
@@ -2440,7 +2440,7 @@ static int wm2200_i2c_remove(struct i2c_client *i2c)
        return 0;
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int wm2200_runtime_suspend(struct device *dev)
 {
        struct wm2200_priv *wm2200 = dev_get_drvdata(dev);
index a01ad62..b80970d 100644 (file)
@@ -2664,7 +2664,7 @@ static int wm5100_i2c_remove(struct i2c_client *i2c)
        return 0;
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int wm5100_runtime_suspend(struct device *dev)
 {
        struct wm5100_priv *wm5100 = dev_get_drvdata(dev);
index 1534d88..d32d554 100644 (file)
@@ -3785,7 +3785,7 @@ static int wm8962_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int wm8962_runtime_resume(struct device *dev)
 {
        struct wm8962_priv *wm8962 = dev_get_drvdata(dev);
index 9deabdd..026a801 100644 (file)
@@ -928,7 +928,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int fsl_asrc_runtime_resume(struct device *dev)
 {
        struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
@@ -954,7 +954,7 @@ static int fsl_asrc_runtime_suspend(struct device *dev)
 
        return 0;
 }
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM */
 
 #ifdef CONFIG_PM_SLEEP
 static int fsl_asrc_suspend(struct device *dev)
index 95340ba..b1a7c5b 100644 (file)
@@ -1135,7 +1135,7 @@ static inline const struct samsung_i2s_dai_data *samsung_i2s_get_driver_data(
                                platform_get_device_id(pdev)->driver_data;
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 static int i2s_runtime_suspend(struct device *dev)
 {
        struct i2s_dai *i2s = dev_get_drvdata(dev);
@@ -1153,7 +1153,7 @@ static int i2s_runtime_resume(struct device *dev)
 
        return 0;
 }
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM */
 
 static int samsung_i2s_probe(struct platform_device *pdev)
 {
index 458d69b..75e66de 100644 (file)
 
 static void cpuidle_cpu_output(unsigned int cpu, int verbose)
 {
-       int idlestates, idlestate;
+       unsigned int idlestates, idlestate;
        char *tmp;
 
        printf(_ ("Analyzing CPU %d:\n"), cpu);
 
        idlestates = sysfs_get_idlestate_count(cpu);
-       if (idlestates < 1) {
+       if (idlestates == 0) {
                printf(_("CPU %u: No idle states\n"), cpu);
                return;
        }
@@ -100,10 +100,10 @@ static void cpuidle_general_output(void)
 static void proc_cpuidle_cpu_output(unsigned int cpu)
 {
        long max_allowed_cstate = 2000000000;
-       int cstate, cstates;
+       unsigned int cstate, cstates;
 
        cstates = sysfs_get_idlestate_count(cpu);
-       if (cstates < 1) {
+       if (cstates == 0) {
                printf(_("CPU %u: No C-states info\n"), cpu);
                return;
        }
index b3831f4..4e51122 100644 (file)
@@ -1,22 +1,23 @@
 TARGETS = breakpoints
 TARGETS += cpu-hotplug
 TARGETS += efivarfs
+TARGETS += exec
+TARGETS += firmware
+TARGETS += ftrace
 TARGETS += kcmp
 TARGETS += memfd
 TARGETS += memory-hotplug
-TARGETS += mqueue
 TARGETS += mount
+TARGETS += mqueue
 TARGETS += net
+TARGETS += powerpc
 TARGETS += ptrace
+TARGETS += size
+TARGETS += sysctl
 TARGETS += timers
-TARGETS += vm
-TARGETS += powerpc
 TARGETS += user
-TARGETS += sysctl
-TARGETS += firmware
-TARGETS += ftrace
-TARGETS += exec
-TARGETS += size
+TARGETS += vm
+#Please keep the TARGETS list alphabetically sorted
 
 TARGETS_HOTPLUG = cpu-hotplug
 TARGETS_HOTPLUG += memory-hotplug
index 9325f46..505ad51 100644 (file)
@@ -3,7 +3,7 @@ test: virtio_test vringh_test
 virtio_test: virtio_ring.o virtio_test.o
 vringh_test: vringh_test.o vringh.o virtio_ring.o
 
-CFLAGS += -g -O2 -Wall -I. -I../include/ -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE
+CFLAGS += -g -O2 -Werror -Wall -I. -I../include/ -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE
 vpath %.c ../../drivers/virtio ../../drivers/vhost
 mod:
        ${MAKE} -C `pwd`/../.. M=`pwd`/vhost_test
index 8eb6421..a3e0701 100644 (file)
@@ -6,6 +6,7 @@
 /* TODO: empty stubs for now. Broken but enough for virtio_ring.c */
 #define list_add_tail(a, b) do {} while (0)
 #define list_del(a) do {} while (0)
+#define list_for_each_entry(a, b, c) while (0)
 /* end of stubs */
 
 struct virtio_device {
diff --git a/tools/virtio/linux/virtio_byteorder.h b/tools/virtio/linux/virtio_byteorder.h
new file mode 100644 (file)
index 0000000..9de9e6a
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _LINUX_VIRTIO_BYTEORDER_STUB_H
+#define _LINUX_VIRTIO_BYTEORDER_STUB_H
+
+#include <asm/byteorder.h>
+#include "../../include/linux/byteorder/generic.h"
+#include "../../include/linux/virtio_byteorder.h"
+
+#endif
index 83b27e8..806d683 100644 (file)
@@ -1,6 +1,72 @@
-#define VIRTIO_TRANSPORT_F_START       28
-#define VIRTIO_TRANSPORT_F_END         32
+#include <linux/virtio_byteorder.h>
+#include <linux/virtio.h>
+#include <uapi/linux/virtio_config.h>
+
+/*
+ * __virtio_test_bit - helper to test feature bits. For use by transports.
+ *                     Devices should normally use virtio_has_feature,
+ *                     which includes more checks.
+ * @vdev: the device
+ * @fbit: the feature bit
+ */
+static inline bool __virtio_test_bit(const struct virtio_device *vdev,
+                                    unsigned int fbit)
+{
+       return vdev->features & (1ULL << fbit);
+}
+
+/**
+ * __virtio_set_bit - helper to set feature bits. For use by transports.
+ * @vdev: the device
+ * @fbit: the feature bit
+ */
+static inline void __virtio_set_bit(struct virtio_device *vdev,
+                                   unsigned int fbit)
+{
+       vdev->features |= (1ULL << fbit);
+}
+
+/**
+ * __virtio_clear_bit - helper to clear feature bits. For use by transports.
+ * @vdev: the device
+ * @fbit: the feature bit
+ */
+static inline void __virtio_clear_bit(struct virtio_device *vdev,
+                                     unsigned int fbit)
+{
+       vdev->features &= ~(1ULL << fbit);
+}
 
 #define virtio_has_feature(dev, feature) \
        (__virtio_test_bit((dev), feature))
 
+static inline u16 virtio16_to_cpu(struct virtio_device *vdev, __virtio16 val)
+{
+       return __virtio16_to_cpu(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val);
+}
+
+static inline __virtio16 cpu_to_virtio16(struct virtio_device *vdev, u16 val)
+{
+       return __cpu_to_virtio16(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val);
+}
+
+static inline u32 virtio32_to_cpu(struct virtio_device *vdev, __virtio32 val)
+{
+       return __virtio32_to_cpu(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val);
+}
+
+static inline __virtio32 cpu_to_virtio32(struct virtio_device *vdev, u32 val)
+{
+       return __cpu_to_virtio32(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val);
+}
+
+static inline u64 virtio64_to_cpu(struct virtio_device *vdev, __virtio64 val)
+{
+       return __virtio64_to_cpu(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val);
+}
+
+static inline __virtio64 cpu_to_virtio64(struct virtio_device *vdev, u64 val)
+{
+       return __cpu_to_virtio64(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val);
+}
+
diff --git a/tools/virtio/uapi/linux/virtio_types.h b/tools/virtio/uapi/linux/virtio_types.h
new file mode 100644 (file)
index 0000000..e7a1096
--- /dev/null
@@ -0,0 +1 @@
+#include "../../include/uapi/linux/virtio_types.h"
index db3437c..e044589 100644 (file)
@@ -11,6 +11,7 @@
 #include <sys/types.h>
 #include <fcntl.h>
 #include <stdbool.h>
+#include <linux/virtio_types.h>
 #include <linux/vhost.h>
 #include <linux/virtio.h>
 #include <linux/virtio_ring.h>
@@ -226,6 +227,14 @@ const struct option longopts[] = {
                .name = "no-indirect",
                .val = 'i',
        },
+       {
+               .name = "virtio-1",
+               .val = '1',
+       },
+       {
+               .name = "no-virtio-1",
+               .val = '0',
+       },
        {
                .name = "delayed-interrupt",
                .val = 'D',
@@ -243,6 +252,7 @@ static void help(void)
        fprintf(stderr, "Usage: virtio_test [--help]"
                " [--no-indirect]"
                " [--no-event-idx]"
+               " [--no-virtio-1]"
                " [--delayed-interrupt]"
                "\n");
 }
@@ -251,7 +261,7 @@ int main(int argc, char **argv)
 {
        struct vdev_info dev;
        unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) |
-               (1ULL << VIRTIO_RING_F_EVENT_IDX);
+               (1ULL << VIRTIO_RING_F_EVENT_IDX) | (1ULL << VIRTIO_F_VERSION_1);
        int o;
        bool delayed = false;
 
@@ -272,6 +282,9 @@ int main(int argc, char **argv)
                case 'i':
                        features &= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC);
                        break;
+               case '0':
+                       features &= ~(1ULL << VIRTIO_F_VERSION_1);
+                       break;
                case 'D':
                        delayed = true;
                        break;
index 9d4b1bc..5f94f51 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/virtio.h>
 #include <linux/vringh.h>
 #include <linux/virtio_ring.h>
+#include <linux/virtio_config.h>
 #include <linux/uaccess.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -131,7 +132,7 @@ static inline int vringh_get_head(struct vringh *vrh, u16 *head)
        return 1;
 }
 
-static int parallel_test(unsigned long features,
+static int parallel_test(u64 features,
                         bool (*getrange)(struct vringh *vrh,
                                          u64 addr, struct vringh_range *r),
                         bool fast_vringh)
@@ -456,6 +457,8 @@ int main(int argc, char *argv[])
                        __virtio_set_bit(&vdev, VIRTIO_RING_F_INDIRECT_DESC);
                else if (strcmp(argv[1], "--eventidx") == 0)
                        __virtio_set_bit(&vdev, VIRTIO_RING_F_EVENT_IDX);
+               else if (strcmp(argv[1], "--virtio-1") == 0)
+                       __virtio_set_bit(&vdev, VIRTIO_F_VERSION_1);
                else if (strcmp(argv[1], "--slow-range") == 0)
                        getrange = getrange_slow;
                else if (strcmp(argv[1], "--fast-vringh") == 0)