]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 10 Aug 2010 22:09:54 +0000 (15:09 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 10 Aug 2010 22:09:54 +0000 (15:09 -0700)
* 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (94 commits)
  V4L/DVB: tvp7002: fix write to H-PLL Feedback Divider LSB register
  V4L/DVB: dvb: siano: free spinlock before schedule()
  V4L/DVB: media: video: pvrusb2: remove custom hex_to_bin()
  V4L/DVB: drivers: usbvideo: remove custom implementation of hex_to_bin()
  V4L/DVB: Report supported QAM modes on bt8xx
  V4L/DVB: media: ir-keytable: null dereference in debug code
  V4L/DVB: ivtv: convert to the new control framework
  V4L/DVB: ivtv: convert gpio subdev to new control framework
  V4L/DVB: wm8739: convert to the new control framework
  V4L/DVB: cs53l32a: convert to new control framework
  V4L/DVB: wm8775: convert to the new control framework
  V4L/DVB: cx2341x: convert to the control framework
  V4L/DVB: cx25840: convert to the new control framework
  V4L/DVB: cx25840/ivtv: replace ugly priv control with s_config
  V4L/DVB: saa717x: convert to the new control framework
  V4L/DVB: msp3400: convert to the new control framework
  V4L/DVB: saa7115: convert to the new control framework
  V4L/DVB: v4l2: hook up the new control framework into the core framework
  V4L/DVB: Documentation: add v4l2-controls.txt documenting the new controls API
  V4L/DVB: v4l2-ctrls: Whitespace cleanups
  ...

109 files changed:
Documentation/DocBook/v4l/lirc_device_interface.xml
Documentation/DocBook/v4l/pixfmt-packed-rgb.xml
Documentation/video4linux/v4l2-controls.txt [new file with mode: 0644]
MAINTAINERS
drivers/media/IR/Kconfig
drivers/media/IR/Makefile
drivers/media/IR/ene_ir.c [new file with mode: 0644]
drivers/media/IR/ene_ir.h [new file with mode: 0644]
drivers/media/IR/imon.c
drivers/media/IR/ir-core-priv.h
drivers/media/IR/ir-jvc-decoder.c
drivers/media/IR/ir-keytable.c
drivers/media/IR/ir-lirc-codec.c
drivers/media/IR/ir-nec-decoder.c
drivers/media/IR/ir-raw-event.c
drivers/media/IR/ir-sysfs.c
drivers/media/IR/keymaps/Makefile
drivers/media/IR/keymaps/rc-empty.c [deleted file]
drivers/media/IR/keymaps/rc-rc5-streamzap.c [new file with mode: 0644]
drivers/media/IR/keymaps/rc-rc6-mce.c
drivers/media/IR/mceusb.c
drivers/media/IR/rc-map.c
drivers/media/IR/streamzap.c [new file with mode: 0644]
drivers/media/common/tuners/Kconfig
drivers/media/dvb/bt8xx/dst.c
drivers/media/dvb/frontends/Kconfig
drivers/media/dvb/siano/smscoreapi.c
drivers/media/video/Kconfig
drivers/media/video/Makefile
drivers/media/video/cs53l32a.c
drivers/media/video/cx2341x.c
drivers/media/video/cx23885/Kconfig
drivers/media/video/cx23885/Makefile
drivers/media/video/cx23885/cx23885-av.c [new file with mode: 0644]
drivers/media/video/cx23885/cx23885-av.h [new file with mode: 0644]
drivers/media/video/cx23885/cx23885-cards.c
drivers/media/video/cx23885/cx23885-core.c
drivers/media/video/cx23885/cx23885-i2c.c
drivers/media/video/cx23885/cx23885-input.c
drivers/media/video/cx23885/cx23885-ir.c
drivers/media/video/cx23885/cx23885-reg.h
drivers/media/video/cx23885/cx23885-vbi.c
drivers/media/video/cx23885/cx23885-video.c
drivers/media/video/cx23885/cx23885.h
drivers/media/video/cx23885/cx23888-ir.c
drivers/media/video/cx25840/Makefile
drivers/media/video/cx25840/cx25840-audio.c
drivers/media/video/cx25840/cx25840-core.c
drivers/media/video/cx25840/cx25840-core.h
drivers/media/video/cx25840/cx25840-ir.c [new file with mode: 0644]
drivers/media/video/gspca/gspca.c
drivers/media/video/gspca/sonixj.c
drivers/media/video/gspca/sq930x.c
drivers/media/video/gspca/t613.c
drivers/media/video/gspca/vc032x.c
drivers/media/video/gspca/zc3xx.c
drivers/media/video/ivtv/ivtv-controls.c
drivers/media/video/ivtv/ivtv-controls.h
drivers/media/video/ivtv/ivtv-driver.c
drivers/media/video/ivtv/ivtv-driver.h
drivers/media/video/ivtv/ivtv-fileops.c
drivers/media/video/ivtv/ivtv-firmware.c
drivers/media/video/ivtv/ivtv-gpio.c
drivers/media/video/ivtv/ivtv-i2c.c
drivers/media/video/ivtv/ivtv-ioctl.c
drivers/media/video/ivtv/ivtv-streams.c
drivers/media/video/msp3400-driver.c
drivers/media/video/msp3400-driver.h
drivers/media/video/msp3400-kthreads.c
drivers/media/video/mt9m111.c
drivers/media/video/mx2_camera.c
drivers/media/video/pvrusb2/pvrusb2-debugifc.c
drivers/media/video/s5p-fimc/Makefile [new file with mode: 0644]
drivers/media/video/s5p-fimc/fimc-core.c [new file with mode: 0644]
drivers/media/video/s5p-fimc/fimc-core.h [new file with mode: 0644]
drivers/media/video/s5p-fimc/fimc-reg.c [new file with mode: 0644]
drivers/media/video/s5p-fimc/regs-fimc.h [new file with mode: 0644]
drivers/media/video/saa7115.c
drivers/media/video/saa717x.c
drivers/media/video/soc_camera.c
drivers/media/video/tvp7002.c
drivers/media/video/usbvideo/usbvideo.c
drivers/media/video/uvc/uvc_driver.c
drivers/media/video/uvc/uvc_queue.c
drivers/media/video/uvc/uvc_video.c
drivers/media/video/uvc/uvcvideo.h
drivers/media/video/v4l2-common.c
drivers/media/video/v4l2-ctrls.c [new file with mode: 0644]
drivers/media/video/v4l2-dev.c
drivers/media/video/v4l2-device.c
drivers/media/video/v4l2-ioctl.c
drivers/media/video/wm8739.c
drivers/media/video/wm8775.c
drivers/staging/lirc/Kconfig
drivers/staging/lirc/Makefile
drivers/staging/lirc/lirc_ene0100.c [deleted file]
drivers/staging/lirc/lirc_it87.c
drivers/staging/lirc/lirc_parallel.c
drivers/staging/lirc/lirc_streamzap.c [deleted file]
include/linux/videodev2.h
include/media/cx2341x.h
include/media/cx25840.h
include/media/ir-core.h
include/media/lirc.h
include/media/rc-map.h
include/media/v4l2-ctrls.h [new file with mode: 0644]
include/media/v4l2-dev.h
include/media/v4l2-device.h
include/media/v4l2-subdev.h

index 0413234023d43ebdd8870eb2421bebcec24fe757..68134c0ab4d1b19aaf66c920bcc4e61662fcd68a 100644 (file)
@@ -229,6 +229,22 @@ on working with the default settings initially.</para>
       and LIRC_SETUP_END. Drivers can also choose to ignore these ioctls.</para>
     </listitem>
   </varlistentry>
+  <varlistentry>
+    <term>LIRC_SET_WIDEBAND_RECEIVER</term>
+    <listitem>
+      <para>Some receivers are equipped with special wide band receiver which is intended
+      to be used to learn output of existing remote.
+      Calling that ioctl with (1) will enable it, and with (0) disable it.
+      This might be useful of receivers that have otherwise narrow band receiver
+      that prevents them to be used with some remotes.
+      Wide band receiver might also be more precise
+      On the other hand its disadvantage it usually reduced range of reception.
+      Note: wide band receiver might be implictly enabled if you enable
+      carrier reports. In that case it will be disabled as soon as you disable
+      carrier reports. Trying to disable wide band receiver while carrier
+      reports are active will do nothing.</para>
+    </listitem>
+  </varlistentry>
 </variablelist>
 
 </section>
index d2dd697a81d8d1f3a9e0c029638219268c47a39a..26e879231088297c059ca5ac79ea04e02121d15d 100644 (file)
@@ -240,6 +240,45 @@ colorspace <constant>V4L2_COLORSPACE_SRGB</constant>.</para>
            <entry>r<subscript>1</subscript></entry>
            <entry>r<subscript>0</subscript></entry>
          </row>
+         <row id="V4L2-PIX-FMT-BGR666">
+           <entry><constant>V4L2_PIX_FMT_BGR666</constant></entry>
+           <entry>'BGRH'</entry>
+           <entry></entry>
+           <entry>b<subscript>5</subscript></entry>
+           <entry>b<subscript>4</subscript></entry>
+           <entry>b<subscript>3</subscript></entry>
+           <entry>b<subscript>2</subscript></entry>
+           <entry>b<subscript>1</subscript></entry>
+           <entry>b<subscript>0</subscript></entry>
+           <entry>g<subscript>5</subscript></entry>
+           <entry>g<subscript>4</subscript></entry>
+           <entry></entry>
+           <entry>g<subscript>3</subscript></entry>
+           <entry>g<subscript>2</subscript></entry>
+           <entry>g<subscript>1</subscript></entry>
+           <entry>g<subscript>0</subscript></entry>
+           <entry>r<subscript>5</subscript></entry>
+           <entry>r<subscript>4</subscript></entry>
+           <entry>r<subscript>3</subscript></entry>
+           <entry>r<subscript>2</subscript></entry>
+           <entry></entry>
+           <entry>r<subscript>1</subscript></entry>
+           <entry>r<subscript>0</subscript></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+         </row>
          <row id="V4L2-PIX-FMT-BGR24">
            <entry><constant>V4L2_PIX_FMT_BGR24</constant></entry>
            <entry>'BGR3'</entry>
@@ -700,6 +739,45 @@ defined in error. Drivers may interpret them as in <xref
            <entry>b<subscript>1</subscript></entry>
            <entry>b<subscript>0</subscript></entry>
          </row>
+         <row id="V4L2-PIX-FMT-BGR666">
+           <entry><constant>V4L2_PIX_FMT_BGR666</constant></entry>
+           <entry>'BGRH'</entry>
+           <entry></entry>
+           <entry>b<subscript>5</subscript></entry>
+           <entry>b<subscript>4</subscript></entry>
+           <entry>b<subscript>3</subscript></entry>
+           <entry>b<subscript>2</subscript></entry>
+           <entry>b<subscript>1</subscript></entry>
+           <entry>b<subscript>0</subscript></entry>
+           <entry>g<subscript>5</subscript></entry>
+           <entry>g<subscript>4</subscript></entry>
+           <entry></entry>
+           <entry>g<subscript>3</subscript></entry>
+           <entry>g<subscript>2</subscript></entry>
+           <entry>g<subscript>1</subscript></entry>
+           <entry>g<subscript>0</subscript></entry>
+           <entry>r<subscript>5</subscript></entry>
+           <entry>r<subscript>4</subscript></entry>
+           <entry>r<subscript>3</subscript></entry>
+           <entry>r<subscript>2</subscript></entry>
+           <entry></entry>
+           <entry>r<subscript>1</subscript></entry>
+           <entry>r<subscript>0</subscript></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+           <entry></entry>
+         </row>
          <row><!-- id="V4L2-PIX-FMT-BGR24" -->
            <entry><constant>V4L2_PIX_FMT_BGR24</constant></entry>
            <entry>'BGR3'</entry>
diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt
new file mode 100644 (file)
index 0000000..8773778
--- /dev/null
@@ -0,0 +1,648 @@
+Introduction
+============
+
+The V4L2 control API seems simple enough, but quickly becomes very hard to
+implement correctly in drivers. But much of the code needed to handle controls
+is actually not driver specific and can be moved to the V4L core framework.
+
+After all, the only part that a driver developer is interested in is:
+
+1) How do I add a control?
+2) How do I set the control's value? (i.e. s_ctrl)
+
+And occasionally:
+
+3) How do I get the control's value? (i.e. g_volatile_ctrl)
+4) How do I validate the user's proposed control value? (i.e. try_ctrl)
+
+All the rest is something that can be done centrally.
+
+The control framework was created in order to implement all the rules of the
+V4L2 specification with respect to controls in a central place. And to make
+life as easy as possible for the driver developer.
+
+Note that the control framework relies on the presence of a struct v4l2_device
+for V4L2 drivers and struct v4l2_subdev for sub-device drivers.
+
+
+Objects in the framework
+========================
+
+There are two main objects:
+
+The v4l2_ctrl object describes the control properties and keeps track of the
+control's value (both the current value and the proposed new value).
+
+v4l2_ctrl_handler is the object that keeps track of controls. It maintains a
+list of v4l2_ctrl objects that it owns and another list of references to
+controls, possibly to controls owned by other handlers.
+
+
+Basic usage for V4L2 and sub-device drivers
+===========================================
+
+1) Prepare the driver:
+
+1.1) Add the handler to your driver's top-level struct:
+
+       struct foo_dev {
+               ...
+               struct v4l2_ctrl_handler ctrl_handler;
+               ...
+       };
+
+       struct foo_dev *foo;
+
+1.2) Initialize the handler:
+
+       v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
+
+  The second argument is a hint telling the function how many controls this
+  handler is expected to handle. It will allocate a hashtable based on this
+  information. It is a hint only.
+
+1.3) Hook the control handler into the driver:
+
+1.3.1) For V4L2 drivers do this:
+
+       struct foo_dev {
+               ...
+               struct v4l2_device v4l2_dev;
+               ...
+               struct v4l2_ctrl_handler ctrl_handler;
+               ...
+       };
+
+       foo->v4l2_dev.ctrl_handler = &foo->ctrl_handler;
+
+  Where foo->v4l2_dev is of type struct v4l2_device.
+
+  Finally, remove all control functions from your v4l2_ioctl_ops:
+  vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl,
+  vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls.
+  Those are now no longer needed.
+
+1.3.2) For sub-device drivers do this:
+
+       struct foo_dev {
+               ...
+               struct v4l2_subdev sd;
+               ...
+               struct v4l2_ctrl_handler ctrl_handler;
+               ...
+       };
+
+       foo->sd.ctrl_handler = &foo->ctrl_handler;
+
+  Where foo->sd is of type struct v4l2_subdev.
+
+  And set all core control ops in your struct v4l2_subdev_core_ops to these
+  helpers:
+
+       .queryctrl = v4l2_subdev_queryctrl,
+       .querymenu = v4l2_subdev_querymenu,
+       .g_ctrl = v4l2_subdev_g_ctrl,
+       .s_ctrl = v4l2_subdev_s_ctrl,
+       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+
+  Note: this is a temporary solution only. Once all V4L2 drivers that depend
+  on subdev drivers are converted to the control framework these helpers will
+  no longer be needed.
+
+1.4) Clean up the handler at the end:
+
+       v4l2_ctrl_handler_free(&foo->ctrl_handler);
+
+
+2) Add controls:
+
+You add non-menu controls by calling v4l2_ctrl_new_std:
+
+       struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+                       const struct v4l2_ctrl_ops *ops,
+                       u32 id, s32 min, s32 max, u32 step, s32 def);
+
+Menu controls are added by calling v4l2_ctrl_new_std_menu:
+
+       struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
+                       const struct v4l2_ctrl_ops *ops,
+                       u32 id, s32 max, s32 skip_mask, s32 def);
+
+These functions are typically called right after the v4l2_ctrl_handler_init:
+
+       v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
+       v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+       v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 255, 1, 128);
+       v4l2_ctrl_new_std_menu(&foo->ctrl_handler, &foo_ctrl_ops,
+                       V4L2_CID_POWER_LINE_FREQUENCY,
+                       V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+                       V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
+       ...
+       if (foo->ctrl_handler.error) {
+               int err = foo->ctrl_handler.error;
+
+               v4l2_ctrl_handler_free(&foo->ctrl_handler);
+               return err;
+       }
+
+The v4l2_ctrl_new_std function returns the v4l2_ctrl pointer to the new
+control, but if you do not need to access the pointer outside the control ops,
+then there is no need to store it.
+
+The v4l2_ctrl_new_std function will fill in most fields based on the control
+ID except for the min, max, step and default values. These are passed in the
+last four arguments. These values are driver specific while control attributes
+like type, name, flags are all global. The control's current value will be set
+to the default value.
+
+The v4l2_ctrl_new_std_menu function is very similar but it is used for menu
+controls. There is no min argument since that is always 0 for menu controls,
+and instead of a step there is a skip_mask argument: if bit X is 1, then menu
+item X is skipped.
+
+Note that if something fails, the function will return NULL or an error and
+set ctrl_handler->error to the error code. If ctrl_handler->error was already
+set, then it will just return and do nothing. This is also true for
+v4l2_ctrl_handler_init if it cannot allocate the internal data structure.
+
+This makes it easy to init the handler and just add all controls and only check
+the error code at the end. Saves a lot of repetitive error checking.
+
+It is recommended to add controls in ascending control ID order: it will be
+a bit faster that way.
+
+3) Optionally force initial control setup:
+
+       v4l2_ctrl_handler_setup(&foo->ctrl_handler);
+
+This will call s_ctrl for all controls unconditionally. Effectively this
+initializes the hardware to the default control values. It is recommended
+that you do this as this ensures that both the internal data structures and
+the hardware are in sync.
+
+4) Finally: implement the v4l2_ctrl_ops
+
+       static const struct v4l2_ctrl_ops foo_ctrl_ops = {
+               .s_ctrl = foo_s_ctrl,
+       };
+
+Usually all you need is s_ctrl:
+
+       static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
+       {
+               struct foo *state = container_of(ctrl->handler, struct foo, ctrl_handler);
+
+               switch (ctrl->id) {
+               case V4L2_CID_BRIGHTNESS:
+                       write_reg(0x123, ctrl->val);
+                       break;
+               case V4L2_CID_CONTRAST:
+                       write_reg(0x456, ctrl->val);
+                       break;
+               }
+               return 0;
+       }
+
+The control ops are called with the v4l2_ctrl pointer as argument.
+The new control value has already been validated, so all you need to do is
+to actually update the hardware registers.
+
+You're done! And this is sufficient for most of the drivers we have. No need
+to do any validation of control values, or implement QUERYCTRL/QUERYMENU. And
+G/S_CTRL as well as G/TRY/S_EXT_CTRLS are automatically supported.
+
+
+==============================================================================
+
+The remainder of this document deals with more advanced topics and scenarios.
+In practice the basic usage as described above is sufficient for most drivers.
+
+===============================================================================
+
+
+Inheriting Controls
+===================
+
+When a sub-device is registered with a V4L2 driver by calling
+v4l2_device_register_subdev() and the ctrl_handler fields of both v4l2_subdev
+and v4l2_device are set, then the controls of the subdev will become
+automatically available in the V4L2 driver as well. If the subdev driver
+contains controls that already exist in the V4L2 driver, then those will be
+skipped (so a V4L2 driver can always override a subdev control).
+
+What happens here is that v4l2_device_register_subdev() calls
+v4l2_ctrl_add_handler() adding the controls of the subdev to the controls
+of v4l2_device.
+
+
+Accessing Control Values
+========================
+
+The v4l2_ctrl struct contains these two unions:
+
+       /* The current control value. */
+       union {
+               s32 val;
+               s64 val64;
+               char *string;
+       } cur;
+
+       /* The new control value. */
+       union {
+               s32 val;
+               s64 val64;
+               char *string;
+       };
+
+Within the control ops you can freely use these. The val and val64 speak for
+themselves. The string pointers point to character buffers of length
+ctrl->maximum + 1, and are always 0-terminated.
+
+In most cases 'cur' contains the current cached control value. When you create
+a new control this value is made identical to the default value. After calling
+v4l2_ctrl_handler_setup() this value is passed to the hardware. It is generally
+a good idea to call this function.
+
+Whenever a new value is set that new value is automatically cached. This means
+that most drivers do not need to implement the g_volatile_ctrl() op. The
+exception is for controls that return a volatile register such as a signal
+strength read-out that changes continuously. In that case you will need to
+implement g_volatile_ctrl like this:
+
+       static int foo_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+       {
+               switch (ctrl->id) {
+               case V4L2_CID_BRIGHTNESS:
+                       ctrl->cur.val = read_reg(0x123);
+                       break;
+               }
+       }
+
+The 'new value' union is not used in g_volatile_ctrl. In general controls
+that need to implement g_volatile_ctrl are read-only controls.
+
+To mark a control as volatile you have to set the is_volatile flag:
+
+       ctrl = v4l2_ctrl_new_std(&sd->ctrl_handler, ...);
+       if (ctrl)
+               ctrl->is_volatile = 1;
+
+For try/s_ctrl the new values (i.e. as passed by the user) are filled in and
+you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union
+contains the current value, which you can use (but not change!) as well.
+
+If s_ctrl returns 0 (OK), then the control framework will copy the new final
+values to the 'cur' union.
+
+While in g_volatile/s/try_ctrl you can access the value of all controls owned
+by the same handler since the handler's lock is held. If you need to access
+the value of controls owned by other handlers, then you have to be very careful
+not to introduce deadlocks.
+
+Outside of the control ops you have to go through to helper functions to get
+or set a single control value safely in your driver:
+
+       s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
+       int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
+
+These functions go through the control framework just as VIDIOC_G/S_CTRL ioctls
+do. Don't use these inside the control ops g_volatile/s/try_ctrl, though, that
+will result in a deadlock since these helpers lock the handler as well.
+
+You can also take the handler lock yourself:
+
+       mutex_lock(&state->ctrl_handler.lock);
+       printk(KERN_INFO "String value is '%s'\n", ctrl1->cur.string);
+       printk(KERN_INFO "Integer value is '%s'\n", ctrl2->cur.val);
+       mutex_unlock(&state->ctrl_handler.lock);
+
+
+Menu Controls
+=============
+
+The v4l2_ctrl struct contains this union:
+
+       union {
+               u32 step;
+               u32 menu_skip_mask;
+       };
+
+For menu controls menu_skip_mask is used. What it does is that it allows you
+to easily exclude certain menu items. This is used in the VIDIOC_QUERYMENU
+implementation where you can return -EINVAL if a certain menu item is not
+present. Note that VIDIOC_QUERYCTRL always returns a step value of 1 for
+menu controls.
+
+A good example is the MPEG Audio Layer II Bitrate menu control where the
+menu is a list of standardized possible bitrates. But in practice hardware
+implementations will only support a subset of those. By setting the skip
+mask you can tell the framework which menu items should be skipped. Setting
+it to 0 means that all menu items are supported.
+
+You set this mask either through the v4l2_ctrl_config struct for a custom
+control, or by calling v4l2_ctrl_new_std_menu().
+
+
+Custom Controls
+===============
+
+Driver specific controls can be created using v4l2_ctrl_new_custom():
+
+       static const struct v4l2_ctrl_config ctrl_filter = {
+               .ops = &ctrl_custom_ops,
+               .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
+               .name = "Spatial Filter",
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .flags = V4L2_CTRL_FLAG_SLIDER,
+               .max = 15,
+               .step = 1,
+       };
+
+       ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_filter, NULL);
+
+The last argument is the priv pointer which can be set to driver-specific
+private data.
+
+The v4l2_ctrl_config struct also has fields to set the is_private and is_volatile
+flags.
+
+If the name field is not set, then the framework will assume this is a standard
+control and will fill in the name, type and flags fields accordingly.
+
+
+Active and Grabbed Controls
+===========================
+
+If you get more complex relationships between controls, then you may have to
+activate and deactivate controls. For example, if the Chroma AGC control is
+on, then the Chroma Gain control is inactive. That is, you may set it, but
+the value will not be used by the hardware as long as the automatic gain
+control is on. Typically user interfaces can disable such input fields.
+
+You can set the 'active' status using v4l2_ctrl_activate(). By default all
+controls are active. Note that the framework does not check for this flag.
+It is meant purely for GUIs. The function is typically called from within
+s_ctrl.
+
+The other flag is the 'grabbed' flag. A grabbed control means that you cannot
+change it because it is in use by some resource. Typical examples are MPEG
+bitrate controls that cannot be changed while capturing is in progress.
+
+If a control is set to 'grabbed' using v4l2_ctrl_grab(), then the framework
+will return -EBUSY if an attempt is made to set this control. The
+v4l2_ctrl_grab() function is typically called from the driver when it
+starts or stops streaming.
+
+
+Control Clusters
+================
+
+By default all controls are independent from the others. But in more
+complex scenarios you can get dependencies from one control to another.
+In that case you need to 'cluster' them:
+
+       struct foo {
+               struct v4l2_ctrl_handler ctrl_handler;
+#define AUDIO_CL_VOLUME (0)
+#define AUDIO_CL_MUTE   (1)
+               struct v4l2_ctrl *audio_cluster[2];
+               ...
+       };
+
+       state->audio_cluster[AUDIO_CL_VOLUME] =
+               v4l2_ctrl_new_std(&state->ctrl_handler, ...);
+       state->audio_cluster[AUDIO_CL_MUTE] =
+               v4l2_ctrl_new_std(&state->ctrl_handler, ...);
+       v4l2_ctrl_cluster(ARRAY_SIZE(state->audio_cluster), state->audio_cluster);
+
+From now on whenever one or more of the controls belonging to the same
+cluster is set (or 'gotten', or 'tried'), only the control ops of the first
+control ('volume' in this example) is called. You effectively create a new
+composite control. Similar to how a 'struct' works in C.
+
+So when s_ctrl is called with V4L2_CID_AUDIO_VOLUME as argument, you should set
+all two controls belonging to the audio_cluster:
+
+       static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
+       {
+               struct foo *state = container_of(ctrl->handler, struct foo, ctrl_handler);
+
+               switch (ctrl->id) {
+               case V4L2_CID_AUDIO_VOLUME: {
+                       struct v4l2_ctrl *mute = ctrl->cluster[AUDIO_CL_MUTE];
+
+                       write_reg(0x123, mute->val ? 0 : ctrl->val);
+                       break;
+               }
+               case V4L2_CID_CONTRAST:
+                       write_reg(0x456, ctrl->val);
+                       break;
+               }
+               return 0;
+       }
+
+In the example above the following are equivalent for the VOLUME case:
+
+       ctrl == ctrl->cluster[AUDIO_CL_VOLUME] == state->audio_cluster[AUDIO_CL_VOLUME]
+       ctrl->cluster[AUDIO_CL_MUTE] == state->audio_cluster[AUDIO_CL_MUTE]
+
+Note that controls in a cluster may be NULL. For example, if for some
+reason mute was never added (because the hardware doesn't support that
+particular feature), then mute will be NULL. So in that case we have a
+cluster of 2 controls, of which only 1 is actually instantiated. The
+only restriction is that the first control of the cluster must always be
+present, since that is the 'master' control of the cluster. The master
+control is the one that identifies the cluster and that provides the
+pointer to the v4l2_ctrl_ops struct that is used for that cluster.
+
+Obviously, all controls in the cluster array must be initialized to either
+a valid control or to NULL.
+
+
+VIDIOC_LOG_STATUS Support
+=========================
+
+This ioctl allow you to dump the current status of a driver to the kernel log.
+The v4l2_ctrl_handler_log_status(ctrl_handler, prefix) can be used to dump the
+value of the controls owned by the given handler to the log. You can supply a
+prefix as well. If the prefix didn't end with a space, then ': ' will be added
+for you.
+
+
+Different Handlers for Different Video Nodes
+============================================
+
+Usually the V4L2 driver has just one control handler that is global for
+all video nodes. But you can also specify different control handlers for
+different video nodes. You can do that by manually setting the ctrl_handler
+field of struct video_device.
+
+That is no problem if there are no subdevs involved but if there are, then
+you need to block the automatic merging of subdev controls to the global
+control handler. You do that by simply setting the ctrl_handler field in
+struct v4l2_device to NULL. Now v4l2_device_register_subdev() will no longer
+merge subdev controls.
+
+After each subdev was added, you will then have to call v4l2_ctrl_add_handler
+manually to add the subdev's control handler (sd->ctrl_handler) to the desired
+control handler. This control handler may be specific to the video_device or
+for a subset of video_device's. For example: the radio device nodes only have
+audio controls, while the video and vbi device nodes share the same control
+handler for the audio and video controls.
+
+If you want to have one handler (e.g. for a radio device node) have a subset
+of another handler (e.g. for a video device node), then you should first add
+the controls to the first handler, add the other controls to the second
+handler and finally add the first handler to the second. For example:
+
+       v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_VOLUME, ...);
+       v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
+       v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
+       v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
+       v4l2_ctrl_add_handler(&video_ctrl_handler, &radio_ctrl_handler);
+
+Or you can add specific controls to a handler:
+
+       volume = v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_AUDIO_VOLUME, ...);
+       v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_BRIGHTNESS, ...);
+       v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_CONTRAST, ...);
+       v4l2_ctrl_add_ctrl(&radio_ctrl_handler, volume);
+
+What you should not do is make two identical controls for two handlers.
+For example:
+
+       v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
+       v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_AUDIO_MUTE, ...);
+
+This would be bad since muting the radio would not change the video mute
+control. The rule is to have one control for each hardware 'knob' that you
+can twiddle.
+
+
+Finding Controls
+================
+
+Normally you have created the controls yourself and you can store the struct
+v4l2_ctrl pointer into your own struct.
+
+But sometimes you need to find a control from another handler that you do
+not own. For example, if you have to find a volume control from a subdev.
+
+You can do that by calling v4l2_ctrl_find:
+
+       struct v4l2_ctrl *volume;
+
+       volume = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_AUDIO_VOLUME);
+
+Since v4l2_ctrl_find will lock the handler you have to be careful where you
+use it. For example, this is not a good idea:
+
+       struct v4l2_ctrl_handler ctrl_handler;
+
+       v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
+       v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
+
+...and in video_ops.s_ctrl:
+
+       case V4L2_CID_BRIGHTNESS:
+               contrast = v4l2_find_ctrl(&ctrl_handler, V4L2_CID_CONTRAST);
+               ...
+
+When s_ctrl is called by the framework the ctrl_handler.lock is already taken, so
+attempting to find another control from the same handler will deadlock.
+
+It is recommended not to use this function from inside the control ops.
+
+
+Inheriting Controls
+===================
+
+When one control handler is added to another using v4l2_ctrl_add_handler, then
+by default all controls from one are merged to the other. But a subdev might
+have low-level controls that make sense for some advanced embedded system, but
+not when it is used in consumer-level hardware. In that case you want to keep
+those low-level controls local to the subdev. You can do this by simply
+setting the 'is_private' flag of the control to 1:
+
+       static const struct v4l2_ctrl_config ctrl_private = {
+               .ops = &ctrl_custom_ops,
+               .id = V4L2_CID_...,
+               .name = "Some Private Control",
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .max = 15,
+               .step = 1,
+               .is_private = 1,
+       };
+
+       ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_private, NULL);
+
+These controls will now be skipped when v4l2_ctrl_add_handler is called.
+
+
+V4L2_CTRL_TYPE_CTRL_CLASS Controls
+==================================
+
+Controls of this type can be used by GUIs to get the name of the control class.
+A fully featured GUI can make a dialog with multiple tabs with each tab
+containing the controls belonging to a particular control class. The name of
+each tab can be found by querying a special control with ID <control class | 1>.
+
+Drivers do not have to care about this. The framework will automatically add
+a control of this type whenever the first control belonging to a new control
+class is added.
+
+
+Differences from the Spec
+=========================
+
+There are a few places where the framework acts slightly differently from the
+V4L2 Specification. Those differences are described in this section. We will
+have to see whether we need to adjust the spec or not.
+
+1) It is no longer required to have all controls contained in a
+v4l2_ext_control array be from the same control class. The framework will be
+able to handle any type of control in the array. You need to set ctrl_class
+to 0 in order to enable this. If ctrl_class is non-zero, then it will still
+check that all controls belong to that control class.
+
+If you set ctrl_class to 0 and count to 0, then it will only return an error
+if there are no controls at all.
+
+2) Clarified the way error_idx works. For get and set it will be equal to
+count if nothing was done yet. If it is less than count then only the controls
+up to error_idx-1 were successfully applied.
+
+3) When attempting to read a button control the framework will return -EACCES
+instead of -EINVAL as stated in the spec. It seems to make more sense since
+button controls are write-only controls.
+
+4) Attempting to write to a read-only control will return -EACCES instead of
+-EINVAL as the spec says.
+
+5) The spec does not mention what should happen when you try to set/get a
+control class controls. ivtv currently returns -EINVAL (indicating that the
+control ID does not exist) while the framework will return -EACCES, which
+makes more sense.
+
+
+Proposals for Extensions
+========================
+
+Some ideas for future extensions to the spec:
+
+1) Add a V4L2_CTRL_FLAG_HEX to have values shown as hexadecimal instead of
+decimal. Useful for e.g. video_mute_yuv.
+
+2) It is possible to mark in the controls array which controls have been
+successfully written and which failed by for example adding a bit to the
+control ID. Not sure if it is worth the effort, though.
+
+3) Trying to set volatile inactive controls should result in -EACCESS.
+
+4) Add a new flag to mark volatile controls. Any application that wants
+to store the state of the controls can then skip volatile inactive controls.
+Currently it is not possible to detect such controls.
index 832f904db114bec15af5e1cad54b2fd62e765f37..89399474d91f188826df41fbff753721d3448a0f 100644 (file)
@@ -2203,6 +2203,12 @@ F:       drivers/misc/cb710/
 F:     drivers/mmc/host/cb710-mmc.*
 F:     include/linux/cb710.h
 
+ENE KB2426 (ENE0100/ENE020XX) INFRARED RECEIVER
+M:     Maxim Levitsky <maximlevitsky@gmail.com>
+S:     Maintained
+F:     drivers/media/IR/ene_ir.c
+F:     drivers/media/IR/ene_ir.h
+
 EPSON 1355 FRAMEBUFFER DRIVER
 M:     Christopher Hoover <ch@murgatroid.com>
 M:     Christopher Hoover <ch@hpl.hp.com>
index 999a8250b3cee372d82e0409621a54ea00da85fe..30e04915a256d7da20049412fcae1f7883cdf23b 100644 (file)
@@ -1,8 +1,10 @@
-config IR_CORE
-       tristate
+menuconfig IR_CORE
+       tristate "Infrared remote controller adapters"
        depends on INPUT
        default INPUT
 
+if IR_CORE
+
 config VIDEO_IR
        tristate
        depends on IR_CORE
@@ -16,7 +18,7 @@ config LIRC
           Enable this option to build the Linux Infrared Remote
           Control (LIRC) core device interface driver. The LIRC
           interface passes raw IR to and from userspace, where the
-          LIRC daemon handles protocol decoding for IR reception ann
+          LIRC daemon handles protocol decoding for IR reception and
           encoding for IR transmitting (aka "blasting").
 
 source "drivers/media/IR/keymaps/Kconfig"
@@ -103,3 +105,31 @@ config IR_MCEUSB
 
           To compile this driver as a module, choose M here: the
           module will be called mceusb.
+
+config IR_ENE
+       tristate "ENE eHome Receiver/Transciever (pnp id: ENE0100/ENE02xxx)"
+       depends on PNP
+       depends on IR_CORE
+       ---help---
+          Say Y here to enable support for integrated infrared receiver
+          /transciever made by ENE.
+
+          You can see if you have it by looking at lspnp output.
+          Output should include ENE0100 ENE0200 or something similiar.
+
+          To compile this driver as a module, choose M here: the
+          module will be called ene_ir.
+
+config IR_STREAMZAP
+       tristate "Streamzap PC Remote IR Receiver"
+       depends on USB_ARCH_HAS_HCD
+       depends on IR_CORE
+       select USB
+       ---help---
+          Say Y here if you want to use a Streamzap PC Remote
+          Infrared Receiver.
+
+          To compile this driver as a module, choose M here: the
+          module will be called streamzap.
+
+endif #IR_CORE
index 2ae4f3abfdbd0ca73c5eb48739b4767d3032e0ec..53676838fe97d9486d7d6c4c66873919a05fd513 100644 (file)
@@ -16,3 +16,5 @@ obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
 # stand-alone IR receivers/transmitters
 obj-$(CONFIG_IR_IMON) += imon.o
 obj-$(CONFIG_IR_MCEUSB) += mceusb.o
+obj-$(CONFIG_IR_ENE) += ene_ir.o
+obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
diff --git a/drivers/media/IR/ene_ir.c b/drivers/media/IR/ene_ir.c
new file mode 100644 (file)
index 0000000..5447750
--- /dev/null
@@ -0,0 +1,1023 @@
+/*
+ * driver for ENE KB3926 B/C/D CIR (pnp id: ENE0XXX)
+ *
+ * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pnp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <media/ir-core.h>
+#include <media/ir-common.h>
+#include "ene_ir.h"
+
+
+static int sample_period = -1;
+static int enable_idle = 1;
+static int input = 1;
+static int debug;
+static int txsim;
+
+static int ene_irq_status(struct ene_device *dev);
+
+/* read a hardware register */
+static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
+{
+       u8 retval;
+       outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+       outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+       retval = inb(dev->hw_io + ENE_IO);
+
+       ene_dbg_verbose("reg %04x == %02x", reg, retval);
+       return retval;
+}
+
+/* write a hardware register */
+static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value)
+{
+       outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+       outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+       outb(value, dev->hw_io + ENE_IO);
+
+       ene_dbg_verbose("reg %04x <- %02x", reg, value);
+}
+
+/* change specific bits in hardware register */
+static void ene_hw_write_reg_mask(struct ene_device *dev,
+                                 u16 reg, u8 value, u8 mask)
+{
+       u8 regvalue;
+
+       outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+       outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+
+       regvalue = inb(dev->hw_io + ENE_IO) & ~mask;
+       regvalue |= (value & mask);
+       outb(regvalue, dev->hw_io + ENE_IO);
+
+       ene_dbg_verbose("reg %04x <- %02x (mask=%02x)", reg, value, mask);
+}
+
+/* detect hardware features */
+static int ene_hw_detect(struct ene_device *dev)
+{
+       u8 chip_major, chip_minor;
+       u8 hw_revision, old_ver;
+       u8 tmp;
+       u8 fw_capabilities;
+       int pll_freq;
+
+       tmp = ene_hw_read_reg(dev, ENE_HW_UNK);
+       ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR);
+
+       chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR);
+       chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR);
+
+       ene_hw_write_reg(dev, ENE_HW_UNK, tmp);
+       hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION);
+       old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD);
+
+       pll_freq = (ene_hw_read_reg(dev, ENE_PLLFRH) << 4) +
+               (ene_hw_read_reg(dev, ENE_PLLFRL) >> 4);
+
+       if (pll_freq != 1000)
+               dev->rx_period_adjust = 4;
+       else
+               dev->rx_period_adjust = 2;
+
+
+       ene_printk(KERN_NOTICE, "PLL freq = %d\n", pll_freq);
+
+       if (hw_revision == 0xFF) {
+
+               ene_printk(KERN_WARNING, "device seems to be disabled\n");
+               ene_printk(KERN_WARNING,
+                       "send a mail to lirc-list@lists.sourceforge.net\n");
+               ene_printk(KERN_WARNING, "please attach output of acpidump\n");
+               return -ENODEV;
+       }
+
+       if (chip_major == 0x33) {
+               ene_printk(KERN_WARNING, "chips 0x33xx aren't supported\n");
+               return -ENODEV;
+       }
+
+       if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
+               dev->hw_revision = ENE_HW_C;
+       } else if (old_ver == 0x24 && hw_revision == 0xC0) {
+               dev->hw_revision = ENE_HW_B;
+               ene_printk(KERN_NOTICE, "KB3926B detected\n");
+       } else {
+               dev->hw_revision = ENE_HW_D;
+               ene_printk(KERN_WARNING,
+                       "unknown ENE chip detected, assuming KB3926D\n");
+               ene_printk(KERN_WARNING,
+                       "driver support might be not complete");
+
+       }
+
+       ene_printk(KERN_DEBUG,
+               "chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x\n",
+                       chip_major, chip_minor, old_ver, hw_revision);
+
+       /* detect features hardware supports */
+       if (dev->hw_revision < ENE_HW_C)
+               return 0;
+
+       fw_capabilities = ene_hw_read_reg(dev, ENE_FW2);
+       ene_dbg("Firmware capabilities: %02x", fw_capabilities);
+
+       dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN;
+       dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING;
+
+       dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable &&
+           (fw_capabilities & ENE_FW2_FAN_AS_NRML_IN);
+
+       ene_printk(KERN_NOTICE, "hardware features:\n");
+       ene_printk(KERN_NOTICE,
+               "learning and transmit %s, gpio40_learn %s, fan_in %s\n",
+              dev->hw_learning_and_tx_capable ? "on" : "off",
+              dev->hw_gpio40_learning ? "on" : "off",
+              dev->hw_fan_as_normal_input ? "on" : "off");
+
+       if (dev->hw_learning_and_tx_capable) {
+               ene_printk(KERN_WARNING,
+               "Device supports transmitting, but that support is\n");
+               ene_printk(KERN_WARNING,
+               "lightly tested. Please test it and mail\n");
+               ene_printk(KERN_WARNING,
+               "lirc-list@lists.sourceforge.net\n");
+       }
+       return 0;
+}
+
+/* this enables/disables IR input via gpio40*/
+static void ene_enable_gpio40_receive(struct ene_device *dev, int enable)
+{
+       ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, enable ?
+                             0 : ENE_CIR_CONF2_GPIO40DIS,
+                             ENE_CIR_CONF2_GPIO40DIS);
+}
+
+/* this enables/disables IR via standard input */
+static void ene_enable_normal_receive(struct ene_device *dev, int enable)
+{
+       ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_RX_ON : 0);
+}
+
+/* this enables/disables IR input via unused fan tachtometer input */
+static void ene_enable_fan_receive(struct ene_device *dev, int enable)
+{
+       if (!enable)
+               ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0);
+       else {
+               ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
+               ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
+       }
+       dev->rx_fan_input_inuse = enable;
+}
+
+
+/* Sense current received carrier */
+static int ene_rx_sense_carrier(struct ene_device *dev)
+{
+       int period = ene_hw_read_reg(dev, ENE_RX_CARRIER);
+       int carrier;
+       ene_dbg("RX: hardware carrier period = %02x", period);
+
+       if (!(period & ENE_RX_CARRIER_VALID))
+               return 0;
+
+       period &= ~ENE_RX_CARRIER_VALID;
+
+       if (!period)
+               return 0;
+
+       carrier = 2000000 / period;
+       ene_dbg("RX: sensed carrier = %d Hz", carrier);
+       return carrier;
+}
+
+/* determine which input to use*/
+static void ene_rx_set_inputs(struct ene_device *dev)
+{
+       int learning_mode = dev->learning_enabled;
+
+       ene_dbg("RX: setup receiver, learning mode = %d", learning_mode);
+
+       ene_enable_normal_receive(dev, 1);
+
+       /* old hardware doesn't support learning mode for sure */
+       if (dev->hw_revision <= ENE_HW_B)
+               return;
+
+       /* receiver not learning capable, still set gpio40 correctly */
+       if (!dev->hw_learning_and_tx_capable) {
+               ene_enable_gpio40_receive(dev, !dev->hw_gpio40_learning);
+               return;
+       }
+
+       /* enable learning mode */
+       if (learning_mode) {
+               ene_enable_gpio40_receive(dev, dev->hw_gpio40_learning);
+
+               /* fan input is not used for learning */
+               if (dev->hw_fan_as_normal_input)
+                       ene_enable_fan_receive(dev, 0);
+
+       /* disable learning mode */
+       } else {
+               if (dev->hw_fan_as_normal_input) {
+                       ene_enable_fan_receive(dev, 1);
+                       ene_enable_normal_receive(dev, 0);
+               } else
+                       ene_enable_gpio40_receive(dev,
+                                       !dev->hw_gpio40_learning);
+       }
+
+       /* set few additional settings for this mode */
+       ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_mode ?
+                             ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1);
+
+       ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_mode ?
+                             ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2);
+
+       if (dev->rx_fan_input_inuse) {
+               dev->props->rx_resolution = ENE_SAMPLE_PERIOD_FAN * 1000;
+
+               dev->props->timeout =
+                       ENE_FAN_VALUE_MASK * ENE_SAMPLE_PERIOD_FAN * 1000;
+       } else {
+               dev->props->rx_resolution = sample_period * 1000;
+               dev->props->timeout = ENE_MAXGAP * 1000;
+       }
+}
+
+/* Enable the device for receive */
+static void ene_rx_enable(struct ene_device *dev)
+{
+       u8 reg_value;
+
+       if (dev->hw_revision < ENE_HW_C) {
+               ene_hw_write_reg(dev, ENEB_IRQ, dev->irq << 1);
+               ene_hw_write_reg(dev, ENEB_IRQ_UNK1, 0x01);
+       } else {
+               reg_value = ene_hw_read_reg(dev, ENEC_IRQ) & 0xF0;
+               reg_value |= ENEC_IRQ_UNK_EN;
+               reg_value &= ~ENEC_IRQ_STATUS;
+               reg_value |= (dev->irq & ENEC_IRQ_MASK);
+               ene_hw_write_reg(dev, ENEC_IRQ, reg_value);
+               ene_hw_write_reg(dev, ENE_TX_UNK1, 0x63);
+       }
+
+       ene_hw_write_reg(dev, ENE_CIR_CONF2, 0x00);
+       ene_rx_set_inputs(dev);
+
+       /* set sampling period */
+       ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period);
+
+       /* ack any pending irqs - just in case */
+       ene_irq_status(dev);
+
+       /* enable firmware bits */
+       ene_hw_write_reg_mask(dev, ENE_FW1,
+                             ENE_FW1_ENABLE | ENE_FW1_IRQ,
+                             ENE_FW1_ENABLE | ENE_FW1_IRQ);
+
+       /* enter idle mode */
+       ir_raw_event_set_idle(dev->idev, 1);
+       ir_raw_event_reset(dev->idev);
+
+}
+
+/* Disable the device receiver */
+static void ene_rx_disable(struct ene_device *dev)
+{
+       /* disable inputs */
+       ene_enable_normal_receive(dev, 0);
+
+       if (dev->hw_fan_as_normal_input)
+               ene_enable_fan_receive(dev, 0);
+
+       /* disable hardware IRQ and firmware flag */
+       ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ);
+
+       ir_raw_event_set_idle(dev->idev, 1);
+       ir_raw_event_reset(dev->idev);
+}
+
+
+/* prepare transmission */
+static void ene_tx_prepare(struct ene_device *dev)
+{
+       u8 conf1;
+
+       conf1 = ene_hw_read_reg(dev, ENE_CIR_CONF1);
+       dev->saved_conf1 = conf1;
+
+       if (dev->hw_revision == ENE_HW_C)
+               conf1 &= ~ENE_CIR_CONF1_TX_CLEAR;
+
+       /* Enable TX engine */
+       conf1 |= ENE_CIR_CONF1_TX_ON;
+
+       /* Set carrier */
+       if (dev->tx_period) {
+
+               /* NOTE: duty cycle handling is just a guess, it might
+                       not be aviable. Default values were tested */
+               int tx_period_in500ns = dev->tx_period * 2;
+
+               int tx_pulse_width_in_500ns =
+                       tx_period_in500ns / (100 / dev->tx_duty_cycle);
+
+               if (!tx_pulse_width_in_500ns)
+                       tx_pulse_width_in_500ns = 1;
+
+               ene_dbg("TX: pulse distance = %d * 500 ns", tx_period_in500ns);
+               ene_dbg("TX: pulse width = %d * 500 ns",
+                                               tx_pulse_width_in_500ns);
+
+               ene_hw_write_reg(dev, ENE_TX_PERIOD, ENE_TX_PERIOD_UNKBIT |
+                                       tx_period_in500ns);
+
+               ene_hw_write_reg(dev, ENE_TX_PERIOD_PULSE,
+                                       tx_pulse_width_in_500ns);
+
+               conf1 |= ENE_CIR_CONF1_TX_CARR;
+       } else
+               conf1 &= ~ENE_CIR_CONF1_TX_CARR;
+
+       ene_hw_write_reg(dev, ENE_CIR_CONF1, conf1);
+
+}
+
+/* end transmission */
+static void ene_tx_complete(struct ene_device *dev)
+{
+       ene_hw_write_reg(dev, ENE_CIR_CONF1, dev->saved_conf1);
+       dev->tx_buffer = NULL;
+}
+
+/* set transmit mask */
+static void ene_tx_hw_set_transmiter_mask(struct ene_device *dev)
+{
+       u8 txport1 = ene_hw_read_reg(dev, ENE_TX_PORT1) & ~ENE_TX_PORT1_EN;
+       u8 txport2 = ene_hw_read_reg(dev, ENE_TX_PORT2) & ~ENE_TX_PORT2_EN;
+
+       if (dev->transmitter_mask & 0x01)
+               txport1 |= ENE_TX_PORT1_EN;
+
+       if (dev->transmitter_mask & 0x02)
+               txport2 |= ENE_TX_PORT2_EN;
+
+       ene_hw_write_reg(dev, ENE_TX_PORT1, txport1);
+       ene_hw_write_reg(dev, ENE_TX_PORT2, txport2);
+}
+
+/* TX one sample - must be called with dev->hw_lock*/
+static void ene_tx_sample(struct ene_device *dev)
+{
+       u8 raw_tx;
+       u32 sample;
+
+       if (!dev->tx_buffer) {
+               ene_dbg("TX: attempt to transmit NULL buffer");
+               return;
+       }
+
+       /* Grab next TX sample */
+       if (!dev->tx_sample) {
+again:
+               if (dev->tx_pos == dev->tx_len + 1) {
+                       if (!dev->tx_done) {
+                               ene_dbg("TX: no more data to send");
+                               dev->tx_done = 1;
+                               goto exit;
+                       } else {
+                               ene_dbg("TX: last sample sent by hardware");
+                               ene_tx_complete(dev);
+                               complete(&dev->tx_complete);
+                               return;
+                       }
+               }
+
+               sample = dev->tx_buffer[dev->tx_pos++];
+               dev->tx_sample_pulse = !dev->tx_sample_pulse;
+
+               ene_dbg("TX: sample %8d (%s)", sample, dev->tx_sample_pulse ?
+                                                       "pulse" : "space");
+
+               dev->tx_sample = DIV_ROUND_CLOSEST(sample, ENE_TX_SMPL_PERIOD);
+
+               /* guard against too short samples */
+               if (!dev->tx_sample)
+                       goto again;
+       }
+
+       raw_tx = min(dev->tx_sample , (unsigned int)ENE_TX_SMLP_MASK);
+       dev->tx_sample -= raw_tx;
+
+       if (dev->tx_sample_pulse)
+               raw_tx |= ENE_TX_PULSE_MASK;
+
+       ene_hw_write_reg(dev, ENE_TX_INPUT1 + dev->tx_reg, raw_tx);
+       dev->tx_reg = !dev->tx_reg;
+exit:
+       /* simulate TX done interrupt */
+       if (txsim)
+               mod_timer(&dev->tx_sim_timer, jiffies + HZ / 500);
+}
+
+/* timer to simulate tx done interrupt */
+static void ene_tx_irqsim(unsigned long data)
+{
+       struct ene_device *dev = (struct ene_device *)data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->hw_lock, flags);
+       ene_tx_sample(dev);
+       spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+
+/* read irq status and ack it */
+static int ene_irq_status(struct ene_device *dev)
+{
+       u8 irq_status;
+       u8 fw_flags1, fw_flags2;
+       int cur_rx_pointer;
+       int retval = 0;
+
+       fw_flags2 = ene_hw_read_reg(dev, ENE_FW2);
+       cur_rx_pointer = !!(fw_flags2 & ENE_FW2_BUF_HIGH);
+
+       if (dev->hw_revision < ENE_HW_C) {
+               irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS);
+
+               if (!(irq_status & ENEB_IRQ_STATUS_IR))
+                       return 0;
+
+               ene_hw_write_reg(dev, ENEB_IRQ_STATUS,
+                                irq_status & ~ENEB_IRQ_STATUS_IR);
+               dev->rx_pointer = cur_rx_pointer;
+               return ENE_IRQ_RX;
+       }
+
+       irq_status = ene_hw_read_reg(dev, ENEC_IRQ);
+
+       if (!(irq_status & ENEC_IRQ_STATUS))
+               return 0;
+
+       /* original driver does that twice - a workaround ? */
+       ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
+       ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
+
+       /* clear unknown flag in F8F9 */
+       if (fw_flags2 & ENE_FW2_IRQ_CLR)
+               ene_hw_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_IRQ_CLR);
+
+       /* check if this is a TX interrupt */
+       fw_flags1 = ene_hw_read_reg(dev, ENE_FW1);
+       if (fw_flags1 & ENE_FW1_TXIRQ) {
+               ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ);
+               retval |= ENE_IRQ_TX;
+       }
+
+       /* Check if this is RX interrupt */
+       if (dev->rx_pointer != cur_rx_pointer) {
+               retval |= ENE_IRQ_RX;
+               dev->rx_pointer = cur_rx_pointer;
+
+       } else if (!(retval & ENE_IRQ_TX)) {
+               ene_dbg("RX: interrupt without change in RX pointer(%d)",
+                       dev->rx_pointer);
+               retval |= ENE_IRQ_RX;
+       }
+
+       if ((retval & ENE_IRQ_RX) && (retval & ENE_IRQ_TX))
+               ene_dbg("both RX and TX interrupt at same time");
+
+       return retval;
+}
+
+/* interrupt handler */
+static irqreturn_t ene_isr(int irq, void *data)
+{
+       u16 hw_value;
+       int i, hw_sample;
+       int pulse;
+       int irq_status;
+       unsigned long flags;
+       int carrier = 0;
+       irqreturn_t retval = IRQ_NONE;
+       struct ene_device *dev = (struct ene_device *)data;
+       struct ir_raw_event ev;
+
+
+       spin_lock_irqsave(&dev->hw_lock, flags);
+       irq_status = ene_irq_status(dev);
+
+       if (!irq_status)
+               goto unlock;
+
+       retval = IRQ_HANDLED;
+
+       if (irq_status & ENE_IRQ_TX) {
+
+               if (!dev->hw_learning_and_tx_capable) {
+                       ene_dbg("TX interrupt on unsupported device!");
+                       goto unlock;
+               }
+               ene_tx_sample(dev);
+       }
+
+       if (!(irq_status & ENE_IRQ_RX))
+               goto unlock;
+
+
+       if (dev->carrier_detect_enabled || debug)
+               carrier = ene_rx_sense_carrier(dev);
+#if 0
+       /* TODO */
+       if (dev->carrier_detect_enabled && carrier)
+               ir_raw_event_report_frequency(dev->idev, carrier);
+#endif
+
+       for (i = 0; i < ENE_SAMPLES_SIZE; i++) {
+               hw_value = ene_hw_read_reg(dev,
+                               ENE_SAMPLE_BUFFER + dev->rx_pointer * 4 + i);
+
+               if (dev->rx_fan_input_inuse) {
+                       /* read high part of the sample */
+                       hw_value |= ene_hw_read_reg(dev,
+                           ENE_SAMPLE_BUFFER_FAN +
+                                       dev->rx_pointer * 4 + i) << 8;
+                       pulse = hw_value & ENE_FAN_SMPL_PULS_MSK;
+
+                       /* clear space bit, and other unused bits */
+                       hw_value &= ENE_FAN_VALUE_MASK;
+                       hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN;
+
+               } else {
+                       pulse = !(hw_value & ENE_SAMPLE_SPC_MASK);
+                       hw_value &= ENE_SAMPLE_VALUE_MASK;
+                       hw_sample = hw_value * sample_period;
+
+                       if (dev->rx_period_adjust) {
+                               hw_sample *= (100 - dev->rx_period_adjust);
+                               hw_sample /= 100;
+                       }
+               }
+               /* no more data */
+               if (!(hw_value))
+                       break;
+
+               ene_dbg("RX: %d (%s)", hw_sample, pulse ? "pulse" : "space");
+
+
+               ev.duration = hw_sample * 1000;
+               ev.pulse = pulse;
+               ir_raw_event_store_with_filter(dev->idev, &ev);
+       }
+
+       ir_raw_event_handle(dev->idev);
+unlock:
+       spin_unlock_irqrestore(&dev->hw_lock, flags);
+       return retval;
+}
+
+/* Initialize default settings */
+static void ene_setup_settings(struct ene_device *dev)
+{
+       dev->tx_period = 32;
+       dev->tx_duty_cycle = 25; /*%*/
+       dev->transmitter_mask = 3;
+
+       /* Force learning mode if (input == 2), otherwise
+               let user set it with LIRC_SET_REC_CARRIER */
+       dev->learning_enabled =
+               (input == 2 && dev->hw_learning_and_tx_capable);
+
+       dev->rx_pointer = -1;
+
+}
+
+/* outside interface: called on first open*/
+static int ene_open(void *data)
+{
+       struct ene_device *dev = (struct ene_device *)data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->hw_lock, flags);
+       dev->in_use = 1;
+       ene_setup_settings(dev);
+       ene_rx_enable(dev);
+       spin_unlock_irqrestore(&dev->hw_lock, flags);
+       return 0;
+}
+
+/* outside interface: called on device close*/
+static void ene_close(void *data)
+{
+       struct ene_device *dev = (struct ene_device *)data;
+       unsigned long flags;
+       spin_lock_irqsave(&dev->hw_lock, flags);
+
+       ene_rx_disable(dev);
+       dev->in_use = 0;
+       spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+/* outside interface: set transmitter mask */
+static int ene_set_tx_mask(void *data, u32 tx_mask)
+{
+       struct ene_device *dev = (struct ene_device *)data;
+       unsigned long flags;
+       ene_dbg("TX: attempt to set transmitter mask %02x", tx_mask);
+
+       /* invalid txmask */
+       if (!tx_mask || tx_mask & ~0x3) {
+               ene_dbg("TX: invalid mask");
+               /* return count of transmitters */
+               return 2;
+       }
+
+       spin_lock_irqsave(&dev->hw_lock, flags);
+       dev->transmitter_mask = tx_mask;
+       spin_unlock_irqrestore(&dev->hw_lock, flags);
+       return 0;
+}
+
+/* outside interface : set tx carrier */
+static int ene_set_tx_carrier(void *data, u32 carrier)
+{
+       struct ene_device *dev = (struct ene_device *)data;
+       unsigned long flags;
+       u32 period = 1000000 / carrier; /* (1 / freq) (* # usec in 1 sec) */
+
+       ene_dbg("TX: attempt to set tx carrier to %d kHz", carrier);
+
+       if (period && (period > ENE_TX_PERIOD_MAX ||
+                       period < ENE_TX_PERIOD_MIN)) {
+
+               ene_dbg("TX: out of range %d-%d carrier, "
+                       "falling back to 32 kHz",
+                       1000 / ENE_TX_PERIOD_MIN,
+                       1000 / ENE_TX_PERIOD_MAX);
+
+               period = 32; /* this is just a coincidence!!! */
+       }
+       ene_dbg("TX: set carrier to %d kHz", carrier);
+
+       spin_lock_irqsave(&dev->hw_lock, flags);
+       dev->tx_period = period;
+       spin_unlock_irqrestore(&dev->hw_lock, flags);
+       return 0;
+}
+
+
+/* outside interface: enable learning mode */
+static int ene_set_learning_mode(void *data, int enable)
+{
+       struct ene_device *dev = (struct ene_device *)data;
+       unsigned long flags;
+       if (enable == dev->learning_enabled)
+               return 0;
+
+       spin_lock_irqsave(&dev->hw_lock, flags);
+       dev->learning_enabled = enable;
+       ene_rx_set_inputs(dev);
+       spin_unlock_irqrestore(&dev->hw_lock, flags);
+       return 0;
+}
+
+/* outside interface: set rec carrier */
+static int ene_set_rec_carrier(void *data, u32 min, u32 max)
+{
+       struct ene_device *dev = (struct ene_device *)data;
+       ene_set_learning_mode(dev,
+               max > ENE_NORMAL_RX_HI || min < ENE_NORMAL_RX_LOW);
+       return 0;
+}
+
+/* outside interface: enable or disable idle mode */
+static void ene_rx_set_idle(void *data, int idle)
+{
+       struct ene_device *dev = (struct ene_device *)data;
+       ene_dbg("%sabling idle mode", idle ? "en" : "dis");
+
+       ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD,
+               (enable_idle && idle) ? 0 : ENE_CIR_SAMPLE_OVERFLOW,
+                       ENE_CIR_SAMPLE_OVERFLOW);
+}
+
+
+/* outside interface: transmit */
+static int ene_transmit(void *data, int *buf, u32 n)
+{
+       struct ene_device *dev = (struct ene_device *)data;
+       unsigned long flags;
+
+       dev->tx_buffer = buf;
+       dev->tx_len = n / sizeof(int);
+       dev->tx_pos = 0;
+       dev->tx_reg = 0;
+       dev->tx_done = 0;
+       dev->tx_sample = 0;
+       dev->tx_sample_pulse = 0;
+
+       ene_dbg("TX: %d samples", dev->tx_len);
+
+       spin_lock_irqsave(&dev->hw_lock, flags);
+
+       ene_tx_hw_set_transmiter_mask(dev);
+       ene_tx_prepare(dev);
+
+       /* Transmit first two samples */
+       ene_tx_sample(dev);
+       ene_tx_sample(dev);
+
+       spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+       if (wait_for_completion_timeout(&dev->tx_complete, 2 * HZ) == 0) {
+               ene_dbg("TX: timeout");
+               spin_lock_irqsave(&dev->hw_lock, flags);
+               ene_tx_complete(dev);
+               spin_unlock_irqrestore(&dev->hw_lock, flags);
+       } else
+               ene_dbg("TX: done");
+       return n;
+}
+
+
+/* probe entry */
+static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
+{
+       int error = -ENOMEM;
+       struct ir_dev_props *ir_props;
+       struct input_dev *input_dev;
+       struct ene_device *dev;
+
+       /* allocate memory */
+       input_dev = input_allocate_device();
+       ir_props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL);
+       dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
+
+       if (!input_dev || !ir_props || !dev)
+               goto error;
+
+       /* validate resources */
+       error = -ENODEV;
+
+       if (!pnp_port_valid(pnp_dev, 0) ||
+           pnp_port_len(pnp_dev, 0) < ENE_MAX_IO)
+               goto error;
+
+       if (!pnp_irq_valid(pnp_dev, 0))
+               goto error;
+
+       dev->hw_io = pnp_port_start(pnp_dev, 0);
+       dev->irq = pnp_irq(pnp_dev, 0);
+       spin_lock_init(&dev->hw_lock);
+
+       /* claim the resources */
+       error = -EBUSY;
+       if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME))
+               goto error;
+
+       if (request_irq(dev->irq, ene_isr,
+                       IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev))
+               goto error;
+
+       pnp_set_drvdata(pnp_dev, dev);
+       dev->pnp_dev = pnp_dev;
+
+       /* detect hardware version and features */
+       error = ene_hw_detect(dev);
+       if (error)
+               goto error;
+
+       ene_setup_settings(dev);
+
+       if (!dev->hw_learning_and_tx_capable && txsim) {
+               dev->hw_learning_and_tx_capable = 1;
+               setup_timer(&dev->tx_sim_timer, ene_tx_irqsim,
+                                               (long unsigned int)dev);
+               ene_printk(KERN_WARNING,
+                       "Simulation of TX activated\n");
+       }
+
+       ir_props->driver_type = RC_DRIVER_IR_RAW;
+       ir_props->allowed_protos = IR_TYPE_ALL;
+       ir_props->priv = dev;
+       ir_props->open = ene_open;
+       ir_props->close = ene_close;
+       ir_props->min_timeout = ENE_MINGAP * 1000;
+       ir_props->max_timeout = ENE_MAXGAP * 1000;
+       ir_props->timeout = ENE_MAXGAP * 1000;
+
+       if (dev->hw_revision == ENE_HW_B)
+               ir_props->s_idle = ene_rx_set_idle;
+
+
+       dev->props = ir_props;
+       dev->idev = input_dev;
+
+       /* don't allow too short/long sample periods */
+       if (sample_period < 5 || sample_period > 0x7F)
+               sample_period = -1;
+
+       /* choose default sample period */
+       if (sample_period == -1) {
+
+               sample_period = 50;
+
+               /* on revB, hardware idle mode eats first sample
+                 if we set too low sample period */
+               if (dev->hw_revision == ENE_HW_B && enable_idle)
+                       sample_period = 75;
+       }
+
+       ir_props->rx_resolution = sample_period * 1000;
+
+       if (dev->hw_learning_and_tx_capable) {
+
+               ir_props->s_learning_mode = ene_set_learning_mode;
+
+               if (input == 0)
+                       ir_props->s_rx_carrier_range = ene_set_rec_carrier;
+
+               init_completion(&dev->tx_complete);
+               ir_props->tx_ir = ene_transmit;
+               ir_props->s_tx_mask = ene_set_tx_mask;
+               ir_props->s_tx_carrier = ene_set_tx_carrier;
+               ir_props->tx_resolution = ENE_TX_SMPL_PERIOD * 1000;
+               /* ir_props->s_carrier_report = ene_set_carrier_report; */
+       }
+
+
+       device_set_wakeup_capable(&pnp_dev->dev, 1);
+       device_set_wakeup_enable(&pnp_dev->dev, 1);
+
+       if (dev->hw_learning_and_tx_capable)
+               input_dev->name = "ENE eHome Infrared Remote Transceiver";
+       else
+               input_dev->name = "ENE eHome Infrared Remote Receiver";
+
+
+       error = -ENODEV;
+       if (ir_input_register(input_dev, RC_MAP_RC6_MCE, ir_props,
+                                                       ENE_DRIVER_NAME))
+               goto error;
+
+
+       ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n");
+       return 0;
+error:
+       if (dev->irq)
+               free_irq(dev->irq, dev);
+       if (dev->hw_io)
+               release_region(dev->hw_io, ENE_MAX_IO);
+
+       input_free_device(input_dev);
+       kfree(ir_props);
+       kfree(dev);
+       return error;
+}
+
+/* main unload function */
+static void ene_remove(struct pnp_dev *pnp_dev)
+{
+       struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->hw_lock, flags);
+       ene_rx_disable(dev);
+       spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+       free_irq(dev->irq, dev);
+       release_region(dev->hw_io, ENE_MAX_IO);
+       ir_input_unregister(dev->idev);
+       kfree(dev->props);
+       kfree(dev);
+}
+
+/* enable wake on IR (wakes on specific button on original remote) */
+static void ene_enable_wake(struct ene_device *dev, int enable)
+{
+       enable = enable && device_may_wakeup(&dev->pnp_dev->dev);
+
+       ene_dbg("wake on IR %s", enable ? "enabled" : "disabled");
+
+       ene_hw_write_reg_mask(dev, ENE_FW1, enable ?
+               ENE_FW1_WAKE : 0, ENE_FW1_WAKE);
+}
+
+#ifdef CONFIG_PM
+static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
+{
+       struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+       ene_enable_wake(dev, 1);
+       return 0;
+}
+
+static int ene_resume(struct pnp_dev *pnp_dev)
+{
+       struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+       if (dev->in_use)
+               ene_rx_enable(dev);
+
+       ene_enable_wake(dev, 0);
+       return 0;
+}
+#endif
+
+static void ene_shutdown(struct pnp_dev *pnp_dev)
+{
+       struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+       ene_enable_wake(dev, 1);
+}
+
+static const struct pnp_device_id ene_ids[] = {
+       {.id = "ENE0100",},
+       {.id = "ENE0200",},
+       {.id = "ENE0201",},
+       {.id = "ENE0202",},
+       {},
+};
+
+static struct pnp_driver ene_driver = {
+       .name = ENE_DRIVER_NAME,
+       .id_table = ene_ids,
+       .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
+
+       .probe = ene_probe,
+       .remove = __devexit_p(ene_remove),
+#ifdef CONFIG_PM
+       .suspend = ene_suspend,
+       .resume = ene_resume,
+#endif
+       .shutdown = ene_shutdown,
+};
+
+static int __init ene_init(void)
+{
+       return pnp_register_driver(&ene_driver);
+}
+
+static void ene_exit(void)
+{
+       pnp_unregister_driver(&ene_driver);
+}
+
+module_param(sample_period, int, S_IRUGO);
+MODULE_PARM_DESC(sample_period, "Hardware sample period (50 us default)");
+
+module_param(enable_idle, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(enable_idle,
+       "Enables turning off signal sampling after long inactivity time; "
+       "if disabled might help detecting input signal (default: enabled)"
+       " (KB3926B only)");
+
+module_param(input, bool, S_IRUGO);
+MODULE_PARM_DESC(input, "select which input to use "
+       "0 - auto, 1 - standard, 2 - wideband(KB3926C+)");
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debug (debug=2 verbose debug output)");
+
+module_param(txsim, bool, S_IRUGO);
+MODULE_PARM_DESC(txsim,
+       "Simulate TX features on unsupported hardware (dangerous)");
+
+MODULE_DEVICE_TABLE(pnp, ene_ids);
+MODULE_DESCRIPTION
+       ("Infrared input driver for KB3926B/KB3926C/KB3926D "
+       "(aka ENE0100/ENE0200/ENE0201) CIR port");
+
+MODULE_AUTHOR("Maxim Levitsky");
+MODULE_LICENSE("GPL");
+
+module_init(ene_init);
+module_exit(ene_exit);
diff --git a/drivers/media/IR/ene_ir.h b/drivers/media/IR/ene_ir.h
new file mode 100644 (file)
index 0000000..54c76af
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * driver for ENE KB3926 B/C/D CIR (also known as ENE0XXX)
+ *
+ * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include <linux/spinlock.h>
+
+
+/* hardware address */
+#define ENE_STATUS             0       /* hardware status - unused */
+#define ENE_ADDR_HI            1       /* hi byte of register address */
+#define ENE_ADDR_LO            2       /* low byte of register address */
+#define ENE_IO                 3       /* read/write window */
+#define ENE_MAX_IO             4
+
+/* 8 bytes of samples, divided in 2 halfs*/
+#define ENE_SAMPLE_BUFFER      0xF8F0  /* regular sample buffer */
+#define ENE_SAMPLE_SPC_MASK    0x80    /* sample is space */
+#define ENE_SAMPLE_VALUE_MASK  0x7F
+#define ENE_SAMPLE_OVERFLOW    0x7F
+#define ENE_SAMPLES_SIZE       4
+
+/* fan input sample buffer */
+#define ENE_SAMPLE_BUFFER_FAN  0xF8FB  /* this buffer holds high byte of */
+                                       /* each sample of normal buffer */
+#define ENE_FAN_SMPL_PULS_MSK  0x8000  /* this bit of combined sample */
+                                       /* if set, says that sample is pulse */
+#define ENE_FAN_VALUE_MASK     0x0FFF  /* mask for valid bits of the value */
+
+/* first firmware register */
+#define ENE_FW1                        0xF8F8
+#define        ENE_FW1_ENABLE          0x01    /* enable fw processing */
+#define ENE_FW1_TXIRQ          0x02    /* TX interrupt pending */
+#define ENE_FW1_WAKE           0x40    /* enable wake from S3 */
+#define ENE_FW1_IRQ            0x80    /* enable interrupt */
+
+/* second firmware register */
+#define ENE_FW2                        0xF8F9
+#define ENE_FW2_BUF_HIGH       0x01    /* which half of the buffer to read */
+#define ENE_FW2_IRQ_CLR                0x04    /* clear this on IRQ */
+#define ENE_FW2_GP40_AS_LEARN  0x08    /* normal input is used as */
+                                       /* learning input */
+#define ENE_FW2_FAN_AS_NRML_IN 0x40    /* fan is used as normal input */
+#define ENE_FW2_LEARNING       0x80    /* hardware supports learning and TX */
+
+/* transmitter ports */
+#define ENE_TX_PORT2           0xFC01  /* this enables one or both */
+#define ENE_TX_PORT2_EN                0x20    /* TX ports */
+#define ENE_TX_PORT1           0xFC08
+#define ENE_TX_PORT1_EN                0x02
+
+/* IRQ registers block (for revision B) */
+#define ENEB_IRQ               0xFD09  /* IRQ number */
+#define ENEB_IRQ_UNK1          0xFD17  /* unknown setting = 1 */
+#define ENEB_IRQ_STATUS                0xFD80  /* irq status */
+#define ENEB_IRQ_STATUS_IR     0x20    /* IR irq */
+
+/* fan as input settings - only if learning capable */
+#define ENE_FAN_AS_IN1         0xFE30  /* fan init reg 1 */
+#define ENE_FAN_AS_IN1_EN      0xCD
+#define ENE_FAN_AS_IN2         0xFE31  /* fan init reg 2 */
+#define ENE_FAN_AS_IN2_EN      0x03
+#define ENE_SAMPLE_PERIOD_FAN   61     /* fan input has fixed sample period */
+
+/* IRQ registers block (for revision C,D) */
+#define ENEC_IRQ               0xFE9B  /* new irq settings register */
+#define ENEC_IRQ_MASK          0x0F    /* irq number mask */
+#define ENEC_IRQ_UNK_EN                0x10    /* always enabled */
+#define ENEC_IRQ_STATUS                0x20    /* irq status and ACK */
+
+/* CIR block settings */
+#define ENE_CIR_CONF1          0xFEC0
+#define ENE_CIR_CONF1_TX_CLEAR 0x01    /* clear that on revC */
+                                       /* while transmitting */
+#define ENE_CIR_CONF1_RX_ON    0x07    /* normal receiver enabled */
+#define ENE_CIR_CONF1_LEARN1   0x08    /* enabled on learning mode */
+#define ENE_CIR_CONF1_TX_ON    0x30    /* enabled on transmit */
+#define ENE_CIR_CONF1_TX_CARR  0x80    /* send TX carrier or not */
+
+#define ENE_CIR_CONF2          0xFEC1  /* unknown setting = 0 */
+#define ENE_CIR_CONF2_LEARN2   0x10    /* set on enable learning */
+#define ENE_CIR_CONF2_GPIO40DIS        0x20    /* disable input via gpio40 */
+
+#define ENE_CIR_SAMPLE_PERIOD  0xFEC8  /* sample period in us */
+#define ENE_CIR_SAMPLE_OVERFLOW        0x80    /* interrupt on overflows if set */
+
+
+/* Two byte tx buffer */
+#define ENE_TX_INPUT1          0xFEC9
+#define ENE_TX_INPUT2          0xFECA
+#define ENE_TX_PULSE_MASK      0x80    /* Transmitted sample is pulse */
+#define ENE_TX_SMLP_MASK       0x7F
+#define ENE_TX_SMPL_PERIOD     50      /* transmit sample period - fixed */
+
+
+/* Unknown TX setting - TX sample period ??? */
+#define ENE_TX_UNK1            0xFECB  /* set to 0x63 */
+
+/* Current received carrier period */
+#define ENE_RX_CARRIER         0xFECC  /* RX period (500 ns) */
+#define ENE_RX_CARRIER_VALID   0x80    /* Register content valid */
+
+
+/* TX period (1/carrier) */
+#define ENE_TX_PERIOD          0xFECE  /* TX period (500 ns) */
+#define ENE_TX_PERIOD_UNKBIT   0x80    /* This bit set on transmit*/
+#define ENE_TX_PERIOD_PULSE    0xFECF  /* TX pulse period (500 ns)*/
+
+/* Hardware versions */
+#define ENE_HW_VERSION         0xFF00  /* hardware revision */
+#define ENE_PLLFRH             0xFF16
+#define ENE_PLLFRL             0xFF17
+
+#define ENE_HW_UNK             0xFF1D
+#define ENE_HW_UNK_CLR         0x04
+#define ENE_HW_VER_MAJOR       0xFF1E  /* chip version */
+#define ENE_HW_VER_MINOR       0xFF1F
+#define ENE_HW_VER_OLD         0xFD00
+
+/* Normal/Learning carrier ranges - only valid if we have learning input*/
+/* TODO: test */
+#define ENE_NORMAL_RX_LOW      34
+#define ENE_NORMAL_RX_HI       38
+
+/* Tx carrier range */
+/* Hardware might be able to do more, but this range is enough for
+   all purposes */
+#define ENE_TX_PERIOD_MAX      32      /* corresponds to 29.4 kHz */
+#define ENE_TX_PERIOD_MIN      16      /* corrsponds to 62.5 kHz */
+
+
+
+/* Minimal and maximal gaps */
+
+/* Normal case:
+       Minimal gap is 0x7F * sample period
+       Maximum gap depends on hardware.
+       For KB3926B, it is unlimited, for newer models its around
+       250000, after which HW stops sending samples, and that is
+       not possible to change */
+
+/* Fan case:
+       Both minimal and maximal gaps are same, and equal to 0xFFF * 0x61
+       And there is nothing to change this setting
+*/
+
+#define ENE_MAXGAP             250000
+#define ENE_MINGAP             (127 * sample_period)
+
+/******************************************************************************/
+
+#define ENE_DRIVER_NAME                "ene_ir"
+
+#define ENE_IRQ_RX             1
+#define ENE_IRQ_TX             2
+
+#define  ENE_HW_B              1       /* 3926B */
+#define  ENE_HW_C              2       /* 3926C */
+#define  ENE_HW_D              3       /* 3926D */
+
+#define ene_printk(level, text, ...) \
+       printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__)
+
+#define ene_dbg(text, ...) \
+       if (debug) \
+               printk(KERN_DEBUG \
+                       ENE_DRIVER_NAME ": " text "\n" , ## __VA_ARGS__)
+
+#define ene_dbg_verbose(text, ...) \
+       if (debug > 1) \
+               printk(KERN_DEBUG \
+                       ENE_DRIVER_NAME ": " text "\n" , ## __VA_ARGS__)
+
+
+struct ene_device {
+       struct pnp_dev *pnp_dev;
+       struct input_dev *idev;
+       struct ir_dev_props *props;
+       int in_use;
+
+       /* hw IO settings */
+       unsigned long hw_io;
+       int irq;
+       spinlock_t hw_lock;
+
+       /* HW features */
+       int hw_revision;                        /* hardware revision */
+       bool hw_learning_and_tx_capable;        /* learning capable */
+       bool hw_gpio40_learning;                /* gpio40 is learning */
+       bool hw_fan_as_normal_input;            /* fan input is used as */
+                                               /* regular input */
+       /* HW state*/
+       int rx_pointer;                         /* hw pointer to rx buffer */
+       bool rx_fan_input_inuse;                /* is fan input in use for rx*/
+       int tx_reg;                             /* current reg used for TX */
+       u8  saved_conf1;                        /* saved FEC0 reg */
+
+       /* TX sample handling */
+       unsigned int tx_sample;                 /* current sample for TX */
+       bool tx_sample_pulse;                   /* current sample is pulse */
+
+       /* TX buffer */
+       int *tx_buffer;                         /* input samples buffer*/
+       int tx_pos;                             /* position in that bufer */
+       int tx_len;                             /* current len of tx buffer */
+       int tx_done;                            /* done transmitting */
+                                               /* one more sample pending*/
+       struct completion tx_complete;          /* TX completion */
+       struct timer_list tx_sim_timer;
+
+       /* TX settings */
+       int tx_period;
+       int tx_duty_cycle;
+       int transmitter_mask;
+
+       /* RX settings */
+       bool learning_enabled;                  /* learning input enabled */
+       bool carrier_detect_enabled;            /* carrier detect enabled */
+       int rx_period_adjust;
+};
index 65c125e44e964ebd71ec15d955bd0a67035e7bb9..c185422ef28c8c31f1c67d0718773614a4e0fc73 100644 (file)
@@ -87,7 +87,6 @@ static ssize_t lcd_write(struct file *file, const char *buf,
 struct imon_context {
        struct device *dev;
        struct ir_dev_props *props;
-       struct ir_input_dev *ir;
        /* Newer devices have two interfaces */
        struct usb_device *usbdev_intf0;
        struct usb_device *usbdev_intf1;
@@ -1656,7 +1655,6 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
 {
        struct input_dev *idev;
        struct ir_dev_props *props;
-       struct ir_input_dev *ir;
        int ret, i;
 
        idev = input_allocate_device();
@@ -1671,12 +1669,6 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
                goto props_alloc_failed;
        }
 
-       ir = kzalloc(sizeof(struct ir_input_dev), GFP_KERNEL);
-       if (!ir) {
-               dev_err(ictx->dev, "remote ir input dev allocation failed\n");
-               goto ir_dev_alloc_failed;
-       }
-
        snprintf(ictx->name_idev, sizeof(ictx->name_idev),
                 "iMON Remote (%04x:%04x)", ictx->vendor, ictx->product);
        idev->name = ictx->name_idev;
@@ -1706,14 +1698,9 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
        props->change_protocol = imon_ir_change_protocol;
        ictx->props = props;
 
-       ictx->ir = ir;
-       memcpy(&ir->dev, ictx->dev, sizeof(struct device));
-
        usb_to_input_id(ictx->usbdev_intf0, &idev->id);
        idev->dev.parent = ictx->dev;
 
-       input_set_drvdata(idev, ir);
-
        ret = ir_input_register(idev, RC_MAP_IMON_PAD, props, MOD_NAME);
        if (ret < 0) {
                dev_err(ictx->dev, "remote input dev register failed\n");
@@ -1723,8 +1710,6 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
        return idev;
 
 idev_register_failed:
-       kfree(ir);
-ir_dev_alloc_failed:
        kfree(props);
 props_alloc_failed:
        input_free_device(idev);
@@ -1944,7 +1929,6 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
 
 urb_submit_failed:
        ir_input_unregister(ictx->idev);
-       input_free_device(ictx->idev);
 idev_setup_failed:
 find_endpoint_failed:
        mutex_unlock(&ictx->lock);
@@ -2014,10 +1998,8 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
        return ictx;
 
 urb_submit_failed:
-       if (ictx->touch) {
+       if (ictx->touch)
                input_unregister_device(ictx->touch);
-               input_free_device(ictx->touch);
-       }
 touch_setup_failed:
 find_endpoint_failed:
        mutex_unlock(&ictx->lock);
index babd52061bc3abe011d53500e39b7d35ca7d1663..a85a8c7c905a69684a8f0333b32cd9665a1cc9b5 100644 (file)
@@ -32,7 +32,7 @@ struct ir_raw_handler {
 
 struct ir_raw_event_ctrl {
        struct list_head                list;           /* to keep track of raw clients */
-       struct work_struct              rx_work;        /* for the rx decoding workqueue */
+       struct task_struct              *thread;
        struct kfifo                    kfifo;          /* fifo for the pulse/space durations */
        ktime_t                         last_event;     /* when last event occurred */
        enum raw_event_type             last_type;      /* last event type */
@@ -41,10 +41,13 @@ struct ir_raw_event_ctrl {
 
        /* raw decoder state follows */
        struct ir_raw_event prev_ev;
+       struct ir_raw_event this_ev;
        struct nec_dec {
                int state;
                unsigned count;
                u32 bits;
+               bool is_nec_x;
+               bool necx_repeat;
        } nec;
        struct rc5_dec {
                int state;
@@ -76,7 +79,7 @@ struct ir_raw_event_ctrl {
        struct lirc_codec {
                struct ir_input_dev *ir_dev;
                struct lirc_driver *drv;
-               int lircdata;
+               int carrier_low;
        } lirc;
 };
 
@@ -104,10 +107,9 @@ static inline void decrease_duration(struct ir_raw_event *ev, unsigned duration)
                ev->duration -= duration;
 }
 
-#define TO_US(duration)                        (((duration) + 500) / 1000)
+#define TO_US(duration)                        DIV_ROUND_CLOSEST((duration), 1000)
 #define TO_STR(is_pulse)               ((is_pulse) ? "pulse" : "space")
 #define IS_RESET(ev)                   (ev.duration == 0)
-
 /*
  * Routines from ir-sysfs.c - Meant to be called only internally inside
  * ir-core
@@ -126,7 +128,8 @@ int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler);
 void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler);
 void ir_raw_init(void);
 
-
+int ir_rcmap_init(void);
+void ir_rcmap_cleanup(void);
 /*
  * Decoder initialization code
  *
index 8894d8b360486e56595efbf6617a14925c0ea67e..77a89c4de0143beba0d01d9ba19ec329ca0dc9b8 100644 (file)
@@ -32,6 +32,7 @@ enum jvc_state {
        STATE_BIT_SPACE,
        STATE_TRAILER_PULSE,
        STATE_TRAILER_SPACE,
+       STATE_CHECK_REPEAT,
 };
 
 /**
@@ -60,6 +61,7 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
        IR_dprintk(2, "JVC decode started at state %d (%uus %s)\n",
                   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
 
+again:
        switch (data->state) {
 
        case STATE_INACTIVE:
@@ -149,8 +151,18 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
                }
 
                data->count = 0;
-               data->state = STATE_BIT_PULSE;
+               data->state = STATE_CHECK_REPEAT;
                return 0;
+
+       case STATE_CHECK_REPEAT:
+               if (!ev.pulse)
+                       break;
+
+               if (eq_margin(ev.duration, JVC_HEADER_PULSE, JVC_UNIT / 2))
+                       data->state = STATE_INACTIVE;
+  else
+                       data->state = STATE_BIT_PULSE;
+               goto again;
        }
 
 out:
index 15a0f192d4134fe3fad6b17c60aa431defc0f1e2..7e82a9df726b51ab6d90f00fc1b1fa22998c8862 100644 (file)
@@ -339,6 +339,8 @@ void ir_repeat(struct input_dev *dev)
 
        spin_lock_irqsave(&ir->keylock, flags);
 
+       input_event(dev, EV_MSC, MSC_SCAN, ir->last_scancode);
+
        if (!ir->keypressed)
                goto out;
 
@@ -370,6 +372,8 @@ void ir_keydown(struct input_dev *dev, int scancode, u8 toggle)
 
        spin_lock_irqsave(&ir->keylock, flags);
 
+       input_event(dev, EV_MSC, MSC_SCAN, scancode);
+
        /* Repeat event? */
        if (ir->keypressed &&
            ir->last_scancode == scancode &&
@@ -383,9 +387,11 @@ void ir_keydown(struct input_dev *dev, int scancode, u8 toggle)
        ir->last_toggle = toggle;
        ir->last_keycode = keycode;
 
+
        if (keycode == KEY_RESERVED)
                goto out;
 
+
        /* Register a keypress */
        ir->keypressed = true;
        IR_dprintk(1, "%s: key down event, key 0x%04x, scancode 0x%04x\n",
@@ -428,7 +434,7 @@ static void ir_close(struct input_dev *input_dev)
  */
 int __ir_input_register(struct input_dev *input_dev,
                      const struct ir_scancode_table *rc_tab,
-                     const struct ir_dev_props *props,
+                     struct ir_dev_props *props,
                      const char *driver_name)
 {
        struct ir_input_dev *ir_dev;
@@ -480,6 +486,8 @@ int __ir_input_register(struct input_dev *input_dev,
 
        set_bit(EV_KEY, input_dev->evbit);
        set_bit(EV_REP, input_dev->evbit);
+       set_bit(EV_MSC, input_dev->evbit);
+       set_bit(MSC_SCAN, input_dev->mscbit);
 
        if (ir_setkeytable(input_dev, &ir_dev->rc_tab, rc_tab)) {
                rc = -ENOMEM;
@@ -499,7 +507,8 @@ int __ir_input_register(struct input_dev *input_dev,
 
        IR_dprintk(1, "Registered input device on %s for %s remote%s.\n",
                   driver_name, rc_tab->name,
-                  ir_dev->props->driver_type == RC_DRIVER_IR_RAW ? " in raw mode" : "");
+                  (ir_dev->props && ir_dev->props->driver_type == RC_DRIVER_IR_RAW) ?
+                       " in raw mode" : "");
 
        return 0;
 
index 3ba482d96c4b17c388fbf4b75c364879cc51a6b3..77b5946413c0203739d9bdcecb129e218f194356 100644 (file)
@@ -32,6 +32,7 @@
 static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
 {
        struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+       int sample;
 
        if (!(ir_dev->raw->enabled_protocols & IR_TYPE_LIRC))
                return 0;
@@ -39,18 +40,20 @@ static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
        if (!ir_dev->raw->lirc.drv || !ir_dev->raw->lirc.drv->rbuf)
                return -EINVAL;
 
+       if (IS_RESET(ev))
+               return 0;
+
        IR_dprintk(2, "LIRC data transfer started (%uus %s)\n",
                   TO_US(ev.duration), TO_STR(ev.pulse));
 
-       ir_dev->raw->lirc.lircdata += ev.duration / 1000;
+       sample = ev.duration / 1000;
        if (ev.pulse)
-               ir_dev->raw->lirc.lircdata |= PULSE_BIT;
+               sample |= PULSE_BIT;
 
        lirc_buffer_write(ir_dev->raw->lirc.drv->rbuf,
-                         (unsigned char *) &ir_dev->raw->lirc.lircdata);
+                         (unsigned char *) &sample);
        wake_up(&ir_dev->raw->lirc.drv->rbuf->wait_poll);
 
-       ir_dev->raw->lirc.lircdata = 0;
 
        return 0;
 }
@@ -92,13 +95,14 @@ out:
        return ret;
 }
 
-static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
+                       unsigned long __user arg)
 {
        struct lirc_codec *lirc;
        struct ir_input_dev *ir_dev;
        int ret = 0;
        void *drv_data;
-       unsigned long val;
+       unsigned long val = 0;
 
        lirc = lirc_get_pdata(filep);
        if (!lirc)
@@ -110,47 +114,106 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long ar
 
        drv_data = ir_dev->props->priv;
 
-       switch (cmd) {
-       case LIRC_SET_TRANSMITTER_MASK:
+       if (_IOC_DIR(cmd) & _IOC_WRITE) {
                ret = get_user(val, (unsigned long *)arg);
                if (ret)
                        return ret;
+       }
+
+       switch (cmd) {
 
-               if (ir_dev->props && ir_dev->props->s_tx_mask)
+       /* legacy support */
+       case LIRC_GET_SEND_MODE:
+               val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK;
+               break;
+
+       case LIRC_SET_SEND_MODE:
+               if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))
+                       return -EINVAL;
+               break;
+
+       /* TX settings */
+       case LIRC_SET_TRANSMITTER_MASK:
+               if (ir_dev->props->s_tx_mask)
                        ret = ir_dev->props->s_tx_mask(drv_data, (u32)val);
                else
                        return -EINVAL;
                break;
 
        case LIRC_SET_SEND_CARRIER:
-               ret = get_user(val, (unsigned long *)arg);
-               if (ret)
-                       return ret;
-
-               if (ir_dev->props && ir_dev->props->s_tx_carrier)
+               if (ir_dev->props->s_tx_carrier)
                        ir_dev->props->s_tx_carrier(drv_data, (u32)val);
                else
                        return -EINVAL;
                break;
 
-       case LIRC_GET_SEND_MODE:
-               val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK;
-               ret = put_user(val, (unsigned long *)arg);
+       case LIRC_SET_SEND_DUTY_CYCLE:
+               if (!ir_dev->props->s_tx_duty_cycle)
+                       return -ENOSYS;
+
+               if (val <= 0 || val >= 100)
+                       return -EINVAL;
+
+               ir_dev->props->s_tx_duty_cycle(ir_dev->props->priv, val);
                break;
 
-       case LIRC_SET_SEND_MODE:
-               ret = get_user(val, (unsigned long *)arg);
-               if (ret)
-                       return ret;
+       /* RX settings */
+       case LIRC_SET_REC_CARRIER:
+               if (ir_dev->props->s_rx_carrier_range)
+                       ret = ir_dev->props->s_rx_carrier_range(
+                               ir_dev->props->priv,
+                               ir_dev->raw->lirc.carrier_low, val);
+               else
+                       return -ENOSYS;
 
-               if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))
+               if (!ret)
+                       ir_dev->raw->lirc.carrier_low = 0;
+               break;
+
+       case LIRC_SET_REC_CARRIER_RANGE:
+               if (val >= 0)
+                       ir_dev->raw->lirc.carrier_low = val;
+               break;
+
+
+       case LIRC_GET_REC_RESOLUTION:
+               val = ir_dev->props->rx_resolution;
+               break;
+
+       case LIRC_SET_WIDEBAND_RECEIVER:
+               if (ir_dev->props->s_learning_mode)
+                       return ir_dev->props->s_learning_mode(
+                               ir_dev->props->priv, !!val);
+               else
+                       return -ENOSYS;
+
+       /* Generic timeout support */
+       case LIRC_GET_MIN_TIMEOUT:
+               if (!ir_dev->props->max_timeout)
+                       return -ENOSYS;
+               val = ir_dev->props->min_timeout / 1000;
+               break;
+
+       case LIRC_GET_MAX_TIMEOUT:
+               if (!ir_dev->props->max_timeout)
+                       return -ENOSYS;
+               val = ir_dev->props->max_timeout / 1000;
+               break;
+
+       case LIRC_SET_REC_TIMEOUT:
+               if (val < ir_dev->props->min_timeout ||
+                   val > ir_dev->props->max_timeout)
                        return -EINVAL;
+               ir_dev->props->timeout = val * 1000;
                break;
 
        default:
                return lirc_dev_fop_ioctl(filep, cmd, arg);
        }
 
+       if (_IOC_DIR(cmd) & _IOC_READ)
+               ret = put_user(val, (unsigned long *)arg);
+
        return ret;
 }
 
@@ -196,13 +259,28 @@ static int ir_lirc_register(struct input_dev *input_dev)
 
        features = LIRC_CAN_REC_MODE2;
        if (ir_dev->props->tx_ir) {
+
                features |= LIRC_CAN_SEND_PULSE;
                if (ir_dev->props->s_tx_mask)
                        features |= LIRC_CAN_SET_TRANSMITTER_MASK;
                if (ir_dev->props->s_tx_carrier)
                        features |= LIRC_CAN_SET_SEND_CARRIER;
+
+               if (ir_dev->props->s_tx_duty_cycle)
+                       features |= LIRC_CAN_SET_REC_DUTY_CYCLE;
        }
 
+       if (ir_dev->props->s_rx_carrier_range)
+               features |= LIRC_CAN_SET_REC_CARRIER |
+                       LIRC_CAN_SET_REC_CARRIER_RANGE;
+
+       if (ir_dev->props->s_learning_mode)
+               features |= LIRC_CAN_USE_WIDEBAND_RECEIVER;
+
+       if (ir_dev->props->max_timeout)
+               features |= LIRC_CAN_SET_REC_TIMEOUT;
+
+
        snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)",
                 ir_dev->driver_name);
        drv->minor = -1;
@@ -224,8 +302,6 @@ static int ir_lirc_register(struct input_dev *input_dev)
 
        ir_dev->raw->lirc.drv = drv;
        ir_dev->raw->lirc.ir_dev = ir_dev;
-       ir_dev->raw->lirc.lircdata = PULSE_MASK;
-
        return 0;
 
 lirc_register_failed:
index 52e0f378ae3df2b25a329bd1e0da1e3ba22ec834..d597421d65470f3ca146a361c06a9aee9b175ed9 100644 (file)
 #define NEC_HEADER_PULSE       (16 * NEC_UNIT)
 #define NECX_HEADER_PULSE      (8  * NEC_UNIT) /* Less common NEC variant */
 #define NEC_HEADER_SPACE       (8  * NEC_UNIT)
-#define NEC_REPEAT_SPACE       (8  * NEC_UNIT)
+#define NEC_REPEAT_SPACE       (4  * NEC_UNIT)
 #define NEC_BIT_PULSE          (1  * NEC_UNIT)
 #define NEC_BIT_0_SPACE                (1  * NEC_UNIT)
 #define NEC_BIT_1_SPACE                (3  * NEC_UNIT)
 #define        NEC_TRAILER_PULSE       (1  * NEC_UNIT)
 #define        NEC_TRAILER_SPACE       (10 * NEC_UNIT) /* even longer in reality */
+#define NECX_REPEAT_BITS       1
 
 enum nec_state {
        STATE_INACTIVE,
@@ -67,8 +68,12 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
                if (!ev.pulse)
                        break;
 
-               if (!eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT / 2) &&
-                   !eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2))
+               if (eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT / 2)) {
+                       data->is_nec_x = false;
+                       data->necx_repeat = false;
+               } else if (eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2))
+                       data->is_nec_x = true;
+               else
                        break;
 
                data->count = 0;
@@ -105,6 +110,17 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
                if (ev.pulse)
                        break;
 
+               if (data->necx_repeat && data->count == NECX_REPEAT_BITS &&
+                       geq_margin(ev.duration,
+                       NEC_TRAILER_SPACE, NEC_UNIT / 2)) {
+                               IR_dprintk(1, "Repeat last key\n");
+                               ir_repeat(input_dev);
+                               data->state = STATE_INACTIVE;
+                               return 0;
+
+               } else if (data->count > NECX_REPEAT_BITS)
+                       data->necx_repeat = false;
+
                data->bits <<= 1;
                if (eq_margin(ev.duration, NEC_BIT_1_SPACE, NEC_UNIT / 2))
                        data->bits |= 1;
@@ -159,6 +175,9 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
                        IR_dprintk(1, "NEC scancode 0x%04x\n", scancode);
                }
 
+               if (data->is_nec_x)
+                       data->necx_repeat = true;
+
                ir_keydown(input_dev, scancode, 0);
                data->state = STATE_INACTIVE;
                return 0;
index 6f192ef31db1ae1be0ab78fd28e9b5ec160d442d..43094e7eccfa92ba6213115ecdab06d88b1387ec 100644 (file)
  *  GNU General Public License for more details.
  */
 
-#include <linux/workqueue.h>
-#include <linux/spinlock.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
 #include <linux/sched.h>
+#include <linux/freezer.h>
 #include "ir-core-priv.h"
 
 /* Define the max number of pulse/space transitions to buffer */
@@ -24,7 +25,7 @@
 static LIST_HEAD(ir_raw_client_list);
 
 /* Used to handle IR raw handler extensions */
-static DEFINE_SPINLOCK(ir_raw_handler_lock);
+static DEFINE_MUTEX(ir_raw_handler_lock);
 static LIST_HEAD(ir_raw_handler_list);
 static u64 available_protocols;
 
@@ -33,20 +34,30 @@ static u64 available_protocols;
 static struct work_struct wq_load;
 #endif
 
-static void ir_raw_event_work(struct work_struct *work)
+static int ir_raw_event_thread(void *data)
 {
        struct ir_raw_event ev;
        struct ir_raw_handler *handler;
-       struct ir_raw_event_ctrl *raw =
-               container_of(work, struct ir_raw_event_ctrl, rx_work);
-
-       while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) {
-               spin_lock(&ir_raw_handler_lock);
-               list_for_each_entry(handler, &ir_raw_handler_list, list)
-                       handler->decode(raw->input_dev, ev);
-               spin_unlock(&ir_raw_handler_lock);
-               raw->prev_ev = ev;
+       struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data;
+
+       while (!kthread_should_stop()) {
+               try_to_freeze();
+
+               mutex_lock(&ir_raw_handler_lock);
+
+               while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) {
+                       list_for_each_entry(handler, &ir_raw_handler_list, list)
+                               handler->decode(raw->input_dev, ev);
+                       raw->prev_ev = ev;
+               }
+
+               mutex_unlock(&ir_raw_handler_lock);
+
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule();
        }
+
+       return 0;
 }
 
 /**
@@ -66,6 +77,9 @@ int ir_raw_event_store(struct input_dev *input_dev, struct ir_raw_event *ev)
        if (!ir->raw)
                return -EINVAL;
 
+       IR_dprintk(2, "sample: (05%dus %s)\n",
+               TO_US(ev->duration), TO_STR(ev->pulse));
+
        if (kfifo_in(&ir->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev))
                return -ENOMEM;
 
@@ -125,6 +139,90 @@ int ir_raw_event_store_edge(struct input_dev *input_dev, enum raw_event_type typ
 }
 EXPORT_SYMBOL_GPL(ir_raw_event_store_edge);
 
+/**
+ * ir_raw_event_store_with_filter() - pass next pulse/space to decoders with some processing
+ * @input_dev: the struct input_dev device descriptor
+ * @type:      the type of the event that has occurred
+ *
+ * This routine (which may be called from an interrupt context) works
+ * in similiar manner to ir_raw_event_store_edge.
+ * This routine is intended for devices with limited internal buffer
+ * It automerges samples of same type, and handles timeouts
+ */
+int ir_raw_event_store_with_filter(struct input_dev *input_dev,
+                                               struct ir_raw_event *ev)
+{
+       struct ir_input_dev *ir = input_get_drvdata(input_dev);
+       struct ir_raw_event_ctrl *raw = ir->raw;
+
+       if (!raw || !ir->props)
+               return -EINVAL;
+
+       /* Ignore spaces in idle mode */
+       if (ir->idle && !ev->pulse)
+               return 0;
+       else if (ir->idle)
+               ir_raw_event_set_idle(input_dev, 0);
+
+       if (!raw->this_ev.duration) {
+               raw->this_ev = *ev;
+       } else if (ev->pulse == raw->this_ev.pulse) {
+               raw->this_ev.duration += ev->duration;
+       } else {
+               ir_raw_event_store(input_dev, &raw->this_ev);
+               raw->this_ev = *ev;
+       }
+
+       /* Enter idle mode if nessesary */
+       if (!ev->pulse && ir->props->timeout &&
+               raw->this_ev.duration >= ir->props->timeout)
+               ir_raw_event_set_idle(input_dev, 1);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ir_raw_event_store_with_filter);
+
+void ir_raw_event_set_idle(struct input_dev *input_dev, int idle)
+{
+       struct ir_input_dev *ir = input_get_drvdata(input_dev);
+       struct ir_raw_event_ctrl *raw = ir->raw;
+       ktime_t now;
+       u64 delta;
+
+       if (!ir->props)
+               return;
+
+       if (!ir->raw)
+               goto out;
+
+       if (idle) {
+               IR_dprintk(2, "enter idle mode\n");
+               raw->last_event = ktime_get();
+       } else {
+               IR_dprintk(2, "exit idle mode\n");
+
+               now = ktime_get();
+               delta = ktime_to_ns(ktime_sub(now, ir->raw->last_event));
+
+               WARN_ON(raw->this_ev.pulse);
+
+               raw->this_ev.duration =
+                       min(raw->this_ev.duration + delta,
+                                               (u64)IR_MAX_DURATION);
+
+               ir_raw_event_store(input_dev, &raw->this_ev);
+
+               if (raw->this_ev.duration == IR_MAX_DURATION)
+                       ir_raw_event_reset(input_dev);
+
+               raw->this_ev.duration = 0;
+       }
+out:
+       if (ir->props->s_idle)
+               ir->props->s_idle(ir->props->priv, idle);
+       ir->idle = idle;
+}
+EXPORT_SYMBOL_GPL(ir_raw_event_set_idle);
+
 /**
  * ir_raw_event_handle() - schedules the decoding of stored ir data
  * @input_dev: the struct input_dev device descriptor
@@ -138,7 +236,7 @@ void ir_raw_event_handle(struct input_dev *input_dev)
        if (!ir->raw)
                return;
 
-       schedule_work(&ir->raw->rx_work);
+       wake_up_process(ir->raw->thread);
 }
 EXPORT_SYMBOL_GPL(ir_raw_event_handle);
 
@@ -147,9 +245,9 @@ u64
 ir_raw_get_allowed_protocols()
 {
        u64 protocols;
-       spin_lock(&ir_raw_handler_lock);
+       mutex_lock(&ir_raw_handler_lock);
        protocols = available_protocols;
-       spin_unlock(&ir_raw_handler_lock);
+       mutex_unlock(&ir_raw_handler_lock);
        return protocols;
 }
 
@@ -167,7 +265,7 @@ int ir_raw_event_register(struct input_dev *input_dev)
                return -ENOMEM;
 
        ir->raw->input_dev = input_dev;
-       INIT_WORK(&ir->raw->rx_work, ir_raw_event_work);
+
        ir->raw->enabled_protocols = ~0;
        rc = kfifo_alloc(&ir->raw->kfifo, sizeof(s64) * MAX_IR_EVENT_SIZE,
                         GFP_KERNEL);
@@ -177,12 +275,21 @@ int ir_raw_event_register(struct input_dev *input_dev)
                return rc;
        }
 
-       spin_lock(&ir_raw_handler_lock);
+       ir->raw->thread = kthread_run(ir_raw_event_thread, ir->raw,
+                       "rc%u",  (unsigned int)ir->devno);
+
+       if (IS_ERR(ir->raw->thread)) {
+               kfree(ir->raw);
+               ir->raw = NULL;
+               return PTR_ERR(ir->raw->thread);
+       }
+
+       mutex_lock(&ir_raw_handler_lock);
        list_add_tail(&ir->raw->list, &ir_raw_client_list);
        list_for_each_entry(handler, &ir_raw_handler_list, list)
                if (handler->raw_register)
                        handler->raw_register(ir->raw->input_dev);
-       spin_unlock(&ir_raw_handler_lock);
+       mutex_unlock(&ir_raw_handler_lock);
 
        return 0;
 }
@@ -195,14 +302,14 @@ void ir_raw_event_unregister(struct input_dev *input_dev)
        if (!ir->raw)
                return;
 
-       cancel_work_sync(&ir->raw->rx_work);
+       kthread_stop(ir->raw->thread);
 
-       spin_lock(&ir_raw_handler_lock);
+       mutex_lock(&ir_raw_handler_lock);
        list_del(&ir->raw->list);
        list_for_each_entry(handler, &ir_raw_handler_list, list)
                if (handler->raw_unregister)
                        handler->raw_unregister(ir->raw->input_dev);
-       spin_unlock(&ir_raw_handler_lock);
+       mutex_unlock(&ir_raw_handler_lock);
 
        kfifo_free(&ir->raw->kfifo);
        kfree(ir->raw);
@@ -217,13 +324,13 @@ int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler)
 {
        struct ir_raw_event_ctrl *raw;
 
-       spin_lock(&ir_raw_handler_lock);
+       mutex_lock(&ir_raw_handler_lock);
        list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list);
        if (ir_raw_handler->raw_register)
                list_for_each_entry(raw, &ir_raw_client_list, list)
                        ir_raw_handler->raw_register(raw->input_dev);
        available_protocols |= ir_raw_handler->protocols;
-       spin_unlock(&ir_raw_handler_lock);
+       mutex_unlock(&ir_raw_handler_lock);
 
        return 0;
 }
@@ -233,13 +340,13 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler)
 {
        struct ir_raw_event_ctrl *raw;
 
-       spin_lock(&ir_raw_handler_lock);
+       mutex_lock(&ir_raw_handler_lock);
        list_del(&ir_raw_handler->list);
        if (ir_raw_handler->raw_unregister)
                list_for_each_entry(raw, &ir_raw_client_list, list)
                        ir_raw_handler->raw_unregister(raw->input_dev);
        available_protocols &= ~ir_raw_handler->protocols;
-       spin_unlock(&ir_raw_handler_lock);
+       mutex_unlock(&ir_raw_handler_lock);
 }
 EXPORT_SYMBOL(ir_raw_handler_unregister);
 
index 6273047e915b4b800527bb50f63463adcf83bbc4..96dafc425c8e61495cd662bf7f4c11182d674e79 100644 (file)
@@ -325,6 +325,7 @@ static int __init ir_core_init(void)
 
        /* Initialize/load the decoders/keymap code that will be used */
        ir_raw_init();
+       ir_rcmap_init();
 
        return 0;
 }
@@ -332,6 +333,7 @@ static int __init ir_core_init(void)
 static void __exit ir_core_exit(void)
 {
        class_unregister(&ir_input_class);
+       ir_rcmap_cleanup();
 }
 
 module_init(ir_core_init);
index cbee06243b512c6b6c03b032e2d308dc8c115016..950e5d953c6f10e4835d7f76bd7bf2c98edebbe4 100644 (file)
@@ -19,7 +19,6 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
                        rc-dm1105-nec.o \
                        rc-dntv-live-dvb-t.o \
                        rc-dntv-live-dvbt-pro.o \
-                       rc-empty.o \
                        rc-em-terratec.o \
                        rc-encore-enltv2.o \
                        rc-encore-enltv.o \
@@ -59,6 +58,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
                        rc-purpletv.o \
                        rc-pv951.o \
                        rc-rc5-hauppauge-new.o \
+                       rc-rc5-streamzap.o \
                        rc-rc5-tv.o \
                        rc-rc6-mce.o \
                        rc-real-audio-220-32-keys.o \
diff --git a/drivers/media/IR/keymaps/rc-empty.c b/drivers/media/IR/keymaps/rc-empty.c
deleted file mode 100644 (file)
index 3b338d8..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/* empty.h - Keytable for empty Remote Controller
- *
- * keymap imported from ir-keymaps.c
- *
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <media/rc-map.h>
-
-/* empty keytable, can be used as placeholder for not-yet created keytables */
-
-static struct ir_scancode empty[] = {
-       { 0x2a, KEY_COFFEE },
-};
-
-static struct rc_keymap empty_map = {
-       .map = {
-               .scan    = empty,
-               .size    = ARRAY_SIZE(empty),
-               .ir_type = IR_TYPE_UNKNOWN,     /* Legacy IR type */
-               .name    = RC_MAP_EMPTY,
-       }
-};
-
-static int __init init_rc_map_empty(void)
-{
-       return ir_register_map(&empty_map);
-}
-
-static void __exit exit_rc_map_empty(void)
-{
-       ir_unregister_map(&empty_map);
-}
-
-module_init(init_rc_map_empty)
-module_exit(exit_rc_map_empty)
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
diff --git a/drivers/media/IR/keymaps/rc-rc5-streamzap.c b/drivers/media/IR/keymaps/rc-rc5-streamzap.c
new file mode 100644 (file)
index 0000000..4c19c58
--- /dev/null
@@ -0,0 +1,81 @@
+/* rc-rc5-streamzap.c - Keytable for Streamzap PC Remote, for use
+ * with the Streamzap PC Remote IR Receiver.
+ *
+ * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+
+static struct ir_scancode rc5_streamzap[] = {
+/*
+ * FIXME: The Streamzap remote isn't actually true RC-5, it has an extra
+ * bit in it, which presently throws the in-kernel RC-5 decoder for a loop.
+ * We either have to enhance the decoder to support it, add a new decoder,
+ * or just rely on lirc userspace decoding.
+ */
+       { 0x00, KEY_NUMERIC_0 },
+       { 0x01, KEY_NUMERIC_1 },
+       { 0x02, KEY_NUMERIC_2 },
+       { 0x03, KEY_NUMERIC_3 },
+       { 0x04, KEY_NUMERIC_4 },
+       { 0x05, KEY_NUMERIC_5 },
+       { 0x06, KEY_NUMERIC_6 },
+       { 0x07, KEY_NUMERIC_7 },
+       { 0x08, KEY_NUMERIC_8 },
+       { 0x0a, KEY_POWER },
+       { 0x0b, KEY_MUTE },
+       { 0x0c, KEY_CHANNELUP },
+       { 0x0d, KEY_VOLUMEUP },
+       { 0x0e, KEY_CHANNELDOWN },
+       { 0x0f, KEY_VOLUMEDOWN },
+       { 0x10, KEY_UP },
+       { 0x11, KEY_LEFT },
+       { 0x12, KEY_OK },
+       { 0x13, KEY_RIGHT },
+       { 0x14, KEY_DOWN },
+       { 0x15, KEY_MENU },
+       { 0x16, KEY_EXIT },
+       { 0x17, KEY_PLAY },
+       { 0x18, KEY_PAUSE },
+       { 0x19, KEY_STOP },
+       { 0x1a, KEY_BACK },
+       { 0x1b, KEY_FORWARD },
+       { 0x1c, KEY_RECORD },
+       { 0x1d, KEY_REWIND },
+       { 0x1e, KEY_FASTFORWARD },
+       { 0x20, KEY_RED },
+       { 0x21, KEY_GREEN },
+       { 0x22, KEY_YELLOW },
+       { 0x23, KEY_BLUE },
+
+};
+
+static struct rc_keymap rc5_streamzap_map = {
+       .map = {
+               .scan    = rc5_streamzap,
+               .size    = ARRAY_SIZE(rc5_streamzap),
+               .ir_type = IR_TYPE_RC5,
+               .name    = RC_MAP_RC5_STREAMZAP,
+       }
+};
+
+static int __init init_rc_map_rc5_streamzap(void)
+{
+       return ir_register_map(&rc5_streamzap_map);
+}
+
+static void __exit exit_rc_map_rc5_streamzap(void)
+{
+       ir_unregister_map(&rc5_streamzap_map);
+}
+
+module_init(init_rc_map_rc5_streamzap)
+module_exit(exit_rc_map_rc5_streamzap)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
index c6726a8039be6c00c1b559eb6d47e1df70e677d8..64264f7f838f29a0be7861f872f8e852bf376669 100644 (file)
@@ -74,6 +74,8 @@ static struct ir_scancode rc6_mce[] = {
        { 0x800f045a, KEY_SUBTITLE }, /* Caption/Teletext */
        { 0x800f044d, KEY_TITLE },
 
+       { 0x800f044e, KEY_PRINT }, /* Print - HP OEM version of remote */
+
        { 0x800f040c, KEY_POWER },
        { 0x800f040d, KEY_PROG1 }, /* Windows MCE button */
 
index 78bf7f77a1a0479fca62573a093c384eb16ca7fb..ac6bb2c01a4810446451d2651df53936b57d104c 100644 (file)
@@ -228,7 +228,6 @@ static struct usb_device_id std_tx_mask_list[] = {
 /* data structure for each usb transceiver */
 struct mceusb_dev {
        /* ir-core bits */
-       struct ir_input_dev *irdev;
        struct ir_dev_props *props;
        struct ir_raw_event rawir;
 
@@ -428,7 +427,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
        }
 }
 
-static void usb_async_callback(struct urb *urb, struct pt_regs *regs)
+static void mce_async_callback(struct urb *urb, struct pt_regs *regs)
 {
        struct mceusb_dev *ir;
        int len;
@@ -477,7 +476,7 @@ static void mce_request_packet(struct mceusb_dev *ir,
                /* outbound data */
                usb_fill_int_urb(async_urb, ir->usbdev,
                        usb_sndintpipe(ir->usbdev, ep->bEndpointAddress),
-                       async_buf, size, (usb_complete_t) usb_async_callback,
+                       async_buf, size, (usb_complete_t)mce_async_callback,
                        ir, ep->bInterval);
                memcpy(async_buf, data, size);
 
@@ -739,7 +738,7 @@ static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
 
        if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
                ir->send_flags = SEND_FLAG_COMPLETE;
-               dev_dbg(&ir->irdev->dev, "setup answer received %d bytes\n",
+               dev_dbg(ir->dev, "setup answer received %d bytes\n",
                        buf_len);
        }
 
@@ -861,7 +860,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
 {
        struct input_dev *idev;
        struct ir_dev_props *props;
-       struct ir_input_dev *irdev;
        struct device *dev = ir->dev;
        int ret = -ENODEV;
 
@@ -878,12 +876,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
                goto props_alloc_failed;
        }
 
-       irdev = kzalloc(sizeof(struct ir_input_dev), GFP_KERNEL);
-       if (!irdev) {
-               dev_err(dev, "remote ir input dev allocation failed\n");
-               goto ir_dev_alloc_failed;
-       }
-
        snprintf(ir->name, sizeof(ir->name), "Media Center Ed. eHome "
                 "Infrared Remote Transceiver (%04x:%04x)",
                 le16_to_cpu(ir->usbdev->descriptor.idVendor),
@@ -902,9 +894,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
        props->tx_ir = mceusb_tx_ir;
 
        ir->props = props;
-       ir->irdev = irdev;
-
-       input_set_drvdata(idev, irdev);
 
        ret = ir_input_register(idev, RC_MAP_RC6_MCE, props, DRIVER_NAME);
        if (ret < 0) {
@@ -915,8 +904,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
        return idev;
 
 irdev_failed:
-       kfree(irdev);
-ir_dev_alloc_failed:
        kfree(props);
 props_alloc_failed:
        input_free_device(idev);
@@ -932,7 +919,6 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
        struct usb_endpoint_descriptor *ep = NULL;
        struct usb_endpoint_descriptor *ep_in = NULL;
        struct usb_endpoint_descriptor *ep_out = NULL;
-       struct usb_host_config *config;
        struct mceusb_dev *ir = NULL;
        int pipe, maxp, i;
        char buf[63], name[128] = "";
@@ -942,7 +928,6 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
 
        dev_dbg(&intf->dev, ": %s called\n", __func__);
 
-       config = dev->actconfig;
        idesc  = intf->cur_altsetting;
 
        is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0;
index 46a8f1524b5ba7e64e64aeb4bd72d978beb64ac7..689143f2fff07a0a6828547d4f01fc505b0e4fbf 100644 (file)
@@ -82,3 +82,26 @@ void ir_unregister_map(struct rc_keymap *map)
 }
 EXPORT_SYMBOL_GPL(ir_unregister_map);
 
+
+static struct ir_scancode empty[] = {
+       { 0x2a, KEY_COFFEE },
+};
+
+static struct rc_keymap empty_map = {
+       .map = {
+               .scan    = empty,
+               .size    = ARRAY_SIZE(empty),
+               .ir_type = IR_TYPE_UNKNOWN,     /* Legacy IR type */
+               .name    = RC_MAP_EMPTY,
+       }
+};
+
+int ir_rcmap_init(void)
+{
+       return ir_register_map(&empty_map);
+}
+
+void ir_rcmap_cleanup(void)
+{
+       ir_unregister_map(&empty_map);
+}
diff --git a/drivers/media/IR/streamzap.c b/drivers/media/IR/streamzap.c
new file mode 100644 (file)
index 0000000..058e29f
--- /dev/null
@@ -0,0 +1,741 @@
+/*
+ * Streamzap Remote Control driver
+ *
+ * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
+ * Copyright (c) 2010 Jarod Wilson <jarod@wilsonet.com>
+ *
+ * This driver was based on the work of Greg Wickham and Adrian
+ * Dewhurst. It was substantially rewritten to support correct signal
+ * gaps and now maintains a delay buffer, which is used to present
+ * consistent timing behaviour to user space applications. Without the
+ * delay buffer an ugly hack would be required in lircd, which can
+ * cause sluggish signal decoding in certain situations.
+ *
+ * Ported to in-kernel ir-core interface by Jarod Wilson
+ *
+ * This driver is based on the USB skeleton driver packaged with the
+ * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/input.h>
+#include <media/ir-core.h>
+
+#define DRIVER_VERSION "1.60"
+#define DRIVER_NAME    "streamzap"
+#define DRIVER_DESC    "Streamzap Remote Control driver"
+
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+
+#define USB_STREAMZAP_VENDOR_ID                0x0e9c
+#define USB_STREAMZAP_PRODUCT_ID       0x0000
+
+/* table of devices that work with this driver */
+static struct usb_device_id streamzap_table[] = {
+       /* Streamzap Remote Control */
+       { USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
+       /* Terminating entry */
+       { }
+};
+
+MODULE_DEVICE_TABLE(usb, streamzap_table);
+
+#define STREAMZAP_PULSE_MASK 0xf0
+#define STREAMZAP_SPACE_MASK 0x0f
+#define STREAMZAP_TIMEOUT    0xff
+#define STREAMZAP_RESOLUTION 256
+
+/* number of samples buffered */
+#define SZ_BUF_LEN 128
+
+enum StreamzapDecoderState {
+       PulseSpace,
+       FullPulse,
+       FullSpace,
+       IgnorePulse
+};
+
+/* structure to hold our device specific stuff */
+struct streamzap_ir {
+
+       /* ir-core */
+       struct ir_dev_props *props;
+       struct ir_raw_event rawir;
+
+       /* core device info */
+       struct device *dev;
+       struct input_dev *idev;
+
+       /* usb */
+       struct usb_device       *usbdev;
+       struct usb_interface    *interface;
+       struct usb_endpoint_descriptor *endpoint;
+       struct urb              *urb_in;
+
+       /* buffer & dma */
+       unsigned char           *buf_in;
+       dma_addr_t              dma_in;
+       unsigned int            buf_in_len;
+
+       /* timer used to support delay buffering */
+       struct timer_list       delay_timer;
+       bool                    timer_running;
+       spinlock_t              timer_lock;
+       struct timer_list       flush_timer;
+       bool                    flush;
+
+       /* delay buffer */
+       struct kfifo fifo;
+       bool fifo_initialized;
+
+       /* track what state we're in */
+       enum StreamzapDecoderState decoder_state;
+       /* tracks whether we are currently receiving some signal */
+       bool                    idle;
+       /* sum of signal lengths received since signal start */
+       unsigned long           sum;
+       /* start time of signal; necessary for gap tracking */
+       struct timeval          signal_last;
+       struct timeval          signal_start;
+       /* bool                 timeout_enabled; */
+
+       char                    name[128];
+       char                    phys[64];
+};
+
+
+/* local function prototypes */
+static int streamzap_probe(struct usb_interface *interface,
+                          const struct usb_device_id *id);
+static void streamzap_disconnect(struct usb_interface *interface);
+static void streamzap_callback(struct urb *urb);
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
+static int streamzap_resume(struct usb_interface *intf);
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver streamzap_driver = {
+       .name =         DRIVER_NAME,
+       .probe =        streamzap_probe,
+       .disconnect =   streamzap_disconnect,
+       .suspend =      streamzap_suspend,
+       .resume =       streamzap_resume,
+       .id_table =     streamzap_table,
+};
+
+static void streamzap_stop_timer(struct streamzap_ir *sz)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&sz->timer_lock, flags);
+       if (sz->timer_running) {
+               sz->timer_running = false;
+               spin_unlock_irqrestore(&sz->timer_lock, flags);
+               del_timer_sync(&sz->delay_timer);
+       } else {
+               spin_unlock_irqrestore(&sz->timer_lock, flags);
+       }
+}
+
+static void streamzap_flush_timeout(unsigned long arg)
+{
+       struct streamzap_ir *sz = (struct streamzap_ir *)arg;
+
+       dev_info(sz->dev, "%s: callback firing\n", __func__);
+
+       /* finally start accepting data */
+       sz->flush = false;
+}
+
+static void streamzap_delay_timeout(unsigned long arg)
+{
+       struct streamzap_ir *sz = (struct streamzap_ir *)arg;
+       struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
+       unsigned long flags;
+       int len, ret;
+       static unsigned long delay;
+       bool wake = false;
+
+       /* deliver data every 10 ms */
+       delay = msecs_to_jiffies(10);
+
+       spin_lock_irqsave(&sz->timer_lock, flags);
+
+       if (kfifo_len(&sz->fifo) > 0) {
+               ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+               if (ret != sizeof(rawir))
+                       dev_err(sz->dev, "Problem w/kfifo_out...\n");
+               ir_raw_event_store(sz->idev, &rawir);
+               wake = true;
+       }
+
+       len = kfifo_len(&sz->fifo);
+       if (len > 0) {
+               while ((len < SZ_BUF_LEN / 2) &&
+                      (len < SZ_BUF_LEN * sizeof(int))) {
+                       ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+                       if (ret != sizeof(rawir))
+                               dev_err(sz->dev, "Problem w/kfifo_out...\n");
+                       ir_raw_event_store(sz->idev, &rawir);
+                       wake = true;
+                       len = kfifo_len(&sz->fifo);
+               }
+               if (sz->timer_running)
+                       mod_timer(&sz->delay_timer, jiffies + delay);
+
+       } else {
+               sz->timer_running = false;
+       }
+
+       if (wake)
+               ir_raw_event_handle(sz->idev);
+
+       spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static void streamzap_flush_delay_buffer(struct streamzap_ir *sz)
+{
+       struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
+       bool wake = false;
+       int ret;
+
+       while (kfifo_len(&sz->fifo) > 0) {
+               ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+               if (ret != sizeof(rawir))
+                       dev_err(sz->dev, "Problem w/kfifo_out...\n");
+               ir_raw_event_store(sz->idev, &rawir);
+               wake = true;
+       }
+
+       if (wake)
+               ir_raw_event_handle(sz->idev);
+}
+
+static void sz_push(struct streamzap_ir *sz)
+{
+       struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&sz->timer_lock, flags);
+       if (kfifo_len(&sz->fifo) >= sizeof(int) * SZ_BUF_LEN) {
+               ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+               if (ret != sizeof(rawir))
+                       dev_err(sz->dev, "Problem w/kfifo_out...\n");
+               ir_raw_event_store(sz->idev, &rawir);
+       }
+
+       kfifo_in(&sz->fifo, &sz->rawir, sizeof(rawir));
+
+       if (!sz->timer_running) {
+               sz->delay_timer.expires = jiffies + (HZ / 10);
+               add_timer(&sz->delay_timer);
+               sz->timer_running = true;
+       }
+
+       spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static void sz_push_full_pulse(struct streamzap_ir *sz,
+                              unsigned char value)
+{
+       if (sz->idle) {
+               long deltv;
+
+               sz->signal_last = sz->signal_start;
+               do_gettimeofday(&sz->signal_start);
+
+               deltv = sz->signal_start.tv_sec - sz->signal_last.tv_sec;
+               sz->rawir.pulse = false;
+               if (deltv > 15) {
+                       /* really long time */
+                       sz->rawir.duration = IR_MAX_DURATION;
+               } else {
+                       sz->rawir.duration = (int)(deltv * 1000000 +
+                               sz->signal_start.tv_usec -
+                               sz->signal_last.tv_usec);
+                       sz->rawir.duration -= sz->sum;
+                       sz->rawir.duration *= 1000;
+                       sz->rawir.duration &= IR_MAX_DURATION;
+               }
+               dev_dbg(sz->dev, "ls %u\n", sz->rawir.duration);
+               sz_push(sz);
+
+               sz->idle = 0;
+               sz->sum = 0;
+       }
+
+       sz->rawir.pulse = true;
+       sz->rawir.duration = ((int) value) * STREAMZAP_RESOLUTION;
+       sz->rawir.duration += STREAMZAP_RESOLUTION / 2;
+       sz->sum += sz->rawir.duration;
+       sz->rawir.duration *= 1000;
+       sz->rawir.duration &= IR_MAX_DURATION;
+       dev_dbg(sz->dev, "p %u\n", sz->rawir.duration);
+       sz_push(sz);
+}
+
+static void sz_push_half_pulse(struct streamzap_ir *sz,
+                              unsigned char value)
+{
+       sz_push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK) >> 4);
+}
+
+static void sz_push_full_space(struct streamzap_ir *sz,
+                              unsigned char value)
+{
+       sz->rawir.pulse = false;
+       sz->rawir.duration = ((int) value) * STREAMZAP_RESOLUTION;
+       sz->rawir.duration += STREAMZAP_RESOLUTION / 2;
+       sz->sum += sz->rawir.duration;
+       sz->rawir.duration *= 1000;
+       dev_dbg(sz->dev, "s %u\n", sz->rawir.duration);
+       sz_push(sz);
+}
+
+static void sz_push_half_space(struct streamzap_ir *sz,
+                              unsigned long value)
+{
+       sz_push_full_space(sz, value & STREAMZAP_SPACE_MASK);
+}
+
+/**
+ * streamzap_callback - usb IRQ handler callback
+ *
+ * This procedure is invoked on reception of data from
+ * the usb remote.
+ */
+static void streamzap_callback(struct urb *urb)
+{
+       struct streamzap_ir *sz;
+       unsigned int i;
+       int len;
+       #if 0
+       static int timeout = (((STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION) &
+                               IR_MAX_DURATION) | 0x03000000);
+       #endif
+
+       if (!urb)
+               return;
+
+       sz = urb->context;
+       len = urb->actual_length;
+
+       switch (urb->status) {
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               /*
+                * this urb is terminated, clean up.
+                * sz might already be invalid at this point
+                */
+               dev_err(sz->dev, "urb terminated, status: %d\n", urb->status);
+               return;
+       default:
+               break;
+       }
+
+       dev_dbg(sz->dev, "%s: received urb, len %d\n", __func__, len);
+       if (!sz->flush) {
+               for (i = 0; i < urb->actual_length; i++) {
+                       dev_dbg(sz->dev, "%d: %x\n", i,
+                               (unsigned char)sz->buf_in[i]);
+                       switch (sz->decoder_state) {
+                       case PulseSpace:
+                               if ((sz->buf_in[i] & STREAMZAP_PULSE_MASK) ==
+                                   STREAMZAP_PULSE_MASK) {
+                                       sz->decoder_state = FullPulse;
+                                       continue;
+                               } else if ((sz->buf_in[i] & STREAMZAP_SPACE_MASK)
+                                          == STREAMZAP_SPACE_MASK) {
+                                       sz_push_half_pulse(sz, sz->buf_in[i]);
+                                       sz->decoder_state = FullSpace;
+                                       continue;
+                               } else {
+                                       sz_push_half_pulse(sz, sz->buf_in[i]);
+                                       sz_push_half_space(sz, sz->buf_in[i]);
+                               }
+                               break;
+                       case FullPulse:
+                               sz_push_full_pulse(sz, sz->buf_in[i]);
+                               sz->decoder_state = IgnorePulse;
+                               break;
+                       case FullSpace:
+                               if (sz->buf_in[i] == STREAMZAP_TIMEOUT) {
+                                       sz->idle = 1;
+                                       streamzap_stop_timer(sz);
+                                       #if 0
+                                       if (sz->timeout_enabled) {
+                                               sz->rawir.pulse = false;
+                                               sz->rawir.duration = timeout;
+                                               sz->rawir.duration *= 1000;
+                                               sz_push(sz);
+                                       }
+                                       #endif
+                                       streamzap_flush_delay_buffer(sz);
+                               } else
+                                       sz_push_full_space(sz, sz->buf_in[i]);
+                               sz->decoder_state = PulseSpace;
+                               break;
+                       case IgnorePulse:
+                               if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
+                                   STREAMZAP_SPACE_MASK) {
+                                       sz->decoder_state = FullSpace;
+                                       continue;
+                               }
+                               sz_push_half_space(sz, sz->buf_in[i]);
+                               sz->decoder_state = PulseSpace;
+                               break;
+                       }
+               }
+       }
+
+       usb_submit_urb(urb, GFP_ATOMIC);
+
+       return;
+}
+
+static struct input_dev *streamzap_init_input_dev(struct streamzap_ir *sz)
+{
+       struct input_dev *idev;
+       struct ir_dev_props *props;
+       struct device *dev = sz->dev;
+       int ret;
+
+       idev = input_allocate_device();
+       if (!idev) {
+               dev_err(dev, "remote input dev allocation failed\n");
+               goto idev_alloc_failed;
+       }
+
+       props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL);
+       if (!props) {
+               dev_err(dev, "remote ir dev props allocation failed\n");
+               goto props_alloc_failed;
+       }
+
+       snprintf(sz->name, sizeof(sz->name), "Streamzap PC Remote Infrared "
+                "Receiver (%04x:%04x)",
+                le16_to_cpu(sz->usbdev->descriptor.idVendor),
+                le16_to_cpu(sz->usbdev->descriptor.idProduct));
+
+       idev->name = sz->name;
+       usb_make_path(sz->usbdev, sz->phys, sizeof(sz->phys));
+       strlcat(sz->phys, "/input0", sizeof(sz->phys));
+       idev->phys = sz->phys;
+
+       props->priv = sz;
+       props->driver_type = RC_DRIVER_IR_RAW;
+       /* FIXME: not sure about supported protocols, check on this */
+       props->allowed_protos = IR_TYPE_RC5 | IR_TYPE_RC6;
+
+       sz->props = props;
+
+       ret = ir_input_register(idev, RC_MAP_RC5_STREAMZAP, props, DRIVER_NAME);
+       if (ret < 0) {
+               dev_err(dev, "remote input device register failed\n");
+               goto irdev_failed;
+       }
+
+       return idev;
+
+irdev_failed:
+       kfree(props);
+props_alloc_failed:
+       input_free_device(idev);
+idev_alloc_failed:
+       return NULL;
+}
+
+static int streamzap_delay_buf_init(struct streamzap_ir *sz)
+{
+       int ret;
+
+       ret = kfifo_alloc(&sz->fifo, sizeof(int) * SZ_BUF_LEN,
+                         GFP_KERNEL);
+       if (ret == 0)
+               sz->fifo_initialized = 1;
+
+       return ret;
+}
+
+static void streamzap_start_flush_timer(struct streamzap_ir *sz)
+{
+       sz->flush_timer.expires = jiffies + HZ;
+       sz->flush = true;
+       add_timer(&sz->flush_timer);
+
+       sz->urb_in->dev = sz->usbdev;
+       if (usb_submit_urb(sz->urb_in, GFP_ATOMIC))
+               dev_err(sz->dev, "urb submit failed\n");
+}
+
+/**
+ *     streamzap_probe
+ *
+ *     Called by usb-core to associated with a candidate device
+ *     On any failure the return value is the ERROR
+ *     On success return 0
+ */
+static int __devinit streamzap_probe(struct usb_interface *intf,
+                                    const struct usb_device_id *id)
+{
+       struct usb_device *usbdev = interface_to_usbdev(intf);
+       struct usb_host_interface *iface_host;
+       struct streamzap_ir *sz = NULL;
+       char buf[63], name[128] = "";
+       int retval = -ENOMEM;
+       int pipe, maxp;
+
+       /* Allocate space for device driver specific data */
+       sz = kzalloc(sizeof(struct streamzap_ir), GFP_KERNEL);
+       if (!sz)
+               return -ENOMEM;
+
+       sz->usbdev = usbdev;
+       sz->interface = intf;
+
+       /* Check to ensure endpoint information matches requirements */
+       iface_host = intf->cur_altsetting;
+
+       if (iface_host->desc.bNumEndpoints != 1) {
+               dev_err(&intf->dev, "%s: Unexpected desc.bNumEndpoints (%d)\n",
+                       __func__, iface_host->desc.bNumEndpoints);
+               retval = -ENODEV;
+               goto free_sz;
+       }
+
+       sz->endpoint = &(iface_host->endpoint[0].desc);
+       if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+           != USB_DIR_IN) {
+               dev_err(&intf->dev, "%s: endpoint doesn't match input device "
+                       "02%02x\n", __func__, sz->endpoint->bEndpointAddress);
+               retval = -ENODEV;
+               goto free_sz;
+       }
+
+       if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+           != USB_ENDPOINT_XFER_INT) {
+               dev_err(&intf->dev, "%s: endpoint attributes don't match xfer "
+                       "02%02x\n", __func__, sz->endpoint->bmAttributes);
+               retval = -ENODEV;
+               goto free_sz;
+       }
+
+       pipe = usb_rcvintpipe(usbdev, sz->endpoint->bEndpointAddress);
+       maxp = usb_maxpacket(usbdev, pipe, usb_pipeout(pipe));
+
+       if (maxp == 0) {
+               dev_err(&intf->dev, "%s: endpoint Max Packet Size is 0!?!\n",
+                       __func__);
+               retval = -ENODEV;
+               goto free_sz;
+       }
+
+       /* Allocate the USB buffer and IRQ URB */
+       sz->buf_in = usb_alloc_coherent(usbdev, maxp, GFP_ATOMIC, &sz->dma_in);
+       if (!sz->buf_in)
+               goto free_sz;
+
+       sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+       if (!sz->urb_in)
+               goto free_buf_in;
+
+       sz->dev = &intf->dev;
+       sz->buf_in_len = maxp;
+
+       if (usbdev->descriptor.iManufacturer
+           && usb_string(usbdev, usbdev->descriptor.iManufacturer,
+                         buf, sizeof(buf)) > 0)
+               strlcpy(name, buf, sizeof(name));
+
+       if (usbdev->descriptor.iProduct
+           && usb_string(usbdev, usbdev->descriptor.iProduct,
+                         buf, sizeof(buf)) > 0)
+               snprintf(name + strlen(name), sizeof(name) - strlen(name),
+                        " %s", buf);
+
+       retval = streamzap_delay_buf_init(sz);
+       if (retval) {
+               dev_err(&intf->dev, "%s: delay buffer init failed\n", __func__);
+               goto free_urb_in;
+       }
+
+       sz->idev = streamzap_init_input_dev(sz);
+       if (!sz->idev)
+               goto input_dev_fail;
+
+       sz->idle = true;
+       sz->decoder_state = PulseSpace;
+       #if 0
+       /* not yet supported, depends on patches from maxim */
+       /* see also: LIRC_GET_REC_RESOLUTION and LIRC_SET_REC_TIMEOUT */
+       sz->timeout_enabled = false;
+       sz->min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION * 1000;
+       sz->max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION * 1000;
+       #endif
+
+       init_timer(&sz->delay_timer);
+       sz->delay_timer.function = streamzap_delay_timeout;
+       sz->delay_timer.data = (unsigned long)sz;
+       spin_lock_init(&sz->timer_lock);
+
+       init_timer(&sz->flush_timer);
+       sz->flush_timer.function = streamzap_flush_timeout;
+       sz->flush_timer.data = (unsigned long)sz;
+
+       do_gettimeofday(&sz->signal_start);
+
+       /* Complete final initialisations */
+       usb_fill_int_urb(sz->urb_in, usbdev, pipe, sz->buf_in,
+                        maxp, (usb_complete_t)streamzap_callback,
+                        sz, sz->endpoint->bInterval);
+       sz->urb_in->transfer_dma = sz->dma_in;
+       sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+       usb_set_intfdata(intf, sz);
+
+       streamzap_start_flush_timer(sz);
+
+       dev_info(sz->dev, "Registered %s on usb%d:%d\n", name,
+                usbdev->bus->busnum, usbdev->devnum);
+
+       return 0;
+
+input_dev_fail:
+       kfifo_free(&sz->fifo);
+free_urb_in:
+       usb_free_urb(sz->urb_in);
+free_buf_in:
+       usb_free_coherent(usbdev, maxp, sz->buf_in, sz->dma_in);
+free_sz:
+       kfree(sz);
+
+       return retval;
+}
+
+/**
+ * streamzap_disconnect
+ *
+ * Called by the usb core when the device is removed from the system.
+ *
+ * This routine guarantees that the driver will not submit any more urbs
+ * by clearing dev->usbdev.  It is also supposed to terminate any currently
+ * active urbs.  Unfortunately, usb_bulk_msg(), used in streamzap_read(),
+ * does not provide any way to do this.
+ */
+static void streamzap_disconnect(struct usb_interface *interface)
+{
+       struct streamzap_ir *sz = usb_get_intfdata(interface);
+       struct usb_device *usbdev = interface_to_usbdev(interface);
+
+       usb_set_intfdata(interface, NULL);
+
+       if (!sz)
+               return;
+
+       if (sz->flush) {
+               sz->flush = false;
+               del_timer_sync(&sz->flush_timer);
+       }
+
+       streamzap_stop_timer(sz);
+
+       sz->usbdev = NULL;
+       ir_input_unregister(sz->idev);
+       usb_kill_urb(sz->urb_in);
+       usb_free_urb(sz->urb_in);
+       usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in);
+
+       kfree(sz);
+}
+
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct streamzap_ir *sz = usb_get_intfdata(intf);
+
+       if (sz->flush) {
+               sz->flush = false;
+               del_timer_sync(&sz->flush_timer);
+       }
+
+       streamzap_stop_timer(sz);
+
+       usb_kill_urb(sz->urb_in);
+
+       return 0;
+}
+
+static int streamzap_resume(struct usb_interface *intf)
+{
+       struct streamzap_ir *sz = usb_get_intfdata(intf);
+
+       if (sz->fifo_initialized)
+               kfifo_reset(&sz->fifo);
+
+       sz->flush_timer.expires = jiffies + HZ;
+       sz->flush = true;
+       add_timer(&sz->flush_timer);
+
+       if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
+               dev_err(sz->dev, "Error sumbiting urb\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/**
+ *     streamzap_init
+ */
+static int __init streamzap_init(void)
+{
+       int ret;
+
+       /* register this driver with the USB subsystem */
+       ret = usb_register(&streamzap_driver);
+       if (ret < 0)
+               printk(KERN_ERR DRIVER_NAME ": usb register failed, "
+                      "result = %d\n", ret);
+
+       return ret;
+}
+
+/**
+ *     streamzap_exit
+ */
+static void __exit streamzap_exit(void)
+{
+       usb_deregister(&streamzap_driver);
+}
+
+
+module_init(streamzap_init);
+module_exit(streamzap_exit);
+
+MODULE_AUTHOR("Jarod Wilson <jarod@wilsonet.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
index 409a4261e5b5fa1c89f63e668499c1b7b5e1cdc8..b3ed5daaacf27411b47d22b8c24d46b580d8cd92 100644 (file)
@@ -34,7 +34,7 @@ config MEDIA_TUNER
 menuconfig MEDIA_TUNER_CUSTOMISE
        bool "Customize analog and hybrid tuner modules to build"
        depends on MEDIA_TUNER
-       default n
+       default y if EMBEDDED
        help
          This allows the user to deselect tuner drivers unnecessary
          for their hardware from the build. Use this option with care
index 248a2a9d8416437a6de74aa6ea1d5443e33790cd..caa4e18ed1c188d4d20575f184f2bf67572358ff 100644 (file)
@@ -1763,7 +1763,15 @@ static struct dvb_frontend_ops dst_dvbt_ops = {
                .frequency_min = 137000000,
                .frequency_max = 858000000,
                .frequency_stepsize = 166667,
-               .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
+               .caps = FE_CAN_FEC_AUTO                 |
+                       FE_CAN_QAM_AUTO                 |
+                       FE_CAN_QAM_16                   |
+                       FE_CAN_QAM_32                   |
+                       FE_CAN_QAM_64                   |
+                       FE_CAN_QAM_128                  |
+                       FE_CAN_QAM_256                  |
+                       FE_CAN_TRANSMISSION_MODE_AUTO   |
+                       FE_CAN_GUARD_INTERVAL_AUTO
        },
 
        .release = dst_release,
index 51d578a758a7cc90a4ef624d79277826071977a0..b5f6a04f9c127c0bb1f1d8c4d2d23d31d5b99070 100644 (file)
@@ -1,7 +1,7 @@
 config DVB_FE_CUSTOMISE
        bool "Customise the frontend modules to build"
        depends on DVB_CORE
-       default N
+       default y if EMBEDDED
        help
          This allows the user to select/deselect frontend drivers for their
          hardware from the build.
index 7f2c94a15ab14612c8239efc3933f6ea3597dac2..d93468cd3a85e1a5a3d8eee586c7ec29b69d3051 100644 (file)
@@ -1113,9 +1113,11 @@ struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev)
         */
 
        prepare_to_wait(&coredev->buffer_mng_waitq, &wait, TASK_INTERRUPTIBLE);
-
-       if (list_empty(&coredev->buffers))
+       if (list_empty(&coredev->buffers)) {
+               spin_unlock_irqrestore(&coredev->bufferslock, flags);
                schedule();
+               spin_lock_irqsave(&coredev->bufferslock, flags);
+       }
 
        finish_wait(&coredev->buffer_mng_waitq, &wait);
 
index 2e15903b976d79064b53a22e3cbc4210a7fb55cf..f6e4d04753510baee5b16bc294f9894bc111390e 100644 (file)
@@ -83,7 +83,7 @@ config VIDEO_FIXED_MINOR_RANGES
 
 config VIDEO_HELPER_CHIPS_AUTO
        bool "Autoselect pertinent encoders/decoders and other helper chips"
-       default y
+       default y if !EMBEDDED
        ---help---
          Most video cards may require additional modules to encode or
          decode audio/video standards. This option will autoselect
@@ -792,10 +792,11 @@ config SOC_CAMERA_MT9M001
          and colour models.
 
 config SOC_CAMERA_MT9M111
-       tristate "mt9m111 and mt9m112 support"
+       tristate "mt9m111, mt9m112 and mt9m131 support"
        depends on SOC_CAMERA && I2C
        help
-         This driver supports MT9M111 and MT9M112 cameras from Micron
+         This driver supports MT9M111, MT9M112 and MT9M131 cameras from
+         Micron/Aptina
 
 config SOC_CAMERA_MT9T031
        tristate "mt9t031 support"
@@ -1016,4 +1017,13 @@ config VIDEO_MEM2MEM_TESTDEV
          This is a virtual test device for the memory-to-memory driver
          framework.
 
+config  VIDEO_SAMSUNG_S5P_FIMC
+       tristate "Samsung S5P FIMC (video postprocessor) driver"
+       depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
+       select VIDEOBUF_DMA_CONTIG
+       select V4L2_MEM2MEM_DEV
+       help
+         This is a v4l2 driver for the S5P camera interface
+         (video postprocessor)
+
 endif # V4L_MEM2MEM_DRIVERS
index 1051ecc602e74c07bde2e4abb55c70ca414c5494..40f98fba5f88f5c503ad66447c114df75af00901 100644 (file)
@@ -11,7 +11,7 @@ stkwebcam-objs        :=      stk-webcam.o stk-sensor.o
 omap2cam-objs  :=      omap24xxcam.o omap24xxcam-dma.o
 
 videodev-objs  :=      v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
-                       v4l2-event.o
+                       v4l2-event.o v4l2-ctrls.o
 
 # V4L2 core modules
 
@@ -163,6 +163,7 @@ obj-$(CONFIG_VIDEO_MX3)                     += mx3_camera.o
 obj-$(CONFIG_VIDEO_PXA27x)             += pxa_camera.o
 obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2)     += sh_mobile_csi2.o
 obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)      += sh_mobile_ceu_camera.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC)   += s5p-fimc/
 
 obj-$(CONFIG_ARCH_DAVINCI)             += davinci/
 
index 3cc135a98d827d624c35fda62020857d8da7abc1..cc9e84d75ea7224b06a6da739d34d7b9c5b7cffe 100644 (file)
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
@@ -43,6 +43,21 @@ module_param(debug, bool, 0644);
 MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On");
 
 
+struct cs53l32a_state {
+       struct v4l2_subdev sd;
+       struct v4l2_ctrl_handler hdl;
+};
+
+static inline struct cs53l32a_state *to_state(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct cs53l32a_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+       return &container_of(ctrl->handler, struct cs53l32a_state, hdl)->sd;
+}
+
 /* ----------------------------------------------------------------------- */
 
 static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value)
@@ -74,31 +89,20 @@ static int cs53l32a_s_routing(struct v4l2_subdev *sd,
        return 0;
 }
 
-static int cs53l32a_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int cs53l32a_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
-               ctrl->value = (cs53l32a_read(sd, 0x03) & 0xc0) != 0;
-               return 0;
-       }
-       if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
-               return -EINVAL;
-       ctrl->value = (s8)cs53l32a_read(sd, 0x04);
-       return 0;
-}
+       struct v4l2_subdev *sd = to_sd(ctrl);
 
-static int cs53l32a_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
-               cs53l32a_write(sd, 0x03, ctrl->value ? 0xf0 : 0x30);
+       switch (ctrl->id) {
+       case V4L2_CID_AUDIO_MUTE:
+               cs53l32a_write(sd, 0x03, ctrl->val ? 0xf0 : 0x30);
+               return 0;
+       case V4L2_CID_AUDIO_VOLUME:
+               cs53l32a_write(sd, 0x04, (u8)ctrl->val);
+               cs53l32a_write(sd, 0x05, (u8)ctrl->val);
                return 0;
        }
-       if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
-               return -EINVAL;
-       if (ctrl->value > 12 || ctrl->value < -96)
-               return -EINVAL;
-       cs53l32a_write(sd, 0x04, (u8) ctrl->value);
-       cs53l32a_write(sd, 0x05, (u8) ctrl->value);
-       return 0;
+       return -EINVAL;
 }
 
 static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
@@ -111,23 +115,30 @@ static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_id
 
 static int cs53l32a_log_status(struct v4l2_subdev *sd)
 {
+       struct cs53l32a_state *state = to_state(sd);
        u8 v = cs53l32a_read(sd, 0x01);
-       u8 m = cs53l32a_read(sd, 0x03);
-       s8 vol = cs53l32a_read(sd, 0x04);
 
-       v4l2_info(sd, "Input:  %d%s\n", (v >> 4) & 3,
-                       (m & 0xC0) ? " (muted)" : "");
-       v4l2_info(sd, "Volume: %d dB\n", vol);
+       v4l2_info(sd, "Input:  %d\n", (v >> 4) & 3);
+       v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
        return 0;
 }
 
 /* ----------------------------------------------------------------------- */
 
+static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = {
+       .s_ctrl = cs53l32a_s_ctrl,
+};
+
 static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
        .log_status = cs53l32a_log_status,
        .g_chip_ident = cs53l32a_g_chip_ident,
-       .g_ctrl = cs53l32a_g_ctrl,
-       .s_ctrl = cs53l32a_s_ctrl,
+       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+       .g_ctrl = v4l2_subdev_g_ctrl,
+       .s_ctrl = v4l2_subdev_s_ctrl,
+       .queryctrl = v4l2_subdev_queryctrl,
+       .querymenu = v4l2_subdev_querymenu,
 };
 
 static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
@@ -151,6 +162,7 @@ static const struct v4l2_subdev_ops cs53l32a_ops = {
 static int cs53l32a_probe(struct i2c_client *client,
                          const struct i2c_device_id *id)
 {
+       struct cs53l32a_state *state;
        struct v4l2_subdev *sd;
        int i;
 
@@ -164,9 +176,10 @@ static int cs53l32a_probe(struct i2c_client *client,
        v4l_info(client, "chip found @ 0x%x (%s)\n",
                        client->addr << 1, client->adapter->name);
 
-       sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
-       if (sd == NULL)
+       state = kzalloc(sizeof(struct cs53l32a_state), GFP_KERNEL);
+       if (state == NULL)
                return -ENOMEM;
+       sd = &state->sd;
        v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops);
 
        for (i = 1; i <= 7; i++) {
@@ -175,15 +188,29 @@ static int cs53l32a_probe(struct i2c_client *client,
                v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
        }
 
+       v4l2_ctrl_handler_init(&state->hdl, 2);
+       v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
+                       V4L2_CID_AUDIO_VOLUME, -96, 12, 1, 0);
+       v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
+                       V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+       sd->ctrl_handler = &state->hdl;
+       if (state->hdl.error) {
+               int err = state->hdl.error;
+
+               v4l2_ctrl_handler_free(&state->hdl);
+               kfree(state);
+               return err;
+       }
+
        /* Set cs53l32a internal register for Adaptec 2010/2410 setup */
 
-       cs53l32a_write(sd, 0x01, (u8) 0x21);
-       cs53l32a_write(sd, 0x02, (u8) 0x29);
-       cs53l32a_write(sd, 0x03, (u8) 0x30);
-       cs53l32a_write(sd, 0x04, (u8) 0x00);
-       cs53l32a_write(sd, 0x05, (u8) 0x00);
-       cs53l32a_write(sd, 0x06, (u8) 0x00);
-       cs53l32a_write(sd, 0x07, (u8) 0x00);
+       cs53l32a_write(sd, 0x01, 0x21);
+       cs53l32a_write(sd, 0x02, 0x29);
+       cs53l32a_write(sd, 0x03, 0x30);
+       cs53l32a_write(sd, 0x04, 0x00);
+       cs53l32a_write(sd, 0x05, 0x00);
+       cs53l32a_write(sd, 0x06, 0x00);
+       cs53l32a_write(sd, 0x07, 0x00);
 
        /* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */
 
@@ -198,9 +225,11 @@ static int cs53l32a_probe(struct i2c_client *client,
 static int cs53l32a_remove(struct i2c_client *client)
 {
        struct v4l2_subdev *sd = i2c_get_clientdata(client);
+       struct cs53l32a_state *state = to_state(sd);
 
        v4l2_device_unregister_subdev(sd);
-       kfree(sd);
+       v4l2_ctrl_handler_free(&state->hdl);
+       kfree(state);
        return 0;
 }
 
index 2bf44ef10fec2dfb870971535b6f21454ce609b9..e5c3c8da4be365bd1fb96d296297189b89e370d6 100644 (file)
@@ -38,6 +38,145 @@ static int debug;
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Debug level (0-1)");
 
+/********************** COMMON CODE *********************/
+
+/* definitions for audio properties bits 29-28 */
+#define CX2341X_AUDIO_ENCODING_METHOD_MPEG     0
+#define CX2341X_AUDIO_ENCODING_METHOD_AC3      1
+#define CX2341X_AUDIO_ENCODING_METHOD_LPCM     2
+
+static const char *cx2341x_get_name(u32 id)
+{
+       switch (id) {
+       case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+               return "Spatial Filter Mode";
+       case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+               return "Spatial Filter";
+       case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+               return "Spatial Luma Filter Type";
+       case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+               return "Spatial Chroma Filter Type";
+       case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+               return "Temporal Filter Mode";
+       case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+               return "Temporal Filter";
+       case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+               return "Median Filter Type";
+       case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+               return "Median Luma Filter Maximum";
+       case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+               return "Median Luma Filter Minimum";
+       case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+               return "Median Chroma Filter Maximum";
+       case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+               return "Median Chroma Filter Minimum";
+       case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+               return "Insert Navigation Packets";
+       }
+       return NULL;
+}
+
+static const char **cx2341x_get_menu(u32 id)
+{
+       static const char *cx2341x_video_spatial_filter_mode_menu[] = {
+               "Manual",
+               "Auto",
+               NULL
+       };
+
+       static const char *cx2341x_video_luma_spatial_filter_type_menu[] = {
+               "Off",
+               "1D Horizontal",
+               "1D Vertical",
+               "2D H/V Separable",
+               "2D Symmetric non-separable",
+               NULL
+       };
+
+       static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = {
+               "Off",
+               "1D Horizontal",
+               NULL
+       };
+
+       static const char *cx2341x_video_temporal_filter_mode_menu[] = {
+               "Manual",
+               "Auto",
+               NULL
+       };
+
+       static const char *cx2341x_video_median_filter_type_menu[] = {
+               "Off",
+               "Horizontal",
+               "Vertical",
+               "Horizontal/Vertical",
+               "Diagonal",
+               NULL
+       };
+
+       switch (id) {
+       case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+               return cx2341x_video_spatial_filter_mode_menu;
+       case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+               return cx2341x_video_luma_spatial_filter_type_menu;
+       case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+               return cx2341x_video_chroma_spatial_filter_type_menu;
+       case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+               return cx2341x_video_temporal_filter_mode_menu;
+       case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+               return cx2341x_video_median_filter_type_menu;
+       }
+       return NULL;
+}
+
+static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
+                   s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
+{
+       *name = cx2341x_get_name(id);
+       *flags = 0;
+
+       switch (id) {
+       case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+       case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+       case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+       case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+       case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+               *type = V4L2_CTRL_TYPE_MENU;
+               *min = 0;
+               *step = 0;
+               break;
+       case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+               *type = V4L2_CTRL_TYPE_BOOLEAN;
+               *min = 0;
+               *max = *step = 1;
+               break;
+       default:
+               *type = V4L2_CTRL_TYPE_INTEGER;
+               break;
+       }
+       switch (id) {
+       case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+       case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+       case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+               *flags |= V4L2_CTRL_FLAG_UPDATE;
+               break;
+       case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+       case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+       case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+       case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+       case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+       case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+               *flags |= V4L2_CTRL_FLAG_SLIDER;
+               break;
+       case V4L2_CID_MPEG_VIDEO_ENCODING:
+               *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+               break;
+       }
+}
+
+
+/********************** OLD CODE *********************/
+
 /* Must be sorted from low to high control ID! */
 const u32 cx2341x_mpeg_ctrls[] = {
        V4L2_CID_MPEG_CLASS,
@@ -134,8 +273,6 @@ static const struct cx2341x_mpeg_params default_params = {
        .video_chroma_median_filter_top = 255,
        .video_chroma_median_filter_bottom = 0,
 };
-
-
 /* Map the control ID to the correct field in the cx2341x_mpeg_params
    struct. Return -EINVAL if the ID is unknown, else return 0. */
 static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params,
@@ -415,83 +552,33 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl,
 {
        const char *name;
 
-       qctrl->flags = 0;
        switch (qctrl->id) {
        /* MPEG controls */
        case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
-               name = "Spatial Filter Mode";
-               break;
        case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
-               name = "Spatial Filter";
-               break;
        case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
-               name = "Spatial Luma Filter Type";
-               break;
        case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
-               name = "Spatial Chroma Filter Type";
-               break;
        case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
-               name = "Temporal Filter Mode";
-               break;
        case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
-               name = "Temporal Filter";
-               break;
        case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
-               name = "Median Filter Type";
-               break;
        case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
-               name = "Median Luma Filter Maximum";
-               break;
        case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
-               name = "Median Luma Filter Minimum";
-               break;
        case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
-               name = "Median Chroma Filter Maximum";
-               break;
        case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
-               name = "Median Chroma Filter Minimum";
-               break;
        case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
-               name = "Insert Navigation Packets";
-               break;
+               cx2341x_ctrl_fill(qctrl->id, &name, &qctrl->type,
+                               &min, &max, &step, &def, &qctrl->flags);
+               qctrl->minimum = min;
+               qctrl->maximum = max;
+               qctrl->step = step;
+               qctrl->default_value = def;
+               qctrl->reserved[0] = qctrl->reserved[1] = 0;
+               strlcpy(qctrl->name, name, sizeof(qctrl->name));
+               return 0;
 
        default:
                return v4l2_ctrl_query_fill(qctrl, min, max, step, def);
        }
-       switch (qctrl->id) {
-       case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
-       case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
-       case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
-       case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
-       case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
-               qctrl->type = V4L2_CTRL_TYPE_MENU;
-               min = 0;
-               step = 1;
-               break;
-       case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
-               qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
-               min = 0;
-               max = 1;
-               step = 1;
-               break;
-       default:
-               qctrl->type = V4L2_CTRL_TYPE_INTEGER;
-               break;
-       }
-       switch (qctrl->id) {
-       case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
-       case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
-       case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
-               qctrl->flags |= V4L2_CTRL_FLAG_UPDATE;
-               break;
-       }
-       qctrl->minimum = min;
-       qctrl->maximum = max;
-       qctrl->step = step;
-       qctrl->default_value = def;
-       qctrl->reserved[0] = qctrl->reserved[1] = 0;
-       snprintf(qctrl->name, sizeof(qctrl->name), name);
-       return 0;
 }
 
 int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params,
@@ -797,42 +884,6 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id)
                NULL
        };
 
-       static const char *cx2341x_video_spatial_filter_mode_menu[] = {
-               "Manual",
-               "Auto",
-               NULL
-       };
-
-       static const char *cx2341x_video_luma_spatial_filter_type_menu[] = {
-               "Off",
-               "1D Horizontal",
-               "1D Vertical",
-               "2D H/V Separable",
-               "2D Symmetric non-separable",
-               NULL
-       };
-
-       static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = {
-               "Off",
-               "1D Horizontal",
-               NULL
-       };
-
-       static const char *cx2341x_video_temporal_filter_mode_menu[] = {
-               "Manual",
-               "Auto",
-               NULL
-       };
-
-       static const char *cx2341x_video_median_filter_type_menu[] = {
-               "Off",
-               "Horizontal",
-               "Vertical",
-               "Horizontal/Vertical",
-               "Diagonal",
-               NULL
-       };
-
        switch (id) {
        case V4L2_CID_MPEG_STREAM_TYPE:
                return (p->capabilities & CX2341X_CAP_HAS_TS) ?
@@ -844,26 +895,17 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id)
        case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
                return NULL;
        case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
-               return cx2341x_video_spatial_filter_mode_menu;
        case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
-               return cx2341x_video_luma_spatial_filter_type_menu;
        case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
-               return cx2341x_video_chroma_spatial_filter_type_menu;
        case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
-               return cx2341x_video_temporal_filter_mode_menu;
        case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
-               return cx2341x_video_median_filter_type_menu;
+               return cx2341x_get_menu(id);
        default:
                return v4l2_ctrl_get_menu(id);
        }
 }
 EXPORT_SYMBOL(cx2341x_ctrl_get_menu);
 
-/* definitions for audio properties bits 29-28 */
-#define CX2341X_AUDIO_ENCODING_METHOD_MPEG     0
-#define CX2341X_AUDIO_ENCODING_METHOD_AC3      1
-#define CX2341X_AUDIO_ENCODING_METHOD_LPCM     2
-
 static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
 {
        params->audio_properties =
@@ -1195,9 +1237,490 @@ void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix)
 }
 EXPORT_SYMBOL(cx2341x_log_status);
 
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
 
+
+/********************** NEW CODE *********************/
+
+static inline struct cx2341x_handler *to_cxhdl(struct v4l2_ctrl *ctrl)
+{
+       return container_of(ctrl->handler, struct cx2341x_handler, hdl);
+}
+
+static int cx2341x_hdl_api(struct cx2341x_handler *hdl,
+                      u32 cmd, int args, ...)
+{
+       u32 data[CX2341X_MBOX_MAX_DATA];
+       va_list vargs;
+       int i;
+
+       va_start(vargs, args);
+
+       for (i = 0; i < args; i++)
+               data[i] = va_arg(vargs, int);
+       va_end(vargs);
+       return hdl->func(hdl->priv, cmd, args, 0, data);
+}
+
+/* ctrl->handler->lock is held, so it is safe to access cur.val */
+static inline int cx2341x_neq(struct v4l2_ctrl *ctrl)
+{
+       return ctrl && ctrl->val != ctrl->cur.val;
+}
+
+static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct cx2341x_handler *hdl = to_cxhdl(ctrl);
+       s32 val = ctrl->val;
+
+       switch (ctrl->id) {
+       case V4L2_CID_MPEG_VIDEO_B_FRAMES: {
+               /* video gop cluster */
+               int b = val + 1;
+               int gop = hdl->video_gop_size->val;
+
+               gop = b * ((gop + b - 1) / b);
+
+               /* Max GOP size = 34 */
+               while (gop > 34)
+                       gop -= b;
+               hdl->video_gop_size->val = gop;
+               break;
+       }
+
+       case V4L2_CID_MPEG_STREAM_TYPE:
+               /* stream type cluster */
+               hdl->video_encoding->val =
+                   (hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_SS ||
+                    hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ?
+                       V4L2_MPEG_VIDEO_ENCODING_MPEG_1 :
+                       V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+               if (hdl->video_encoding->val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+                       /* MPEG-1 implies CBR */
+                       hdl->video_bitrate_mode->val =
+                               V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+               /* peak bitrate shall be >= normal bitrate */
+               if (hdl->video_bitrate_mode->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+                   hdl->video_bitrate_peak->val < hdl->video_bitrate->val)
+                       hdl->video_bitrate_peak->val = hdl->video_bitrate->val;
+               break;
+       }
+       return 0;
+}
+
+static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       static const int mpeg_stream_type[] = {
+               0,      /* MPEG-2 PS */
+               1,      /* MPEG-2 TS */
+               2,      /* MPEG-1 SS */
+               14,     /* DVD */
+               11,     /* VCD */
+               12,     /* SVCD */
+       };
+       struct cx2341x_handler *hdl = to_cxhdl(ctrl);
+       s32 val = ctrl->val;
+       u32 props;
+       int err;
+
+       switch (ctrl->id) {
+       case V4L2_CID_MPEG_STREAM_VBI_FMT:
+               if (hdl->ops && hdl->ops->s_stream_vbi_fmt)
+                       return hdl->ops->s_stream_vbi_fmt(hdl, val);
+               return 0;
+
+       case V4L2_CID_MPEG_VIDEO_ASPECT:
+               return cx2341x_hdl_api(hdl,
+                       CX2341X_ENC_SET_ASPECT_RATIO, 1, val + 1);
+
+       case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+               return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_CLOSURE, 1, val);
+
+       case V4L2_CID_MPEG_AUDIO_MUTE:
+               return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_AUDIO, 1, val);
+
+       case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
+               return cx2341x_hdl_api(hdl,
+                       CX2341X_ENC_SET_FRAME_DROP_RATE, 1, val);
+
+       case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+               return cx2341x_hdl_api(hdl, CX2341X_ENC_MISC, 2, 7, val);
+
+       case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+               /* audio properties cluster */
+               props = (hdl->audio_sampling_freq->val << 0) |
+                       (hdl->audio_mode->val << 8) |
+                       (hdl->audio_mode_extension->val << 10) |
+                       (hdl->audio_crc->val << 14);
+               if (hdl->audio_emphasis->val == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17)
+                       props |= 3 << 12;
+               else
+                       props |= hdl->audio_emphasis->val << 12;
+
+               if (hdl->audio_encoding->val == V4L2_MPEG_AUDIO_ENCODING_AC3) {
+                       props |=
+#if 1
+                               /* Not sure if this MPEG Layer II setting is required */
+                               ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) |
+#endif
+                               (hdl->audio_ac3_bitrate->val << 4) |
+                               (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28);
+               } else {
+                       /* Assuming MPEG Layer II */
+                       props |=
+                               ((3 - hdl->audio_encoding->val) << 2) |
+                               ((1 + hdl->audio_l2_bitrate->val) << 4);
+               }
+               err = cx2341x_hdl_api(hdl,
+                               CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, props);
+               if (err)
+                       return err;
+
+               hdl->audio_properties = props;
+               if (hdl->audio_ac3_bitrate) {
+                       int is_ac3 = hdl->audio_encoding->val ==
+                                               V4L2_MPEG_AUDIO_ENCODING_AC3;
+
+                       v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3);
+                       v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3);
+               }
+               v4l2_ctrl_activate(hdl->audio_mode_extension,
+                       hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO);
+               if (cx2341x_neq(hdl->audio_sampling_freq) &&
+                   hdl->ops && hdl->ops->s_audio_sampling_freq)
+                       return hdl->ops->s_audio_sampling_freq(hdl, hdl->audio_sampling_freq->val);
+               if (cx2341x_neq(hdl->audio_mode) &&
+                   hdl->ops && hdl->ops->s_audio_mode)
+                       return hdl->ops->s_audio_mode(hdl, hdl->audio_mode->val);
+               return 0;
+
+       case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+               /* video gop cluster */
+               return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_PROPERTIES, 2,
+                               hdl->video_gop_size->val,
+                               hdl->video_b_frames->val + 1);
+
+       case V4L2_CID_MPEG_STREAM_TYPE:
+               /* stream type cluster */
+               err = cx2341x_hdl_api(hdl,
+                       CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[val]);
+               if (err)
+                       return err;
+
+               err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_BIT_RATE, 5,
+                               hdl->video_bitrate_mode->val,
+                               hdl->video_bitrate->val,
+                               hdl->video_bitrate_peak->val / 400, 0, 0);
+               if (err)
+                       return err;
+
+               v4l2_ctrl_activate(hdl->video_bitrate_mode,
+                       hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1);
+               v4l2_ctrl_activate(hdl->video_bitrate_peak,
+                       hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+               if (cx2341x_neq(hdl->video_encoding) &&
+                   hdl->ops && hdl->ops->s_video_encoding)
+                       return hdl->ops->s_video_encoding(hdl, hdl->video_encoding->val);
+               return 0;
+
+       case V4L2_CID_MPEG_VIDEO_MUTE:
+               /* video mute cluster */
+               return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_VIDEO, 1,
+                               hdl->video_mute->val |
+                                       (hdl->video_mute_yuv->val << 8));
+
+       case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: {
+               int active_filter;
+
+               /* video filter mode */
+               err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_MODE, 2,
+                               hdl->video_spatial_filter_mode->val |
+                                       (hdl->video_temporal_filter_mode->val << 1),
+                               hdl->video_median_filter_type->val);
+               if (err)
+                       return err;
+
+               active_filter = hdl->video_spatial_filter_mode->val !=
+                               V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO;
+               v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter);
+               v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter);
+               v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter);
+               active_filter = hdl->video_temporal_filter_mode->val !=
+                               V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO;
+               v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter);
+               active_filter = hdl->video_median_filter_type->val !=
+                               V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF;
+               v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter);
+               v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter);
+               v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter);
+               v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter);
+               return 0;
+       }
+
+       case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+               /* video filter type cluster */
+               return cx2341x_hdl_api(hdl,
+                               CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2,
+                               hdl->video_luma_spatial_filter_type->val,
+                               hdl->video_chroma_spatial_filter_type->val);
+
+       case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+               /* video filter cluster */
+               return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2,
+                               hdl->video_spatial_filter->val,
+                               hdl->video_temporal_filter->val);
+
+       case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+               /* video median cluster */
+               return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_CORING_LEVELS, 4,
+                               hdl->video_luma_median_filter_bottom->val,
+                               hdl->video_luma_median_filter_top->val,
+                               hdl->video_chroma_median_filter_bottom->val,
+                               hdl->video_chroma_median_filter_top->val);
+       }
+       return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops cx2341x_ops = {
+       .try_ctrl = cx2341x_try_ctrl,
+       .s_ctrl = cx2341x_s_ctrl,
+};
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+                       u32 id, s32 min, s32 max, s32 step, s32 def)
+{
+       struct v4l2_ctrl_config cfg;
+
+       cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags);
+       cfg.ops = &cx2341x_ops;
+       cfg.id = id;
+       cfg.min = min;
+       cfg.max = max;
+       cfg.def = def;
+       if (cfg.type == V4L2_CTRL_TYPE_MENU) {
+               cfg.step = 0;
+               cfg.menu_skip_mask = step;
+               cfg.qmenu = cx2341x_get_menu(id);
+       } else {
+               cfg.step = step;
+               cfg.menu_skip_mask = 0;
+       }
+       return v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+}
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+                       u32 id, s32 min, s32 max, s32 step, s32 def)
+{
+       return v4l2_ctrl_new_std(hdl, &cx2341x_ops, id, min, max, step, def);
+}
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_menu(struct v4l2_ctrl_handler *hdl,
+                       u32 id, s32 max, s32 mask, s32 def)
+{
+       return v4l2_ctrl_new_std_menu(hdl, &cx2341x_ops, id, max, mask, def);
+}
+
+int cx2341x_handler_init(struct cx2341x_handler *cxhdl,
+                        unsigned nr_of_controls_hint)
+{
+       struct v4l2_ctrl_handler *hdl = &cxhdl->hdl;
+       u32 caps = cxhdl->capabilities;
+       int has_sliced_vbi = caps & CX2341X_CAP_HAS_SLICED_VBI;
+       int has_ac3 = caps & CX2341X_CAP_HAS_AC3;
+       int has_ts = caps & CX2341X_CAP_HAS_TS;
+
+       cxhdl->width = 720;
+       cxhdl->height = 480;
+
+       v4l2_ctrl_handler_init(hdl, nr_of_controls_hint);
+
+       /* Add controls in ascending control ID order for fastest
+          insertion time. */
+       cxhdl->stream_type = cx2341x_ctrl_new_menu(hdl,
+                       V4L2_CID_MPEG_STREAM_TYPE,
+                       V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, has_ts ? 0 : 2,
+                       V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
+       cxhdl->stream_vbi_fmt = cx2341x_ctrl_new_menu(hdl,
+                       V4L2_CID_MPEG_STREAM_VBI_FMT,
+                       V4L2_MPEG_STREAM_VBI_FMT_IVTV, has_sliced_vbi ? 0 : 2,
+                       V4L2_MPEG_STREAM_VBI_FMT_NONE);
+       cxhdl->audio_sampling_freq = cx2341x_ctrl_new_menu(hdl,
+                       V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+                       V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 0,
+                       V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
+       cxhdl->audio_encoding = cx2341x_ctrl_new_menu(hdl,
+                       V4L2_CID_MPEG_AUDIO_ENCODING,
+                       V4L2_MPEG_AUDIO_ENCODING_AC3, has_ac3 ? ~0x12 : ~0x2,
+                       V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
+       cxhdl->audio_l2_bitrate = cx2341x_ctrl_new_menu(hdl,
+                       V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+                       V4L2_MPEG_AUDIO_L2_BITRATE_384K, 0x1ff,
+                       V4L2_MPEG_AUDIO_L2_BITRATE_224K);
+       cxhdl->audio_mode = cx2341x_ctrl_new_menu(hdl,
+                       V4L2_CID_MPEG_AUDIO_MODE,
+                       V4L2_MPEG_AUDIO_MODE_MONO, 0,
+                       V4L2_MPEG_AUDIO_MODE_STEREO);
+       cxhdl->audio_mode_extension = cx2341x_ctrl_new_menu(hdl,
+                       V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
+                       V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 0,
+                       V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4);
+       cxhdl->audio_emphasis = cx2341x_ctrl_new_menu(hdl,
+                       V4L2_CID_MPEG_AUDIO_EMPHASIS,
+                       V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 0,
+                       V4L2_MPEG_AUDIO_EMPHASIS_NONE);
+       cxhdl->audio_crc = cx2341x_ctrl_new_menu(hdl,
+                       V4L2_CID_MPEG_AUDIO_CRC,
+                       V4L2_MPEG_AUDIO_CRC_CRC16, 0,
+                       V4L2_MPEG_AUDIO_CRC_NONE);
+
+       cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_AUDIO_MUTE, 0, 1, 1, 0);
+       if (has_ac3)
+               cxhdl->audio_ac3_bitrate = cx2341x_ctrl_new_menu(hdl,
+                               V4L2_CID_MPEG_AUDIO_AC3_BITRATE,
+                               V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 0x03,
+                               V4L2_MPEG_AUDIO_AC3_BITRATE_224K);
+       cxhdl->video_encoding = cx2341x_ctrl_new_menu(hdl,
+                       V4L2_CID_MPEG_VIDEO_ENCODING,
+                       V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 0,
+                       V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+       cx2341x_ctrl_new_menu(hdl,
+                       V4L2_CID_MPEG_VIDEO_ASPECT,
+                       V4L2_MPEG_VIDEO_ASPECT_221x100, 0,
+                       V4L2_MPEG_VIDEO_ASPECT_4x3);
+       cxhdl->video_b_frames = cx2341x_ctrl_new_std(hdl,
+                       V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 33, 1, 2);
+       cxhdl->video_gop_size = cx2341x_ctrl_new_std(hdl,
+                       V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+                       1, 34, 1, cxhdl->is_50hz ? 12 : 15);
+       cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1);
+       cxhdl->video_bitrate_mode = cx2341x_ctrl_new_menu(hdl,
+                       V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+                       V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
+                       V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+       cxhdl->video_bitrate = cx2341x_ctrl_new_std(hdl,
+                       V4L2_CID_MPEG_VIDEO_BITRATE,
+                       0, 27000000, 1, 6000000);
+       cxhdl->video_bitrate_peak = cx2341x_ctrl_new_std(hdl,
+                       V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+                       0, 27000000, 1, 8000000);
+       cx2341x_ctrl_new_std(hdl,
+                       V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, 0, 255, 1, 0);
+       cxhdl->video_mute = cx2341x_ctrl_new_std(hdl,
+                       V4L2_CID_MPEG_VIDEO_MUTE, 0, 1, 1, 0);
+       cxhdl->video_mute_yuv = cx2341x_ctrl_new_std(hdl,
+                       V4L2_CID_MPEG_VIDEO_MUTE_YUV, 0, 0xffffff, 1, 0x008080);
+
+       /* CX23415/6 specific */
+       cxhdl->video_spatial_filter_mode = cx2341x_ctrl_new_custom(hdl,
+                       V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
+                       V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+                       V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 0,
+                       V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL);
+       cxhdl->video_spatial_filter = cx2341x_ctrl_new_custom(hdl,
+                       V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
+                       0, 15, 1, 0);
+       cxhdl->video_luma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl,
+                       V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
+                       V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
+                       V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE,
+                       0,
+                       V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR);
+       cxhdl->video_chroma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl,
+                       V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
+                       V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
+                       V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+                       0,
+                       V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR);
+       cxhdl->video_temporal_filter_mode = cx2341x_ctrl_new_custom(hdl,
+                       V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
+                       V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+                       V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO,
+                       0,
+                       V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL);
+       cxhdl->video_temporal_filter = cx2341x_ctrl_new_custom(hdl,
+                       V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
+                       0, 31, 1, 8);
+       cxhdl->video_median_filter_type = cx2341x_ctrl_new_custom(hdl,
+                       V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
+                       V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+                       V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG,
+                       0,
+                       V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF);
+       cxhdl->video_luma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl,
+                       V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
+                       0, 255, 1, 0);
+       cxhdl->video_luma_median_filter_top = cx2341x_ctrl_new_custom(hdl,
+                       V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
+                       0, 255, 1, 255);
+       cxhdl->video_chroma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl,
+                       V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
+                       0, 255, 1, 0);
+       cxhdl->video_chroma_median_filter_top = cx2341x_ctrl_new_custom(hdl,
+                       V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
+                       0, 255, 1, 255);
+       cx2341x_ctrl_new_custom(hdl, V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS,
+                       0, 1, 1, 0);
+
+       if (hdl->error) {
+               int err = hdl->error;
+
+               v4l2_ctrl_handler_free(hdl);
+               return err;
+       }
+
+       v4l2_ctrl_cluster(8, &cxhdl->audio_sampling_freq);
+       v4l2_ctrl_cluster(2, &cxhdl->video_b_frames);
+       v4l2_ctrl_cluster(5, &cxhdl->stream_type);
+       v4l2_ctrl_cluster(2, &cxhdl->video_mute);
+       v4l2_ctrl_cluster(3, &cxhdl->video_spatial_filter_mode);
+       v4l2_ctrl_cluster(2, &cxhdl->video_luma_spatial_filter_type);
+       v4l2_ctrl_cluster(2, &cxhdl->video_spatial_filter);
+       v4l2_ctrl_cluster(4, &cxhdl->video_luma_median_filter_top);
+
+       return 0;
+}
+EXPORT_SYMBOL(cx2341x_handler_init);
+
+void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz)
+{
+       cxhdl->is_50hz = is_50hz;
+       cxhdl->video_gop_size->default_value = cxhdl->is_50hz ? 12 : 15;
+}
+EXPORT_SYMBOL(cx2341x_handler_set_50hz);
+
+int cx2341x_handler_setup(struct cx2341x_handler *cxhdl)
+{
+       int h = cxhdl->height;
+       int w = cxhdl->width;
+       int err;
+
+       err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_OUTPUT_PORT, 2, cxhdl->port, 0);
+       if (err)
+               return err;
+       err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_RATE, 1, cxhdl->is_50hz);
+       if (err)
+               return err;
+
+       if (v4l2_ctrl_g_ctrl(cxhdl->video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) {
+               w /= 2;
+               h /= 2;
+       }
+       err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w);
+       if (err)
+               return err;
+       return v4l2_ctrl_handler_setup(&cxhdl->hdl);
+}
+EXPORT_SYMBOL(cx2341x_handler_setup);
+
+void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy)
+{
+       v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy);
+       v4l2_ctrl_grab(cxhdl->audio_encoding, busy);
+       v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy);
+       v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy);
+       v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy);
+       v4l2_ctrl_grab(cxhdl->stream_type, busy);
+       v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy);
+       v4l2_ctrl_grab(cxhdl->video_bitrate, busy);
+       v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy);
+}
+EXPORT_SYMBOL(cx2341x_handler_set_busy);
index bcdda9a9aa962795798c2a42f52b7f3dc91b2c32..768f000e4b21eeed9078226aff99a3f54c44703c 100644 (file)
@@ -5,7 +5,7 @@ config VIDEO_CX23885
        select VIDEO_BTCX
        select VIDEO_TUNER
        select VIDEO_TVEEPROM
-       select VIDEO_IR
+       select IR_CORE
        select VIDEOBUF_DVB
        select VIDEOBUF_DMA_SG
        select VIDEO_CX25840
index 5787ae243631fe9c393e9062e8812a03fd218cb3..e2ee95f660d8760e21a61be69b3a16ff49d7ff0a 100644 (file)
@@ -1,7 +1,8 @@
 cx23885-objs   := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
                    cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
-                   cx23885-ioctl.o cx23885-ir.o cx23885-input.o cx23888-ir.o \
-                   netup-init.o cimax2.o netup-eeprom.o cx23885-f300.o
+                   cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \
+                   cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \
+                   cx23885-f300.o
 
 obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
 
diff --git a/drivers/media/video/cx23885/cx23885-av.c b/drivers/media/video/cx23885/cx23885-av.c
new file mode 100644 (file)
index 0000000..134ebdd
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  AV device support routines - non-input, non-vl42_subdev routines
+ *
+ *  Copyright (C) 2010  Andy Walls <awalls@md.metrocast.net>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include "cx23885.h"
+
+void cx23885_av_work_handler(struct work_struct *work)
+{
+       struct cx23885_dev *dev =
+                          container_of(work, struct cx23885_dev, cx25840_work);
+       bool handled;
+
+       v4l2_subdev_call(dev->sd_cx25840, core, interrupt_service_routine,
+                        PCI_MSK_AV_CORE, &handled);
+       cx23885_irq_enable(dev, PCI_MSK_AV_CORE);
+}
diff --git a/drivers/media/video/cx23885/cx23885-av.h b/drivers/media/video/cx23885/cx23885-av.h
new file mode 100644 (file)
index 0000000..d2915c3
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  AV device support routines - non-input, non-vl42_subdev routines
+ *
+ *  Copyright (C) 2010  Andy Walls <awalls@md.metrocast.net>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef _CX23885_AV_H_
+#define _CX23885_AV_H_
+void cx23885_av_work_handler(struct work_struct *work);
+#endif
index 2014daedee8b3b827bbffc11eab4095290ce356a..e76ce8709afd2154d79a8792969c66aa2743d4ed 100644 (file)
 #include "netup-init.h"
 #include "cx23888-ir.h"
 
+static unsigned int enable_885_ir;
+module_param(enable_885_ir, int, 0644);
+MODULE_PARM_DESC(enable_885_ir,
+                "Enable integrated IR controller for supported\n"
+                "\t\t    CX2388[57] boards that are wired for it:\n"
+                "\t\t\tHVR-1250 (reported safe)\n"
+                "\t\t\tTeVii S470 (reported unsafe)\n"
+                "\t\t    This can cause an interrupt storm with some cards.\n"
+                "\t\t    Default: 0 [Disabled]");
+
 /* ------------------------------------------------------------------ */
 /* board config info                                                  */
 
@@ -626,6 +636,9 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
        case 79101:
                /* WinTV-HVR1250 (PCIe, Retail, IR, half height,
                        ATSC and Basic analog */
+       case 79501:
+               /* WinTV-HVR1250 (PCIe, No IR, half height,
+                       ATSC [at least] and Basic analog) */
        case 79561:
                /* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
                        ATSC and Basic analog */
@@ -959,9 +972,37 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
 
 int cx23885_ir_init(struct cx23885_dev *dev)
 {
+       static struct v4l2_subdev_io_pin_config ir_rxtx_pin_cfg[] = {
+               {
+                       .flags    = V4L2_SUBDEV_IO_PIN_INPUT,
+                       .pin      = CX23885_PIN_IR_RX_GPIO19,
+                       .function = CX23885_PAD_IR_RX,
+                       .value    = 0,
+                       .strength = CX25840_PIN_DRIVE_MEDIUM,
+               }, {
+                       .flags    = V4L2_SUBDEV_IO_PIN_OUTPUT,
+                       .pin      = CX23885_PIN_IR_TX_GPIO20,
+                       .function = CX23885_PAD_IR_TX,
+                       .value    = 0,
+                       .strength = CX25840_PIN_DRIVE_MEDIUM,
+               }
+       };
+       const size_t ir_rxtx_pin_cfg_count = ARRAY_SIZE(ir_rxtx_pin_cfg);
+
+       static struct v4l2_subdev_io_pin_config ir_rx_pin_cfg[] = {
+               {
+                       .flags    = V4L2_SUBDEV_IO_PIN_INPUT,
+                       .pin      = CX23885_PIN_IR_RX_GPIO19,
+                       .function = CX23885_PAD_IR_RX,
+                       .value    = 0,
+                       .strength = CX25840_PIN_DRIVE_MEDIUM,
+               }
+       };
+       const size_t ir_rx_pin_cfg_count = ARRAY_SIZE(ir_rx_pin_cfg);
+
+       struct v4l2_subdev_ir_parameters params;
        int ret = 0;
        switch (dev->board) {
-       case CX23885_BOARD_HAUPPAUGE_HVR1250:
        case CX23885_BOARD_HAUPPAUGE_HVR1500:
        case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
        case CX23885_BOARD_HAUPPAUGE_HVR1800:
@@ -979,7 +1020,41 @@ int cx23885_ir_init(struct cx23885_dev *dev)
                if (ret)
                        break;
                dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
-               dev->pci_irqmask |= PCI_MSK_IR;
+               v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+                                ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
+               /*
+                * For these boards we need to invert the Tx output via the
+                * IR controller to have the LED off while idle
+                */
+               v4l2_subdev_call(dev->sd_ir, ir, tx_g_parameters, &params);
+               params.enable = false;
+               params.shutdown = false;
+               params.invert_level = true;
+               v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
+               params.shutdown = true;
+               v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
+               break;
+       case CX23885_BOARD_TEVII_S470:
+               if (!enable_885_ir)
+                       break;
+               dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+               if (dev->sd_ir == NULL) {
+                       ret = -ENODEV;
+                       break;
+               }
+               v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+                                ir_rx_pin_cfg_count, ir_rx_pin_cfg);
+               break;
+       case CX23885_BOARD_HAUPPAUGE_HVR1250:
+               if (!enable_885_ir)
+                       break;
+               dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+               if (dev->sd_ir == NULL) {
+                       ret = -ENODEV;
+                       break;
+               }
+               v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+                                ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
                break;
        case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
                request_module("ir-kbd-i2c");
@@ -994,11 +1069,16 @@ void cx23885_ir_fini(struct cx23885_dev *dev)
        switch (dev->board) {
        case CX23885_BOARD_HAUPPAUGE_HVR1850:
        case CX23885_BOARD_HAUPPAUGE_HVR1290:
-               dev->pci_irqmask &= ~PCI_MSK_IR;
-               cx_clear(PCI_INT_MSK, PCI_MSK_IR);
+               cx23885_irq_remove(dev, PCI_MSK_IR);
                cx23888_ir_remove(dev);
                dev->sd_ir = NULL;
                break;
+       case CX23885_BOARD_TEVII_S470:
+       case CX23885_BOARD_HAUPPAUGE_HVR1250:
+               cx23885_irq_remove(dev, PCI_MSK_AV_CORE);
+               /* sd_ir is a duplicate pointer to the AV Core, just clear it */
+               dev->sd_ir = NULL;
+               break;
        }
 }
 
@@ -1007,8 +1087,13 @@ void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
        switch (dev->board) {
        case CX23885_BOARD_HAUPPAUGE_HVR1850:
        case CX23885_BOARD_HAUPPAUGE_HVR1290:
-               if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_IR))
-                       cx_set(PCI_INT_MSK, PCI_MSK_IR);
+               if (dev->sd_ir)
+                       cx23885_irq_add_enable(dev, PCI_MSK_IR);
+               break;
+       case CX23885_BOARD_TEVII_S470:
+       case CX23885_BOARD_HAUPPAUGE_HVR1250:
+               if (dev->sd_ir)
+                       cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE);
                break;
        }
 }
@@ -1028,6 +1113,13 @@ void cx23885_card_setup(struct cx23885_dev *dev)
 
        switch (dev->board) {
        case CX23885_BOARD_HAUPPAUGE_HVR1250:
+               if (dev->i2c_bus[0].i2c_rc == 0) {
+                       if (eeprom[0x80] != 0x84)
+                               hauppauge_eeprom(dev, eeprom+0xc0);
+                       else
+                               hauppauge_eeprom(dev, eeprom+0x80);
+               }
+               break;
        case CX23885_BOARD_HAUPPAUGE_HVR1500:
        case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
        case CX23885_BOARD_HAUPPAUGE_HVR1400:
@@ -1136,6 +1228,11 @@ void cx23885_card_setup(struct cx23885_dev *dev)
         * loaded, ensure this happens.
         */
        switch (dev->board) {
+       case CX23885_BOARD_TEVII_S470:
+       case CX23885_BOARD_HAUPPAUGE_HVR1250:
+               /* Currently only enabled for the integrated IR controller */
+               if (!enable_885_ir)
+                       break;
        case CX23885_BOARD_HAUPPAUGE_HVR1800:
        case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
        case CX23885_BOARD_HAUPPAUGE_HVR1700:
@@ -1151,7 +1248,10 @@ void cx23885_card_setup(struct cx23885_dev *dev)
                dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
                                &dev->i2c_bus[2].i2c_adap,
                                "cx25840", "cx25840", 0x88 >> 1, NULL);
-               v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
+               if (dev->sd_cx25840) {
+                       dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE;
+                       v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
+               }
                break;
        }
 
index ff76f64edac1e196f8c276e543fff216669cc445..f6b62e7398afaf1f0e704059ce95aac5c36848d3 100644 (file)
@@ -34,6 +34,7 @@
 #include "cimax2.h"
 #include "cx23888-ir.h"
 #include "cx23885-ir.h"
+#include "cx23885-av.h"
 #include "cx23885-input.h"
 
 MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
@@ -299,6 +300,83 @@ static struct sram_channel cx23887_sram_channels[] = {
        },
 };
 
+void cx23885_irq_add(struct cx23885_dev *dev, u32 mask)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+       dev->pci_irqmask |= mask;
+
+       spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+       dev->pci_irqmask |= mask;
+       cx_set(PCI_INT_MSK, mask);
+
+       spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask)
+{
+       u32 v;
+       unsigned long flags;
+       spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+       v = mask & dev->pci_irqmask;
+       if (v)
+               cx_set(PCI_INT_MSK, v);
+
+       spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static inline void cx23885_irq_enable_all(struct cx23885_dev *dev)
+{
+       cx23885_irq_enable(dev, 0xffffffff);
+}
+
+void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+       cx_clear(PCI_INT_MSK, mask);
+
+       spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static inline void cx23885_irq_disable_all(struct cx23885_dev *dev)
+{
+       cx23885_irq_disable(dev, 0xffffffff);
+}
+
+void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+       dev->pci_irqmask &= ~mask;
+       cx_clear(PCI_INT_MSK, mask);
+
+       spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static u32 cx23885_irq_get_mask(struct cx23885_dev *dev)
+{
+       u32 v;
+       unsigned long flags;
+       spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+       v = cx_read(PCI_INT_MSK);
+
+       spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+       return v;
+}
+
 static int cx23885_risc_decode(u32 risc)
 {
        static char *instr[16] = {
@@ -548,7 +626,7 @@ static void cx23885_shutdown(struct cx23885_dev *dev)
        cx_write(UART_CTL, 0);
 
        /* Disable Interrupts */
-       cx_write(PCI_INT_MSK, 0);
+       cx23885_irq_disable_all(dev);
        cx_write(VID_A_INT_MSK, 0);
        cx_write(VID_B_INT_MSK, 0);
        cx_write(VID_C_INT_MSK, 0);
@@ -774,6 +852,8 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
 {
        int i;
 
+       spin_lock_init(&dev->pci_irqmask_lock);
+
        mutex_init(&dev->lock);
        mutex_init(&dev->gpio_lock);
 
@@ -820,9 +900,9 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
 
        dev->pci_bus  = dev->pci->bus->number;
        dev->pci_slot = PCI_SLOT(dev->pci->devfn);
-       dev->pci_irqmask = 0x001f00;
+       cx23885_irq_add(dev, 0x001f00);
        if (cx23885_boards[dev->board].cimax > 0)
-               dev->pci_irqmask |= 0x01800000; /* for CiMaxes */
+               cx23885_irq_add(dev, 0x01800000); /* for CiMaxes */
 
        /* External Master 1 Bus */
        dev->i2c_bus[0].nr = 0;
@@ -1156,7 +1236,7 @@ static void cx23885_tsport_reg_dump(struct cx23885_tsport *port)
        dprintk(1, "%s() DEV_CNTRL2               0x%08X\n", __func__,
                cx_read(DEV_CNTRL2));
        dprintk(1, "%s() PCI_INT_MSK              0x%08X\n", __func__,
-               cx_read(PCI_INT_MSK));
+               cx23885_irq_get_mask(dev));
        dprintk(1, "%s() AUD_INT_INT_MSK          0x%08X\n", __func__,
                cx_read(AUDIO_INT_INT_MSK));
        dprintk(1, "%s() AUD_INT_DMA_CTL          0x%08X\n", __func__,
@@ -1292,7 +1372,8 @@ static int cx23885_start_dma(struct cx23885_tsport *port,
                dprintk(1, "%s() enabling TS int's and DMA\n", __func__);
                cx_set(port->reg_ts_int_msk,  port->ts_int_msk_val);
                cx_set(port->reg_dma_ctl, port->dma_ctl_val);
-               cx_set(PCI_INT_MSK, dev->pci_irqmask | port->pci_irqmask);
+               cx23885_irq_add(dev, port->pci_irqmask);
+               cx23885_irq_enable_all(dev);
                break;
        default:
                BUG();
@@ -1650,10 +1731,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
        u32 ts1_status, ts1_mask;
        u32 ts2_status, ts2_mask;
        int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
-       bool ir_handled = false;
+       bool subdev_handled;
 
        pci_status = cx_read(PCI_INT_STAT);
-       pci_mask = cx_read(PCI_INT_MSK);
+       pci_mask = cx23885_irq_get_mask(dev);
        vida_status = cx_read(VID_A_INT_STAT);
        vida_mask = cx_read(VID_A_INT_MSK);
        ts1_status = cx_read(VID_B_INT_STAT);
@@ -1681,7 +1762,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
                          PCI_MSK_VID_C   | PCI_MSK_VID_B   | PCI_MSK_VID_A   |
                          PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT |
                          PCI_MSK_GPIO0   | PCI_MSK_GPIO1   |
-                         PCI_MSK_IR)) {
+                         PCI_MSK_AV_CORE | PCI_MSK_IR)) {
 
                if (pci_status & PCI_MSK_RISC_RD)
                        dprintk(7, " (PCI_MSK_RISC_RD   0x%08x)\n",
@@ -1731,6 +1812,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
                        dprintk(7, " (PCI_MSK_GPIO1     0x%08x)\n",
                                PCI_MSK_GPIO1);
 
+               if (pci_status & PCI_MSK_AV_CORE)
+                       dprintk(7, " (PCI_MSK_AV_CORE   0x%08x)\n",
+                               PCI_MSK_AV_CORE);
+
                if (pci_status & PCI_MSK_IR)
                        dprintk(7, " (PCI_MSK_IR        0x%08x)\n",
                                PCI_MSK_IR);
@@ -1765,12 +1850,22 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
                handled += cx23885_video_irq(dev, vida_status);
 
        if (pci_status & PCI_MSK_IR) {
-               v4l2_subdev_call(dev->sd_ir, ir, interrupt_service_routine,
-                                pci_status, &ir_handled);
-               if (ir_handled)
+               subdev_handled = false;
+               v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine,
+                                pci_status, &subdev_handled);
+               if (subdev_handled)
                        handled++;
        }
 
+       if ((pci_status & pci_mask) & PCI_MSK_AV_CORE) {
+               cx23885_irq_disable(dev, PCI_MSK_AV_CORE);
+               if (!schedule_work(&dev->cx25840_work))
+                       printk(KERN_ERR "%s: failed to set up deferred work for"
+                              " AV Core/IR interrupt. Interrupt is disabled"
+                              " and won't be re-enabled\n", dev->name);
+               handled++;
+       }
+
        if (handled)
                cx_write(PCI_INT_STAT, pci_status);
 out:
@@ -1788,11 +1883,11 @@ static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
        dev = to_cx23885(sd->v4l2_dev);
 
        switch (notification) {
-       case V4L2_SUBDEV_IR_RX_NOTIFY: /* Called in an IRQ context */
+       case V4L2_SUBDEV_IR_RX_NOTIFY: /* Possibly called in an IRQ context */
                if (sd == dev->sd_ir)
                        cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg);
                break;
-       case V4L2_SUBDEV_IR_TX_NOTIFY: /* Called in an IRQ context */
+       case V4L2_SUBDEV_IR_TX_NOTIFY: /* Possibly called in an IRQ context */
                if (sd == dev->sd_ir)
                        cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg);
                break;
@@ -1801,6 +1896,7 @@ static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
 
 static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev)
 {
+       INIT_WORK(&dev->cx25840_work, cx23885_av_work_handler);
        INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler);
        INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler);
        dev->v4l2_dev.notify = cx23885_v4l2_dev_notify;
@@ -1967,7 +2063,7 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
 
        switch (dev->board) {
        case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
-               cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */
+               cx23885_irq_add_enable(dev, 0x01800000); /* for NetUP */
                break;
        }
 
index d4746e06451692f8c853bf8c73924c87f6ad70b3..1a391486e551a9b62512712870203f24b6ea35d4 100644 (file)
@@ -99,7 +99,7 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
                if (!i2c_wait_done(i2c_adap))
                        return -EIO;
                if (!i2c_slave_did_ack(i2c_adap))
-                       return -EIO;
+                       return -ENXIO;
 
                dprintk(1, "%s() returns 0\n", __func__);
                return 0;
@@ -120,11 +120,12 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
        cx_write(bus->reg_wdata, wdata);
        cx_write(bus->reg_ctrl, ctrl);
 
-       retval = i2c_wait_done(i2c_adap);
-       if (retval < 0)
-               goto err;
-       if (retval == 0)
+       if (!i2c_wait_done(i2c_adap))
                goto eio;
+       if (!i2c_slave_did_ack(i2c_adap)) {
+               retval = -ENXIO;
+               goto err;
+       }
        if (i2c_debug) {
                printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
                if (!(ctrl & I2C_NOSTOP))
@@ -145,10 +146,7 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
                cx_write(bus->reg_wdata, wdata);
                cx_write(bus->reg_ctrl, ctrl);
 
-               retval = i2c_wait_done(i2c_adap);
-               if (retval < 0)
-                       goto err;
-               if (retval == 0)
+               if (!i2c_wait_done(i2c_adap))
                        goto eio;
                if (i2c_debug) {
                        dprintk(1, " %02x", msg->buf[cnt]);
@@ -185,7 +183,7 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
                if (!i2c_wait_done(i2c_adap))
                        return -EIO;
                if (!i2c_slave_did_ack(i2c_adap))
-                       return -EIO;
+                       return -ENXIO;
 
 
                dprintk(1, "%s() returns 0\n", __func__);
@@ -209,11 +207,12 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
                cx_write(bus->reg_addr, msg->addr << 25);
                cx_write(bus->reg_ctrl, ctrl);
 
-               retval = i2c_wait_done(i2c_adap);
-               if (retval < 0)
-                       goto err;
-               if (retval == 0)
+               if (!i2c_wait_done(i2c_adap))
                        goto eio;
+               if (cnt == 0 && !i2c_slave_did_ack(i2c_adap)) {
+                       retval = -ENXIO;
+                       goto err;
+               }
                msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
                if (i2c_debug) {
                        dprintk(1, " %02x", msg->buf[cnt]);
index d0b1613ede2f4e3e418eef2b772a7ccb22e5b173..bb61870b8d6ed39d25c11aa676b55bd0a94dc235 100644 (file)
 
 #define MODULE_NAME "cx23885"
 
-static void convert_measurement(u32 x, struct ir_raw_event *y)
-{
-       if (x == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) {
-               y->pulse = false;
-               y->duration = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
-               return;
-       }
-
-       y->pulse = (x & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? true : false;
-       y->duration = x & V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
-}
-
 static void cx23885_input_process_measurements(struct cx23885_dev *dev,
                                               bool overrun)
 {
        struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir;
-       struct ir_raw_event kernel_ir_event;
 
-       u32 sd_ir_data[64];
        ssize_t num;
        int count, i;
        bool handle = false;
+       struct ir_raw_event ir_core_event[64];
 
        do {
                num = 0;
-               v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) sd_ir_data,
-                                sizeof(sd_ir_data), &num);
+               v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ir_core_event,
+                                sizeof(ir_core_event), &num);
 
-               count = num / sizeof(u32);
+               count = num / sizeof(struct ir_raw_event);
 
                for (i = 0; i < count; i++) {
-                       convert_measurement(sd_ir_data[i], &kernel_ir_event);
                        ir_raw_event_store(kernel_ir->inp_dev,
-                                          &kernel_ir_event);
+                                          &ir_core_event[i]);
                        handle = true;
                }
        } while (num != 0);
@@ -99,8 +85,10 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
        switch (dev->board) {
        case CX23885_BOARD_HAUPPAUGE_HVR1850:
        case CX23885_BOARD_HAUPPAUGE_HVR1290:
+       case CX23885_BOARD_TEVII_S470:
+       case CX23885_BOARD_HAUPPAUGE_HVR1250:
                /*
-                * The only board we handle right now.  However other boards
+                * The only boards we handle right now.  However other boards
                 * using the CX2388x integrated IR controller should be similar
                 */
                break;
@@ -148,6 +136,7 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
        switch (dev->board) {
        case CX23885_BOARD_HAUPPAUGE_HVR1850:
        case CX23885_BOARD_HAUPPAUGE_HVR1290:
+       case CX23885_BOARD_HAUPPAUGE_HVR1250:
                /*
                 * The IR controller on this board only returns pulse widths.
                 * Any other mode setting will fail to set up the device.
@@ -170,7 +159,38 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
                 * mark is received as low logic level;
                 * falling edges are detected as rising edges; etc.
                 */
-               params.invert = true;
+               params.invert_level = true;
+               break;
+       case CX23885_BOARD_TEVII_S470:
+               /*
+                * The IR controller on this board only returns pulse widths.
+                * Any other mode setting will fail to set up the device.
+                */
+               params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+               params.enable = true;
+               params.interrupt_enable = true;
+               params.shutdown = false;
+
+               /* Setup for a standard NEC protocol */
+               params.carrier_freq = 37917; /* Hz, 455 kHz/12 for NEC */
+               params.carrier_range_lower = 33000; /* Hz */
+               params.carrier_range_upper = 43000; /* Hz */
+               params.duty_cycle = 33; /* percent, 33 percent for NEC */
+
+               /*
+                * NEC max pulse width: (64/3)/(455 kHz/12) * 16 nec_units
+                * (64/3)/(455 kHz/12) * 16 nec_units * 1.375 = 12378022 ns
+                */
+               params.max_pulse_width = 12378022; /* ns */
+
+               /*
+                * NEC noise filter min width: (64/3)/(455 kHz/12) * 1 nec_unit
+                * (64/3)/(455 kHz/12) * 1 nec_units * 0.625 = 351648 ns
+                */
+               params.noise_filter_min_width = 351648; /* ns */
+
+               params.modulation = false;
+               params.invert_level = true;
                break;
        }
        v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
@@ -244,12 +264,20 @@ int cx23885_input_init(struct cx23885_dev *dev)
        switch (dev->board) {
        case CX23885_BOARD_HAUPPAUGE_HVR1850:
        case CX23885_BOARD_HAUPPAUGE_HVR1290:
-               /* Integrated CX23888 IR controller */
+       case CX23885_BOARD_HAUPPAUGE_HVR1250:
+               /* Integrated CX2388[58] IR controller */
                driver_type = RC_DRIVER_IR_RAW;
                allowed_protos = IR_TYPE_ALL;
                /* The grey Hauppauge RC-5 remote */
                rc_map = RC_MAP_RC5_HAUPPAUGE_NEW;
                break;
+       case CX23885_BOARD_TEVII_S470:
+               /* Integrated CX23885 IR controller */
+               driver_type = RC_DRIVER_IR_RAW;
+               allowed_protos = IR_TYPE_ALL;
+               /* A guess at the remote */
+               rc_map = RC_MAP_TEVII_NEC;
+               break;
        default:
                return -ENODEV;
        }
index 6ceabd4fba07ba024bbef9b7273eda93972fcf32..7125247dd25558678c823ee3262675570c9aa630 100644 (file)
@@ -72,7 +72,7 @@ void cx23885_ir_tx_work_handler(struct work_struct *work)
 
 }
 
-/* Called in an IRQ context */
+/* Possibly called in an IRQ context */
 void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
 {
        struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
@@ -86,10 +86,18 @@ void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
                set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications);
        if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN)
                set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications);
-       schedule_work(&dev->ir_rx_work);
+
+       /*
+        * For the integrated AV core, we are already in a workqueue context.
+        * For the CX23888 integrated IR, we are in an interrupt context.
+        */
+       if (sd == dev->sd_cx25840)
+               cx23885_ir_rx_work_handler(&dev->ir_rx_work);
+       else
+               schedule_work(&dev->ir_rx_work);
 }
 
-/* Called in an IRQ context */
+/* Possibly called in an IRQ context */
 void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
 {
        struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
@@ -97,5 +105,13 @@ void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
 
        if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ)
                set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications);
-       schedule_work(&dev->ir_tx_work);
+
+       /*
+        * For the integrated AV core, we are already in a workqueue context.
+        * For the CX23888 integrated IR, we are in an interrupt context.
+        */
+       if (sd == dev->sd_cx25840)
+               cx23885_ir_tx_work_handler(&dev->ir_tx_work);
+       else
+               schedule_work(&dev->ir_tx_work);
 }
index c0bc9a068954b91deb4e2c1f0c467693555b9ec5..a28772db11f0e0da220f85142febbf0d0b235ddb 100644 (file)
@@ -213,6 +213,7 @@ Channel manager Data Structure entry = 20 DWORD
 #define DEV_CNTRL2     0x00040000
 
 #define PCI_MSK_IR        (1 << 28)
+#define PCI_MSK_AV_CORE   (1 << 27)
 #define PCI_MSK_GPIO1     (1 << 24)
 #define PCI_MSK_GPIO0     (1 << 23)
 #define PCI_MSK_APB_DMA   (1 << 12)
index 708a8c766d1ad3377bdbce956a62fbe467bef546..c0b60382ad13602ff9c2fc512cf92fe531568cc2 100644 (file)
@@ -74,7 +74,7 @@ static int cx23885_start_vbi_dma(struct cx23885_dev    *dev,
        q->count = 1;
 
        /* enable irqs */
-       cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
+       cx23885_irq_add_enable(dev, 0x01);
        cx_set(VID_A_INT_MSK, 0x000022);
 
        /* start dma */
index 4e44dcda38756e298aa2da930907cde71ae5a113..da66e5f8d91d1c40d60042d14bdd3c8f4cb21b36 100644 (file)
@@ -441,7 +441,7 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev,
        q->count = 1;
 
        /* enable irq */
-       cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
+       cx23885_irq_add_enable(dev, 0x01);
        cx_set(VID_A_INT_MSK, 0x000011);
 
        /* start dma */
@@ -1205,6 +1205,21 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
        return 0;
 }
 
+static int vidioc_log_status(struct file *file, void *priv)
+{
+       struct cx23885_fh  *fh  = priv;
+       struct cx23885_dev *dev = fh->dev;
+
+       printk(KERN_INFO
+               "%s/0: ============  START LOG STATUS  ============\n",
+              dev->name);
+       call_all(dev, core, log_status);
+       printk(KERN_INFO
+               "%s/0: =============  END LOG STATUS  =============\n",
+              dev->name);
+       return 0;
+}
+
 static int vidioc_queryctrl(struct file *file, void *priv,
                                struct v4l2_queryctrl *qctrl)
 {
@@ -1410,6 +1425,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
        .vidioc_enum_input    = vidioc_enum_input,
        .vidioc_g_input       = vidioc_g_input,
        .vidioc_s_input       = vidioc_s_input,
+       .vidioc_log_status    = vidioc_log_status,
        .vidioc_queryctrl     = vidioc_queryctrl,
        .vidioc_g_ctrl        = vidioc_g_ctrl,
        .vidioc_s_ctrl        = vidioc_s_ctrl,
@@ -1449,7 +1465,7 @@ static const struct v4l2_file_operations radio_fops = {
 void cx23885_video_unregister(struct cx23885_dev *dev)
 {
        dprintk(1, "%s()\n", __func__);
-       cx_clear(PCI_INT_MSK, 1);
+       cx23885_irq_remove(dev, 0x01);
 
        if (dev->video_dev) {
                if (video_is_registered(dev->video_dev))
@@ -1486,7 +1502,8 @@ int cx23885_video_register(struct cx23885_dev *dev)
                VID_A_DMA_CTL, 0x11, 0x00);
 
        /* Don't enable VBI yet */
-       cx_set(PCI_INT_MSK, 1);
+
+       cx23885_irq_add_enable(dev, 0x01);
 
        if (TUNER_ABSENT != dev->tuner_type) {
                struct v4l2_subdev *sd = NULL;
index a33f2b71467bcc29e7e701d5d26ee6dde8a4a21c..ed94b17dd8a5c1e1e9ff8b777965fe1fc061ce9a 100644 (file)
@@ -325,6 +325,7 @@ struct cx23885_dev {
        u32                        __iomem *lmmio;
        u8                         __iomem *bmmio;
        int                        pci_irqmask;
+       spinlock_t                 pci_irqmask_lock; /* protects mask reg too */
        int                        hwrevision;
 
        /* This valud is board specific and is used to configure the
@@ -365,6 +366,7 @@ struct cx23885_dev {
        unsigned char              radio_addr;
        unsigned int               has_radio;
        struct v4l2_subdev         *sd_cx25840;
+       struct work_struct         cx25840_work;
 
        /* Infrared */
        struct v4l2_subdev         *sd_ir;
@@ -403,7 +405,8 @@ static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev)
 #define call_all(dev, o, f, args...) \
        v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
 
-#define CX23885_HW_888_IR (1 << 0)
+#define CX23885_HW_888_IR  (1 << 0)
+#define CX23885_HW_AV_CORE (1 << 1)
 
 #define call_hw(dev, grpid, o, f, args...) \
        v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args)
@@ -484,6 +487,10 @@ extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask);
 extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask,
        int asoutput);
 
+extern void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask);
 
 /* ----------------------------------------------------------- */
 /* cx23885-cards.c                                             */
index f63d378257a76ccc733fe953b513e0e6e84e8b62..2502a0a6709783b8c01d5de639d759d097f0f1cd 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/ir-core.h>
 
 #include "cx23885.h"
 
@@ -60,6 +61,8 @@ MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]");
 #define CNTRL_CPL      0x00001000
 #define CNTRL_LBM      0x00002000
 #define CNTRL_R                0x00004000
+/* CX23888 specific control flag */
+#define CNTRL_IVO      0x00008000
 
 #define CX23888_IR_TXCLK_REG   0x170004
 #define TXCLK_TCD      0x0000FFFF
@@ -111,8 +114,18 @@ MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]");
 #define CX23888_VIDCLK_FREQ    108000000 /* 108 MHz, BT.656 */
 #define CX23888_IR_REFCLK_FREQ (CX23888_VIDCLK_FREQ / 2)
 
-#define CX23888_IR_RX_KFIFO_SIZE       (512 * sizeof(u32))
-#define CX23888_IR_TX_KFIFO_SIZE       (512 * sizeof(u32))
+/*
+ * We use this union internally for convenience, but callers to tx_write
+ * and rx_read will be expecting records of type struct ir_raw_event.
+ * Always ensure the size of this union is dictated by struct ir_raw_event.
+ */
+union cx23888_ir_fifo_rec {
+       u32 hw_fifo_data;
+       struct ir_raw_event ir_core_data;
+};
+
+#define CX23888_IR_RX_KFIFO_SIZE    (256 * sizeof(union cx23888_ir_fifo_rec))
+#define CX23888_IR_TX_KFIFO_SIZE    (256 * sizeof(union cx23888_ir_fifo_rec))
 
 struct cx23888_ir_state {
        struct v4l2_subdev sd;
@@ -423,6 +436,13 @@ static inline void control_tx_polarity_invert(struct cx23885_dev *dev,
                           invert ? CNTRL_CPL : 0);
 }
 
+static inline void control_tx_level_invert(struct cx23885_dev *dev,
+                                         bool invert)
+{
+       cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_IVO,
+                          invert ? CNTRL_IVO : 0);
+}
+
 /*
  * IR Rx & Tx Clock Register helpers
  */
@@ -449,8 +469,8 @@ static u32 txclk_tx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns,
 {
        u64 pulse_clocks;
 
-       if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
-               ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+       if (ns > IR_MAX_DURATION)
+               ns = IR_MAX_DURATION;
        pulse_clocks = ns_to_pulse_clocks(ns);
        *divider = pulse_clocks_to_clock_divider(pulse_clocks);
        cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider);
@@ -462,8 +482,8 @@ static u32 rxclk_rx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns,
 {
        u64 pulse_clocks;
 
-       if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
-               ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+       if (ns > IR_MAX_DURATION)
+               ns = IR_MAX_DURATION;
        pulse_clocks = ns_to_pulse_clocks(ns);
        *divider = pulse_clocks_to_clock_divider(pulse_clocks);
        cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider);
@@ -526,8 +546,8 @@ static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status,
        u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG);
        u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG);
 
-       u32 rx_data[FIFO_RX_DEPTH];
-       int i, j, k;
+       union cx23888_ir_fifo_rec rx_data[FIFO_RX_DEPTH];
+       unsigned int i, j, k;
        u32 events, v;
        int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
 
@@ -588,11 +608,12 @@ static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status,
                        for (j = 0;
                             (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
                                v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG);
-                               rx_data[i++] = v & ~FIFO_RX_NDV;
+                               rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV;
+                               i++;
                        }
                        if (i == 0)
                                break;
-                       j = i * sizeof(u32);
+                       j = i * sizeof(union cx23888_ir_fifo_rec);
                        k = kfifo_in_locked(&state->rx_kfifo,
                                      (unsigned char *) rx_data, j,
                                      &state->rx_kfifo_lock);
@@ -651,10 +672,11 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
        u16 divider = (u16) atomic_read(&state->rxclk_divider);
 
        unsigned int i, n;
-       u32 *p;
-       u32 u, v;
+       union cx23888_ir_fifo_rec *p;
+       unsigned u, v;
 
-       n = count / sizeof(u32) * sizeof(u32);
+       n = count / sizeof(union cx23888_ir_fifo_rec)
+               * sizeof(union cx23888_ir_fifo_rec);
        if (n == 0) {
                *num = 0;
                return 0;
@@ -662,26 +684,28 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
 
        n = kfifo_out_locked(&state->rx_kfifo, buf, n, &state->rx_kfifo_lock);
 
-       n /= sizeof(u32);
-       *num = n * sizeof(u32);
+       n /= sizeof(union cx23888_ir_fifo_rec);
+       *num = n * sizeof(union cx23888_ir_fifo_rec);
+
+       for (p = (union cx23888_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) {
 
-       for (p = (u32 *) buf, i = 0; i < n; p++, i++) {
-               if ((*p & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
-                       *p = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END;
+               if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
+                       /* Assume RTO was because of no IR light input */
+                       u = 0;
                        v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n");
-                       continue;
+               } else {
+                       u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
+                       if (invert)
+                               u = u ? 0 : 1;
                }
 
-               u = (*p & FIFO_RXTX_LVL) ? V4L2_SUBDEV_IR_PULSE_LEVEL_MASK : 0;
-               if (invert)
-                       u = u ? 0 : V4L2_SUBDEV_IR_PULSE_LEVEL_MASK;
+               v = (unsigned) pulse_width_count_to_ns(
+                                 (u16) (p->hw_fifo_data & FIFO_RXTX), divider);
+               if (v > IR_MAX_DURATION)
+                       v = IR_MAX_DURATION;
 
-               v = (u32) pulse_width_count_to_ns((u16) (*p & FIFO_RXTX),
-                                                 divider);
-               if (v >= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
-                       v = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS - 1;
-
-               *p = u | v;
+               p->ir_core_data.pulse = u;
+               p->ir_core_data.duration = v;
 
                v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns  %s\n",
                         v, u ? "mark" : "space");
@@ -740,7 +764,8 @@ static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
 
        o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
 
-       o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32);
+       o->bytes_per_data_element = p->bytes_per_data_element
+                                 = sizeof(union cx23888_ir_fifo_rec);
 
        /* Before we tweak the hardware, we have to disable the receiver */
        irqenable_rx(dev, 0);
@@ -762,12 +787,15 @@ static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
                                            &p->carrier_range_upper);
                o->carrier_range_lower = p->carrier_range_lower;
                o->carrier_range_upper = p->carrier_range_upper;
+
+               p->max_pulse_width =
+                       (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider);
        } else {
                p->max_pulse_width =
                            rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width,
                                                       &rxclk_divider);
-               o->max_pulse_width = p->max_pulse_width;
        }
+       o->max_pulse_width = p->max_pulse_width;
        atomic_set(&state->rxclk_divider, rxclk_divider);
 
        p->noise_filter_min_width =
@@ -782,8 +810,8 @@ static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
 
        control_rx_s_edge_detection(dev, CNTRL_EDG_BOTH);
 
-       o->invert = p->invert;
-       atomic_set(&state->rx_invert, p->invert);
+       o->invert_level = p->invert_level;
+       atomic_set(&state->rx_invert, p->invert_level);
 
        o->interrupt_enable = p->interrupt_enable;
        o->enable = p->enable;
@@ -864,7 +892,8 @@ static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd,
 
        o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
 
-       o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32);
+       o->bytes_per_data_element = p->bytes_per_data_element
+                                 = sizeof(union cx23888_ir_fifo_rec);
 
        /* Before we tweak the hardware, we have to disable the transmitter */
        irqenable_tx(dev, 0);
@@ -880,12 +909,15 @@ static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd,
 
                p->duty_cycle = cduty_tx_s_duty_cycle(dev, p->duty_cycle);
                o->duty_cycle = p->duty_cycle;
+
+               p->max_pulse_width =
+                       (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider);
        } else {
                p->max_pulse_width =
                            txclk_tx_s_max_pulse_width(dev, p->max_pulse_width,
                                                       &txclk_divider);
-               o->max_pulse_width = p->max_pulse_width;
        }
+       o->max_pulse_width = p->max_pulse_width;
        atomic_set(&state->txclk_divider, txclk_divider);
 
        p->resolution = clock_divider_to_resolution(txclk_divider);
@@ -894,8 +926,11 @@ static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd,
        /* FIXME - make this dependent on resolution for better performance */
        control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY);
 
-       control_tx_polarity_invert(dev, p->invert);
-       o->invert = p->invert;
+       control_tx_polarity_invert(dev, p->invert_carrier_sense);
+       o->invert_carrier_sense = p->invert_carrier_sense;
+
+       control_tx_level_invert(dev, p->invert_level);
+       o->invert_level = p->invert_level;
 
        o->interrupt_enable = p->interrupt_enable;
        o->enable = p->enable;
@@ -988,12 +1023,10 @@ static int cx23888_ir_log_status(struct v4l2_subdev *sd)
                          "-%1d/+%1d, %u to %u Hz\n", i, j,
                          clock_divider_to_freq(rxclk, 16 + j),
                          clock_divider_to_freq(rxclk, 16 - i));
-       } else {
-               v4l2_info(sd, "\tMax measurable pulse width:        %u us, "
-                         "%llu ns\n",
-                         pulse_width_count_to_us(FIFO_RXTX, rxclk),
-                         pulse_width_count_to_ns(FIFO_RXTX, rxclk));
        }
+       v4l2_info(sd, "\tMax measurable pulse width:        %u us, %llu ns\n",
+                 pulse_width_count_to_us(FIFO_RXTX, rxclk),
+                 pulse_width_count_to_ns(FIFO_RXTX, rxclk));
        v4l2_info(sd, "\tLow pass filter:                   %s\n",
                  filtr ? "enabled" : "disabled");
        if (filtr)
@@ -1025,19 +1058,20 @@ static int cx23888_ir_log_status(struct v4l2_subdev *sd)
                  cntrl & CNTRL_TFE ? "enabled" : "disabled");
        v4l2_info(sd, "\tFIFO interrupt watermark:          %s\n",
                  cntrl & CNTRL_TIC ? "not empty" : "half full or less");
-       v4l2_info(sd, "\tSignal polarity:                   %s\n",
-                 cntrl & CNTRL_CPL ? "0:mark 1:space" : "0:space 1:mark");
+       v4l2_info(sd, "\tOutput pin level inversion         %s\n",
+                 cntrl & CNTRL_IVO ? "yes" : "no");
+       v4l2_info(sd, "\tCarrier polarity:                  %s\n",
+                 cntrl & CNTRL_CPL ? "space:burst mark:noburst"
+                                   : "space:noburst mark:burst");
        if (cntrl & CNTRL_MOD) {
                v4l2_info(sd, "\tCarrier (16 clocks):               %u Hz\n",
                          clock_divider_to_carrier_freq(txclk));
                v4l2_info(sd, "\tCarrier duty cycle:                %2u/16\n",
                          cduty + 1);
-       } else {
-               v4l2_info(sd, "\tMax pulse width:                   %u us, "
-                         "%llu ns\n",
-                         pulse_width_count_to_us(FIFO_RXTX, txclk),
-                         pulse_width_count_to_ns(FIFO_RXTX, txclk));
        }
+       v4l2_info(sd, "\tMax pulse width:                   %u us, %llu ns\n",
+                 pulse_width_count_to_us(FIFO_RXTX, txclk),
+                 pulse_width_count_to_ns(FIFO_RXTX, txclk));
        v4l2_info(sd, "\tBusy:                              %s\n",
                  stats & STATS_TBY ? "yes" : "no");
        v4l2_info(sd, "\tFIFO service requested:            %s\n",
@@ -1111,11 +1145,10 @@ static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = {
        .g_register = cx23888_ir_g_register,
        .s_register = cx23888_ir_s_register,
 #endif
+       .interrupt_service_routine = cx23888_ir_irq_handler,
 };
 
 static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = {
-       .interrupt_service_routine = cx23888_ir_irq_handler,
-
        .rx_read = cx23888_ir_rx_read,
        .rx_g_parameters = cx23888_ir_rx_g_parameters,
        .rx_s_parameters = cx23888_ir_rx_s_parameters,
@@ -1131,7 +1164,7 @@ static const struct v4l2_subdev_ops cx23888_ir_controller_ops = {
 };
 
 static const struct v4l2_subdev_ir_parameters default_rx_params = {
-       .bytes_per_data_element = sizeof(u32),
+       .bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec),
        .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
 
        .enable = false,
@@ -1146,11 +1179,11 @@ static const struct v4l2_subdev_ir_parameters default_rx_params = {
        .noise_filter_min_width = 333333, /* ns */
        .carrier_range_lower = 35000,
        .carrier_range_upper = 37000,
-       .invert = false,
+       .invert_level = false,
 };
 
 static const struct v4l2_subdev_ir_parameters default_tx_params = {
-       .bytes_per_data_element = sizeof(u32),
+       .bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec),
        .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
 
        .enable = false,
@@ -1160,7 +1193,8 @@ static const struct v4l2_subdev_ir_parameters default_tx_params = {
        .modulation = true,
        .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
        .duty_cycle = 25,      /* 25 %   - RC-5 carrier */
-       .invert = false,
+       .invert_level = false,
+       .invert_carrier_sense = false,
 };
 
 int cx23888_ir_probe(struct cx23885_dev *dev)
index 6e8665be8954f4699e783719bdb8f25df5b687bf..2ee96d3973b865277766ca95e78af54f10f98ff3 100644 (file)
@@ -1,5 +1,5 @@
 cx25840-objs    := cx25840-core.o cx25840-audio.o cx25840-firmware.o \
-                  cx25840-vbi.o
+                  cx25840-vbi.o cx25840-ir.o
 
 obj-$(CONFIG_VIDEO_CX25840) += cx25840.o
 
index 45608d50529c97233a8e0947976053b3dda3a2df..6faad34df3ac3b835449f08c76b09ba4fd0a2a6d 100644 (file)
@@ -474,33 +474,10 @@ void cx25840_audio_set_path(struct i2c_client *client)
                cx25840_and_or(client, 0x803, ~0x10, 0x10);
 }
 
-static int get_volume(struct i2c_client *client)
-{
-       struct cx25840_state *state = to_state(i2c_get_clientdata(client));
-       int vol;
-
-       if (state->unmute_volume >= 0)
-               return state->unmute_volume;
-
-       /* Volume runs +18dB to -96dB in 1/2dB steps
-        * change to fit the msp3400 -114dB to +12dB range */
-
-       /* check PATH1_VOLUME */
-       vol = 228 - cx25840_read(client, 0x8d4);
-       vol = (vol / 2) + 23;
-       return vol << 9;
-}
-
 static void set_volume(struct i2c_client *client, int volume)
 {
-       struct cx25840_state *state = to_state(i2c_get_clientdata(client));
        int vol;
 
-       if (state->unmute_volume >= 0) {
-               state->unmute_volume = volume;
-               return;
-       }
-
        /* Convert the volume to msp3400 values (0-127) */
        vol = volume >> 9;
 
@@ -517,52 +494,6 @@ static void set_volume(struct i2c_client *client, int volume)
        cx25840_write(client, 0x8d4, 228 - (vol * 2));
 }
 
-static int get_bass(struct i2c_client *client)
-{
-       /* bass is 49 steps +12dB to -12dB */
-
-       /* check PATH1_EQ_BASS_VOL */
-       int bass = cx25840_read(client, 0x8d9) & 0x3f;
-       bass = (((48 - bass) * 0xffff) + 47) / 48;
-       return bass;
-}
-
-static void set_bass(struct i2c_client *client, int bass)
-{
-       /* PATH1_EQ_BASS_VOL */
-       cx25840_and_or(client, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
-}
-
-static int get_treble(struct i2c_client *client)
-{
-       /* treble is 49 steps +12dB to -12dB */
-
-       /* check PATH1_EQ_TREBLE_VOL */
-       int treble = cx25840_read(client, 0x8db) & 0x3f;
-       treble = (((48 - treble) * 0xffff) + 47) / 48;
-       return treble;
-}
-
-static void set_treble(struct i2c_client *client, int treble)
-{
-       /* PATH1_EQ_TREBLE_VOL */
-       cx25840_and_or(client, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
-}
-
-static int get_balance(struct i2c_client *client)
-{
-       /* balance is 7 bit, 0 to -96dB */
-
-       /* check PATH1_BAL_LEVEL */
-       int balance = cx25840_read(client, 0x8d5) & 0x7f;
-       /* check PATH1_BAL_LEFT */
-       if ((cx25840_read(client, 0x8d5) & 0x80) == 0)
-               balance = 0x80 - balance;
-       else
-               balance = 0x80 + balance;
-       return balance << 8;
-}
-
 static void set_balance(struct i2c_client *client, int balance)
 {
        int bal = balance >> 8;
@@ -579,31 +510,6 @@ static void set_balance(struct i2c_client *client, int balance)
        }
 }
 
-static int get_mute(struct i2c_client *client)
-{
-       struct cx25840_state *state = to_state(i2c_get_clientdata(client));
-
-       return state->unmute_volume >= 0;
-}
-
-static void set_mute(struct i2c_client *client, int mute)
-{
-       struct cx25840_state *state = to_state(i2c_get_clientdata(client));
-
-       if (mute && state->unmute_volume == -1) {
-               int vol = get_volume(client);
-
-               set_volume(client, 0);
-               state->unmute_volume = vol;
-       }
-       else if (!mute && state->unmute_volume != -1) {
-               int vol = state->unmute_volume;
-
-               state->unmute_volume = -1;
-               set_volume(client, vol);
-       }
-}
-
 int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
 {
        struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -624,25 +530,31 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
        return retval;
 }
 
-int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int cx25840_audio_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+       struct v4l2_subdev *sd = to_sd(ctrl);
+       struct cx25840_state *state = to_state(sd);
        struct i2c_client *client = v4l2_get_subdevdata(sd);
 
        switch (ctrl->id) {
        case V4L2_CID_AUDIO_VOLUME:
-               ctrl->value = get_volume(client);
+               if (state->mute->val)
+                       set_volume(client, 0);
+               else
+                       set_volume(client, state->volume->val);
                break;
        case V4L2_CID_AUDIO_BASS:
-               ctrl->value = get_bass(client);
+               /* PATH1_EQ_BASS_VOL */
+               cx25840_and_or(client, 0x8d9, ~0x3f,
+                                       48 - (ctrl->val * 48 / 0xffff));
                break;
        case V4L2_CID_AUDIO_TREBLE:
-               ctrl->value = get_treble(client);
+               /* PATH1_EQ_TREBLE_VOL */
+               cx25840_and_or(client, 0x8db, ~0x3f,
+                                       48 - (ctrl->val * 48 / 0xffff));
                break;
        case V4L2_CID_AUDIO_BALANCE:
-               ctrl->value = get_balance(client);
-               break;
-       case V4L2_CID_AUDIO_MUTE:
-               ctrl->value = get_mute(client);
+               set_balance(client, ctrl->val);
                break;
        default:
                return -EINVAL;
@@ -650,28 +562,6 @@ int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
        return 0;
 }
 
-int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-
-       switch (ctrl->id) {
-       case V4L2_CID_AUDIO_VOLUME:
-               set_volume(client, ctrl->value);
-               break;
-       case V4L2_CID_AUDIO_BASS:
-               set_bass(client, ctrl->value);
-               break;
-       case V4L2_CID_AUDIO_TREBLE:
-               set_treble(client, ctrl->value);
-               break;
-       case V4L2_CID_AUDIO_BALANCE:
-               set_balance(client, ctrl->value);
-               break;
-       case V4L2_CID_AUDIO_MUTE:
-               set_mute(client, ctrl->value);
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
+const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = {
+       .s_ctrl = cx25840_audio_s_ctrl,
+};
index bb4872b2ceb0d0a6455d792f84ddfa9217c0014c..86ca8c2359dd8bb409cadd434dc951a17ff170a9 100644 (file)
@@ -15,6 +15,9 @@
  *
  * CX23885 support by Steven Toth <stoth@linuxtv.org>.
  *
+ * CX2388[578] IRQ handling, IO Pin mux configuration and other small fixes are
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
@@ -48,6 +51,28 @@ MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver");
 MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford");
 MODULE_LICENSE("GPL");
 
+#define CX25840_VID_INT_STAT_REG 0x410
+#define CX25840_VID_INT_STAT_BITS 0x0000ffff
+#define CX25840_VID_INT_MASK_BITS 0xffff0000
+#define CX25840_VID_INT_MASK_SHFT 16
+#define CX25840_VID_INT_MASK_REG 0x412
+
+#define CX23885_AUD_MC_INT_MASK_REG 0x80c
+#define CX23885_AUD_MC_INT_STAT_BITS 0xffff0000
+#define CX23885_AUD_MC_INT_CTRL_BITS 0x0000ffff
+#define CX23885_AUD_MC_INT_STAT_SHFT 16
+
+#define CX25840_AUD_INT_CTRL_REG 0x812
+#define CX25840_AUD_INT_STAT_REG 0x813
+
+#define CX23885_PIN_CTRL_IRQ_REG 0x123
+#define CX23885_PIN_CTRL_IRQ_IR_STAT  0x40
+#define CX23885_PIN_CTRL_IRQ_AUD_STAT 0x20
+#define CX23885_PIN_CTRL_IRQ_VID_STAT 0x10
+
+#define CX25840_IR_STATS_REG   0x210
+#define CX25840_IR_IRQEN_REG   0x214
+
 static int cx25840_debug;
 
 module_param_named(debug,cx25840_debug, int, 0644);
@@ -80,33 +105,53 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value)
 
 u8 cx25840_read(struct i2c_client * client, u16 addr)
 {
-       u8 buffer[2];
-       buffer[0] = addr >> 8;
-       buffer[1] = addr & 0xff;
-
-       if (i2c_master_send(client, buffer, 2) < 2)
-               return 0;
-
-       if (i2c_master_recv(client, buffer, 1) < 1)
+       struct i2c_msg msgs[2];
+       u8 tx_buf[2], rx_buf[1];
+
+       /* Write register address */
+       tx_buf[0] = addr >> 8;
+       tx_buf[1] = addr & 0xff;
+       msgs[0].addr = client->addr;
+       msgs[0].flags = 0;
+       msgs[0].len = 2;
+       msgs[0].buf = (char *) tx_buf;
+
+       /* Read data from register */
+       msgs[1].addr = client->addr;
+       msgs[1].flags = I2C_M_RD;
+       msgs[1].len = 1;
+       msgs[1].buf = (char *) rx_buf;
+
+       if (i2c_transfer(client->adapter, msgs, 2) < 2)
                return 0;
 
-       return buffer[0];
+       return rx_buf[0];
 }
 
 u32 cx25840_read4(struct i2c_client * client, u16 addr)
 {
-       u8 buffer[4];
-       buffer[0] = addr >> 8;
-       buffer[1] = addr & 0xff;
-
-       if (i2c_master_send(client, buffer, 2) < 2)
-               return 0;
-
-       if (i2c_master_recv(client, buffer, 4) < 4)
+       struct i2c_msg msgs[2];
+       u8 tx_buf[2], rx_buf[4];
+
+       /* Write register address */
+       tx_buf[0] = addr >> 8;
+       tx_buf[1] = addr & 0xff;
+       msgs[0].addr = client->addr;
+       msgs[0].flags = 0;
+       msgs[0].len = 2;
+       msgs[0].buf = (char *) tx_buf;
+
+       /* Read data from registers */
+       msgs[1].addr = client->addr;
+       msgs[1].flags = I2C_M_RD;
+       msgs[1].len = 4;
+       msgs[1].buf = (char *) rx_buf;
+
+       if (i2c_transfer(client->adapter, msgs, 2) < 2)
                return 0;
 
-       return (buffer[3] << 24) | (buffer[2] << 16) |
-           (buffer[1] << 8) | buffer[0];
+       return (rx_buf[3] << 24) | (rx_buf[2] << 16) | (rx_buf[1] << 8) |
+               rx_buf[0];
 }
 
 int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask,
@@ -117,6 +162,14 @@ int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask,
                             or_value);
 }
 
+int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask,
+                   u32 or_value)
+{
+       return cx25840_write4(client, addr,
+                             (cx25840_read4(client, addr) & and_mask) |
+                             or_value);
+}
+
 /* ----------------------------------------------------------------------- */
 
 static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input,
@@ -124,6 +177,158 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
 
 /* ----------------------------------------------------------------------- */
 
+static int cx23885_s_io_pin_config(struct v4l2_subdev *sd, size_t n,
+                                     struct v4l2_subdev_io_pin_config *p)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int i;
+       u32 pin_ctrl;
+       u8 gpio_oe, gpio_data, strength;
+
+       pin_ctrl = cx25840_read4(client, 0x120);
+       gpio_oe = cx25840_read(client, 0x160);
+       gpio_data = cx25840_read(client, 0x164);
+
+       for (i = 0; i < n; i++) {
+               strength = p[i].strength;
+               if (strength > CX25840_PIN_DRIVE_FAST)
+                       strength = CX25840_PIN_DRIVE_FAST;
+
+               switch (p[i].pin) {
+               case CX23885_PIN_IRQ_N_GPIO16:
+                       if (p[i].function != CX23885_PAD_IRQ_N) {
+                               /* GPIO16 */
+                               pin_ctrl &= ~(0x1 << 25);
+                       } else {
+                               /* IRQ_N */
+                               if (p[i].flags &
+                                       (V4L2_SUBDEV_IO_PIN_DISABLE |
+                                        V4L2_SUBDEV_IO_PIN_INPUT)) {
+                                       pin_ctrl &= ~(0x1 << 25);
+                               } else {
+                                       pin_ctrl |= (0x1 << 25);
+                               }
+                               if (p[i].flags &
+                                       V4L2_SUBDEV_IO_PIN_ACTIVE_LOW) {
+                                       pin_ctrl &= ~(0x1 << 24);
+                               } else {
+                                       pin_ctrl |= (0x1 << 24);
+                               }
+                       }
+                       break;
+               case CX23885_PIN_IR_RX_GPIO19:
+                       if (p[i].function != CX23885_PAD_GPIO19) {
+                               /* IR_RX */
+                               gpio_oe |= (0x1 << 0);
+                               pin_ctrl &= ~(0x3 << 18);
+                               pin_ctrl |= (strength << 18);
+                       } else {
+                               /* GPIO19 */
+                               gpio_oe &= ~(0x1 << 0);
+                               if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+                                       gpio_data &= ~(0x1 << 0);
+                                       gpio_data |= ((p[i].value & 0x1) << 0);
+                               }
+                               pin_ctrl &= ~(0x3 << 12);
+                               pin_ctrl |= (strength << 12);
+                       }
+                       break;
+               case CX23885_PIN_IR_TX_GPIO20:
+                       if (p[i].function != CX23885_PAD_GPIO20) {
+                               /* IR_TX */
+                               gpio_oe |= (0x1 << 1);
+                               if (p[i].flags & V4L2_SUBDEV_IO_PIN_DISABLE)
+                                       pin_ctrl &= ~(0x1 << 10);
+                               else
+                                       pin_ctrl |= (0x1 << 10);
+                               pin_ctrl &= ~(0x3 << 18);
+                               pin_ctrl |= (strength << 18);
+                       } else {
+                               /* GPIO20 */
+                               gpio_oe &= ~(0x1 << 1);
+                               if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+                                       gpio_data &= ~(0x1 << 1);
+                                       gpio_data |= ((p[i].value & 0x1) << 1);
+                               }
+                               pin_ctrl &= ~(0x3 << 12);
+                               pin_ctrl |= (strength << 12);
+                       }
+                       break;
+               case CX23885_PIN_I2S_SDAT_GPIO21:
+                       if (p[i].function != CX23885_PAD_GPIO21) {
+                               /* I2S_SDAT */
+                               /* TODO: Input or Output config */
+                               gpio_oe |= (0x1 << 2);
+                               pin_ctrl &= ~(0x3 << 22);
+                               pin_ctrl |= (strength << 22);
+                       } else {
+                               /* GPIO21 */
+                               gpio_oe &= ~(0x1 << 2);
+                               if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+                                       gpio_data &= ~(0x1 << 2);
+                                       gpio_data |= ((p[i].value & 0x1) << 2);
+                               }
+                               pin_ctrl &= ~(0x3 << 12);
+                               pin_ctrl |= (strength << 12);
+                       }
+                       break;
+               case CX23885_PIN_I2S_WCLK_GPIO22:
+                       if (p[i].function != CX23885_PAD_GPIO22) {
+                               /* I2S_WCLK */
+                               /* TODO: Input or Output config */
+                               gpio_oe |= (0x1 << 3);
+                               pin_ctrl &= ~(0x3 << 22);
+                               pin_ctrl |= (strength << 22);
+                       } else {
+                               /* GPIO22 */
+                               gpio_oe &= ~(0x1 << 3);
+                               if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+                                       gpio_data &= ~(0x1 << 3);
+                                       gpio_data |= ((p[i].value & 0x1) << 3);
+                               }
+                               pin_ctrl &= ~(0x3 << 12);
+                               pin_ctrl |= (strength << 12);
+                       }
+                       break;
+               case CX23885_PIN_I2S_BCLK_GPIO23:
+                       if (p[i].function != CX23885_PAD_GPIO23) {
+                               /* I2S_BCLK */
+                               /* TODO: Input or Output config */
+                               gpio_oe |= (0x1 << 4);
+                               pin_ctrl &= ~(0x3 << 22);
+                               pin_ctrl |= (strength << 22);
+                       } else {
+                               /* GPIO23 */
+                               gpio_oe &= ~(0x1 << 4);
+                               if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+                                       gpio_data &= ~(0x1 << 4);
+                                       gpio_data |= ((p[i].value & 0x1) << 4);
+                               }
+                               pin_ctrl &= ~(0x3 << 12);
+                               pin_ctrl |= (strength << 12);
+                       }
+                       break;
+               }
+       }
+
+       cx25840_write(client, 0x164, gpio_data);
+       cx25840_write(client, 0x160, gpio_oe);
+       cx25840_write4(client, 0x120, pin_ctrl);
+       return 0;
+}
+
+static int common_s_io_pin_config(struct v4l2_subdev *sd, size_t n,
+                                     struct v4l2_subdev_io_pin_config *pincfg)
+{
+       struct cx25840_state *state = to_state(sd);
+
+       if (is_cx2388x(state))
+               return cx23885_s_io_pin_config(sd, n, pincfg);
+       return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
 static void init_dll1(struct i2c_client *client)
 {
        /* This is the Hauppauge sequence used to
@@ -420,6 +625,13 @@ static void cx23885_initialize(struct i2c_client *client)
 
        /* start microcontroller */
        cx25840_and_or(client, 0x803, ~0x10, 0x10);
+
+       /* Disable and clear video interrupts - we don't use them */
+       cx25840_write4(client, CX25840_VID_INT_STAT_REG, 0xffffffff);
+
+       /* Disable and clear audio interrupts - we don't use them */
+       cx25840_write(client, CX25840_AUD_INT_CTRL_REG, 0xff);
+       cx25840_write(client, CX25840_AUD_INT_STAT_REG, 0xff);
 }
 
 /* ----------------------------------------------------------------------- */
@@ -909,102 +1121,29 @@ static int set_v4lstd(struct i2c_client *client)
 
 /* ----------------------------------------------------------------------- */
 
-static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct cx25840_state *state = to_state(sd);
+       struct v4l2_subdev *sd = to_sd(ctrl);
        struct i2c_client *client = v4l2_get_subdevdata(sd);
 
        switch (ctrl->id) {
-       case CX25840_CID_ENABLE_PVR150_WORKAROUND:
-               state->pvr150_workaround = ctrl->value;
-               set_input(client, state->vid_input, state->aud_input);
-               break;
-
        case V4L2_CID_BRIGHTNESS:
-               if (ctrl->value < 0 || ctrl->value > 255) {
-                       v4l_err(client, "invalid brightness setting %d\n",
-                                   ctrl->value);
-                       return -ERANGE;
-               }
-
-               cx25840_write(client, 0x414, ctrl->value - 128);
+               cx25840_write(client, 0x414, ctrl->val - 128);
                break;
 
        case V4L2_CID_CONTRAST:
-               if (ctrl->value < 0 || ctrl->value > 127) {
-                       v4l_err(client, "invalid contrast setting %d\n",
-                                   ctrl->value);
-                       return -ERANGE;
-               }
-
-               cx25840_write(client, 0x415, ctrl->value << 1);
+               cx25840_write(client, 0x415, ctrl->val << 1);
                break;
 
        case V4L2_CID_SATURATION:
-               if (ctrl->value < 0 || ctrl->value > 127) {
-                       v4l_err(client, "invalid saturation setting %d\n",
-                                   ctrl->value);
-                       return -ERANGE;
-               }
-
-               cx25840_write(client, 0x420, ctrl->value << 1);
-               cx25840_write(client, 0x421, ctrl->value << 1);
+               cx25840_write(client, 0x420, ctrl->val << 1);
+               cx25840_write(client, 0x421, ctrl->val << 1);
                break;
 
        case V4L2_CID_HUE:
-               if (ctrl->value < -128 || ctrl->value > 127) {
-                       v4l_err(client, "invalid hue setting %d\n", ctrl->value);
-                       return -ERANGE;
-               }
-
-               cx25840_write(client, 0x422, ctrl->value);
+               cx25840_write(client, 0x422, ctrl->val);
                break;
 
-       case V4L2_CID_AUDIO_VOLUME:
-       case V4L2_CID_AUDIO_BASS:
-       case V4L2_CID_AUDIO_TREBLE:
-       case V4L2_CID_AUDIO_BALANCE:
-       case V4L2_CID_AUDIO_MUTE:
-               if (is_cx2583x(state))
-                       return -EINVAL;
-               return cx25840_audio_s_ctrl(sd, ctrl);
-
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       struct cx25840_state *state = to_state(sd);
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-
-       switch (ctrl->id) {
-       case CX25840_CID_ENABLE_PVR150_WORKAROUND:
-               ctrl->value = state->pvr150_workaround;
-               break;
-       case V4L2_CID_BRIGHTNESS:
-               ctrl->value = (s8)cx25840_read(client, 0x414) + 128;
-               break;
-       case V4L2_CID_CONTRAST:
-               ctrl->value = cx25840_read(client, 0x415) >> 1;
-               break;
-       case V4L2_CID_SATURATION:
-               ctrl->value = cx25840_read(client, 0x420) >> 1;
-               break;
-       case V4L2_CID_HUE:
-               ctrl->value = (s8)cx25840_read(client, 0x422);
-               break;
-       case V4L2_CID_AUDIO_VOLUME:
-       case V4L2_CID_AUDIO_BASS:
-       case V4L2_CID_AUDIO_TREBLE:
-       case V4L2_CID_AUDIO_BALANCE:
-       case V4L2_CID_AUDIO_MUTE:
-               if (is_cx2583x(state))
-                       return -EINVAL;
-               return cx25840_audio_g_ctrl(sd, ctrl);
        default:
                return -EINVAL;
        }
@@ -1163,8 +1302,6 @@ static void log_audio_status(struct i2c_client *client)
        default: p = "not defined";
        }
        v4l_info(client, "Detected audio standard:   %s\n", p);
-       v4l_info(client, "Audio muted:               %s\n",
-                   (state->unmute_volume >= 0) ? "yes" : "no");
        v4l_info(client, "Audio microcontroller:     %s\n",
                    (download_ctl & 0x10) ?
                                ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped");
@@ -1381,40 +1518,6 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
        return 0;
 }
 
-static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
-       struct cx25840_state *state = to_state(sd);
-
-       switch (qc->id) {
-       case V4L2_CID_BRIGHTNESS:
-               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
-       case V4L2_CID_CONTRAST:
-       case V4L2_CID_SATURATION:
-               return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
-       case V4L2_CID_HUE:
-               return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
-       default:
-               break;
-       }
-       if (is_cx2583x(state))
-               return -EINVAL;
-
-       switch (qc->id) {
-       case V4L2_CID_AUDIO_VOLUME:
-               return v4l2_ctrl_query_fill(qc, 0, 65535,
-                               65535 / 100, state->default_volume);
-       case V4L2_CID_AUDIO_MUTE:
-               return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-       case V4L2_CID_AUDIO_BALANCE:
-       case V4L2_CID_AUDIO_BASS:
-       case V4L2_CID_AUDIO_TREBLE:
-               return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
-       default:
-               return -EINVAL;
-       }
-       return -EINVAL;
-}
-
 static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
 {
        struct cx25840_state *state = to_state(sd);
@@ -1576,24 +1679,134 @@ static int cx25840_log_status(struct v4l2_subdev *sd)
        log_video_status(client);
        if (!is_cx2583x(state))
                log_audio_status(client);
+       cx25840_ir_log_status(sd);
+       v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
+       return 0;
+}
+
+static int cx25840_s_config(struct v4l2_subdev *sd, int irq, void *platform_data)
+{
+       struct cx25840_state *state = to_state(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+       if (platform_data) {
+               struct cx25840_platform_data *pdata = platform_data;
+
+               state->pvr150_workaround = pdata->pvr150_workaround;
+               set_input(client, state->vid_input, state->aud_input);
+       }
        return 0;
 }
 
+static int cx23885_irq_handler(struct v4l2_subdev *sd, u32 status,
+                              bool *handled)
+{
+       struct cx25840_state *state = to_state(sd);
+       struct i2c_client *c = v4l2_get_subdevdata(sd);
+       u8 irq_stat, aud_stat, aud_en, ir_stat, ir_en;
+       u32 vid_stat, aud_mc_stat;
+       bool block_handled;
+       int ret = 0;
+
+       irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG);
+       v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (entry): %s %s %s\n",
+               irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : "  ",
+               irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : "   ",
+               irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : "   ");
+
+       if ((is_cx23885(state) || is_cx23887(state))) {
+               ir_stat = cx25840_read(c, CX25840_IR_STATS_REG);
+               ir_en = cx25840_read(c, CX25840_IR_IRQEN_REG);
+               v4l_dbg(2, cx25840_debug, c,
+                       "AV Core ir IRQ status: %#04x disables: %#04x\n",
+                       ir_stat, ir_en);
+               if (irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT) {
+                       block_handled = false;
+                       ret = cx25840_ir_irq_handler(sd,
+                                                    status, &block_handled);
+                       if (block_handled)
+                               *handled = true;
+               }
+       }
+
+       aud_stat = cx25840_read(c, CX25840_AUD_INT_STAT_REG);
+       aud_en = cx25840_read(c, CX25840_AUD_INT_CTRL_REG);
+       v4l_dbg(2, cx25840_debug, c,
+               "AV Core audio IRQ status: %#04x disables: %#04x\n",
+               aud_stat, aud_en);
+       aud_mc_stat = cx25840_read4(c, CX23885_AUD_MC_INT_MASK_REG);
+       v4l_dbg(2, cx25840_debug, c,
+               "AV Core audio MC IRQ status: %#06x enables: %#06x\n",
+               aud_mc_stat >> CX23885_AUD_MC_INT_STAT_SHFT,
+               aud_mc_stat & CX23885_AUD_MC_INT_CTRL_BITS);
+       if (irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT) {
+               if (aud_stat) {
+                       cx25840_write(c, CX25840_AUD_INT_STAT_REG, aud_stat);
+                       *handled = true;
+               }
+       }
+
+       vid_stat = cx25840_read4(c, CX25840_VID_INT_STAT_REG);
+       v4l_dbg(2, cx25840_debug, c,
+               "AV Core video IRQ status: %#06x disables: %#06x\n",
+               vid_stat & CX25840_VID_INT_STAT_BITS,
+               vid_stat >> CX25840_VID_INT_MASK_SHFT);
+       if (irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT) {
+               if (vid_stat & CX25840_VID_INT_STAT_BITS) {
+                       cx25840_write4(c, CX25840_VID_INT_STAT_REG, vid_stat);
+                       *handled = true;
+               }
+       }
+
+       irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG);
+       v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (exit): %s %s %s\n",
+               irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : "  ",
+               irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : "   ",
+               irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : "   ");
+
+       return ret;
+}
+
+static int cx25840_irq_handler(struct v4l2_subdev *sd, u32 status,
+                              bool *handled)
+{
+       struct cx25840_state *state = to_state(sd);
+
+       *handled = false;
+
+       /* Only support the CX2388[578] AV Core for now */
+       if (is_cx2388x(state))
+               return cx23885_irq_handler(sd, status, handled);
+
+       return -ENODEV;
+}
+
 /* ----------------------------------------------------------------------- */
 
+static const struct v4l2_ctrl_ops cx25840_ctrl_ops = {
+       .s_ctrl = cx25840_s_ctrl,
+};
+
 static const struct v4l2_subdev_core_ops cx25840_core_ops = {
        .log_status = cx25840_log_status,
+       .s_config = cx25840_s_config,
        .g_chip_ident = cx25840_g_chip_ident,
-       .g_ctrl = cx25840_g_ctrl,
-       .s_ctrl = cx25840_s_ctrl,
-       .queryctrl = cx25840_queryctrl,
+       .g_ctrl = v4l2_subdev_g_ctrl,
+       .s_ctrl = v4l2_subdev_s_ctrl,
+       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+       .queryctrl = v4l2_subdev_queryctrl,
+       .querymenu = v4l2_subdev_querymenu,
        .s_std = cx25840_s_std,
        .reset = cx25840_reset,
        .load_fw = cx25840_load_fw,
+       .s_io_pin_config = common_s_io_pin_config,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        .g_register = cx25840_g_register,
        .s_register = cx25840_s_register,
 #endif
+       .interrupt_service_routine = cx25840_irq_handler,
 };
 
 static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
@@ -1628,6 +1841,7 @@ static const struct v4l2_subdev_ops cx25840_ops = {
        .audio = &cx25840_audio_ops,
        .video = &cx25840_video_ops,
        .vbi = &cx25840_vbi_ops,
+       .ir = &cx25840_ir_ops,
 };
 
 /* ----------------------------------------------------------------------- */
@@ -1675,6 +1889,7 @@ static int cx25840_probe(struct i2c_client *client,
 {
        struct cx25840_state *state;
        struct v4l2_subdev *sd;
+       int default_volume;
        u32 id = V4L2_IDENT_NONE;
        u16 device_id;
 
@@ -1718,6 +1933,7 @@ static int cx25840_probe(struct i2c_client *client,
 
        sd = &state->sd;
        v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
+
        switch (id) {
        case V4L2_IDENT_CX23885_AV:
                v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n",
@@ -1762,22 +1978,62 @@ static int cx25840_probe(struct i2c_client *client,
        state->audclk_freq = 48000;
        state->pvr150_workaround = 0;
        state->audmode = V4L2_TUNER_MODE_LANG1;
-       state->unmute_volume = -1;
-       state->default_volume = 228 - cx25840_read(client, 0x8d4);
-       state->default_volume = ((state->default_volume / 2) + 23) << 9;
        state->vbi_line_offset = 8;
        state->id = id;
        state->rev = device_id;
+       v4l2_ctrl_handler_init(&state->hdl, 9);
+       v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+       v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 127, 1, 64);
+       v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 127, 1, 64);
+       v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+                       V4L2_CID_HUE, -128, 127, 1, 0);
+       if (!is_cx2583x(state)) {
+               default_volume = 228 - cx25840_read(client, 0x8d4);
+               default_volume = ((default_volume / 2) + 23) << 9;
+
+               state->volume = v4l2_ctrl_new_std(&state->hdl,
+                       &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME,
+                       0, 65335, 65535 / 100, default_volume);
+               state->mute = v4l2_ctrl_new_std(&state->hdl,
+                       &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE,
+                       0, 1, 1, 0);
+               v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+                       V4L2_CID_AUDIO_BALANCE,
+                       0, 65535, 65535 / 100, 32768);
+               v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+                       V4L2_CID_AUDIO_BASS,
+                       0, 65535, 65535 / 100, 32768);
+               v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+                       V4L2_CID_AUDIO_TREBLE,
+                       0, 65535, 65535 / 100, 32768);
+       }
+       sd->ctrl_handler = &state->hdl;
+       if (state->hdl.error) {
+               int err = state->hdl.error;
+
+               v4l2_ctrl_handler_free(&state->hdl);
+               kfree(state);
+               return err;
+       }
+       v4l2_ctrl_cluster(2, &state->volume);
+       v4l2_ctrl_handler_setup(&state->hdl);
 
+       cx25840_ir_probe(sd);
        return 0;
 }
 
 static int cx25840_remove(struct i2c_client *client)
 {
        struct v4l2_subdev *sd = i2c_get_clientdata(client);
+       struct cx25840_state *state = to_state(sd);
 
+       cx25840_ir_remove(sd);
        v4l2_device_unregister_subdev(sd);
-       kfree(to_state(sd));
+       v4l2_ctrl_handler_free(&state->hdl);
+       kfree(state);
        return 0;
 }
 
index 04393b9715671eb1ef31894ae7e7c243d053a0c5..bd4ada28b490ad40619ca21de690b6cdfb84f9f8 100644 (file)
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
 #include <linux/i2c.h>
 
-/* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is
-   present in Hauppauge PVR-150 (and possibly PVR-500) cards that have
-   certain NTSC tuners (tveeprom tuner model numbers 85, 99 and 112). The
-   audio autodetect fails on some channels for these models and the workaround
-   is to select the audio standard explicitly. Many thanks to Hauppauge for
-   providing this information. */
-#define CX25840_CID_ENABLE_PVR150_WORKAROUND (V4L2_CID_PRIVATE_BASE+0)
+struct cx25840_ir_state;
 
 struct cx25840_state {
        struct i2c_client *c;
        struct v4l2_subdev sd;
+       struct v4l2_ctrl_handler hdl;
+       struct {
+               /* volume cluster */
+               struct v4l2_ctrl *volume;
+               struct v4l2_ctrl *mute;
+       };
        int pvr150_workaround;
        int radio;
        v4l2_std_id std;
@@ -44,14 +45,13 @@ struct cx25840_state {
        enum cx25840_audio_input aud_input;
        u32 audclk_freq;
        int audmode;
-       int unmute_volume; /* -1 if not muted */
-       int default_volume;
        int vbi_line_offset;
        u32 id;
        u32 rev;
        int is_initialized;
        wait_queue_head_t fw_wait;    /* wake up when the fw load is finished */
        struct work_struct fw_work;   /* work entry for fw load */
+       struct cx25840_ir_state *ir_state;
 };
 
 static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
@@ -59,6 +59,11 @@ static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
        return container_of(sd, struct cx25840_state, sd);
 }
 
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+       return &container_of(ctrl->handler, struct cx25840_state, hdl)->sd;
+}
+
 static inline bool is_cx2583x(struct cx25840_state *state)
 {
        return state->id == V4L2_IDENT_CX25836 ||
@@ -77,6 +82,21 @@ static inline bool is_cx2388x(struct cx25840_state *state)
               state->id == V4L2_IDENT_CX23888_AV;
 }
 
+static inline bool is_cx23885(struct cx25840_state *state)
+{
+       return state->id == V4L2_IDENT_CX23885_AV;
+}
+
+static inline bool is_cx23887(struct cx25840_state *state)
+{
+       return state->id == V4L2_IDENT_CX23887_AV;
+}
+
+static inline bool is_cx23888(struct cx25840_state *state)
+{
+       return state->id == V4L2_IDENT_CX23888_AV;
+}
+
 /* ----------------------------------------------------------------------- */
 /* cx25850-core.c                                                         */
 int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
@@ -84,6 +104,8 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value);
 u8 cx25840_read(struct i2c_client *client, u16 addr);
 u32 cx25840_read4(struct i2c_client *client, u16 addr);
 int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value);
+int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask,
+                   u32 or_value);
 void cx25840_std_setup(struct i2c_client *client);
 
 /* ----------------------------------------------------------------------- */
@@ -94,8 +116,8 @@ int cx25840_loadfw(struct i2c_client *client);
 /* cx25850-audio.c                                                         */
 void cx25840_audio_set_path(struct i2c_client *client);
 int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
-int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
-int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+
+extern const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops;
 
 /* ----------------------------------------------------------------------- */
 /* cx25850-vbi.c                                                           */
@@ -104,4 +126,12 @@ int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *
 int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
 int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi);
 
+/* ----------------------------------------------------------------------- */
+/* cx25850-ir.c                                                            */
+extern const struct v4l2_subdev_ir_ops cx25840_ir_ops;
+int cx25840_ir_log_status(struct v4l2_subdev *sd);
+int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled);
+int cx25840_ir_probe(struct v4l2_subdev *sd);
+int cx25840_ir_remove(struct v4l2_subdev *sd);
+
 #endif
diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c
new file mode 100644 (file)
index 0000000..c2b4c14
--- /dev/null
@@ -0,0 +1,1279 @@
+/*
+ *  Driver for the Conexant CX2584x Audio/Video decoder chip and related cores
+ *
+ *  Integrated Consumer Infrared Controller
+ *
+ *  Copyright (C) 2010  Andy Walls <awalls@md.metrocast.net>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/kfifo.h>
+#include <media/cx25840.h>
+#include <media/ir-core.h>
+
+#include "cx25840-core.h"
+
+static unsigned int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable integrated IR debug messages");
+
+#define CX25840_IR_REG_BASE    0x200
+
+#define CX25840_IR_CNTRL_REG   0x200
+#define CNTRL_WIN_3_3  0x00000000
+#define CNTRL_WIN_4_3  0x00000001
+#define CNTRL_WIN_3_4  0x00000002
+#define CNTRL_WIN_4_4  0x00000003
+#define CNTRL_WIN      0x00000003
+#define CNTRL_EDG_NONE 0x00000000
+#define CNTRL_EDG_FALL 0x00000004
+#define CNTRL_EDG_RISE 0x00000008
+#define CNTRL_EDG_BOTH 0x0000000C
+#define CNTRL_EDG      0x0000000C
+#define CNTRL_DMD      0x00000010
+#define CNTRL_MOD      0x00000020
+#define CNTRL_RFE      0x00000040
+#define CNTRL_TFE      0x00000080
+#define CNTRL_RXE      0x00000100
+#define CNTRL_TXE      0x00000200
+#define CNTRL_RIC      0x00000400
+#define CNTRL_TIC      0x00000800
+#define CNTRL_CPL      0x00001000
+#define CNTRL_LBM      0x00002000
+#define CNTRL_R                0x00004000
+
+#define CX25840_IR_TXCLK_REG   0x204
+#define TXCLK_TCD      0x0000FFFF
+
+#define CX25840_IR_RXCLK_REG   0x208
+#define RXCLK_RCD      0x0000FFFF
+
+#define CX25840_IR_CDUTY_REG   0x20C
+#define CDUTY_CDC      0x0000000F
+
+#define CX25840_IR_STATS_REG   0x210
+#define STATS_RTO      0x00000001
+#define STATS_ROR      0x00000002
+#define STATS_RBY      0x00000004
+#define STATS_TBY      0x00000008
+#define STATS_RSR      0x00000010
+#define STATS_TSR      0x00000020
+
+#define CX25840_IR_IRQEN_REG   0x214
+#define IRQEN_RTE      0x00000001
+#define IRQEN_ROE      0x00000002
+#define IRQEN_RSE      0x00000010
+#define IRQEN_TSE      0x00000020
+#define IRQEN_MSK      0x00000033
+
+#define CX25840_IR_FILTR_REG   0x218
+#define FILTR_LPF      0x0000FFFF
+
+#define CX25840_IR_FIFO_REG    0x23C
+#define FIFO_RXTX      0x0000FFFF
+#define FIFO_RXTX_LVL  0x00010000
+#define FIFO_RXTX_RTO  0x0001FFFF
+#define FIFO_RX_NDV    0x00020000
+#define FIFO_RX_DEPTH  8
+#define FIFO_TX_DEPTH  8
+
+#define CX25840_VIDCLK_FREQ    108000000 /* 108 MHz, BT.656 */
+#define CX25840_IR_REFCLK_FREQ (CX25840_VIDCLK_FREQ / 2)
+
+/*
+ * We use this union internally for convenience, but callers to tx_write
+ * and rx_read will be expecting records of type struct ir_raw_event.
+ * Always ensure the size of this union is dictated by struct ir_raw_event.
+ */
+union cx25840_ir_fifo_rec {
+       u32 hw_fifo_data;
+       struct ir_raw_event ir_core_data;
+};
+
+#define CX25840_IR_RX_KFIFO_SIZE    (256 * sizeof(union cx25840_ir_fifo_rec))
+#define CX25840_IR_TX_KFIFO_SIZE    (256 * sizeof(union cx25840_ir_fifo_rec))
+
+struct cx25840_ir_state {
+       struct i2c_client *c;
+
+       struct v4l2_subdev_ir_parameters rx_params;
+       struct mutex rx_params_lock; /* protects Rx parameter settings cache */
+       atomic_t rxclk_divider;
+       atomic_t rx_invert;
+
+       struct kfifo rx_kfifo;
+       spinlock_t rx_kfifo_lock; /* protect Rx data kfifo */
+
+       struct v4l2_subdev_ir_parameters tx_params;
+       struct mutex tx_params_lock; /* protects Tx parameter settings cache */
+       atomic_t txclk_divider;
+};
+
+static inline struct cx25840_ir_state *to_ir_state(struct v4l2_subdev *sd)
+{
+       struct cx25840_state *state = to_state(sd);
+       return state ? state->ir_state : NULL;
+}
+
+
+/*
+ * Rx and Tx Clock Divider register computations
+ *
+ * Note the largest clock divider value of 0xffff corresponds to:
+ *     (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_clock_divider(unsigned int d)
+{
+       if (d > RXCLK_RCD + 1)
+               d = RXCLK_RCD;
+       else if (d < 2)
+               d = 1;
+       else
+               d--;
+       return (u16) d;
+}
+
+static inline u16 ns_to_clock_divider(unsigned int ns)
+{
+       return count_to_clock_divider(
+               DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int clock_divider_to_ns(unsigned int divider)
+{
+       /* Period of the Rx or Tx clock in ns */
+       return DIV_ROUND_CLOSEST((divider + 1) * 1000,
+                                CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline u16 carrier_freq_to_clock_divider(unsigned int freq)
+{
+       return count_to_clock_divider(
+                         DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * 16));
+}
+
+static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider)
+{
+       return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, (divider + 1) * 16);
+}
+
+static inline u16 freq_to_clock_divider(unsigned int freq,
+                                       unsigned int rollovers)
+{
+       return count_to_clock_divider(
+                  DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * rollovers));
+}
+
+static inline unsigned int clock_divider_to_freq(unsigned int divider,
+                                                unsigned int rollovers)
+{
+       return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ,
+                                (divider + 1) * rollovers);
+}
+
+/*
+ * Low Pass Filter register calculations
+ *
+ * Note the largest count value of 0xffff corresponds to:
+ *     0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_lpf_count(unsigned int d)
+{
+       if (d > FILTR_LPF)
+               d = FILTR_LPF;
+       else if (d < 4)
+               d = 0;
+       return (u16) d;
+}
+
+static inline u16 ns_to_lpf_count(unsigned int ns)
+{
+       return count_to_lpf_count(
+               DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int lpf_count_to_ns(unsigned int count)
+{
+       /* Duration of the Low Pass Filter rejection window in ns */
+       return DIV_ROUND_CLOSEST(count * 1000,
+                                CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline unsigned int lpf_count_to_us(unsigned int count)
+{
+       /* Duration of the Low Pass Filter rejection window in us */
+       return DIV_ROUND_CLOSEST(count, CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+/*
+ * FIFO register pulse width count compuations
+ */
+static u32 clock_divider_to_resolution(u16 divider)
+{
+       /*
+        * Resolution is the duration of 1 tick of the readable portion of
+        * of the pulse width counter as read from the FIFO.  The two lsb's are
+        * not readable, hence the << 2.  This function returns ns.
+        */
+       return DIV_ROUND_CLOSEST((1 << 2)  * ((u32) divider + 1) * 1000,
+                                CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static u64 pulse_width_count_to_ns(u16 count, u16 divider)
+{
+       u64 n;
+       u32 rem;
+
+       /*
+        * The 2 lsb's of the pulse width timer count are not readable, hence
+        * the (count << 2) | 0x3
+        */
+       n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */
+       rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000);     /* / MHz => ns */
+       if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2)
+               n++;
+       return n;
+}
+
+#if 0
+/* Keep as we will need this for Transmit functionality */
+static u16 ns_to_pulse_width_count(u32 ns, u16 divider)
+{
+       u64 n;
+       u32 d;
+       u32 rem;
+
+       /*
+        * The 2 lsb's of the pulse width timer count are not accessable, hence
+        * the (1 << 2)
+        */
+       n = ((u64) ns) * CX25840_IR_REFCLK_FREQ / 1000000; /* millicycles */
+       d = (1 << 2) * ((u32) divider + 1) * 1000; /* millicycles/count */
+       rem = do_div(n, d);
+       if (rem >= d / 2)
+               n++;
+
+       if (n > FIFO_RXTX)
+               n = FIFO_RXTX;
+       else if (n == 0)
+               n = 1;
+       return (u16) n;
+}
+
+#endif
+static unsigned int pulse_width_count_to_us(u16 count, u16 divider)
+{
+       u64 n;
+       u32 rem;
+
+       /*
+        * The 2 lsb's of the pulse width timer count are not readable, hence
+        * the (count << 2) | 0x3
+        */
+       n = (((u64) count << 2) | 0x3) * (divider + 1);    /* cycles      */
+       rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => us */
+       if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2)
+               n++;
+       return (unsigned int) n;
+}
+
+/*
+ * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts
+ *
+ * The total pulse clock count is an 18 bit pulse width timer count as the most
+ * significant part and (up to) 16 bit clock divider count as a modulus.
+ * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse
+ * width timer count's least significant bit.
+ */
+static u64 ns_to_pulse_clocks(u32 ns)
+{
+       u64 clocks;
+       u32 rem;
+       clocks = CX25840_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles  */
+       rem = do_div(clocks, 1000);                         /* /1000 = cycles */
+       if (rem >= 1000 / 2)
+               clocks++;
+       return clocks;
+}
+
+static u16 pulse_clocks_to_clock_divider(u64 count)
+{
+       u32 rem;
+
+       rem = do_div(count, (FIFO_RXTX << 2) | 0x3);
+
+       /* net result needs to be rounded down and decremented by 1 */
+       if (count > RXCLK_RCD + 1)
+               count = RXCLK_RCD;
+       else if (count < 2)
+               count = 1;
+       else
+               count--;
+       return (u16) count;
+}
+
+/*
+ * IR Control Register helpers
+ */
+enum tx_fifo_watermark {
+       TX_FIFO_HALF_EMPTY = 0,
+       TX_FIFO_EMPTY      = CNTRL_TIC,
+};
+
+enum rx_fifo_watermark {
+       RX_FIFO_HALF_FULL = 0,
+       RX_FIFO_NOT_EMPTY = CNTRL_RIC,
+};
+
+static inline void control_tx_irq_watermark(struct i2c_client *c,
+                                           enum tx_fifo_watermark level)
+{
+       cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_TIC, level);
+}
+
+static inline void control_rx_irq_watermark(struct i2c_client *c,
+                                           enum rx_fifo_watermark level)
+{
+       cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_RIC, level);
+}
+
+static inline void control_tx_enable(struct i2c_client *c, bool enable)
+{
+       cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE),
+                       enable ? (CNTRL_TXE | CNTRL_TFE) : 0);
+}
+
+static inline void control_rx_enable(struct i2c_client *c, bool enable)
+{
+       cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE),
+                       enable ? (CNTRL_RXE | CNTRL_RFE) : 0);
+}
+
+static inline void control_tx_modulation_enable(struct i2c_client *c,
+                                               bool enable)
+{
+       cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_MOD,
+                       enable ? CNTRL_MOD : 0);
+}
+
+static inline void control_rx_demodulation_enable(struct i2c_client *c,
+                                                 bool enable)
+{
+       cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_DMD,
+                       enable ? CNTRL_DMD : 0);
+}
+
+static inline void control_rx_s_edge_detection(struct i2c_client *c,
+                                              u32 edge_types)
+{
+       cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_EDG_BOTH,
+                       edge_types & CNTRL_EDG_BOTH);
+}
+
+static void control_rx_s_carrier_window(struct i2c_client *c,
+                                       unsigned int carrier,
+                                       unsigned int *carrier_range_low,
+                                       unsigned int *carrier_range_high)
+{
+       u32 v;
+       unsigned int c16 = carrier * 16;
+
+       if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) {
+               v = CNTRL_WIN_3_4;
+               *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4);
+       } else {
+               v = CNTRL_WIN_3_3;
+               *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3);
+       }
+
+       if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) {
+               v |= CNTRL_WIN_4_3;
+               *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4);
+       } else {
+               v |= CNTRL_WIN_3_3;
+               *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3);
+       }
+       cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_WIN, v);
+}
+
+static inline void control_tx_polarity_invert(struct i2c_client *c,
+                                             bool invert)
+{
+       cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_CPL,
+                       invert ? CNTRL_CPL : 0);
+}
+
+/*
+ * IR Rx & Tx Clock Register helpers
+ */
+static unsigned int txclk_tx_s_carrier(struct i2c_client *c,
+                                      unsigned int freq,
+                                      u16 *divider)
+{
+       *divider = carrier_freq_to_clock_divider(freq);
+       cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider);
+       return clock_divider_to_carrier_freq(*divider);
+}
+
+static unsigned int rxclk_rx_s_carrier(struct i2c_client *c,
+                                      unsigned int freq,
+                                      u16 *divider)
+{
+       *divider = carrier_freq_to_clock_divider(freq);
+       cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider);
+       return clock_divider_to_carrier_freq(*divider);
+}
+
+static u32 txclk_tx_s_max_pulse_width(struct i2c_client *c, u32 ns,
+                                     u16 *divider)
+{
+       u64 pulse_clocks;
+
+       if (ns > IR_MAX_DURATION)
+               ns = IR_MAX_DURATION;
+       pulse_clocks = ns_to_pulse_clocks(ns);
+       *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+       cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider);
+       return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+static u32 rxclk_rx_s_max_pulse_width(struct i2c_client *c, u32 ns,
+                                     u16 *divider)
+{
+       u64 pulse_clocks;
+
+       if (ns > IR_MAX_DURATION)
+               ns = IR_MAX_DURATION;
+       pulse_clocks = ns_to_pulse_clocks(ns);
+       *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+       cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider);
+       return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+/*
+ * IR Tx Carrier Duty Cycle register helpers
+ */
+static unsigned int cduty_tx_s_duty_cycle(struct i2c_client *c,
+                                         unsigned int duty_cycle)
+{
+       u32 n;
+       n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */
+       if (n != 0)
+               n--;
+       if (n > 15)
+               n = 15;
+       cx25840_write4(c, CX25840_IR_CDUTY_REG, n);
+       return DIV_ROUND_CLOSEST((n + 1) * 100, 16);
+}
+
+/*
+ * IR Filter Register helpers
+ */
+static u32 filter_rx_s_min_width(struct i2c_client *c, u32 min_width_ns)
+{
+       u32 count = ns_to_lpf_count(min_width_ns);
+       cx25840_write4(c, CX25840_IR_FILTR_REG, count);
+       return lpf_count_to_ns(count);
+}
+
+/*
+ * IR IRQ Enable Register helpers
+ */
+static inline void irqenable_rx(struct v4l2_subdev *sd, u32 mask)
+{
+       struct cx25840_state *state = to_state(sd);
+
+       if (is_cx23885(state) || is_cx23887(state))
+               mask ^= IRQEN_MSK;
+       mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE);
+       cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG,
+                       ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask);
+}
+
+static inline void irqenable_tx(struct v4l2_subdev *sd, u32 mask)
+{
+       struct cx25840_state *state = to_state(sd);
+
+       if (is_cx23885(state) || is_cx23887(state))
+               mask ^= IRQEN_MSK;
+       mask &= IRQEN_TSE;
+       cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, ~IRQEN_TSE, mask);
+}
+
+/*
+ * V4L2 Subdevice IR Ops
+ */
+int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled)
+{
+       struct cx25840_state *state = to_state(sd);
+       struct cx25840_ir_state *ir_state = to_ir_state(sd);
+       struct i2c_client *c = NULL;
+       unsigned long flags;
+
+       union cx25840_ir_fifo_rec rx_data[FIFO_RX_DEPTH];
+       unsigned int i, j, k;
+       u32 events, v;
+       int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
+       u32 cntrl, irqen, stats;
+
+       *handled = false;
+       if (ir_state == NULL)
+               return -ENODEV;
+
+       c = ir_state->c;
+
+       /* Only support the IR controller for the CX2388[57] AV Core for now */
+       if (!(is_cx23885(state) || is_cx23887(state)))
+               return -ENODEV;
+
+       cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG);
+       irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG);
+       if (is_cx23885(state) || is_cx23887(state))
+               irqen ^= IRQEN_MSK;
+       stats = cx25840_read4(c, CX25840_IR_STATS_REG);
+
+       tsr = stats & STATS_TSR; /* Tx FIFO Service Request */
+       rsr = stats & STATS_RSR; /* Rx FIFO Service Request */
+       rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */
+       ror = stats & STATS_ROR; /* Rx FIFO Over Run */
+
+       tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */
+       rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */
+       rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */
+       roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */
+
+       v4l2_dbg(2, ir_debug, sd, "IR IRQ Status:  %s %s %s %s %s %s\n",
+                tsr ? "tsr" : "   ", rsr ? "rsr" : "   ",
+                rto ? "rto" : "   ", ror ? "ror" : "   ",
+                stats & STATS_TBY ? "tby" : "   ",
+                stats & STATS_RBY ? "rby" : "   ");
+
+       v4l2_dbg(2, ir_debug, sd, "IR IRQ Enables: %s %s %s %s\n",
+                tse ? "tse" : "   ", rse ? "rse" : "   ",
+                rte ? "rte" : "   ", roe ? "roe" : "   ");
+
+       /*
+        * Transmitter interrupt service
+        */
+       if (tse && tsr) {
+               /*
+                * TODO:
+                * Check the watermark threshold setting
+                * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo
+                * Push the data to the hardware FIFO.
+                * If there was nothing more to send in the tx_kfifo, disable
+                *      the TSR IRQ and notify the v4l2_device.
+                * If there was something in the tx_kfifo, check the tx_kfifo
+                *      level and notify the v4l2_device, if it is low.
+                */
+               /* For now, inhibit TSR interrupt until Tx is implemented */
+               irqenable_tx(sd, 0);
+               events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
+               v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events);
+               *handled = true;
+       }
+
+       /*
+        * Receiver interrupt service
+        */
+       kror = 0;
+       if ((rse && rsr) || (rte && rto)) {
+               /*
+                * Receive data on RSR to clear the STATS_RSR.
+                * Receive data on RTO, since we may not have yet hit the RSR
+                * watermark when we receive the RTO.
+                */
+               for (i = 0, v = FIFO_RX_NDV;
+                    (v & FIFO_RX_NDV) && !kror; i = 0) {
+                       for (j = 0;
+                            (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
+                               v = cx25840_read4(c, CX25840_IR_FIFO_REG);
+                               rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV;
+                               i++;
+                       }
+                       if (i == 0)
+                               break;
+                       j = i * sizeof(union cx25840_ir_fifo_rec);
+                       k = kfifo_in_locked(&ir_state->rx_kfifo,
+                                           (unsigned char *) rx_data, j,
+                                           &ir_state->rx_kfifo_lock);
+                       if (k != j)
+                               kror++; /* rx_kfifo over run */
+               }
+               *handled = true;
+       }
+
+       events = 0;
+       v = 0;
+       if (kror) {
+               events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
+               v4l2_err(sd, "IR receiver software FIFO overrun\n");
+       }
+       if (roe && ror) {
+               /*
+                * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear
+                * the Rx FIFO Over Run status (STATS_ROR)
+                */
+               v |= CNTRL_RFE;
+               events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
+               v4l2_err(sd, "IR receiver hardware FIFO overrun\n");
+       }
+       if (rte && rto) {
+               /*
+                * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear
+                * the Rx Pulse Width Timer Time Out (STATS_RTO)
+                */
+               v |= CNTRL_RXE;
+               events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
+       }
+       if (v) {
+               /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */
+               cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl & ~v);
+               cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl);
+               *handled = true;
+       }
+       spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags);
+       if (kfifo_len(&ir_state->rx_kfifo) >= CX25840_IR_RX_KFIFO_SIZE / 2)
+               events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
+       spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags);
+
+       if (events)
+               v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events);
+       return 0;
+}
+
+/* Receiver */
+static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
+                             ssize_t *num)
+{
+       struct cx25840_ir_state *ir_state = to_ir_state(sd);
+       bool invert;
+       u16 divider;
+       unsigned int i, n;
+       union cx25840_ir_fifo_rec *p;
+       unsigned u, v;
+
+       if (ir_state == NULL)
+               return -ENODEV;
+
+       invert = (bool) atomic_read(&ir_state->rx_invert);
+       divider = (u16) atomic_read(&ir_state->rxclk_divider);
+
+       n = count / sizeof(union cx25840_ir_fifo_rec)
+               * sizeof(union cx25840_ir_fifo_rec);
+       if (n == 0) {
+               *num = 0;
+               return 0;
+       }
+
+       n = kfifo_out_locked(&ir_state->rx_kfifo, buf, n,
+                            &ir_state->rx_kfifo_lock);
+
+       n /= sizeof(union cx25840_ir_fifo_rec);
+       *num = n * sizeof(union cx25840_ir_fifo_rec);
+
+       for (p = (union cx25840_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) {
+
+               if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
+                       /* Assume RTO was because of no IR light input */
+                       u = 0;
+                       v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n");
+               } else {
+                       u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
+                       if (invert)
+                               u = u ? 0 : 1;
+               }
+
+               v = (unsigned) pulse_width_count_to_ns(
+                                 (u16) (p->hw_fifo_data & FIFO_RXTX), divider);
+               if (v > IR_MAX_DURATION)
+                       v = IR_MAX_DURATION;
+
+               p->ir_core_data.pulse = u;
+               p->ir_core_data.duration = v;
+
+               v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns  %s\n",
+                        v, u ? "mark" : "space");
+       }
+       return 0;
+}
+
+static int cx25840_ir_rx_g_parameters(struct v4l2_subdev *sd,
+                                     struct v4l2_subdev_ir_parameters *p)
+{
+       struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+       if (ir_state == NULL)
+               return -ENODEV;
+
+       mutex_lock(&ir_state->rx_params_lock);
+       memcpy(p, &ir_state->rx_params,
+                                     sizeof(struct v4l2_subdev_ir_parameters));
+       mutex_unlock(&ir_state->rx_params_lock);
+       return 0;
+}
+
+static int cx25840_ir_rx_shutdown(struct v4l2_subdev *sd)
+{
+       struct cx25840_ir_state *ir_state = to_ir_state(sd);
+       struct i2c_client *c;
+
+       if (ir_state == NULL)
+               return -ENODEV;
+
+       c = ir_state->c;
+       mutex_lock(&ir_state->rx_params_lock);
+
+       /* Disable or slow down all IR Rx circuits and counters */
+       irqenable_rx(sd, 0);
+       control_rx_enable(c, false);
+       control_rx_demodulation_enable(c, false);
+       control_rx_s_edge_detection(c, CNTRL_EDG_NONE);
+       filter_rx_s_min_width(c, 0);
+       cx25840_write4(c, CX25840_IR_RXCLK_REG, RXCLK_RCD);
+
+       ir_state->rx_params.shutdown = true;
+
+       mutex_unlock(&ir_state->rx_params_lock);
+       return 0;
+}
+
+static int cx25840_ir_rx_s_parameters(struct v4l2_subdev *sd,
+                                     struct v4l2_subdev_ir_parameters *p)
+{
+       struct cx25840_ir_state *ir_state = to_ir_state(sd);
+       struct i2c_client *c;
+       struct v4l2_subdev_ir_parameters *o;
+       u16 rxclk_divider;
+
+       if (ir_state == NULL)
+               return -ENODEV;
+
+       if (p->shutdown)
+               return cx25840_ir_rx_shutdown(sd);
+
+       if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+               return -ENOSYS;
+
+       c = ir_state->c;
+       o = &ir_state->rx_params;
+
+       mutex_lock(&ir_state->rx_params_lock);
+
+       o->shutdown = p->shutdown;
+
+       p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+       o->mode = p->mode;
+
+       p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec);
+       o->bytes_per_data_element = p->bytes_per_data_element;
+
+       /* Before we tweak the hardware, we have to disable the receiver */
+       irqenable_rx(sd, 0);
+       control_rx_enable(c, false);
+
+       control_rx_demodulation_enable(c, p->modulation);
+       o->modulation = p->modulation;
+
+       if (p->modulation) {
+               p->carrier_freq = rxclk_rx_s_carrier(c, p->carrier_freq,
+                                                    &rxclk_divider);
+
+               o->carrier_freq = p->carrier_freq;
+
+               p->duty_cycle = 50;
+               o->duty_cycle = p->duty_cycle;
+
+               control_rx_s_carrier_window(c, p->carrier_freq,
+                                           &p->carrier_range_lower,
+                                           &p->carrier_range_upper);
+               o->carrier_range_lower = p->carrier_range_lower;
+               o->carrier_range_upper = p->carrier_range_upper;
+
+               p->max_pulse_width =
+                       (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider);
+       } else {
+               p->max_pulse_width =
+                           rxclk_rx_s_max_pulse_width(c, p->max_pulse_width,
+                                                      &rxclk_divider);
+       }
+       o->max_pulse_width = p->max_pulse_width;
+       atomic_set(&ir_state->rxclk_divider, rxclk_divider);
+
+       p->noise_filter_min_width =
+                           filter_rx_s_min_width(c, p->noise_filter_min_width);
+       o->noise_filter_min_width = p->noise_filter_min_width;
+
+       p->resolution = clock_divider_to_resolution(rxclk_divider);
+       o->resolution = p->resolution;
+
+       /* FIXME - make this dependent on resolution for better performance */
+       control_rx_irq_watermark(c, RX_FIFO_HALF_FULL);
+
+       control_rx_s_edge_detection(c, CNTRL_EDG_BOTH);
+
+       o->invert_level = p->invert_level;
+       atomic_set(&ir_state->rx_invert, p->invert_level);
+
+       o->interrupt_enable = p->interrupt_enable;
+       o->enable = p->enable;
+       if (p->enable) {
+               unsigned long flags;
+
+               spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags);
+               kfifo_reset(&ir_state->rx_kfifo);
+               spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags);
+               if (p->interrupt_enable)
+                       irqenable_rx(sd, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE);
+               control_rx_enable(c, p->enable);
+       }
+
+       mutex_unlock(&ir_state->rx_params_lock);
+       return 0;
+}
+
+/* Transmitter */
+static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count,
+                              ssize_t *num)
+{
+       struct cx25840_ir_state *ir_state = to_ir_state(sd);
+       struct i2c_client *c;
+
+       if (ir_state == NULL)
+               return -ENODEV;
+
+       c = ir_state->c;
+#if 0
+       /*
+        * FIXME - the code below is an incomplete and untested sketch of what
+        * may need to be done.  The critical part is to get 4 (or 8) pulses
+        * from the tx_kfifo, or converted from ns to the proper units from the
+        * input, and push them off to the hardware Tx FIFO right away, if the
+        * HW TX fifo needs service.  The rest can be pushed to the tx_kfifo in
+        * a less critical timeframe.  Also watch out for overruning the
+        * tx_kfifo - don't let it happen and let the caller know not all his
+        * pulses were written.
+        */
+       u32 *ns_pulse = (u32 *) buf;
+       unsigned int n;
+       u32 fifo_pulse[FIFO_TX_DEPTH];
+       u32 mark;
+
+       /* Compute how much we can fit in the tx kfifo */
+       n = CX25840_IR_TX_KFIFO_SIZE - kfifo_len(ir_state->tx_kfifo);
+       n = min(n, (unsigned int) count);
+       n /= sizeof(u32);
+
+       /* FIXME - turn on Tx Fifo service interrupt
+        * check hardware fifo level, and other stuff
+        */
+       for (i = 0; i < n; ) {
+               for (j = 0; j < FIFO_TX_DEPTH / 2 && i < n; j++) {
+                       mark = ns_pulse[i] & LEVEL_MASK;
+                       fifo_pulse[j] = ns_to_pulse_width_count(
+                                        ns_pulse[i] &
+                                              ~LEVEL_MASK,
+                                        ir_state->txclk_divider);
+                       if (mark)
+                               fifo_pulse[j] &= FIFO_RXTX_LVL;
+                       i++;
+               }
+               kfifo_put(ir_state->tx_kfifo, (u8 *) fifo_pulse,
+                                                              j * sizeof(u32));
+       }
+       *num = n * sizeof(u32);
+#else
+       /* For now enable the Tx FIFO Service interrupt & pretend we did work */
+       irqenable_tx(sd, IRQEN_TSE);
+       *num = count;
+#endif
+       return 0;
+}
+
+static int cx25840_ir_tx_g_parameters(struct v4l2_subdev *sd,
+                                     struct v4l2_subdev_ir_parameters *p)
+{
+       struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+       if (ir_state == NULL)
+               return -ENODEV;
+
+       mutex_lock(&ir_state->tx_params_lock);
+       memcpy(p, &ir_state->tx_params,
+                                     sizeof(struct v4l2_subdev_ir_parameters));
+       mutex_unlock(&ir_state->tx_params_lock);
+       return 0;
+}
+
+static int cx25840_ir_tx_shutdown(struct v4l2_subdev *sd)
+{
+       struct cx25840_ir_state *ir_state = to_ir_state(sd);
+       struct i2c_client *c;
+
+       if (ir_state == NULL)
+               return -ENODEV;
+
+       c = ir_state->c;
+       mutex_lock(&ir_state->tx_params_lock);
+
+       /* Disable or slow down all IR Tx circuits and counters */
+       irqenable_tx(sd, 0);
+       control_tx_enable(c, false);
+       control_tx_modulation_enable(c, false);
+       cx25840_write4(c, CX25840_IR_TXCLK_REG, TXCLK_TCD);
+
+       ir_state->tx_params.shutdown = true;
+
+       mutex_unlock(&ir_state->tx_params_lock);
+       return 0;
+}
+
+static int cx25840_ir_tx_s_parameters(struct v4l2_subdev *sd,
+                                     struct v4l2_subdev_ir_parameters *p)
+{
+       struct cx25840_ir_state *ir_state = to_ir_state(sd);
+       struct i2c_client *c;
+       struct v4l2_subdev_ir_parameters *o;
+       u16 txclk_divider;
+
+       if (ir_state == NULL)
+               return -ENODEV;
+
+       if (p->shutdown)
+               return cx25840_ir_tx_shutdown(sd);
+
+       if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+               return -ENOSYS;
+
+       c = ir_state->c;
+       o = &ir_state->tx_params;
+       mutex_lock(&ir_state->tx_params_lock);
+
+       o->shutdown = p->shutdown;
+
+       p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+       o->mode = p->mode;
+
+       p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec);
+       o->bytes_per_data_element = p->bytes_per_data_element;
+
+       /* Before we tweak the hardware, we have to disable the transmitter */
+       irqenable_tx(sd, 0);
+       control_tx_enable(c, false);
+
+       control_tx_modulation_enable(c, p->modulation);
+       o->modulation = p->modulation;
+
+       if (p->modulation) {
+               p->carrier_freq = txclk_tx_s_carrier(c, p->carrier_freq,
+                                                    &txclk_divider);
+               o->carrier_freq = p->carrier_freq;
+
+               p->duty_cycle = cduty_tx_s_duty_cycle(c, p->duty_cycle);
+               o->duty_cycle = p->duty_cycle;
+
+               p->max_pulse_width =
+                       (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider);
+       } else {
+               p->max_pulse_width =
+                           txclk_tx_s_max_pulse_width(c, p->max_pulse_width,
+                                                      &txclk_divider);
+       }
+       o->max_pulse_width = p->max_pulse_width;
+       atomic_set(&ir_state->txclk_divider, txclk_divider);
+
+       p->resolution = clock_divider_to_resolution(txclk_divider);
+       o->resolution = p->resolution;
+
+       /* FIXME - make this dependent on resolution for better performance */
+       control_tx_irq_watermark(c, TX_FIFO_HALF_EMPTY);
+
+       control_tx_polarity_invert(c, p->invert_carrier_sense);
+       o->invert_carrier_sense = p->invert_carrier_sense;
+
+       /*
+        * FIXME: we don't have hardware help for IO pin level inversion
+        * here like we have on the CX23888.
+        * Act on this with some mix of logical inversion of data levels,
+        * carrier polarity, and carrier duty cycle.
+        */
+       o->invert_level = p->invert_level;
+
+       o->interrupt_enable = p->interrupt_enable;
+       o->enable = p->enable;
+       if (p->enable) {
+               /* reset tx_fifo here */
+               if (p->interrupt_enable)
+                       irqenable_tx(sd, IRQEN_TSE);
+               control_tx_enable(c, p->enable);
+       }
+
+       mutex_unlock(&ir_state->tx_params_lock);
+       return 0;
+}
+
+
+/*
+ * V4L2 Subdevice Core Ops support
+ */
+int cx25840_ir_log_status(struct v4l2_subdev *sd)
+{
+       struct cx25840_state *state = to_state(sd);
+       struct i2c_client *c = state->c;
+       char *s;
+       int i, j;
+       u32 cntrl, txclk, rxclk, cduty, stats, irqen, filtr;
+
+       /* The CX23888 chip doesn't have an IR controller on the A/V core */
+       if (is_cx23888(state))
+               return 0;
+
+       cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG);
+       txclk = cx25840_read4(c, CX25840_IR_TXCLK_REG) & TXCLK_TCD;
+       rxclk = cx25840_read4(c, CX25840_IR_RXCLK_REG) & RXCLK_RCD;
+       cduty = cx25840_read4(c, CX25840_IR_CDUTY_REG) & CDUTY_CDC;
+       stats = cx25840_read4(c, CX25840_IR_STATS_REG);
+       irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG);
+       if (is_cx23885(state) || is_cx23887(state))
+               irqen ^= IRQEN_MSK;
+       filtr = cx25840_read4(c, CX25840_IR_FILTR_REG) & FILTR_LPF;
+
+       v4l2_info(sd, "IR Receiver:\n");
+       v4l2_info(sd, "\tEnabled:                           %s\n",
+                 cntrl & CNTRL_RXE ? "yes" : "no");
+       v4l2_info(sd, "\tDemodulation from a carrier:       %s\n",
+                 cntrl & CNTRL_DMD ? "enabled" : "disabled");
+       v4l2_info(sd, "\tFIFO:                              %s\n",
+                 cntrl & CNTRL_RFE ? "enabled" : "disabled");
+       switch (cntrl & CNTRL_EDG) {
+       case CNTRL_EDG_NONE:
+               s = "disabled";
+               break;
+       case CNTRL_EDG_FALL:
+               s = "falling edge";
+               break;
+       case CNTRL_EDG_RISE:
+               s = "rising edge";
+               break;
+       case CNTRL_EDG_BOTH:
+               s = "rising & falling edges";
+               break;
+       default:
+               s = "??? edge";
+               break;
+       }
+       v4l2_info(sd, "\tPulse timers' start/stop trigger:  %s\n", s);
+       v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n",
+                 cntrl & CNTRL_R ? "not loaded" : "overflow marker");
+       v4l2_info(sd, "\tFIFO interrupt watermark:          %s\n",
+                 cntrl & CNTRL_RIC ? "not empty" : "half full or greater");
+       v4l2_info(sd, "\tLoopback mode:                     %s\n",
+                 cntrl & CNTRL_LBM ? "loopback active" : "normal receive");
+       if (cntrl & CNTRL_DMD) {
+               v4l2_info(sd, "\tExpected carrier (16 clocks):      %u Hz\n",
+                         clock_divider_to_carrier_freq(rxclk));
+               switch (cntrl & CNTRL_WIN) {
+               case CNTRL_WIN_3_3:
+                       i = 3;
+                       j = 3;
+                       break;
+               case CNTRL_WIN_4_3:
+                       i = 4;
+                       j = 3;
+                       break;
+               case CNTRL_WIN_3_4:
+                       i = 3;
+                       j = 4;
+                       break;
+               case CNTRL_WIN_4_4:
+                       i = 4;
+                       j = 4;
+                       break;
+               default:
+                       i = 0;
+                       j = 0;
+                       break;
+               }
+               v4l2_info(sd, "\tNext carrier edge window:          16 clocks "
+                         "-%1d/+%1d, %u to %u Hz\n", i, j,
+                         clock_divider_to_freq(rxclk, 16 + j),
+                         clock_divider_to_freq(rxclk, 16 - i));
+       }
+       v4l2_info(sd, "\tMax measurable pulse width:        %u us, %llu ns\n",
+                 pulse_width_count_to_us(FIFO_RXTX, rxclk),
+                 pulse_width_count_to_ns(FIFO_RXTX, rxclk));
+       v4l2_info(sd, "\tLow pass filter:                   %s\n",
+                 filtr ? "enabled" : "disabled");
+       if (filtr)
+               v4l2_info(sd, "\tMin acceptable pulse width (LPF):  %u us, "
+                         "%u ns\n",
+                         lpf_count_to_us(filtr),
+                         lpf_count_to_ns(filtr));
+       v4l2_info(sd, "\tPulse width timer timed-out:       %s\n",
+                 stats & STATS_RTO ? "yes" : "no");
+       v4l2_info(sd, "\tPulse width timer time-out intr:   %s\n",
+                 irqen & IRQEN_RTE ? "enabled" : "disabled");
+       v4l2_info(sd, "\tFIFO overrun:                      %s\n",
+                 stats & STATS_ROR ? "yes" : "no");
+       v4l2_info(sd, "\tFIFO overrun interrupt:            %s\n",
+                 irqen & IRQEN_ROE ? "enabled" : "disabled");
+       v4l2_info(sd, "\tBusy:                              %s\n",
+                 stats & STATS_RBY ? "yes" : "no");
+       v4l2_info(sd, "\tFIFO service requested:            %s\n",
+                 stats & STATS_RSR ? "yes" : "no");
+       v4l2_info(sd, "\tFIFO service request interrupt:    %s\n",
+                 irqen & IRQEN_RSE ? "enabled" : "disabled");
+
+       v4l2_info(sd, "IR Transmitter:\n");
+       v4l2_info(sd, "\tEnabled:                           %s\n",
+                 cntrl & CNTRL_TXE ? "yes" : "no");
+       v4l2_info(sd, "\tModulation onto a carrier:         %s\n",
+                 cntrl & CNTRL_MOD ? "enabled" : "disabled");
+       v4l2_info(sd, "\tFIFO:                              %s\n",
+                 cntrl & CNTRL_TFE ? "enabled" : "disabled");
+       v4l2_info(sd, "\tFIFO interrupt watermark:          %s\n",
+                 cntrl & CNTRL_TIC ? "not empty" : "half full or less");
+       v4l2_info(sd, "\tCarrier polarity:                  %s\n",
+                 cntrl & CNTRL_CPL ? "space:burst mark:noburst"
+                                   : "space:noburst mark:burst");
+       if (cntrl & CNTRL_MOD) {
+               v4l2_info(sd, "\tCarrier (16 clocks):               %u Hz\n",
+                         clock_divider_to_carrier_freq(txclk));
+               v4l2_info(sd, "\tCarrier duty cycle:                %2u/16\n",
+                         cduty + 1);
+       }
+       v4l2_info(sd, "\tMax pulse width:                   %u us, %llu ns\n",
+                 pulse_width_count_to_us(FIFO_RXTX, txclk),
+                 pulse_width_count_to_ns(FIFO_RXTX, txclk));
+       v4l2_info(sd, "\tBusy:                              %s\n",
+                 stats & STATS_TBY ? "yes" : "no");
+       v4l2_info(sd, "\tFIFO service requested:            %s\n",
+                 stats & STATS_TSR ? "yes" : "no");
+       v4l2_info(sd, "\tFIFO service request interrupt:    %s\n",
+                 irqen & IRQEN_TSE ? "enabled" : "disabled");
+
+       return 0;
+}
+
+
+const struct v4l2_subdev_ir_ops cx25840_ir_ops = {
+       .rx_read = cx25840_ir_rx_read,
+       .rx_g_parameters = cx25840_ir_rx_g_parameters,
+       .rx_s_parameters = cx25840_ir_rx_s_parameters,
+
+       .tx_write = cx25840_ir_tx_write,
+       .tx_g_parameters = cx25840_ir_tx_g_parameters,
+       .tx_s_parameters = cx25840_ir_tx_s_parameters,
+};
+
+
+static const struct v4l2_subdev_ir_parameters default_rx_params = {
+       .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec),
+       .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+       .enable = false,
+       .interrupt_enable = false,
+       .shutdown = true,
+
+       .modulation = true,
+       .carrier_freq = 36000, /* 36 kHz - RC-5, and RC-6 carrier */
+
+       /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+       /* RC-6: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+       .noise_filter_min_width = 333333, /* ns */
+       .carrier_range_lower = 35000,
+       .carrier_range_upper = 37000,
+       .invert_level = false,
+};
+
+static const struct v4l2_subdev_ir_parameters default_tx_params = {
+       .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec),
+       .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+       .enable = false,
+       .interrupt_enable = false,
+       .shutdown = true,
+
+       .modulation = true,
+       .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
+       .duty_cycle = 25,      /* 25 %   - RC-5 carrier */
+       .invert_level = false,
+       .invert_carrier_sense = false,
+};
+
+int cx25840_ir_probe(struct v4l2_subdev *sd)
+{
+       struct cx25840_state *state = to_state(sd);
+       struct cx25840_ir_state *ir_state;
+       struct v4l2_subdev_ir_parameters default_params;
+
+       /* Only init the IR controller for the CX2388[57] AV Core for now */
+       if (!(is_cx23885(state) || is_cx23887(state)))
+               return 0;
+
+       ir_state = kzalloc(sizeof(struct cx25840_ir_state), GFP_KERNEL);
+       if (ir_state == NULL)
+               return -ENOMEM;
+
+       spin_lock_init(&ir_state->rx_kfifo_lock);
+       if (kfifo_alloc(&ir_state->rx_kfifo,
+                       CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL)) {
+               kfree(ir_state);
+               return -ENOMEM;
+       }
+
+       ir_state->c = state->c;
+       state->ir_state = ir_state;
+
+       /* Ensure no interrupts arrive yet */
+       if (is_cx23885(state) || is_cx23887(state))
+               cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, IRQEN_MSK);
+       else
+               cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0);
+
+       mutex_init(&ir_state->rx_params_lock);
+       memcpy(&default_params, &default_rx_params,
+                      sizeof(struct v4l2_subdev_ir_parameters));
+       v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params);
+
+       mutex_init(&ir_state->tx_params_lock);
+       memcpy(&default_params, &default_tx_params,
+                      sizeof(struct v4l2_subdev_ir_parameters));
+       v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params);
+
+       return 0;
+}
+
+int cx25840_ir_remove(struct v4l2_subdev *sd)
+{
+       struct cx25840_state *state = to_state(sd);
+       struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+       if (ir_state == NULL)
+               return -ENODEV;
+
+       cx25840_ir_rx_shutdown(sd);
+       cx25840_ir_tx_shutdown(sd);
+
+       kfifo_free(&ir_state->rx_kfifo);
+       kfree(ir_state);
+       state->ir_state = NULL;
+       return 0;
+}
index d951b0f0e05319afa85daf80550a73cd348997a5..b9846106913eb4871f429924092dd27563f0f115 100644 (file)
@@ -55,7 +55,7 @@ MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
 MODULE_DESCRIPTION("GSPCA USB Camera Driver");
 MODULE_LICENSE("GPL");
 
-#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 9, 0)
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 10, 0)
 
 #ifdef GSPCA_DEBUG
 int gspca_debug = D_ERR | D_PROBE;
@@ -440,10 +440,15 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
                frame->v4l2_buf.sequence = ++gspca_dev->sequence;
                gspca_dev->image = frame->data;
                gspca_dev->image_len = 0;
-       } else if (gspca_dev->last_packet_type == DISCARD_PACKET) {
-               if (packet_type == LAST_PACKET)
-                       gspca_dev->last_packet_type = packet_type;
-               return;
+       } else {
+               switch (gspca_dev->last_packet_type) {
+               case DISCARD_PACKET:
+                       if (packet_type == LAST_PACKET)
+                               gspca_dev->last_packet_type = packet_type;
+                       return;
+               case LAST_PACKET:
+                       return;
+               }
        }
 
        /* append the packet to the frame buffer */
@@ -454,6 +459,12 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
                                gspca_dev->frsz);
                        packet_type = DISCARD_PACKET;
                } else {
+/* !! image is NULL only when last pkt is LAST or DISCARD
+                       if (gspca_dev->image == NULL) {
+                               err("gspca_frame_add() image == NULL");
+                               return;
+                       }
+ */
                        memcpy(gspca_dev->image + gspca_dev->image_len,
                                data, len);
                        gspca_dev->image_len += len;
index ee17b034bf6b9b6b3e77431d236e629afebc5f84..370544361be2ea4a8831d8dae95443fa1485f8ff 100644 (file)
@@ -66,7 +66,11 @@ struct sd {
 #define BRIDGE_SN9C110 2
 #define BRIDGE_SN9C120 3
        u8 sensor;                      /* Type of image sensor chip */
-enum {
+       u8 i2c_addr;
+
+       u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+enum sensors {
        SENSOR_ADCM1700,
        SENSOR_GC0307,
        SENSOR_HV7131R,
@@ -81,10 +85,6 @@ enum {
        SENSOR_PO2030N,
        SENSOR_SOI768,
        SENSOR_SP80708,
-} sensors;
-       u8 i2c_addr;
-
-       u8 jpeg_hdr[JPEG_HDR_SZ];
 };
 
 /* V4L2 controls supported by the driver */
index 37cee5e063cfb8bc51443ab026cf08dfa112c03b..7ae6522d4edf030cf6b42380cbeec015a2153c24 100644 (file)
@@ -23,7 +23,6 @@
 #define MODULE_NAME "sq930x"
 
 #include "gspca.h"
-#include "jpeg.h"
 
 MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
                "Gerard Klaver <gerard at gkall dot hobby dot nl\n"
@@ -31,8 +30,6 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
 MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver");
 MODULE_LICENSE("GPL");
 
-#define BULK_TRANSFER_LEN 5128
-
 /* Structure to hold all of our device specific stuff */
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
@@ -40,28 +37,20 @@ struct sd {
        u16 expo;
        u8 gain;
 
-       u8 quality;             /* webcam quality 0..3 */
-#define QUALITY_DEF 1
-
-       u8 gpio[2];
-
-       u8 eof_len;
        u8 do_ctrl;
-
+       u8 gpio[2];
        u8 sensor;
-enum {
+       u8 type;
+#define Generic 0
+#define Creative_live_motion 1
+};
+enum sensors {
        SENSOR_ICX098BQ,
        SENSOR_LZ24BP,
        SENSOR_MI0360,
-       SENSOR_MT9V111,
+       SENSOR_MT9V111,         /* = MI360SOC */
        SENSOR_OV7660,
        SENSOR_OV9630,
-} sensors;
-       u8 type;
-#define Generic 0
-#define Creative_live_motion 1
-
-       u8 jpeg_hdr[JPEG_HDR_SZ];
 };
 
 static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val);
@@ -78,7 +67,7 @@ static const struct ctrl sd_ctrls[] = {
                .minimum = 0x0001,
                .maximum = 0x0fff,
                .step = 1,
-#define EXPO_DEF 0x027d
+#define EXPO_DEF 0x0356
                .default_value = EXPO_DEF,
            },
            .set = sd_setexpo,
@@ -92,7 +81,7 @@ static const struct ctrl sd_ctrls[] = {
                .minimum = 0x01,
                .maximum = 0xff,
                .step = 1,
-#define GAIN_DEF 0x61
+#define GAIN_DEF 0x8d
                .default_value = GAIN_DEF,
            },
            .set = sd_setgain,
@@ -101,30 +90,18 @@ static const struct ctrl sd_ctrls[] = {
 };
 
 static struct v4l2_pix_format vga_mode[] = {
-       {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
-               .bytesperline = 160,
-               .sizeimage = 160 * 120 * 5 / 8 + 590,
-               .colorspace = V4L2_COLORSPACE_JPEG,
-               .priv = 0},
-       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+       {320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
                .bytesperline = 320,
-               .sizeimage = 320 * 240 * 4 / 8 + 590,
-               .colorspace = V4L2_COLORSPACE_JPEG,
-               .priv = 1},
-       {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .sizeimage = 320 * 240,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+       {640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
                .bytesperline = 640,
-               .sizeimage = 640 * 480 * 3 / 8 + 590,
-               .colorspace = V4L2_COLORSPACE_JPEG,
-               .priv = 2},
+               .sizeimage = 640 * 480,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
 };
 
-/* JPEG quality indexed by webcam quality */
-#define QUAL_0 90
-#define QUAL_1 85
-#define QUAL_2 75
-#define QUAL_3 70
-static const u8 quality_tb[4] = { QUAL_0, QUAL_1, QUAL_2, QUAL_3 };
-
 /* sq930x registers */
 #define SQ930_CTRL_UCBUS_IO    0x0001
 #define SQ930_CTRL_I2C_IO      0x0002
@@ -302,7 +279,7 @@ static const struct i2c_write_cmd mt9v111_init_0[] = {
        {0x01, 0x0001},         /* select IFP/SOC registers */
        {0x06, 0x300c},         /* operating mode control */
        {0x08, 0xcc00},         /* output format control (RGB) */
-       {0x01, 0x0004},         /* select core registers */
+       {0x01, 0x0004},         /* select sensor core registers */
 };
 static const struct i2c_write_cmd mt9v111_init_1[] = {
        {0x03, 0x01e5},         /* window height */
@@ -330,7 +307,8 @@ static const struct i2c_write_cmd mt9v111_init_3[] = {
        {0x62, 0x0405},
 };
 static const struct i2c_write_cmd mt9v111_init_4[] = {
-       {0x05, 0x00ce},         /* horizontal blanking */
+/*     {0x05, 0x00ce}, */
+       {0x05, 0x005d},         /* horizontal blanking */
 };
 
 static const struct ucbus_write_cmd ov7660_start_0[] = {
@@ -343,78 +321,58 @@ static const struct ucbus_write_cmd ov9630_start_0[] = {
        {0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03}
 };
 
+/* start parameters indexed by [sensor][mode] */
 static const struct cap_s {
        u8      cc_sizeid;
        u8      cc_bytes[32];
-} capconfig[4][3] = {
+} capconfig[4][2] = {
        [SENSOR_ICX098BQ] = {
-               {0,                             /* JPEG, 160x120 */
+               {2,                             /* Bayer 320x240 */
+                 {0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
+                  0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0,
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+               {4,                             /* Bayer 640x480 */
                  {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
                   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
-                  0x02, 0x8b, 0x00, 0x8b, 0x00, 0x41, 0x01, 0x41,
-                  0x01, 0x41, 0x01, 0x05, 0x40, 0x01, 0xf0, 0x00} },
-               {2,                             /* JPEG, 320x240 */
-                 {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
-                  0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
-                  0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
-                  0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
-               {4,                             /* JPEG, 640x480 */
-                 {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xf0,
-                  0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
-                  0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
-                  0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
        },
        [SENSOR_LZ24BP] = {
-               {0,                             /* JPEG, 160x120 */
-                 {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
-                  0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
-                  0x02, 0x8b, 0x00, 0x8b, 0x00, 0x41, 0x01, 0x41,
-                  0x01, 0x41, 0x01, 0x05, 0x40, 0x01, 0xf0, 0x00} },
-               {2,                             /* JPEG, 320x240 */
+               {2,                             /* Bayer 320x240 */
+                 {0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
+                  0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+               {4,                             /* Bayer 640x480 */
                  {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
                   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
-                  0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
-                  0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
-               {4,                             /* JPEG, 640x480 */
-                 {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xf0,
-                  0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
-                  0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
-                  0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
        },
        [SENSOR_MI0360] = {
-               {0,                             /* JPEG, 160x120 */
-                 {0x05, 0x3d, 0x20, 0x0b, 0x00, 0xbd, 0x02, 0x0b,
-                  0x02, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
-                  0x02, 0x01, 0x01, 0x01, 0x01, 0x9f, 0x00, 0x9f,
-                  0x00, 0x9f, 0x01, 0x05, 0xa0, 0x00, 0x80, 0x00} },
-               {2,                             /* JPEG, 320x240 */
+               {2,                             /* Bayer 320x240 */
+                 {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+                  0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+               {4,                             /* Bayer 640x480 */
                  {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
-/*fixme                                       03                      e3 */
                   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
-                  0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
-                  0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
-               {4,                             /* JPEG, 640x480 */
-                 {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe3,
-                  0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
-                  0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
-                  0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
        },
        [SENSOR_MT9V111] = {
-               {0,                             /* JPEG, 160x120 */
-                 {0x05, 0x3d, 0x20, 0x0b, 0x00, 0xbd, 0x02, 0x0b,
-                  0x02, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
-                  0x02, 0x01, 0x01, 0x01, 0x01, 0x9f, 0x00, 0x9f,
-                  0x00, 0x9f, 0x01, 0x05, 0xa0, 0x00, 0x80, 0x00} },
-               {2,                             /* JPEG, 320x240 */
-                 {0x01, 0x02, 0x20, 0x03, 0x20, 0x82, 0x02, 0xe3,
-                  0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
-                  0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
-                  0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
-               {4,                             /* JPEG, 640x480 */
-                 {0x01, 0x02, 0x20, 0x03, 0x20, 0x82, 0x02, 0xe3,
+               {2,                             /* Bayer 320x240 */
+                 {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+                  0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+               {4,                             /* Bayer 640x480 */
+                 {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
                   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
-                  0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
-                  0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
        },
 };
 
@@ -864,7 +822,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
                buf[i++] = 0x35;        /* reg = global gain */
                buf[i++] = 0x00;        /* val H */
                buf[i++] = sensor->i2c_dum;
-               buf[i++] = sd->gain;    /* val L */
+               buf[i++] = 0x80 + sd->gain / 2; /* val L */
                buf[i++] = 0x00;
                buf[i++] = 0x00;
                buf[i++] = 0x00;
@@ -889,10 +847,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
        cam->nmodes = ARRAY_SIZE(vga_mode);
 
        cam->bulk = 1;
-       cam->bulk_size = BULK_TRANSFER_LEN;
-/*     cam->bulk_nurbs = 2;    fixme: if no setexpo sync */
 
-       sd->quality = QUALITY_DEF;
        sd->gain = GAIN_DEF;
        sd->expo = EXPO_DEF;
 
@@ -945,13 +900,10 @@ static int sd_init(struct gspca_dev *gspca_dev)
        if (sd->sensor == SENSOR_MI0360) {
 
                /* no sensor probe for icam tracer */
-               if (gspca_dev->usb_buf[5] == 0xf6) {    /* if CMOS */
+               if (gspca_dev->usb_buf[5] == 0xf6)      /* if CMOS */
                        sd->sensor = SENSOR_ICX098BQ;
-                       gspca_dev->cam.cam_mode = &vga_mode[1];
-                       gspca_dev->cam.nmodes = 1;      /* only 320x240 */
-               } else {
+               else
                        cmos_probe(gspca_dev);
-               }
        }
 
        PDEBUG(D_PROBE, "Sensor %s", sensor_tb[sd->sensor].name);
@@ -960,51 +912,24 @@ static int sd_init(struct gspca_dev *gspca_dev)
        return gspca_dev->usb_err;
 }
 
-/* special function to create the quantization tables of the JPEG header */
-static void sd_jpeg_set_qual(u8 *jpeg_hdr,
-                               int quality)
-{
-       int i, sc1, sc2;
-
-       quality = quality_tb[quality];  /* convert to JPEG quality */
-/*
- * approximative qualities for Y and U/V:
- *     quant = 0:94%/91% 1:91%/87% 2:82%/73% 3:69%/56%
- * should have:
- *     quant = 0:94%/91% 1:91%/87.5% 2:81.5%/72% 3:69%/54.5%
- */
-       sc1 = 200 - quality * 2;
-       quality = quality * 7 / 5 - 40;         /* UV quality */
-       sc2 = 200 - quality * 2;
-       for (i = 0; i < 64; i++) {
-               jpeg_hdr[JPEG_QT0_OFFSET + i] =
-                       (jpeg_head[JPEG_QT0_OFFSET + i] * sc1 + 50) / 100;
-               jpeg_hdr[JPEG_QT1_OFFSET + i] =
-                       (jpeg_head[JPEG_QT1_OFFSET + i] * sc2 + 50) / 100;
-       }
-}
-
 /* send the start/stop commands to the webcam */
 static void send_start(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        const struct cap_s *cap;
-       int mode, quality;
+       int mode;
 
        mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
        cap = &capconfig[sd->sensor][mode];
-       quality = sd->quality;
-       reg_wb(gspca_dev, (quality << 12)
-                                | 0x0a00       /* 900 for Bayer */
-                                | SQ930_CTRL_CAP_START,
-                       0x0500                  /* a00 for Bayer */
-                                | cap->cc_sizeid,
+       reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START,
+                       0x0a00 | cap->cc_sizeid,
                        cap->cc_bytes, 32);
-};
+}
+
 static void send_stop(struct gspca_dev *gspca_dev)
 {
        reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0);
-};
+}
 
 /* function called at start time before URB creation */
 static int sd_isoc_init(struct gspca_dev *gspca_dev)
@@ -1013,6 +938,7 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev)
 
        gspca_dev->cam.bulk_nurbs = 1;  /* there must be one URB only */
        sd->do_ctrl = 0;
+       gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height + 8;
        return 0;
 }
 
@@ -1022,11 +948,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
        struct sd *sd = (struct sd *) gspca_dev;
        int mode;
 
-       /* initialize the JPEG header */
-       jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
-                       0x21);          /* JPEG 422 */
-       sd_jpeg_set_qual(sd->jpeg_hdr, sd->quality);
-
        bridge_init(sd);
        global_init(sd, 0);
        msleep(100);
@@ -1071,7 +992,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
                                ARRAY_SIZE(lz24bp_start_2),
                                6);
                mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
-               lz24bp_ppl(sd, mode == 2 ? 0x0564 : 0x0310);
+               lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310);
                msleep(10);
                break;
        case SENSOR_MI0360:
@@ -1095,7 +1016,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
                /* 1st start */
                send_start(gspca_dev);
                msleep(60);
-               reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000);
+               send_stop(gspca_dev);
 
                i2c_write(sd,
                        mi0360_start_4, ARRAY_SIZE(mi0360_start_4));
@@ -1113,7 +1034,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
                                ARRAY_SIZE(mt9v111_init_2));
                ucbus_write(gspca_dev, mt9v111_start_1,
                                ARRAY_SIZE(mt9v111_start_1),
-                               8);
+                               5);
                i2c_write(sd, mt9v111_init_3,
                                ARRAY_SIZE(mt9v111_init_3));
                i2c_write(sd, mt9v111_init_4,
@@ -1125,8 +1046,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
 out:
        msleep(1000);
 
-       sd->eof_len = 0;        /* init packet scan */
-
        if (sd->sensor == SENSOR_MT9V111)
                gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED);
 
@@ -1166,94 +1085,17 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
        msleep(100);
 }
 
-/* move a packet adding 0x00 after 0xff */
-static void add_packet(struct gspca_dev *gspca_dev,
-                       u8 *data,
-                       int len)
-{
-       int i;
-
-       i = 0;
-       do {
-               if (data[i] == 0xff) {
-                       gspca_frame_add(gspca_dev, INTER_PACKET,
-                                       data, i + 1);
-                       len -= i;
-                       data += i;
-                       *data = 0x00;
-                       i = 0;
-               }
-       } while (++i < len);
-       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
-}
-
-/* end a frame and start a new one */
-static void eof_sof(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       static const u8 ffd9[] = {0xff, 0xd9};
-
-       /* if control set, stop bulk transfer */
-       if (sd->do_ctrl
-        && gspca_dev->last_packet_type == INTER_PACKET)
-               gspca_dev->cam.bulk_nurbs = 0;
-       gspca_frame_add(gspca_dev, LAST_PACKET,
-                       ffd9, 2);
-       gspca_frame_add(gspca_dev, FIRST_PACKET,
-                       sd->jpeg_hdr, JPEG_HDR_SZ);
-}
-
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                        u8 *data,               /* isoc packet */
                        int len)                /* iso packet length */
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u8 *p;
-       int l;
-
-       len -= 8;       /* ignore last 8 bytes (00 00 55 aa 55 aa 00 00) */
-
-       /*
-        * the end/start of frame is indicated by
-        *      0x00 * 16 -  0xab * 8
-        * aligned on 8 bytes boundary
-        */
-       if (sd->eof_len != 0) {         /* if 'abababab' in previous pkt */
-               if (*((u32 *) data) == 0xabababab) {
-                               /*fixme: should remove previous 0000ababab*/
-                       eof_sof(gspca_dev);
-                       data += 4;
-                       len -= 4;
-               }
-               sd->eof_len = 0;
-       }
-       p = data;
-       l = len;
-       for (;;) {
-               if (*((u32 *) p) == 0xabababab) {
-                       if (l < 8) {            /* (may be 4 only) */
-                               sd->eof_len = 1;
-                               break;
-                       }
-                       if (*((u32 *) p + 1) == 0xabababab) {
-                               add_packet(gspca_dev, data, p - data - 16);
-                                               /* remove previous zeros */
-                               eof_sof(gspca_dev);
-                               p += 8;
-                               l -= 8;
-                               if (l <= 0)
-                                       return;
-                               len = l;
-                               data = p;
-                               continue;
-                       }
-               }
-               p += 4;
-               l -= 4;
-               if (l <= 0)
-                       break;
-       }
-       add_packet(gspca_dev, data, len);
+
+       if (sd->do_ctrl)
+               gspca_dev->cam.bulk_nurbs = 0;
+       gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8);
+       gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
 }
 
 static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
@@ -1291,45 +1133,6 @@ static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val)
        return 0;
 }
 
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
-                       struct v4l2_jpegcompression *jcomp)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       int quality;
-
-       if (jcomp->quality >= (QUAL_0 + QUAL_1) / 2)
-               quality = 0;
-       else if (jcomp->quality >= (QUAL_1 + QUAL_2) / 2)
-               quality = 1;
-       else if (jcomp->quality >= (QUAL_2 + QUAL_3) / 2)
-               quality = 2;
-       else
-               quality = 3;
-
-       if (quality != sd->quality) {
-               sd->quality = quality;
-               if (gspca_dev->streaming) {
-                       send_stop(gspca_dev);
-                       sd_jpeg_set_qual(sd->jpeg_hdr, sd->quality);
-                       msleep(70);
-                       send_start(gspca_dev);
-               }
-       }
-       return gspca_dev->usb_err;
-}
-
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
-                       struct v4l2_jpegcompression *jcomp)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       memset(jcomp, 0, sizeof *jcomp);
-       jcomp->quality = quality_tb[sd->quality];
-       jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
-                       | V4L2_JPEG_MARKER_DQT;
-       return 0;
-}
-
 /* sub-driver description */
 static const struct sd_desc sd_desc = {
        .name   = MODULE_NAME,
@@ -1342,8 +1145,6 @@ static const struct sd_desc sd_desc = {
        .stopN  = sd_stopN,
        .pkt_scan = sd_pkt_scan,
        .dq_callback = sd_dq_callback,
-       .get_jcomp = sd_get_jcomp,
-       .set_jcomp = sd_set_jcomp,
 };
 
 /* Table of supported USB devices */
index 2a0f12d55e48aab7d6f44c7c2d4a675106d426ff..3b3b983f2b9d8291cb1399b86c514f792f6ff537 100644 (file)
@@ -55,12 +55,12 @@ struct sd {
        u8 effect;
 
        u8 sensor;
-enum {
+};
+enum sensors {
        SENSOR_OM6802,
        SENSOR_OTHER,
        SENSOR_TAS5130A,
        SENSOR_LT168G,          /* must verify if this is the actual model */
-} sensors;
 };
 
 /* V4L2 controls supported by the driver */
index 031266a4081bfe0eee9a317f6f73c1f3cf31bce0..b16fd47e8ced11ef4e5f1644ef55d47a52f38fa6 100644 (file)
@@ -39,6 +39,10 @@ struct sd {
        u8 vflip;
        u8 lightfreq;
        s8 sharpness;
+       u16 exposure;
+       u8 gain;
+       u8 autogain;
+       u8 backlight;
 
        u8 image_offset;
 
@@ -77,6 +81,14 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
 static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val);
 
 static const struct ctrl sd_ctrls[] = {
 #define BRIGHTNESS_IDX 0
@@ -185,6 +197,66 @@ static const struct ctrl sd_ctrls[] = {
         .set = sd_setsharpness,
         .get = sd_getsharpness,
         },
+#define GAIN_IDX 7
+       {
+           {
+               .id      = V4L2_CID_GAIN,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Gain",
+               .minimum = 0,
+               .maximum = 78,
+               .step    = 1,
+#define GAIN_DEF 0
+               .default_value = GAIN_DEF,
+           },
+           .set = sd_setgain,
+           .get = sd_getgain,
+       },
+#define EXPOSURE_IDX 8
+       {
+               {
+                       .id = V4L2_CID_EXPOSURE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "Exposure",
+#define EXPOSURE_DEF 450
+                       .minimum = 0,
+                       .maximum = 4095,
+                       .step = 1,
+                       .default_value = EXPOSURE_DEF,
+               },
+               .set = sd_setexposure,
+               .get = sd_getexposure,
+       },
+#define AUTOGAIN_IDX 9
+       {
+               {
+                       .id = V4L2_CID_AUTOGAIN,
+                       .type = V4L2_CTRL_TYPE_BOOLEAN,
+                       .name = "Automatic Gain and Exposure",
+                       .minimum = 0,
+                       .maximum = 1,
+                       .step = 1,
+#define AUTOGAIN_DEF 1
+                       .default_value = AUTOGAIN_DEF,
+               },
+               .set = sd_setautogain,
+               .get = sd_getautogain,
+       },
+#define BACKLIGHT_IDX 10
+       {
+               {
+                       .id = V4L2_CID_BACKLIGHT_COMPENSATION,
+                       .type = V4L2_CTRL_TYPE_BOOLEAN,
+                       .name = "Backlight Compensation",
+                       .minimum = 0,
+                       .maximum = 15,
+                       .step = 1,
+#define BACKLIGHT_DEF 15
+                       .default_value = BACKLIGHT_DEF,
+               },
+               .set = sd_setbacklight,
+               .get = sd_getbacklight,
+       },
 };
 
 /* table of the disabled controls */
@@ -192,33 +264,51 @@ static u32 ctrl_dis[] = {
 /* SENSOR_HV7131R 0 */
        (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
                | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
-               | (1 << SHARPNESS_IDX),
+               | (1 << SHARPNESS_IDX)
+               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
 /* SENSOR_MI0360 1 */
        (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
                | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
-               | (1 << SHARPNESS_IDX),
+               | (1 << SHARPNESS_IDX)
+               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
 /* SENSOR_MI1310_SOC 2 */
        (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+               | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
 /* SENSOR_MI1320 3 */
        (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+               | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
 /* SENSOR_MI1320_SOC 4 */
        (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+               | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
 /* SENSOR_OV7660 5 */
        (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+               | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
 /* SENSOR_OV7670 6 */
        (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << SHARPNESS_IDX),
+               | (1 << SHARPNESS_IDX)
+               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
 /* SENSOR_PO1200 7 */
        (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
-               | (1 << LIGHTFREQ_IDX),
+               | (1 << LIGHTFREQ_IDX)
+               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
 /* SENSOR_PO3130NC 8 */
        (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
                | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
-               | (1 << SHARPNESS_IDX),
+               | (1 << SHARPNESS_IDX)
+               | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+               | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
 /* SENSOR_POxxxx 9 */
        (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX),
 };
@@ -2825,7 +2915,9 @@ static const u8 poxxxx_init_common[][4] = {
        {0x00, 0x1e, 0xc6, 0xaa},
        {0x00, 0x00, 0x40, 0xdd},
        {0x00, 0x1d, 0x05, 0xaa},
-
+       {}
+};
+static const u8 poxxxx_gamma[][4] = {
        {0x00, 0xd6, 0x22, 0xaa},       /* gamma 0 */
        {0x00, 0x73, 0x00, 0xaa},
        {0x00, 0x74, 0x0a, 0xaa},
@@ -2867,19 +2959,9 @@ static const u8 poxxxx_init_common[][4] = {
        {0x00, 0x7c, 0xba, 0xaa},
        {0x00, 0x7d, 0xd4, 0xaa},
        {0x00, 0x7e, 0xea, 0xaa},
-
-       {0x00, 0xaa, 0xff, 0xaa},       /* back light comp */
-       {0x00, 0xc4, 0x03, 0xaa},
-       {0x00, 0xc5, 0x19, 0xaa},
-       {0x00, 0xc6, 0x03, 0xaa},
-       {0x00, 0xc7, 0x91, 0xaa},
-       {0x00, 0xc8, 0x01, 0xaa},
-       {0x00, 0xc9, 0xdd, 0xaa},
-       {0x00, 0xca, 0x02, 0xaa},
-       {0x00, 0xcb, 0x37, 0xaa},
-
-/* read d1 */
-       {0x00, 0xd1, 0x3c, 0xaa},
+       {}
+};
+static const u8 poxxxx_init_start_3[][4] = {
        {0x00, 0xb8, 0x28, 0xaa},
        {0x00, 0xb9, 0x1e, 0xaa},
        {0x00, 0xb6, 0x14, 0xaa},
@@ -2959,9 +3041,6 @@ static const u8 poxxxx_init_end_1[][4] = {
        {0x00, 0xb3, 0x08, 0xaa},
        {0x00, 0xb4, 0x0b, 0xaa},
        {0x00, 0xb5, 0x0d, 0xaa},
-       {0x00, 0x59, 0x7e, 0xaa},       /* sharpness */
-       {0x00, 0x16, 0x00, 0xaa},       /* white balance */
-       {0x00, 0x18, 0x00, 0xaa},
        {}
 };
 static const u8 poxxxx_init_end_2[][4] = {
@@ -3310,6 +3389,33 @@ static void usb_exchange(struct gspca_dev *gspca_dev,
 /* this function is called at probe time */
 static int sd_config(struct gspca_dev *gspca_dev,
                        const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->bridge = id->driver_info >> 8;
+       sd->flags = id->driver_info & 0xff;
+
+       if (id->idVendor == 0x046d &&
+           (id->idProduct == 0x0892 || id->idProduct == 0x0896))
+               sd->sensor = SENSOR_POxxxx;     /* no probe */
+
+       sd->brightness = BRIGHTNESS_DEF;
+       sd->contrast = CONTRAST_DEF;
+       sd->colors = COLOR_DEF;
+       sd->hflip = HFLIP_DEF;
+       sd->vflip = VFLIP_DEF;
+       sd->lightfreq = FREQ_DEF;
+       sd->sharpness = SHARPNESS_DEF;
+       sd->gain = GAIN_DEF;
+       sd->exposure = EXPOSURE_DEF;
+       sd->autogain = AUTOGAIN_DEF;
+       sd->backlight = BACKLIGHT_DEF;
+
+       return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        struct cam *cam;
@@ -3327,14 +3433,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
                128,            /* POxxxx 9 */
        };
 
-       cam = &gspca_dev->cam;
-       sd->bridge = id->driver_info >> 8;
-       sd->flags = id->driver_info & 0xff;
-       if (id->idVendor == 0x046d &&
-           (id->idProduct == 0x0892 || id->idProduct == 0x0896))
-               sensor = SENSOR_POxxxx;
-       else
+       if (sd->sensor != SENSOR_POxxxx)
                sensor = vc032x_probe_sensor(gspca_dev);
+       else
+               sensor = sd->sensor;
+
        switch (sensor) {
        case -1:
                PDEBUG(D_PROBE, "Unknown sensor...");
@@ -3373,6 +3476,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
        }
        sd->sensor = sensor;
 
+       cam = &gspca_dev->cam;
        if (sd->bridge == BRIDGE_VC0321) {
                cam->cam_mode = vc0321_mode;
                cam->nmodes = ARRAY_SIZE(vc0321_mode);
@@ -3401,28 +3505,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
                }
        }
        cam->npkt = npkt[sd->sensor];
-
-       sd->brightness = BRIGHTNESS_DEF;
-       sd->contrast = CONTRAST_DEF;
-       sd->colors = COLOR_DEF;
-       sd->hflip = HFLIP_DEF;
-       sd->vflip = VFLIP_DEF;
-       sd->lightfreq = FREQ_DEF;
-       sd->sharpness = SHARPNESS_DEF;
-
        gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
 
        if (sd->sensor == SENSOR_OV7670)
                sd->flags |= FL_HFLIP | FL_VFLIP;
 
-       return 0;
-}
-
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
        if (sd->bridge == BRIDGE_VC0321) {
                reg_r(gspca_dev, 0x8a, 0, 3);
                reg_w(gspca_dev, 0x87, 0x00, 0x0f0f);
@@ -3433,8 +3520,8 @@ static int sd_init(struct gspca_dev *gspca_dev)
                        if (gspca_dev->usb_buf[0] != 0) {
                                reg_w(gspca_dev, 0xa0, 0x26, 0xb300);
                                reg_w(gspca_dev, 0xa0, 0x04, 0xb300);
-                               reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
                        }
+                       reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
                }
        }
        return gspca_dev->usb_err;
@@ -3551,6 +3638,82 @@ static void setsharpness(struct gspca_dev *gspca_dev)
                break;
        }
 }
+static void setgain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (gspca_dev->ctrl_dis & (1 << GAIN_IDX))
+               return;
+       i2c_write(gspca_dev, 0x15, &sd->gain, 1);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       u8 data;
+
+       if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
+               return;
+       data = sd->exposure >> 8;
+       i2c_write(gspca_dev, 0x1a, &data, 1);
+       data = sd->exposure;
+       i2c_write(gspca_dev, 0x1b, &data, 1);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       static const u8 data[2] = {0x28, 0x3c};
+
+       if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX))
+               return;
+       i2c_write(gspca_dev, 0xd1, &data[sd->autogain], 1);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+/*fixme:to do */
+       usb_exchange(gspca_dev, poxxxx_gamma);
+}
+
+static void setbacklight(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       u16 v;
+       u8 data;
+
+       data = (sd->backlight << 4) | 0x0f;
+       i2c_write(gspca_dev, 0xaa, &data, 1);
+       v = 613 + 12 * sd->backlight;
+       data = v >> 8;
+       i2c_write(gspca_dev, 0xc4, &data, 1);
+       data = v;
+       i2c_write(gspca_dev, 0xc5, &data, 1);
+       v = 1093 - 12 * sd->backlight;
+       data = v >> 8;
+       i2c_write(gspca_dev, 0xc6, &data, 1);
+       data = v;
+       i2c_write(gspca_dev, 0xc7, &data, 1);
+       v = 342 + 9 * sd->backlight;
+       data = v >> 8;
+       i2c_write(gspca_dev, 0xc8, &data, 1);
+       data = v;
+       i2c_write(gspca_dev, 0xc9, &data, 1);
+       v = 702 - 9 * sd->backlight;
+       data = v >> 8;
+       i2c_write(gspca_dev, 0xca, &data, 1);
+       data = v;
+       i2c_write(gspca_dev, 0xcb, &data, 1);
+}
+
+static void setwb(struct gspca_dev *gspca_dev)
+{
+/*fixme:to do - valid when reg d1 = 0x1c - (reg16 + reg15 = 0xa3)*/
+       static const u8 data[2] = {0x00, 0x00};
+
+       i2c_write(gspca_dev, 0x16, &data[0], 1);
+       i2c_write(gspca_dev, 0x18, &data[1], 1);
+}
 
 static int sd_start(struct gspca_dev *gspca_dev)
 {
@@ -3662,6 +3825,16 @@ static int sd_start(struct gspca_dev *gspca_dev)
        default:
 /*     case SENSOR_POxxxx: */
                usb_exchange(gspca_dev, poxxxx_init_common);
+               setgamma(gspca_dev);
+               setbacklight(gspca_dev);
+               setbrightness(gspca_dev);
+               setcontrast(gspca_dev);
+               setcolors(gspca_dev);
+               setsharpness(gspca_dev);
+               setautogain(gspca_dev);
+               setexposure(gspca_dev);
+               setgain(gspca_dev);
+               usb_exchange(gspca_dev, poxxxx_init_start_3);
                if (mode)
                        init = poxxxx_initQVGA;
                else
@@ -3693,7 +3866,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
                        break;
                }
                msleep(100);
-               setsharpness(gspca_dev);
                sethvflip(gspca_dev);
                setlightfreq(gspca_dev);
        }
@@ -3704,14 +3876,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
                reg_w(gspca_dev, 0xa0, 0x0000, 0xbfff);
                break;
        case SENSOR_POxxxx:
-               setcolors(gspca_dev);
-               setbrightness(gspca_dev);
-               setcontrast(gspca_dev);
-
-               /* led on */
-               msleep(80);
-               reg_w(gspca_dev, 0x89, 0xffff, 0xfdff);
                usb_exchange(gspca_dev, poxxxx_init_end_2);
+               setwb(gspca_dev);
+               msleep(80);             /* led on */
+               reg_w(gspca_dev, 0x89, 0xffff, 0xfdff);
                break;
        }
        return gspca_dev->usb_err;
@@ -3911,6 +4079,80 @@ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
        return 0;
 }
 
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->gain = val;
+       if (gspca_dev->streaming)
+               setgain(gspca_dev);
+       return gspca_dev->usb_err;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->gain;
+       return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->exposure = val;
+       if (gspca_dev->streaming)
+               setexposure(gspca_dev);
+       return gspca_dev->usb_err;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->exposure;
+       return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->autogain = val;
+       if (gspca_dev->streaming)
+               setautogain(gspca_dev);
+
+       return gspca_dev->usb_err;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->autogain;
+       return 0;
+}
+
+static int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->backlight = val;
+       if (gspca_dev->streaming)
+               setbacklight(gspca_dev);
+
+       return gspca_dev->usb_err;
+}
+
+static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->backlight;
+       return 0;
+}
+
 static int sd_querymenu(struct gspca_dev *gspca_dev,
                        struct v4l2_querymenu *menu)
 {
index 4473f0fb8b73bb779ae88bcc160ffea77cc0346e..0666038a51b02297026967dc25a879fcf2fa94e1 100644 (file)
@@ -21,7 +21,9 @@
 
 #define MODULE_NAME "zc3xx"
 
+#ifdef CONFIG_INPUT
 #include <linux/input.h>
+#endif
 #include "gspca.h"
 #include "jpeg.h"
 
@@ -50,33 +52,38 @@ struct sd {
 #define QUALITY_MAX 80
 #define QUALITY_DEF 70
 
+       u8 bridge;
        u8 sensor;              /* Type of image sensor chip */
-/* !! values used in different tables */
-#define SENSOR_ADCM2700 0
-#define SENSOR_CS2102 1
-#define SENSOR_CS2102K 2
-#define SENSOR_GC0305 3
-#define SENSOR_HDCS2020b 4
-#define SENSOR_HV7131B 5
-#define SENSOR_HV7131C 6
-#define SENSOR_ICM105A 7
-#define SENSOR_MC501CB 8
-#define SENSOR_MI0360SOC 9
-#define SENSOR_OV7620 10
-/*#define SENSOR_OV7648 10 - same values */
-#define SENSOR_OV7630C 11
-#define SENSOR_PAS106 12
-#define SENSOR_PAS202B 13
-#define SENSOR_PB0330 14       /* (MI0360) */
-#define SENSOR_PO2030 15
-#define SENSOR_TAS5130CK 16
-#define SENSOR_TAS5130CXX 17
-#define SENSOR_TAS5130C_VF0250 18
-#define SENSOR_MAX 19
-       unsigned short chip_revision;
+       u16 chip_revision;
 
        u8 jpeg_hdr[JPEG_HDR_SZ];
 };
+enum bridges {
+       BRIDGE_ZC301,
+       BRIDGE_ZC303,
+};
+enum sensors {
+       SENSOR_ADCM2700,
+       SENSOR_CS2102,
+       SENSOR_CS2102K,
+       SENSOR_GC0305,
+       SENSOR_HDCS2020b,
+       SENSOR_HV7131B,
+       SENSOR_HV7131R,
+       SENSOR_ICM105A,
+       SENSOR_MC501CB,
+       SENSOR_MT9V111_1,       /* (mi360soc) zc301 */
+       SENSOR_MT9V111_3,       /* (mi360soc) zc303 */
+       SENSOR_OV7620,          /* OV7648 - same values */
+       SENSOR_OV7630C,
+       SENSOR_PAS106,
+       SENSOR_PAS202B,
+       SENSOR_PB0330,
+       SENSOR_PO2030,
+       SENSOR_TAS5130C,
+       SENSOR_TAS5130C_VF0250,
+       SENSOR_MAX
+};
 
 /* V4L2 controls supported by the driver */
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
@@ -2074,6 +2081,7 @@ static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */
        {}
 };
 
+/* from lPEPI264v.inf (hv7131b!) */
 static const struct usb_action hv7131r_InitialScale[] = {
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
        {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
@@ -2081,8 +2089,8 @@ static const struct usb_action hv7131r_InitialScale[] = {
        {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
        {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
        {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
-       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
        {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
        {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
@@ -2095,6 +2103,8 @@ static const struct usb_action hv7131r_InitialScale[] = {
        {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
        {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
        {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xdd, 0x00, 0x0200},
        {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xaa, 0x01, 0x000c},
        {0xaa, 0x11, 0x0000},
@@ -2103,10 +2113,10 @@ static const struct usb_action hv7131r_InitialScale[] = {
        {0xaa, 0x15, 0x00e8},
        {0xaa, 0x16, 0x0002},
        {0xaa, 0x17, 0x0088},
-
+       {0xaa, 0x30, 0x000b},
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
        {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
-       {0xa0, 0x89, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},
        {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
        {0xa0, 0x00, 0x01ad},
        {0xa0, 0xc0, 0x019b},
@@ -2116,96 +2126,44 @@ static const struct usb_action hv7131r_InitialScale[] = {
        {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
        {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
        {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
-       {0xa1, 0x01, 0x0002},
-       {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
-       {0xa1, 0x01, 0x0091},
-       {0xa1, 0x01, 0x0095},
-       {0xa1, 0x01, 0x0096},
-
-       {0xa1, 0x01, 0x0008},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
-       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
-       {0xa1, 0x01, 0x01c8},
-       {0xa1, 0x01, 0x01c9},
-       {0xa1, 0x01, 0x01ca},
-       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
-
-       {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf0, ZC3XX_R10B_RGB01},
-       {0xa0, 0xf0, ZC3XX_R10C_RGB02},
-       {0xa0, 0xf0, ZC3XX_R10D_RGB10},
-       {0xa0, 0x60, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf0, ZC3XX_R10F_RGB12},
-       {0xa0, 0xf0, ZC3XX_R110_RGB20},
-       {0xa0, 0xf0, ZC3XX_R111_RGB21},
-       {0xa0, 0x60, ZC3XX_R112_RGB22},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xaa, 0x25, 0x0007},
-       {0xaa, 0x26, 0x0053},
-       {0xaa, 0x27, 0x0000},
-
-       {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 2f */
-       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},      /* 9b */
-       {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW},      /* 80 */
-       {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
-       {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID},
-       {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW},
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
-       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
-       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
-       {0xa0, 0x13, ZC3XX_R1AA_DIGITALGAINSTEP},
-       {0xa1, 0x01, 0x001d},
-       {0xa1, 0x01, 0x001e},
-       {0xa1, 0x01, 0x001f},
-       {0xa1, 0x01, 0x0020},
-       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
        {}
 };
-
 static const struct usb_action hv7131r_Initial[] = {
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
-
-       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},   /* diff */
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
        {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
        {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
        {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
        {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
-
-       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
-
+       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
        {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
        {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
-       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},        /* 1e0 */
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
 
        {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
        {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
        {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
-       {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+       {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
        {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
-       {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+       {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
        {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
        {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xdd, 0x00, 0x0200},
        {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xaa, 0x01, 0x000c},
        {0xaa, 0x11, 0x0000},
        {0xaa, 0x13, 0x0000},
        {0xaa, 0x14, 0x0001},
-       {0xaa, 0x15, 0x00e8},
+       {0xaa, 0x15, 0x00e6},
        {0xaa, 0x16, 0x0002},
-       {0xaa, 0x17, 0x0088},
-
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00 */
-
+       {0xaa, 0x17, 0x0086},
+       {0xaa, 0x30, 0x000b},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
        {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
-       {0xa0, 0x89, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},
        {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
        {0xa0, 0x00, 0x01ad},
        {0xa0, 0xc0, 0x019b},
@@ -2215,58 +2173,114 @@ static const struct usb_action hv7131r_Initial[] = {
        {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
        {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
        {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
-       {0xa1, 0x01, 0x0002},
-       {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
-                                               /* read the i2c chips ident */
-       {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
-       {0xa1, 0x01, 0x0091},
-       {0xa1, 0x01, 0x0095},
-       {0xa1, 0x01, 0x0096},
-
-       {0xa1, 0x01, 0x0008},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
-       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
-       {0xa1, 0x01, 0x01c8},
-       {0xa1, 0x01, 0x01c9},
-       {0xa1, 0x01, 0x01ca},
-       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
-
-       {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf0, ZC3XX_R10B_RGB01},
-       {0xa0, 0xf0, ZC3XX_R10C_RGB02},
-       {0xa0, 0xf0, ZC3XX_R10D_RGB10},
-       {0xa0, 0x60, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf0, ZC3XX_R10F_RGB12},
-       {0xa0, 0xf0, ZC3XX_R110_RGB20},
-       {0xa0, 0xf0, ZC3XX_R111_RGB21},
-       {0xa0, 0x60, ZC3XX_R112_RGB22},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+static const struct usb_action hv7131r_50HZ[] = {
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xaa, 0x25, 0x0007},
-       {0xaa, 0x26, 0x0053},
-       {0xaa, 0x27, 0x0000},
-
-       {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 2f */
-       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},      /* 9b */
-       {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW},      /* 80 */
-
+       {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x68, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+       {}
+};
+static const struct usb_action hv7131r_50HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0xd1, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x40, ZC3XX_R192_EXPOSURELIMITLOW},
        {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
        {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID},
        {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW},
-
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
        {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
-       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
        {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
-       {0xa0, 0x13, ZC3XX_R1AA_DIGITALGAINSTEP},
-       {0xa1, 0x01, 0x001d},
-       {0xa1, 0x01, 0x001e},
-       {0xa1, 0x01, 0x001f},
-       {0xa1, 0x01, 0x0020},
-       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+       {}
+};
+static const struct usb_action hv7131r_60HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x1a, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+       {}
+};
+static const struct usb_action hv7131r_60HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+       {}
+};
+static const struct usb_action hv7131r_NoFliker[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+       {}
+};
+static const struct usb_action hv7131r_NoFlikerScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x04, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0xb0, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
        {}
 };
 
@@ -3350,7 +3364,7 @@ static const struct usb_action ov7620_NoFliker[] = {
        {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
        {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,01,cc */
 /*     {0xa0, 0x44, ZC3XX_R002_CLOCKSELECT},    * 00,02,44,cc
-                                                - if mode1 (320x240) */
+                                                * if mode1 (320x240) */
 /* ?? was
        {0xa0, 0x00, 0x0039},  * 00,00,00,dd *
        {0xa1, 0x01, 0x0037},           */
@@ -3439,7 +3453,6 @@ static const struct usb_action ov7630c_InitialScale[] = {
        {0xa0, 0xf8, ZC3XX_R110_RGB20},
        {0xa0, 0xf8, ZC3XX_R111_RGB21},
        {0xa0, 0x50, ZC3XX_R112_RGB22},
-/* 0x03, */
        {0xa1, 0x01, 0x0008},
        {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
        {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
@@ -3719,7 +3732,7 @@ static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */
        {0xaa, 0x0e, 0x0002},
        {0xaa, 0x14, 0x0081},
 
-/* Other registors */
+/* Other registers */
        {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
 /* Frame retreiving */
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
@@ -3730,7 +3743,7 @@ static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */
 /* Sharpness */
        {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
        {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
-/* Other registors */
+/* Other registers */
        {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
 /* Auto exposure and white balance */
        {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
@@ -3837,7 +3850,7 @@ static const struct usb_action pas106b_Initial[] = {      /* 352x288 */
        {0xaa, 0x0e, 0x0002},
        {0xaa, 0x14, 0x0081},
 
-/* Other registors */
+/* Other registers */
        {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
 /* Frame retreiving */
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
@@ -3848,7 +3861,7 @@ static const struct usb_action pas106b_Initial[] = {      /* 352x288 */
 /* Sharpness */
        {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
        {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
-/* Other registors */
+/* Other registers */
        {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
 /* Auto exposure and white balance */
        {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
@@ -4241,8 +4254,8 @@ static const struct usb_action pas202b_NoFlikerScale[] = {
        {}
 };
 
-/* mi0360soc and pb0330 from vm30x.inf for 0ac8:301b and 0ac8:303b 07/02/13 */
-static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */
+/* mt9v111 (mi0360soc) and pb0330 from vm30x.inf 0ac8:301b 07/02/13 */
+static const struct usb_action mt9v111_1_Initial[] = { /* 640x480 */
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
        {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
        {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
@@ -4253,14 +4266,14 @@ static const struct usb_action mi0360soc_Initial[] = {  /* 640x480 */
        {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
        {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
        {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
-       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},      /*jfm: was 03*/
-/*     {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */
+       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
        {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
        {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
        {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
        {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
        {0xdd, 0x00, 0x0200},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xaa, 0x01, 0x0001},
        {0xaa, 0x06, 0x0000},
        {0xaa, 0x08, 0x0483},
@@ -4270,18 +4283,18 @@ static const struct usb_action mi0360soc_Initial[] = {  /* 640x480 */
        {0xaa, 0x03, 0x01e5},                   /*jfm: was 01e7*/
        {0xaa, 0x04, 0x0285},                   /*jfm: was 0287*/
        {0xaa, 0x07, 0x3002},
-       {0xaa, 0x20, 0x5100},                   /*jfm: was 1100*/
-       {0xaa, 0x35, 0x507f},                   /*jfm: was 0050*/
+       {0xaa, 0x20, 0x5100},
+       {0xaa, 0x35, 0x507f},
        {0xaa, 0x30, 0x0005},
        {0xaa, 0x31, 0x0000},
        {0xaa, 0x58, 0x0078},
        {0xaa, 0x62, 0x0411},
-       {0xaa, 0x2b, 0x0028},
+       {0xaa, 0x2b, 0x007f},
        {0xaa, 0x2c, 0x007f},                   /*jfm: was 0030*/
        {0xaa, 0x2d, 0x007f},                   /*jfm: was 0030*/
        {0xaa, 0x2e, 0x007f},                   /*jfm: was 0030*/
        {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
-       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/
+       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
        {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
        {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
@@ -4291,12 +4304,12 @@ static const struct usb_action mi0360soc_Initial[] = {  /* 640x480 */
        {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
        {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
        {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
-       {0xa0, 0x6c, ZC3XX_R18D_YTARGET},       /* jfm: was 78 */
+       {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
        {0xa0, 0x61, ZC3XX_R116_RGAIN},
        {0xa0, 0x65, ZC3XX_R118_BGAIN},
        {}
 };
-static const struct usb_action mi0360soc_InitialScale[] = {    /* 320x240 */
+static const struct usb_action mt9v111_1_InitialScale[] = {    /* 320x240 */
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
        {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
        {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
@@ -4307,14 +4320,14 @@ static const struct usb_action mi0360soc_InitialScale[] = {     /* 320x240 */
        {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
        {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
        {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
-       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},      /*jfm: was 03*/
-/*     {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */
+       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
        {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
        {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
        {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
        {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
        {0xdd, 0x00, 0x0200},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xaa, 0x01, 0x0001},
        {0xaa, 0x06, 0x0000},
        {0xaa, 0x08, 0x0483},
@@ -4324,7 +4337,7 @@ static const struct usb_action mi0360soc_InitialScale[] = {       /* 320x240 */
        {0xaa, 0x03, 0x01e7},
        {0xaa, 0x04, 0x0287},
        {0xaa, 0x07, 0x3002},
-       {0xaa, 0x20, 0x5100},                   /*jfm: was 1100*/
+       {0xaa, 0x20, 0x5100},
        {0xaa, 0x35, 0x007f},                   /*jfm: was 0050*/
        {0xaa, 0x30, 0x0005},
        {0xaa, 0x31, 0x0000},
@@ -4335,7 +4348,7 @@ static const struct usb_action mi0360soc_InitialScale[] = {       /* 320x240 */
        {0xaa, 0x2d, 0x007f},                   /*jfm: was 30*/
        {0xaa, 0x2e, 0x007f},                   /*jfm: was 28*/
        {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
-       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},      /*jfm: was 37*/
+       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
        {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
        {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
@@ -4345,12 +4358,12 @@ static const struct usb_action mi0360soc_InitialScale[] = {     /* 320x240 */
        {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
        {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
        {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
-       {0xa0, 0x6c, ZC3XX_R18D_YTARGET},       /*jfm: was 78*/
+       {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
        {0xa0, 0x61, ZC3XX_R116_RGAIN},
        {0xa0, 0x65, ZC3XX_R118_BGAIN},
        {}
 };
-static const struct usb_action mi360soc_AE50HZ[] = {
+static const struct usb_action mt9v111_1_AE50HZ[] = {
        {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
        {0xbb, 0x00, 0x0562},
@@ -4373,7 +4386,7 @@ static const struct usb_action mi360soc_AE50HZ[] = {
        {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
        {}
 };
-static const struct usb_action mi360soc_AE50HZScale[] = {
+static const struct usb_action mt9v111_1_AE50HZScale[] = {
        {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
        {0xbb, 0x00, 0x0509},
@@ -4395,11 +4408,11 @@ static const struct usb_action mi360soc_AE50HZScale[] = {
        {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
        {}
 };
-static const struct usb_action mi360soc_AE60HZ[] = {
+static const struct usb_action mt9v111_1_AE60HZ[] = {
        {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xbb, 0x00, 0x053d},
-       {0xbb, 0x01, 0x096e},
+       {0xaa, 0x05, 0x003d},
+       {0xaa, 0x09, 0x016e},
        {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
        {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
        {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW},
@@ -4418,7 +4431,7 @@ static const struct usb_action mi360soc_AE60HZ[] = {
        {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
        {}
 };
-static const struct usb_action mi360soc_AE60HZScale[] = {
+static const struct usb_action mt9v111_1_AE60HZScale[] = {
        {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
        {0xbb, 0x00, 0x0509},
@@ -4440,7 +4453,7 @@ static const struct usb_action mi360soc_AE60HZScale[] = {
        {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
        {}
 };
-static const struct usb_action mi360soc_AENoFliker[] = {
+static const struct usb_action mt9v111_1_AENoFliker[] = {
        {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
        {0xbb, 0x00, 0x0509},
@@ -4463,7 +4476,7 @@ static const struct usb_action mi360soc_AENoFliker[] = {
        {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
        {}
 };
-static const struct usb_action mi360soc_AENoFlikerScale[] = {
+static const struct usb_action mt9v111_1_AENoFlikerScale[] = {
        {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
        {0xbb, 0x00, 0x0534},
@@ -4486,180 +4499,425 @@ static const struct usb_action mi360soc_AENoFlikerScale[] = {
        {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
        {}
 };
-
-static const struct usb_action pb0330_Initial[] = {    /* 640x480 */
+/* from usbvm303.inf 0ac8:303b 07/03/25 (3 - tas5130c) */
+static const struct usb_action mt9v111_3_Initial[] = {
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
        {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
-       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT},
        {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
        {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
        {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
        {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
        {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
-       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
        {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
        {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
        {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
        {0xdd, 0x00, 0x0200},
        {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
-       {0xaa, 0x01, 0x0006},
-       {0xaa, 0x02, 0x0011},
-       {0xaa, 0x03, 0x01e5},                   /*jfm: was 1e7*/
-       {0xaa, 0x04, 0x0285},                   /*jfm: was 0287*/
-       {0xaa, 0x06, 0x0003},
-       {0xaa, 0x07, 0x3002},
-       {0xaa, 0x20, 0x1100},
-       {0xaa, 0x2f, 0xf7b0},
+       {0xaa, 0x01, 0x0001},           /* select IFP/SOC registers */
+       {0xaa, 0x06, 0x0000},           /* operating mode control */
+       {0xaa, 0x08, 0x0483},           /* output format control */
+                                       /* H red first, V red or blue first,
+                                        * raw Bayer, auto flicker */
+       {0xaa, 0x01, 0x0004},           /* select sensor core registers */
+       {0xaa, 0x08, 0x0006},           /* row start */
+       {0xaa, 0x02, 0x0011},           /* column start */
+       {0xaa, 0x03, 0x01e5},           /* window height - 1 */
+       {0xaa, 0x04, 0x0285},           /* window width - 1 */
+       {0xaa, 0x07, 0x3002},           /* output control */
+       {0xaa, 0x20, 0x1100},           /* read mode: bits 8 & 12 (?) */
+       {0xaa, 0x35, 0x007f},           /* global gain */
        {0xaa, 0x30, 0x0005},
        {0xaa, 0x31, 0x0000},
-       {0xaa, 0x34, 0x0100},
-       {0xaa, 0x35, 0x0060},
-       {0xaa, 0x3d, 0x068f},
-       {0xaa, 0x40, 0x01e0},
        {0xaa, 0x58, 0x0078},
        {0xaa, 0x62, 0x0411},
+       {0xaa, 0x2b, 0x007f},           /* green1 gain */
+       {0xaa, 0x2c, 0x007f},           /* blue gain */
+       {0xaa, 0x2d, 0x007f},           /* red gain */
+       {0xaa, 0x2e, 0x007f},           /* green2 gain */
        {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
        {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
        {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
        {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
-       {0xa0, 0x09, 0x01ad},                   /*jfm: was 00 */
-       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x00, 0x01ad},
        {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
        {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
        {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
        {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
        {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
-       {0xa0, 0x78, ZC3XX_R18D_YTARGET},       /*jfm: was 6c*/
+       {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x61, ZC3XX_R116_RGAIN},
+       {0xa0, 0x65, ZC3XX_R118_BGAIN},
        {}
 };
-static const struct usb_action pb0330_InitialScale[] = {       /* 320x240 */
+static const struct usb_action mt9v111_3_InitialScale[] = {
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
        {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
        {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
        {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
        {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
        {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
        {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
        {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
-       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
        {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
        {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
        {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
        {0xdd, 0x00, 0x0200},
        {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
-       {0xaa, 0x01, 0x0006},
+       {0xaa, 0x01, 0x0001},
+       {0xaa, 0x06, 0x0000},
+       {0xaa, 0x08, 0x0483},
+       {0xaa, 0x01, 0x0004},
+       {0xaa, 0x08, 0x0006},
        {0xaa, 0x02, 0x0011},
        {0xaa, 0x03, 0x01e7},
        {0xaa, 0x04, 0x0287},
-       {0xaa, 0x06, 0x0003},
        {0xaa, 0x07, 0x3002},
        {0xaa, 0x20, 0x1100},
-       {0xaa, 0x2f, 0xf7b0},
+       {0xaa, 0x35, 0x007f},
        {0xaa, 0x30, 0x0005},
        {0xaa, 0x31, 0x0000},
-       {0xaa, 0x34, 0x0100},
-       {0xaa, 0x35, 0x0060},
-       {0xaa, 0x3d, 0x068f},
-       {0xaa, 0x40, 0x01e0},
        {0xaa, 0x58, 0x0078},
        {0xaa, 0x62, 0x0411},
+       {0xaa, 0x2b, 0x007f},
+       {0xaa, 0x2c, 0x007f},
+       {0xaa, 0x2d, 0x007f},
+       {0xaa, 0x2e, 0x007f},
        {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
        {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
        {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
        {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x00, 0x01ad},
        {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
        {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
        {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
        {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
        {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
-       {0xa0, 0x78, ZC3XX_R18D_YTARGET},       /*jfm: was 6c*/
+       {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x61, ZC3XX_R116_RGAIN},
+       {0xa0, 0x65, ZC3XX_R118_BGAIN},
        {}
 };
-static const struct usb_action pb0330_50HZ[] = {
+static const struct usb_action mt9v111_3_AE50HZ[] = {
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xbb, 0x00, 0x055c},
-       {0xbb, 0x01, 0x09aa},
-       {0xbb, 0x00, 0x1001},
-       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xaa, 0x05, 0x0009},           /* horizontal blanking */
+       {0xaa, 0x09, 0x01ce},           /* shutter width */
        {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
        {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
-       {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
        {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
        {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
-       {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
        {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
-       {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
        {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
-       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
-       {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0},
-       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
-       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
        {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
        {}
 };
-static const struct usb_action pb0330_50HZScale[] = {
+static const struct usb_action mt9v111_3_AE50HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xbb, 0x00, 0x0566},
-       {0xbb, 0x02, 0x09b2},
-       {0xbb, 0x00, 0x1002},
-       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xaa, 0x05, 0x0009},
+       {0xaa, 0x09, 0x01ce},
        {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
        {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
-       {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
        {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
        {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
-       {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
        {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
-       {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
        {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
-       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
        {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
-       {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
-       {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
        {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
        {}
 };
-static const struct usb_action pb0330_60HZ[] = {
+static const struct usb_action mt9v111_3_AE60HZ[] = {
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xbb, 0x00, 0x0535},
-       {0xbb, 0x01, 0x0974},
-       {0xbb, 0x00, 0x1001},
-       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xaa, 0x05, 0x0009},
+       {0xaa, 0x09, 0x0083},
        {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
        {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
-       {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
        {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
        {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
-       {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
        {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
-       {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
        {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
-       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
-       {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
-       {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
-       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
-       {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
        {}
 };
-static const struct usb_action pb0330_60HZScale[] = {
+static const struct usb_action mt9v111_3_AE60HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xbb, 0x00, 0x0535},
-       {0xbb, 0x02, 0x096c},
-       {0xbb, 0x00, 0x1002},
-       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xaa, 0x05, 0x0009},
+       {0xaa, 0x09, 0x0083},
        {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
        {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
-       {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+static const struct usb_action mt9v111_3_AENoFliker[] = {
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x05, 0x0034},
+       {0xaa, 0x09, 0x0260},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+static const struct usb_action mt9v111_3_AENoFlikerScale[] = {
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x05, 0x0034},
+       {0xaa, 0x09, 0x0260},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+
+static const struct usb_action pb0330_Initial[] = {    /* 640x480 */
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
+       {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xdd, 0x00, 0x0200},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xaa, 0x01, 0x0006},
+       {0xaa, 0x02, 0x0011},
+       {0xaa, 0x03, 0x01e5},                   /*jfm: was 1e7*/
+       {0xaa, 0x04, 0x0285},                   /*jfm: was 0287*/
+       {0xaa, 0x06, 0x0003},
+       {0xaa, 0x07, 0x3002},
+       {0xaa, 0x20, 0x1100},
+       {0xaa, 0x2f, 0xf7b0},
+       {0xaa, 0x30, 0x0005},
+       {0xaa, 0x31, 0x0000},
+       {0xaa, 0x34, 0x0100},
+       {0xaa, 0x35, 0x0060},
+       {0xaa, 0x3d, 0x068f},
+       {0xaa, 0x40, 0x01e0},
+       {0xaa, 0x58, 0x0078},
+       {0xaa, 0x62, 0x0411},
+       {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x09, 0x01ad},                   /*jfm: was 00 */
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},       /*jfm: was 6c*/
+       {}
+};
+static const struct usb_action pb0330_InitialScale[] = {       /* 320x240 */
+       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
+       {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xdd, 0x00, 0x0200},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xaa, 0x01, 0x0006},
+       {0xaa, 0x02, 0x0011},
+       {0xaa, 0x03, 0x01e7},
+       {0xaa, 0x04, 0x0287},
+       {0xaa, 0x06, 0x0003},
+       {0xaa, 0x07, 0x3002},
+       {0xaa, 0x20, 0x1100},
+       {0xaa, 0x2f, 0xf7b0},
+       {0xaa, 0x30, 0x0005},
+       {0xaa, 0x31, 0x0000},
+       {0xaa, 0x34, 0x0100},
+       {0xaa, 0x35, 0x0060},
+       {0xaa, 0x3d, 0x068f},
+       {0xaa, 0x40, 0x01e0},
+       {0xaa, 0x58, 0x0078},
+       {0xaa, 0x62, 0x0411},
+       {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},       /*jfm: was 6c*/
+       {}
+};
+static const struct usb_action pb0330_50HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xbb, 0x00, 0x055c},
+       {0xbb, 0x01, 0x09aa},
+       {0xbb, 0x00, 0x1001},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {}
+};
+static const struct usb_action pb0330_50HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xbb, 0x00, 0x0566},
+       {0xbb, 0x02, 0x09b2},
+       {0xbb, 0x00, 0x1002},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {}
+};
+static const struct usb_action pb0330_60HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xbb, 0x00, 0x0535},
+       {0xbb, 0x01, 0x0974},
+       {0xbb, 0x00, 0x1001},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
+       {}
+};
+static const struct usb_action pb0330_60HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xbb, 0x00, 0x0535},
+       {0xbb, 0x02, 0x096c},
+       {0xbb, 0x00, 0x1002},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW},
        {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
        {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
        {0xa0, 0x7c, ZC3XX_R197_ANTIFLICKERLOW},
@@ -4855,492 +5113,80 @@ static const struct usb_action po2030_InitialScale[] = {       /* 320x240 */
        {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */
        {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */
        {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */
-       {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */
-       {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */
-       {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */
-       {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */
-       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
-       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
-       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
-       {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
-       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
-       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
-       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
-       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
-       {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */
-       {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */
-       {}
-};
-
-static const struct usb_action po2030_50HZ[] = {
-       {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
-       {0xaa, 0x1a, 0x0001}, /* 00,1a,01,aa */
-       {0xaa, 0x1b, 0x000a}, /* 00,1b,0a,aa */
-       {0xaa, 0x1c, 0x00b0}, /* 00,1c,b0,aa */
-       {0xa0, 0x05, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,05,cc */
-       {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,35,cc */
-       {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x85, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,85,cc */
-       {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,58,cc */
-       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
-       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
-       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
-       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
-       {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */
-       {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */
-       {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
-       {}
-};
-
-static const struct usb_action po2030_60HZ[] = {
-       {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
-       {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
-       {0xaa, 0x1b, 0x00de}, /* 00,1b,de,aa */
-       {0xaa, 0x1c, 0x0040}, /* 00,1c,40,aa */
-       {0xa0, 0x08, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,08,cc */
-       {0xa0, 0xae, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,ae,cc */
-       {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x6f, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,6f,cc */
-       {0xa0, 0x20, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,20,cc */
-       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
-       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
-       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
-       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
-       {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP},       /* 01,aa,22,cc */
-       {0xa0, 0x88, ZC3XX_R18D_YTARGET},               /* 01,8d,88,cc */
-                                                       /* win: 01,8d,80 */
-       {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN},            /* 01,1d,58,cc */
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},     /* 01,80,42,cc */
-       {}
-};
-
-static const struct usb_action po2030_NoFliker[] = {
-       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
-       {0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */
-       {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
-       {0xaa, 0x1b, 0x0002}, /* 00,1b,02,aa */
-       {0xaa, 0x1c, 0x0078}, /* 00,1c,78,aa */
-       {0xaa, 0x46, 0x0000}, /* 00,46,00,aa */
-       {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
-       {}
-};
-
-/* TEST */
-static const struct usb_action tas5130cK_InitialScale[] = {
-       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
-       {0xa0, 0x01, 0x003b},
-       {0xa0, 0x0e, 0x003a},
-       {0xa0, 0x01, 0x0038},
-       {0xa0, 0x0b, 0x0039},
-       {0xa0, 0x00, 0x0038},
-       {0xa0, 0x0b, 0x0039},
-       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
-       {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
-       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
-       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
-       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
-       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
-       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
-       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
-       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
-       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
-       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
-       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
-       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
-       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
-       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
-       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
-       {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x83, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x06, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x02, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0xE7, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x87, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x02, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x07, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x30, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x51, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x35, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x30, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x05, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x31, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x58, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x78, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x62, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x2B, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x2c, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x2D, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x2e, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
-       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
-       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
-       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
-       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
-       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
-       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
-       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
-       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
-       {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
-       {0xa0, 0x61, ZC3XX_R116_RGAIN},
-       {0xa0, 0x65, ZC3XX_R118_BGAIN},
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x15, 0x01ae},
-       {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf1, ZC3XX_R10B_RGB01},
-       {0xa0, 0x03, ZC3XX_R10C_RGB02},
-       {0xa0, 0xfe, ZC3XX_R10D_RGB10},
-       {0xa0, 0x51, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf1, ZC3XX_R10F_RGB12},
-       {0xa0, 0xec, ZC3XX_R110_RGB20},
-       {0xa0, 0x03, ZC3XX_R111_RGB21},
-       {0xa0, 0x51, ZC3XX_R112_RGB22},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
-       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
-       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
-       {0xa0, 0x38, ZC3XX_R120_GAMMA00},       /* gamma > 5 */
-       {0xa0, 0x51, ZC3XX_R121_GAMMA01},
-       {0xa0, 0x6e, ZC3XX_R122_GAMMA02},
-       {0xa0, 0x8c, ZC3XX_R123_GAMMA03},
-       {0xa0, 0xa2, ZC3XX_R124_GAMMA04},
-       {0xa0, 0xb6, ZC3XX_R125_GAMMA05},
-       {0xa0, 0xc8, ZC3XX_R126_GAMMA06},
-       {0xa0, 0xd6, ZC3XX_R127_GAMMA07},
-       {0xa0, 0xe2, ZC3XX_R128_GAMMA08},
-       {0xa0, 0xed, ZC3XX_R129_GAMMA09},
-       {0xa0, 0xf5, ZC3XX_R12A_GAMMA0A},
-       {0xa0, 0xfc, ZC3XX_R12B_GAMMA0B},
-       {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
-       {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
-       {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
-       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
-       {0xa0, 0x12, ZC3XX_R130_GAMMA10},
-       {0xa0, 0x1b, ZC3XX_R131_GAMMA11},
-       {0xa0, 0x1d, ZC3XX_R132_GAMMA12},
-       {0xa0, 0x1a, ZC3XX_R133_GAMMA13},
-       {0xa0, 0x15, ZC3XX_R134_GAMMA14},
-       {0xa0, 0x12, ZC3XX_R135_GAMMA15},
-       {0xa0, 0x0f, ZC3XX_R136_GAMMA16},
-       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
-       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
-       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
-       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
-       {0xa0, 0x05, ZC3XX_R13B_GAMMA1B},
-       {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
-       {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
-       {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
-       {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
-       {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf1, ZC3XX_R10B_RGB01},
-       {0xa0, 0x03, ZC3XX_R10C_RGB02},
-       {0xa0, 0xfe, ZC3XX_R10D_RGB10},
-       {0xa0, 0x51, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf1, ZC3XX_R10F_RGB12},
-       {0xa0, 0xec, ZC3XX_R110_RGB20},
-       {0xa0, 0x03, ZC3XX_R111_RGB21},
-       {0xa0, 0x51, ZC3XX_R112_RGB22},
-       {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x09, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x34, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
-       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
-       {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
-       {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
-       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
-       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
-       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
-       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
-       {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
-       {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
-       {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
-       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x15, 0x01ae},
-       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */
+       {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */
+       {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */
+       {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */
+       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+       {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+       {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */
+       {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */
        {}
 };
 
-static const struct usb_action tas5130cK_Initial[] = {
-       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
-       {0xa0, 0x01, 0x003b},
-       {0xa0, 0x0e, 0x003a},
-       {0xa0, 0x01, 0x0038},
-       {0xa0, 0x0b, 0x0039},
-       {0xa0, 0x00, 0x0038},
-       {0xa0, 0x0b, 0x0039},
-       {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
-       {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
-       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
-       {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
-       {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
-       {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
-       {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
-       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
-       {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
-       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
-       {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
-       {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
-       {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
-       {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
-       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
-       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
-       {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x83, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x06, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x02, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0xe5, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x85, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x02, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x07, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x30, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x51, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x35, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x50, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x30, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x05, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x31, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x58, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x78, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x62, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x2B, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x2C, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x2D, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x2e, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
-       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
-       {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
-       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
-       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
-       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
-       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
-       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
-       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
-       {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
-       {0xa0, 0x61, ZC3XX_R116_RGAIN},
-       {0xa0, 0x65, ZC3XX_R118_BGAIN},
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x15, 0x01ae},
-       {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf1, ZC3XX_R10B_RGB01},
-       {0xa0, 0x03, ZC3XX_R10C_RGB02},
-       {0xa0, 0xfe, ZC3XX_R10D_RGB10},
-       {0xa0, 0x51, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf1, ZC3XX_R10F_RGB12},
-       {0xa0, 0xec, ZC3XX_R110_RGB20},
-       {0xa0, 0x03, ZC3XX_R111_RGB21},
-       {0xa0, 0x51, ZC3XX_R112_RGB22},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
-       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
-       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
-       {0xa0, 0x38, ZC3XX_R120_GAMMA00},       /* gamma > 5 */
-       {0xa0, 0x51, ZC3XX_R121_GAMMA01},
-       {0xa0, 0x6e, ZC3XX_R122_GAMMA02},
-       {0xa0, 0x8c, ZC3XX_R123_GAMMA03},
-       {0xa0, 0xa2, ZC3XX_R124_GAMMA04},
-       {0xa0, 0xb6, ZC3XX_R125_GAMMA05},
-       {0xa0, 0xc8, ZC3XX_R126_GAMMA06},
-       {0xa0, 0xd6, ZC3XX_R127_GAMMA07},
-       {0xa0, 0xe2, ZC3XX_R128_GAMMA08},
-       {0xa0, 0xed, ZC3XX_R129_GAMMA09},
-       {0xa0, 0xf5, ZC3XX_R12A_GAMMA0A},
-       {0xa0, 0xfc, ZC3XX_R12B_GAMMA0B},
-       {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
-       {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
-       {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
-       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
-       {0xa0, 0x12, ZC3XX_R130_GAMMA10},
-       {0xa0, 0x1b, ZC3XX_R131_GAMMA11},
-       {0xa0, 0x1d, ZC3XX_R132_GAMMA12},
-       {0xa0, 0x1a, ZC3XX_R133_GAMMA13},
-       {0xa0, 0x15, ZC3XX_R134_GAMMA14},
-       {0xa0, 0x12, ZC3XX_R135_GAMMA15},
-       {0xa0, 0x0f, ZC3XX_R136_GAMMA16},
-       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
-       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
-       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
-       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
-       {0xa0, 0x05, ZC3XX_R13B_GAMMA1B},
-       {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
-       {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
-       {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
-       {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
-       {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf1, ZC3XX_R10B_RGB01},
-       {0xa0, 0x03, ZC3XX_R10C_RGB02},
-       {0xa0, 0xfe, ZC3XX_R10D_RGB10},
-       {0xa0, 0x51, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf1, ZC3XX_R10F_RGB12},
-       {0xa0, 0xec, ZC3XX_R110_RGB20},
-       {0xa0, 0x03, ZC3XX_R111_RGB21},
-       {0xa0, 0x51, ZC3XX_R112_RGB22},
-       {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x62, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0xaa, ZC3XX_R093_I2CSETVALUE},
-       {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
-       {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
-       {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
-       {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW},
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
-       {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
-       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
-       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
-       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
-       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
-       {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
-       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
-       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
-       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
-       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x15, 0x01ae},
-       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x30, 0x0007},
-       {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING},
-       {0xa0, 0x00, 0x0007},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+static const struct usb_action po2030_50HZ[] = {
+       {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+       {0xaa, 0x1a, 0x0001}, /* 00,1a,01,aa */
+       {0xaa, 0x1b, 0x000a}, /* 00,1b,0a,aa */
+       {0xaa, 0x1c, 0x00b0}, /* 00,1c,b0,aa */
+       {0xa0, 0x05, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,05,cc */
+       {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,35,cc */
+       {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x85, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,85,cc */
+       {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,58,cc */
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+       {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */
+       {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */
+       {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+       {}
+};
+
+static const struct usb_action po2030_60HZ[] = {
+       {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+       {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+       {0xaa, 0x1b, 0x00de}, /* 00,1b,de,aa */
+       {0xaa, 0x1c, 0x0040}, /* 00,1c,40,aa */
+       {0xa0, 0x08, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,08,cc */
+       {0xa0, 0xae, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,ae,cc */
+       {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+       {0xa0, 0x6f, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,6f,cc */
+       {0xa0, 0x20, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,20,cc */
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+       {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP},       /* 01,aa,22,cc */
+       {0xa0, 0x88, ZC3XX_R18D_YTARGET},               /* 01,8d,88,cc */
+                                                       /* win: 01,8d,80 */
+       {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN},            /* 01,1d,58,cc */
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},     /* 01,80,42,cc */
+       {}
+};
+
+static const struct usb_action po2030_NoFliker[] = {
+       {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+       {0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */
+       {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+       {0xaa, 0x1b, 0x0002}, /* 00,1b,02,aa */
+       {0xaa, 0x1c, 0x0078}, /* 00,1c,78,aa */
+       {0xaa, 0x46, 0x0000}, /* 00,46,00,aa */
+       {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
        {}
 };
 
-static const struct usb_action tas5130cxx_InitialScale[] = {   /* 320x240 */
+static const struct usb_action tas5130c_InitialScale[] = {     /* 320x240 */
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
        {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT},
        {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
@@ -5377,7 +5223,7 @@ static const struct usb_action tas5130cxx_InitialScale[] = {      /* 320x240 */
        {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
        {}
 };
-static const struct usb_action tas5130cxx_Initial[] = {        /* 640x480 */
+static const struct usb_action tas5130c_Initial[] = {  /* 640x480 */
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
        {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
        {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING},
@@ -5413,7 +5259,7 @@ static const struct usb_action tas5130cxx_Initial[] = {   /* 640x480 */
        {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
        {}
 };
-static const struct usb_action tas5130cxx_50HZ[] = {
+static const struct usb_action tas5130c_50HZ[] = {
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
        {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
        {0xaa, 0xa4, 0x0063}, /* 00,a4,63,aa */
@@ -5438,7 +5284,7 @@ static const struct usb_action tas5130cxx_50HZ[] = {
        {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
        {}
 };
-static const struct usb_action tas5130cxx_50HZScale[] = {
+static const struct usb_action tas5130c_50HZScale[] = {
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
        {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
        {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */
@@ -5463,7 +5309,7 @@ static const struct usb_action tas5130cxx_50HZScale[] = {
        {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
        {}
 };
-static const struct usb_action tas5130cxx_60HZ[] = {
+static const struct usb_action tas5130c_60HZ[] = {
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
        {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
        {0xaa, 0xa4, 0x0036}, /* 00,a4,36,aa */
@@ -5488,7 +5334,7 @@ static const struct usb_action tas5130cxx_60HZ[] = {
        {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
        {}
 };
-static const struct usb_action tas5130cxx_60HZScale[] = {
+static const struct usb_action tas5130c_60HZScale[] = {
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
        {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
        {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */
@@ -5513,7 +5359,7 @@ static const struct usb_action tas5130cxx_60HZScale[] = {
        {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
        {}
 };
-static const struct usb_action tas5130cxx_NoFliker[] = {
+static const struct usb_action tas5130c_NoFliker[] = {
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
        {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
        {0xaa, 0xa4, 0x0040}, /* 00,a4,40,aa */
@@ -5539,7 +5385,7 @@ static const struct usb_action tas5130cxx_NoFliker[] = {
        {}
 };
 
-static const struct usb_action tas5130cxx_NoFlikerScale[] = {
+static const struct usb_action tas5130c_NoFlikerScale[] = {
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
        {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
        {0xaa, 0xa4, 0x0090}, /* 00,a4,90,aa */
@@ -5840,13 +5686,22 @@ static const struct usb_action tas5130c_vf0250_NoFliker[] = {
 static u8 reg_r_i(struct gspca_dev *gspca_dev,
                u16 index)
 {
-       usb_control_msg(gspca_dev->dev,
+       int ret;
+
+       if (gspca_dev->usb_err < 0)
+               return 0;
+       ret = usb_control_msg(gspca_dev->dev,
                        usb_rcvctrlpipe(gspca_dev->dev, 0),
                        0xa1,
                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                        0x01,                   /* value */
                        index, gspca_dev->usb_buf, 1,
                        500);
+       if (ret < 0) {
+               PDEBUG(D_ERR, "reg_r_i err %d", ret);
+               gspca_dev->usb_err = ret;
+               return 0;
+       }
        return gspca_dev->usb_buf[0];
 }
 
@@ -5860,24 +5715,32 @@ static u8 reg_r(struct gspca_dev *gspca_dev,
        return ret;
 }
 
-static void reg_w_i(struct usb_device *dev,
+static void reg_w_i(struct gspca_dev *gspca_dev,
                        u8 value,
                        u16 index)
 {
-       usb_control_msg(dev,
-                       usb_sndctrlpipe(dev, 0),
+       int ret;
+
+       if (gspca_dev->usb_err < 0)
+               return;
+       ret = usb_control_msg(gspca_dev->dev,
+                       usb_sndctrlpipe(gspca_dev->dev, 0),
                        0xa0,
                        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                        value, index, NULL, 0,
                        500);
+       if (ret < 0) {
+               PDEBUG(D_ERR, "reg_w_i err %d", ret);
+               gspca_dev->usb_err = ret;
+       }
 }
 
-static void reg_w(struct usb_device *dev,
+static void reg_w(struct gspca_dev *gspca_dev,
                        u8 value,
                        u16 index)
 {
        PDEBUG(D_USBO, "reg w [%04x] = %02x", index, value);
-       reg_w_i(dev, value, index);
+       reg_w_i(gspca_dev, value, index);
 }
 
 static u16 i2c_read(struct gspca_dev *gspca_dev,
@@ -5886,8 +5749,10 @@ static u16 i2c_read(struct gspca_dev *gspca_dev,
        u8 retbyte;
        u16 retval;
 
-       reg_w_i(gspca_dev->dev, reg, 0x0092);
-       reg_w_i(gspca_dev->dev, 0x02, 0x0090);          /* <- read command */
+       if (gspca_dev->usb_err < 0)
+               return 0;
+       reg_w_i(gspca_dev, reg, 0x0092);
+       reg_w_i(gspca_dev, 0x02, 0x0090);               /* <- read command */
        msleep(20);
        retbyte = reg_r_i(gspca_dev, 0x0091);           /* read status */
        if (retbyte != 0x00)
@@ -5906,10 +5771,12 @@ static u8 i2c_write(struct gspca_dev *gspca_dev,
 {
        u8 retbyte;
 
-       reg_w_i(gspca_dev->dev, reg, 0x92);
-       reg_w_i(gspca_dev->dev, valL, 0x93);
-       reg_w_i(gspca_dev->dev, valH, 0x94);
-       reg_w_i(gspca_dev->dev, 0x01, 0x90);            /* <- write command */
+       if (gspca_dev->usb_err < 0)
+               return 0;
+       reg_w_i(gspca_dev, reg, 0x92);
+       reg_w_i(gspca_dev, valL, 0x93);
+       reg_w_i(gspca_dev, valH, 0x94);
+       reg_w_i(gspca_dev, 0x01, 0x90);         /* <- write command */
        msleep(1);
        retbyte = reg_r_i(gspca_dev, 0x0091);           /* read status */
        if (retbyte != 0x00)
@@ -5925,7 +5792,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev,
        while (action->req) {
                switch (action->req) {
                case 0xa0:      /* write register */
-                       reg_w(gspca_dev->dev, action->val, action->idx);
+                       reg_w(gspca_dev, action->val, action->idx);
                        break;
                case 0xa1:      /* read status */
                        reg_r(gspca_dev, action->idx);
@@ -5974,38 +5841,37 @@ static void setmatrix(struct gspca_dev *gspca_dev)
        static const u8 vf0250_matrix[9] =
                {0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b};
        static const u8 *matrix_tb[SENSOR_MAX] = {
-               adcm2700_matrix, /* SENSOR_ADCM2700 0 */
-               ov7620_matrix,  /* SENSOR_CS2102 1 */
-               NULL,           /* SENSOR_CS2102K 2 */
-               gc0305_matrix,  /* SENSOR_GC0305 3 */
-               NULL,           /* SENSOR_HDCS2020b 4 */
-               NULL,           /* SENSOR_HV7131B 5 */
-               NULL,           /* SENSOR_HV7131C 6 */
-               NULL,           /* SENSOR_ICM105A 7 */
-               NULL,           /* SENSOR_MC501CB 8 */
-               gc0305_matrix,  /* SENSOR_MI0360SOC 9 */
-               ov7620_matrix,  /* SENSOR_OV7620 10 */
-               NULL,           /* SENSOR_OV7630C 11 */
-               NULL,           /* SENSOR_PAS106 12 */
-               pas202b_matrix, /* SENSOR_PAS202B 13 */
-               gc0305_matrix,  /* SENSOR_PB0330 14 */
-               po2030_matrix,  /* SENSOR_PO2030 15 */
-               NULL,           /* SENSOR_TAS5130CK 16 */
-               tas5130c_matrix, /* SENSOR_TAS5130CXX 17 */
-               vf0250_matrix,  /* SENSOR_TAS5130C_VF0250 18 */
+               [SENSOR_ADCM2700] =     adcm2700_matrix,
+               [SENSOR_CS2102] =       ov7620_matrix,
+               [SENSOR_CS2102K] =      NULL,
+               [SENSOR_GC0305] =       gc0305_matrix,
+               [SENSOR_HDCS2020b] =    NULL,
+               [SENSOR_HV7131B] =      NULL,
+               [SENSOR_HV7131R] =      NULL,
+               [SENSOR_ICM105A] =      po2030_matrix,
+               [SENSOR_MC501CB] =      NULL,
+               [SENSOR_MT9V111_1] =    gc0305_matrix,
+               [SENSOR_MT9V111_3] =    gc0305_matrix,
+               [SENSOR_OV7620] =       ov7620_matrix,
+               [SENSOR_OV7630C] =      NULL,
+               [SENSOR_PAS106] =       NULL,
+               [SENSOR_PAS202B] =      pas202b_matrix,
+               [SENSOR_PB0330] =       gc0305_matrix,
+               [SENSOR_PO2030] =       po2030_matrix,
+               [SENSOR_TAS5130C] =     tas5130c_matrix,
+               [SENSOR_TAS5130C_VF0250] = vf0250_matrix,
        };
 
        matrix = matrix_tb[sd->sensor];
        if (matrix == NULL)
                return;         /* matrix already loaded */
        for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++)
-               reg_w(gspca_dev->dev, matrix[i], 0x010a + i);
+               reg_w(gspca_dev, matrix[i], 0x010a + i);
 }
 
 static void setsharpness(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       struct usb_device *dev = gspca_dev->dev;
        int sharpness;
        static const u8 sharpness_tb[][2] = {
                {0x02, 0x03},
@@ -6015,17 +5881,16 @@ static void setsharpness(struct gspca_dev *gspca_dev)
        };
 
        sharpness = sd->sharpness;
-       reg_w(dev, sharpness_tb[sharpness][0], 0x01c6);
+       reg_w(gspca_dev, sharpness_tb[sharpness][0], 0x01c6);
        reg_r(gspca_dev, 0x01c8);
        reg_r(gspca_dev, 0x01c9);
        reg_r(gspca_dev, 0x01ca);
-       reg_w(dev, sharpness_tb[sharpness][1], 0x01cb);
+       reg_w(gspca_dev, sharpness_tb[sharpness][1], 0x01cb);
 }
 
 static void setcontrast(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       struct usb_device *dev = gspca_dev->dev;
        const u8 *Tgamma;
        int g, i, brightness, contrast, adj, gp1, gp2;
        u8 gr[16];
@@ -6063,7 +5928,7 @@ static void setcontrast(struct gspca_dev *gspca_dev)
                        g = 0xff;
                else if (g < 0)
                        g = 0;
-               reg_w(dev, g, 0x0120 + i);      /* gamma */
+               reg_w(gspca_dev, g, 0x0120 + i);        /* gamma */
                if (contrast > 0)
                        adj--;
                else if (contrast < 0)
@@ -6077,13 +5942,12 @@ static void setcontrast(struct gspca_dev *gspca_dev)
        }
        gr[15] = (0xff - gp2) / 2;
        for (i = 0; i < 16; i++)
-               reg_w(dev, gr[i], 0x0130 + i);  /* gradient */
+               reg_w(gspca_dev, gr[i], 0x0130 + i);    /* gradient */
 }
 
 static void setquality(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       struct usb_device *dev = gspca_dev->dev;
        u8 frxt;
 
        switch (sd->sensor) {
@@ -6096,9 +5960,9 @@ static void setquality(struct gspca_dev *gspca_dev)
                return;
        }
 /*fixme: is it really 0008 0007 0018 for all other sensors? */
-       reg_w(dev, QUANT_VAL, 0x0008);
+       reg_w(gspca_dev, QUANT_VAL, 0x0008);
        frxt = 0x30;
-       reg_w(dev, frxt, 0x0007);
+       reg_w(gspca_dev, frxt, 0x0007);
 #if QUANT_VAL == 0 || QUANT_VAL == 1 || QUANT_VAL == 2
        frxt = 0xff;
 #elif QUANT_VAL == 3
@@ -6108,7 +5972,7 @@ static void setquality(struct gspca_dev *gspca_dev)
 #else
        frxt = 0x20;
 #endif
-       reg_w(dev, frxt, 0x0018);
+       reg_w(gspca_dev, frxt, 0x0018);
 }
 
 /* Matches the sensor's internal frame rate to the lighting frequency.
@@ -6116,87 +5980,86 @@ static void setquality(struct gspca_dev *gspca_dev)
  *     50Hz, for European and Asian lighting (default)
  *     60Hz, for American lighting
  *     0 = No Fliker (for outdoore usage)
- * Returns: 0 for success
  */
-static int setlightfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int i, mode;
        const struct usb_action *zc3_freq;
        static const struct usb_action *freq_tb[SENSOR_MAX][6] = {
-/* SENSOR_ADCM2700 0 */
+       [SENSOR_ADCM2700] =
                {adcm2700_NoFliker, adcm2700_NoFliker,
                 adcm2700_50HZ, adcm2700_50HZ,
                 adcm2700_60HZ, adcm2700_60HZ},
-/* SENSOR_CS2102 1 */
+       [SENSOR_CS2102] =
                {cs2102_NoFliker, cs2102_NoFlikerScale,
                 cs2102_50HZ, cs2102_50HZScale,
                 cs2102_60HZ, cs2102_60HZScale},
-/* SENSOR_CS2102K 2 */
+       [SENSOR_CS2102K] =
                {cs2102_NoFliker, cs2102_NoFlikerScale,
                 NULL, NULL, /* currently disabled */
                 NULL, NULL},
-/* SENSOR_GC0305 3 */
+       [SENSOR_GC0305] =
                {gc0305_NoFliker, gc0305_NoFliker,
                 gc0305_50HZ, gc0305_50HZ,
                 gc0305_60HZ, gc0305_60HZ},
-/* SENSOR_HDCS2020b 4 */
+       [SENSOR_HDCS2020b] =
                {hdcs2020b_NoFliker, hdcs2020b_NoFliker,
                 hdcs2020b_50HZ, hdcs2020b_50HZ,
                 hdcs2020b_60HZ, hdcs2020b_60HZ},
-/* SENSOR_HV7131B 5 */
+       [SENSOR_HV7131B] =
                {hv7131b_NoFliker, hv7131b_NoFlikerScale,
                 hv7131b_50HZ, hv7131b_50HZScale,
                 hv7131b_60HZ, hv7131b_60HZScale},
-/* SENSOR_HV7131C 6 */
-               {NULL, NULL,
-                NULL, NULL,
-                NULL, NULL},
-/* SENSOR_ICM105A 7 */
+       [SENSOR_HV7131R] =
+               {hv7131r_NoFliker, hv7131r_NoFlikerScale,
+                hv7131r_50HZ, hv7131r_50HZScale,
+                hv7131r_60HZ, hv7131r_60HZScale},
+       [SENSOR_ICM105A] =
                {icm105a_NoFliker, icm105a_NoFlikerScale,
                 icm105a_50HZ, icm105a_50HZScale,
                 icm105a_60HZ, icm105a_60HZScale},
-/* SENSOR_MC501CB 8 */
+       [SENSOR_MC501CB] =
                {mc501cb_NoFliker, mc501cb_NoFlikerScale,
                 mc501cb_50HZ, mc501cb_50HZScale,
                 mc501cb_60HZ, mc501cb_60HZScale},
-/* SENSOR_MI0360SOC 9 */
-               {mi360soc_AENoFliker, mi360soc_AENoFlikerScale,
-                mi360soc_AE50HZ, mi360soc_AE50HZScale,
-                mi360soc_AE60HZ, mi360soc_AE60HZScale},
-/* SENSOR_OV7620 10 */
+       [SENSOR_MT9V111_1] =
+               {mt9v111_1_AENoFliker, mt9v111_1_AENoFlikerScale,
+                mt9v111_1_AE50HZ, mt9v111_1_AE50HZScale,
+                mt9v111_1_AE60HZ, mt9v111_1_AE60HZScale},
+       [SENSOR_MT9V111_3] =
+               {mt9v111_3_AENoFliker, mt9v111_3_AENoFlikerScale,
+                mt9v111_3_AE50HZ, mt9v111_3_AE50HZScale,
+                mt9v111_3_AE60HZ, mt9v111_3_AE60HZScale},
+       [SENSOR_OV7620] =
                {ov7620_NoFliker, ov7620_NoFliker,
                 ov7620_50HZ, ov7620_50HZ,
                 ov7620_60HZ, ov7620_60HZ},
-/* SENSOR_OV7630C 11 */
+       [SENSOR_OV7630C] =
                {NULL, NULL,
                 NULL, NULL,
                 NULL, NULL},
-/* SENSOR_PAS106 12 */
+       [SENSOR_PAS106] =
                {pas106b_NoFliker, pas106b_NoFliker,
                 pas106b_50HZ, pas106b_50HZ,
                 pas106b_60HZ, pas106b_60HZ},
-/* SENSOR_PAS202B 13 */
+       [SENSOR_PAS202B] =
                {pas202b_NoFliker, pas202b_NoFlikerScale,
                 pas202b_50HZ, pas202b_50HZScale,
                 pas202b_60HZ, pas202b_60HZScale},
-/* SENSOR_PB0330 14 */
+       [SENSOR_PB0330] =
                {pb0330_NoFliker, pb0330_NoFlikerScale,
                 pb0330_50HZ, pb0330_50HZScale,
                 pb0330_60HZ, pb0330_60HZScale},
-/* SENSOR_PO2030 15 */
+       [SENSOR_PO2030] =
                {po2030_NoFliker, po2030_NoFliker,
                 po2030_50HZ, po2030_50HZ,
                 po2030_60HZ, po2030_60HZ},
-/* SENSOR_TAS5130CK 16 */
-               {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
-                tas5130cxx_50HZ, tas5130cxx_50HZScale,
-                tas5130cxx_60HZ, tas5130cxx_60HZScale},
-/* SENSOR_TAS5130CXX 17 */
-               {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
-                tas5130cxx_50HZ, tas5130cxx_50HZScale,
-                tas5130cxx_60HZ, tas5130cxx_60HZScale},
-/* SENSOR_TAS5130C_VF0250 18 */
+       [SENSOR_TAS5130C] =
+               {tas5130c_NoFliker, tas5130c_NoFlikerScale,
+                tas5130c_50HZ, tas5130c_50HZScale,
+                tas5130c_60HZ, tas5130c_60HZScale},
+       [SENSOR_TAS5130C_VF0250] =
                {tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale,
                 tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale,
                 tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale},
@@ -6207,29 +6070,28 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
        if (mode)
                i++;                    /* 320x240 */
        zc3_freq = freq_tb[sd->sensor][i];
-       if (zc3_freq != NULL) {
-               usb_exchange(gspca_dev, zc3_freq);
-               switch (sd->sensor) {
-               case SENSOR_GC0305:
-                       if (mode                        /* if 320x240 */
-                           && sd->lightfreq == 1)      /* and 50Hz */
-                               reg_w(gspca_dev->dev, 0x85, 0x018d);
-                                               /* win: 0x80, 0x018d */
-                       break;
-               case SENSOR_OV7620:
-                       if (!mode) {                    /* if 640x480 */
-                               if (sd->lightfreq != 0) /* and 50 or 60 Hz */
-                                       reg_w(gspca_dev->dev, 0x40, 0x0002);
-                               else
-                                       reg_w(gspca_dev->dev, 0x44, 0x0002);
-                       }
-                       break;
-               case SENSOR_PAS202B:
-                       reg_w(gspca_dev->dev, 0x00, 0x01a7);
-                       break;
+       if (zc3_freq == NULL)
+               return;
+       usb_exchange(gspca_dev, zc3_freq);
+       switch (sd->sensor) {
+       case SENSOR_GC0305:
+               if (mode                        /* if 320x240 */
+                   && sd->lightfreq == 1)      /* and 50Hz */
+                       reg_w(gspca_dev, 0x85, 0x018d);
+                                       /* win: 0x80, 0x018d */
+               break;
+       case SENSOR_OV7620:
+               if (!mode) {                    /* if 640x480 */
+                       if (sd->lightfreq != 0) /* and 50 or 60 Hz */
+                               reg_w(gspca_dev, 0x40, 0x0002);
+                       else
+                               reg_w(gspca_dev, 0x44, 0x0002);
                }
+               break;
+       case SENSOR_PAS202B:
+               reg_w(gspca_dev, 0x00, 0x01a7);
+               break;
        }
-       return 0;
 }
 
 static void setautogain(struct gspca_dev *gspca_dev)
@@ -6241,45 +6103,46 @@ static void setautogain(struct gspca_dev *gspca_dev)
                autoval = 0x42;
        else
                autoval = 0x02;
-       reg_w(gspca_dev->dev, autoval, 0x0180);
+       reg_w(gspca_dev, autoval, 0x0180);
 }
 
-static void send_unknown(struct usb_device *dev, int sensor)
+static void send_unknown(struct gspca_dev *gspca_dev, int sensor)
 {
-       reg_w(dev, 0x01, 0x0000);               /* led off */
+       reg_w(gspca_dev, 0x01, 0x0000);         /* led off */
        switch (sensor) {
        case SENSOR_PAS106:
-               reg_w(dev, 0x03, 0x003a);
-               reg_w(dev, 0x0c, 0x003b);
-               reg_w(dev, 0x08, 0x0038);
+               reg_w(gspca_dev, 0x03, 0x003a);
+               reg_w(gspca_dev, 0x0c, 0x003b);
+               reg_w(gspca_dev, 0x08, 0x0038);
                break;
        case SENSOR_ADCM2700:
        case SENSOR_GC0305:
        case SENSOR_OV7620:
-       case SENSOR_MI0360SOC:
+       case SENSOR_MT9V111_1:
+       case SENSOR_MT9V111_3:
        case SENSOR_PB0330:
        case SENSOR_PO2030:
-               reg_w(dev, 0x0d, 0x003a);
-               reg_w(dev, 0x02, 0x003b);
-               reg_w(dev, 0x00, 0x0038);
+               reg_w(gspca_dev, 0x0d, 0x003a);
+               reg_w(gspca_dev, 0x02, 0x003b);
+               reg_w(gspca_dev, 0x00, 0x0038);
                break;
        case SENSOR_PAS202B:
-               reg_w(dev, 0x03, 0x003b);
-               reg_w(dev, 0x0c, 0x003a);
-               reg_w(dev, 0x0b, 0x0039);
-               reg_w(dev, 0x0b, 0x0038);
+               reg_w(gspca_dev, 0x03, 0x003b);
+               reg_w(gspca_dev, 0x0c, 0x003a);
+               reg_w(gspca_dev, 0x0b, 0x0039);
+               reg_w(gspca_dev, 0x0b, 0x0038);
                break;
        }
 }
 
 /* start probe 2 wires */
-static void start_2wr_probe(struct usb_device *dev, int sensor)
+static void start_2wr_probe(struct gspca_dev *gspca_dev, int sensor)
 {
-       reg_w(dev, 0x01, 0x0000);
-       reg_w(dev, sensor, 0x0010);
-       reg_w(dev, 0x01, 0x0001);
-       reg_w(dev, 0x03, 0x0012);
-       reg_w(dev, 0x01, 0x0012);
+       reg_w(gspca_dev, 0x01, 0x0000);
+       reg_w(gspca_dev, sensor, 0x0010);
+       reg_w(gspca_dev, 0x01, 0x0001);
+       reg_w(gspca_dev, 0x03, 0x0012);
+       reg_w(gspca_dev, 0x01, 0x0012);
 /*     msleep(2); */
 }
 
@@ -6287,14 +6150,14 @@ static int sif_probe(struct gspca_dev *gspca_dev)
 {
        u16 checkword;
 
-       start_2wr_probe(gspca_dev->dev, 0x0f);          /* PAS106 */
-       reg_w(gspca_dev->dev, 0x08, 0x008d);
+       start_2wr_probe(gspca_dev, 0x0f);               /* PAS106 */
+       reg_w(gspca_dev, 0x08, 0x008d);
        msleep(150);
        checkword = ((i2c_read(gspca_dev, 0x00) & 0x0f) << 4)
                        | ((i2c_read(gspca_dev, 0x01) & 0xf0) >> 4);
        PDEBUG(D_PROBE, "probe sif 0x%04x", checkword);
        if (checkword == 0x0007) {
-               send_unknown(gspca_dev->dev, SENSOR_PAS106);
+               send_unknown(gspca_dev, SENSOR_PAS106);
                return 0x0f;                    /* PAS106 */
        }
        return -1;
@@ -6302,23 +6165,22 @@ static int sif_probe(struct gspca_dev *gspca_dev)
 
 static int vga_2wr_probe(struct gspca_dev *gspca_dev)
 {
-       struct usb_device *dev = gspca_dev->dev;
        u16 retword;
 
-       start_2wr_probe(dev, 0x00);             /* HV7131B */
+       start_2wr_probe(gspca_dev, 0x00);       /* HV7131B */
        i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
        retword = i2c_read(gspca_dev, 0x01);
        if (retword != 0)
                return 0x00;                    /* HV7131B */
 
-       start_2wr_probe(dev, 0x04);             /* CS2102 */
+       start_2wr_probe(gspca_dev, 0x04);       /* CS2102 */
        i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
        retword = i2c_read(gspca_dev, 0x01);
        if (retword != 0)
                return 0x04;                    /* CS2102 */
 
-       start_2wr_probe(dev, 0x06);             /* OmniVision */
-       reg_w(dev, 0x08, 0x008d);
+       start_2wr_probe(gspca_dev, 0x06);       /* OmniVision */
+       reg_w(gspca_dev, 0x08, 0x008d);
        i2c_write(gspca_dev, 0x11, 0xaa, 0x00);
        retword = i2c_read(gspca_dev, 0x11);
        if (retword != 0) {
@@ -6327,14 +6189,14 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev)
                goto ov_check;
        }
 
-       start_2wr_probe(dev, 0x08);             /* HDCS2020 */
+       start_2wr_probe(gspca_dev, 0x08);       /* HDCS2020 */
        i2c_write(gspca_dev, 0x1c, 0x00, 0x00);
        i2c_write(gspca_dev, 0x15, 0xaa, 0x00);
        retword = i2c_read(gspca_dev, 0x15);
        if (retword != 0)
                return 0x08;                    /* HDCS2020 */
 
-       start_2wr_probe(dev, 0x0a);             /* PB0330 */
+       start_2wr_probe(gspca_dev, 0x0a);       /* PB0330 */
        i2c_write(gspca_dev, 0x07, 0xaa, 0xaa);
        retword = i2c_read(gspca_dev, 0x07);
        if (retword != 0)
@@ -6346,23 +6208,23 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev)
        if (retword != 0)
                return 0x0a;                    /* PB0330 ?? */
 
-       start_2wr_probe(dev, 0x0c);             /* ICM105A */
+       start_2wr_probe(gspca_dev, 0x0c);       /* ICM105A */
        i2c_write(gspca_dev, 0x01, 0x11, 0x00);
        retword = i2c_read(gspca_dev, 0x01);
        if (retword != 0)
                return 0x0c;                    /* ICM105A */
 
-       start_2wr_probe(dev, 0x0e);             /* PAS202BCB */
-       reg_w(dev, 0x08, 0x008d);
+       start_2wr_probe(gspca_dev, 0x0e);       /* PAS202BCB */
+       reg_w(gspca_dev, 0x08, 0x008d);
        i2c_write(gspca_dev, 0x03, 0xaa, 0x00);
        msleep(50);
        retword = i2c_read(gspca_dev, 0x03);
        if (retword != 0) {
-               send_unknown(dev, SENSOR_PAS202B);
+               send_unknown(gspca_dev, SENSOR_PAS202B);
                return 0x0e;                    /* PAS202BCB */
        }
 
-       start_2wr_probe(dev, 0x02);             /* TAS5130C */
+       start_2wr_probe(gspca_dev, 0x02);       /* TAS5130C */
        i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
        retword = i2c_read(gspca_dev, 0x01);
        if (retword != 0)
@@ -6371,20 +6233,20 @@ ov_check:
        reg_r(gspca_dev, 0x0010);               /* ?? */
        reg_r(gspca_dev, 0x0010);
 
-       reg_w(dev, 0x01, 0x0000);
-       reg_w(dev, 0x01, 0x0001);
-       reg_w(dev, 0x06, 0x0010);               /* OmniVision */
-       reg_w(dev, 0xa1, 0x008b);
-       reg_w(dev, 0x08, 0x008d);
+       reg_w(gspca_dev, 0x01, 0x0000);
+       reg_w(gspca_dev, 0x01, 0x0001);
+       reg_w(gspca_dev, 0x06, 0x0010);         /* OmniVision */
+       reg_w(gspca_dev, 0xa1, 0x008b);
+       reg_w(gspca_dev, 0x08, 0x008d);
        msleep(500);
-       reg_w(dev, 0x01, 0x0012);
+       reg_w(gspca_dev, 0x01, 0x0012);
        i2c_write(gspca_dev, 0x12, 0x80, 0x00); /* sensor reset */
        retword = i2c_read(gspca_dev, 0x0a) << 8;
        retword |= i2c_read(gspca_dev, 0x0b);
        PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", retword);
        switch (retword) {
        case 0x7631:                            /* OV7630C */
-               reg_w(dev, 0x06, 0x0010);
+               reg_w(gspca_dev, 0x06, 0x0010);
                break;
        case 0x7620:                            /* OV7620 */
        case 0x7648:                            /* OV7648 */
@@ -6401,32 +6263,31 @@ struct sensor_by_chipset_revision {
 };
 static const struct sensor_by_chipset_revision chipset_revision_sensor[] = {
        {0xc000, 0x12},         /* TAS5130C */
-       {0xc001, 0x13},         /* MI0360SOC */
+       {0xc001, 0x13},         /* MT9V111 */
        {0xe001, 0x13},
        {0x8001, 0x13},
        {0x8000, 0x14},         /* CS2102K */
-       {0x8400, 0x15},         /* TAS5130K */
+       {0x8400, 0x15},         /* MT9V111 */
        {0xe400, 0x15},
 };
 
 static int vga_3wr_probe(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       struct usb_device *dev = gspca_dev->dev;
        int i;
        u8 retbyte;
        u16 retword;
 
 /*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/
-       reg_w(dev, 0x02, 0x0010);
+       reg_w(gspca_dev, 0x02, 0x0010);
        reg_r(gspca_dev, 0x0010);
-       reg_w(dev, 0x01, 0x0000);
-       reg_w(dev, 0x00, 0x0010);
-       reg_w(dev, 0x01, 0x0001);
-       reg_w(dev, 0x91, 0x008b);
-       reg_w(dev, 0x03, 0x0012);
-       reg_w(dev, 0x01, 0x0012);
-       reg_w(dev, 0x05, 0x0012);
+       reg_w(gspca_dev, 0x01, 0x0000);
+       reg_w(gspca_dev, 0x00, 0x0010);
+       reg_w(gspca_dev, 0x01, 0x0001);
+       reg_w(gspca_dev, 0x91, 0x008b);
+       reg_w(gspca_dev, 0x03, 0x0012);
+       reg_w(gspca_dev, 0x01, 0x0012);
+       reg_w(gspca_dev, 0x05, 0x0012);
        retword = i2c_read(gspca_dev, 0x14);
        if (retword != 0)
                return 0x11;                    /* HV7131R */
@@ -6437,93 +6298,90 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
        if (retword != 0)
                return 0x11;                    /* HV7131R */
 
-       reg_w(dev, 0x02, 0x0010);
+       reg_w(gspca_dev, 0x02, 0x0010);
        retword = reg_r(gspca_dev, 0x000b) << 8;
        retword |= reg_r(gspca_dev, 0x000a);
        PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", retword);
        reg_r(gspca_dev, 0x0010);
-       /* value 0x4001 is meaningless */
-       if (retword != 0x4001) {
-               if ((retword & 0xff00) == 0x6400)
-                       return 0x02;            /* TAS5130C */
-               for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) {
-                       if (chipset_revision_sensor[i].revision == retword) {
-                               sd->chip_revision = retword;
-                               send_unknown(dev, SENSOR_PB0330);
-                               return chipset_revision_sensor[i]
-                                                       .internal_sensor_id;
-                       }
+       if ((retword & 0xff00) == 0x6400)
+               return 0x02;            /* TAS5130C */
+       for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) {
+               if (chipset_revision_sensor[i].revision == retword) {
+                       sd->chip_revision = retword;
+                       send_unknown(gspca_dev, SENSOR_PB0330);
+                       return chipset_revision_sensor[i]
+                                               .internal_sensor_id;
                }
        }
 
-       reg_w(dev, 0x01, 0x0000);       /* check PB0330 */
-       reg_w(dev, 0x01, 0x0001);
-       reg_w(dev, 0xdd, 0x008b);
-       reg_w(dev, 0x0a, 0x0010);
-       reg_w(dev, 0x03, 0x0012);
-       reg_w(dev, 0x01, 0x0012);
+       reg_w(gspca_dev, 0x01, 0x0000); /* check PB0330 */
+       reg_w(gspca_dev, 0x01, 0x0001);
+       reg_w(gspca_dev, 0xdd, 0x008b);
+       reg_w(gspca_dev, 0x0a, 0x0010);
+       reg_w(gspca_dev, 0x03, 0x0012);
+       reg_w(gspca_dev, 0x01, 0x0012);
        retword = i2c_read(gspca_dev, 0x00);
        if (retword != 0) {
-               PDEBUG(D_PROBE, "probe 3wr vga type 0a ?");
+               PDEBUG(D_PROBE, "probe 3wr vga type 0a");
                return 0x0a;                    /* PB0330 */
        }
 
-       reg_w(dev, 0x01, 0x0000);
-       reg_w(dev, 0x01, 0x0001);
-       reg_w(dev, 0x98, 0x008b);
-       reg_w(dev, 0x01, 0x0010);
-       reg_w(dev, 0x03, 0x0012);
+       reg_w(gspca_dev, 0x01, 0x0000);
+       reg_w(gspca_dev, 0x01, 0x0001);
+       reg_w(gspca_dev, 0x98, 0x008b);
+       reg_w(gspca_dev, 0x01, 0x0010);
+       reg_w(gspca_dev, 0x03, 0x0012);
        msleep(2);
-       reg_w(dev, 0x01, 0x0012);
+       reg_w(gspca_dev, 0x01, 0x0012);
        retword = i2c_read(gspca_dev, 0x00);
        if (retword != 0) {
                PDEBUG(D_PROBE, "probe 3wr vga type %02x", retword);
                if (retword == 0x0011)                  /* VF0250 */
                        return 0x0250;
                if (retword == 0x0029)                  /* gc0305 */
-                       send_unknown(dev, SENSOR_GC0305);
+                       send_unknown(gspca_dev, SENSOR_GC0305);
                return retword;
        }
 
-       reg_w(dev, 0x01, 0x0000);       /* check OmniVision */
-       reg_w(dev, 0x01, 0x0001);
-       reg_w(dev, 0xa1, 0x008b);
-       reg_w(dev, 0x08, 0x008d);
-       reg_w(dev, 0x06, 0x0010);
-       reg_w(dev, 0x01, 0x0012);
-       reg_w(dev, 0x05, 0x0012);
+       reg_w(gspca_dev, 0x01, 0x0000); /* check OmniVision */
+       reg_w(gspca_dev, 0x01, 0x0001);
+       reg_w(gspca_dev, 0xa1, 0x008b);
+       reg_w(gspca_dev, 0x08, 0x008d);
+       reg_w(gspca_dev, 0x06, 0x0010);
+       reg_w(gspca_dev, 0x01, 0x0012);
+       reg_w(gspca_dev, 0x05, 0x0012);
        if (i2c_read(gspca_dev, 0x1c) == 0x007f /* OV7610 - manufacturer ID */
            && i2c_read(gspca_dev, 0x1d) == 0x00a2) {
-               send_unknown(dev, SENSOR_OV7620);
+               send_unknown(gspca_dev, SENSOR_OV7620);
                return 0x06;            /* OmniVision confirm ? */
        }
 
-       reg_w(dev, 0x01, 0x0000);
-       reg_w(dev, 0x00, 0x0002);
-       reg_w(dev, 0x01, 0x0010);
-       reg_w(dev, 0x01, 0x0001);
-       reg_w(dev, 0xee, 0x008b);
-       reg_w(dev, 0x03, 0x0012);
-       reg_w(dev, 0x01, 0x0012);
-       reg_w(dev, 0x05, 0x0012);
+       reg_w(gspca_dev, 0x01, 0x0000);
+       reg_w(gspca_dev, 0x00, 0x0002);
+       reg_w(gspca_dev, 0x01, 0x0010);
+       reg_w(gspca_dev, 0x01, 0x0001);
+       reg_w(gspca_dev, 0xee, 0x008b);
+       reg_w(gspca_dev, 0x03, 0x0012);
+       reg_w(gspca_dev, 0x01, 0x0012);
+       reg_w(gspca_dev, 0x05, 0x0012);
        retword = i2c_read(gspca_dev, 0x00) << 8;       /* ID 0 */
        retword |= i2c_read(gspca_dev, 0x01);           /* ID 1 */
        PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", retword);
        if (retword == 0x2030) {
                retbyte = i2c_read(gspca_dev, 0x02);    /* revision number */
                PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte);
-               send_unknown(dev, SENSOR_PO2030);
+               send_unknown(gspca_dev, SENSOR_PO2030);
                return retword;
        }
 
-       reg_w(dev, 0x01, 0x0000);
-       reg_w(dev, 0x0a, 0x0010);
-       reg_w(dev, 0xd3, 0x008b);
-       reg_w(dev, 0x01, 0x0001);
-       reg_w(dev, 0x03, 0x0012);
-       reg_w(dev, 0x01, 0x0012);
-       reg_w(dev, 0x05, 0x0012);
-       reg_w(dev, 0xd3, 0x008b);
+       reg_w(gspca_dev, 0x01, 0x0000);
+       reg_w(gspca_dev, 0x0a, 0x0010);
+       reg_w(gspca_dev, 0xd3, 0x008b);
+       reg_w(gspca_dev, 0x01, 0x0001);
+       reg_w(gspca_dev, 0x03, 0x0012);
+       reg_w(gspca_dev, 0x01, 0x0012);
+       reg_w(gspca_dev, 0x05, 0x0012);
+       reg_w(gspca_dev, 0xd3, 0x008b);
        retword = i2c_read(gspca_dev, 0x01);
        if (retword != 0) {
                PDEBUG(D_PROBE, "probe 3wr vga type 0a ? ret: %04x", retword);
@@ -6558,56 +6416,76 @@ static int zcxx_probeSensor(struct gspca_dev *gspca_dev)
 /* this function is called at probe time */
 static int sd_config(struct gspca_dev *gspca_dev,
                        const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (id->idProduct == 0x301b)
+               sd->bridge = BRIDGE_ZC301;
+       else
+               sd->bridge = BRIDGE_ZC303;
+
+       /* define some sensors from the vendor/product */
+       sd->sensor = id->driver_info;
+
+       sd->sharpness = SHARPNESS_DEF;
+       sd->brightness = BRIGHTNESS_DEF;
+       sd->contrast = CONTRAST_DEF;
+       sd->autogain = AUTOGAIN_DEF;
+       sd->lightfreq = FREQ_DEF;
+       sd->quality = QUALITY_DEF;
+
+       return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        struct cam *cam;
        int sensor;
        static const u8 gamma[SENSOR_MAX] = {
-               4,      /* SENSOR_ADCM2700 0 */
-               4,      /* SENSOR_CS2102 1 */
-               5,      /* SENSOR_CS2102K 2 */
-               4,      /* SENSOR_GC0305 3 */
-               4,      /* SENSOR_HDCS2020b 4 */
-               4,      /* SENSOR_HV7131B 5 */
-               4,      /* SENSOR_HV7131C 6 */
-               4,      /* SENSOR_ICM105A 7 */
-               4,      /* SENSOR_MC501CB 8 */
-               4,      /* SENSOR_MI0360SOC 9 */
-               3,      /* SENSOR_OV7620 10 */
-               4,      /* SENSOR_OV7630C 11 */
-               4,      /* SENSOR_PAS106 12 */
-               4,      /* SENSOR_PAS202B 13 */
-               4,      /* SENSOR_PB0330 14 */
-               4,      /* SENSOR_PO2030 15 */
-               4,      /* SENSOR_TAS5130CK 16 */
-               3,      /* SENSOR_TAS5130CXX 17 */
-               3,      /* SENSOR_TAS5130C_VF0250 18 */
+               [SENSOR_ADCM2700] =     4,
+               [SENSOR_CS2102] =       4,
+               [SENSOR_CS2102K] =      5,
+               [SENSOR_GC0305] =       4,
+               [SENSOR_HDCS2020b] =    4,
+               [SENSOR_HV7131B] =      4,
+               [SENSOR_HV7131R] =      4,
+               [SENSOR_ICM105A] =      4,
+               [SENSOR_MC501CB] =      4,
+               [SENSOR_MT9V111_1] =    4,
+               [SENSOR_MT9V111_3] =    4,
+               [SENSOR_OV7620] =       3,
+               [SENSOR_OV7630C] =      4,
+               [SENSOR_PAS106] =       4,
+               [SENSOR_PAS202B] =      4,
+               [SENSOR_PB0330] =       4,
+               [SENSOR_PO2030] =       4,
+               [SENSOR_TAS5130C] =     3,
+               [SENSOR_TAS5130C_VF0250] = 3,
        };
        static const u8 mode_tb[SENSOR_MAX] = {
-               2,      /* SENSOR_ADCM2700 0 */
-               1,      /* SENSOR_CS2102 1 */
-               1,      /* SENSOR_CS2102K 2 */
-               1,      /* SENSOR_GC0305 3 */
-               1,      /* SENSOR_HDCS2020b 4 */
-               1,      /* SENSOR_HV7131B 5 */
-               1,      /* SENSOR_HV7131C 6 */
-               1,      /* SENSOR_ICM105A 7 */
-               2,      /* SENSOR_MC501CB 8 */
-               1,      /* SENSOR_MI0360SOC 9 */
-               2,      /* SENSOR_OV7620 10 */
-               1,      /* SENSOR_OV7630C 11 */
-               0,      /* SENSOR_PAS106 12 */
-               1,      /* SENSOR_PAS202B 13 */
-               1,      /* SENSOR_PB0330 14 */
-               1,      /* SENSOR_PO2030 15 */
-               1,      /* SENSOR_TAS5130CK 16 */
-               1,      /* SENSOR_TAS5130CXX 17 */
-               1,      /* SENSOR_TAS5130C_VF0250 18 */
+               [SENSOR_ADCM2700] =     2,
+               [SENSOR_CS2102] =       1,
+               [SENSOR_CS2102K] =      1,
+               [SENSOR_GC0305] =       1,
+               [SENSOR_HDCS2020b] =    1,
+               [SENSOR_HV7131B] =      1,
+               [SENSOR_HV7131R] =      1,
+               [SENSOR_ICM105A] =      1,
+               [SENSOR_MC501CB] =      2,
+               [SENSOR_MT9V111_1] =    1,
+               [SENSOR_MT9V111_3] =    1,
+               [SENSOR_OV7620] =       2,
+               [SENSOR_OV7630C] =      1,
+               [SENSOR_PAS106] =       0,
+               [SENSOR_PAS202B] =      1,
+               [SENSOR_PB0330] =       1,
+               [SENSOR_PO2030] =       1,
+               [SENSOR_TAS5130C] =     1,
+               [SENSOR_TAS5130C_VF0250] = 1,
        };
 
-       /* define some sensors from the vendor/product */
-       sd->sharpness = SHARPNESS_DEF;
-       sd->sensor = id->driver_info;
        sensor = zcxx_probeSensor(gspca_dev);
        if (sensor >= 0)
                PDEBUG(D_PROBE, "probe sensor -> %04x", sensor);
@@ -6626,8 +6504,8 @@ static int sd_config(struct gspca_dev *gspca_dev,
                                break;
                        default:
                                PDEBUG(D_PROBE,
-                                       "Sensor UNKNOWN_0 force Tas5130");
-                               sd->sensor = SENSOR_TAS5130CXX;
+                                       "Unknown sensor - set to TAS5130C");
+                               sd->sensor = SENSOR_TAS5130C;
                        }
                        break;
                case 0:
@@ -6642,14 +6520,14 @@ static int sd_config(struct gspca_dev *gspca_dev,
                                break;
                        default:
 /*                     case 2:                  * hv7131r */
-                               PDEBUG(D_PROBE, "Find Sensor HV7131R(c)");
-                               sd->sensor = SENSOR_HV7131C;
+                               PDEBUG(D_PROBE, "Find Sensor HV7131R");
+                               sd->sensor = SENSOR_HV7131R;
                                break;
                        }
                        break;
                case 0x02:
                        PDEBUG(D_PROBE, "Sensor TAS5130C");
-                       sd->sensor = SENSOR_TAS5130CXX;
+                       sd->sensor = SENSOR_TAS5130C;
                        break;
                case 0x04:
                        PDEBUG(D_PROBE, "Find Sensor CS2102");
@@ -6681,17 +6559,20 @@ static int sd_config(struct gspca_dev *gspca_dev,
                case 0x10:
                case 0x12:
                        PDEBUG(D_PROBE, "Find Sensor TAS5130C");
-                       sd->sensor = SENSOR_TAS5130CXX;
+                       sd->sensor = SENSOR_TAS5130C;
                        break;
                case 0x11:
-                       PDEBUG(D_PROBE, "Find Sensor HV7131R(c)");
-                       sd->sensor = SENSOR_HV7131C;
+                       PDEBUG(D_PROBE, "Find Sensor HV7131R");
+                       sd->sensor = SENSOR_HV7131R;
                        break;
                case 0x13:
+               case 0x15:
                        PDEBUG(D_PROBE,
-                               "Find Sensor MI0360SOC. Chip revision %x",
+                               "Sensor MT9V111. Chip revision %04x",
                                sd->chip_revision);
-                       sd->sensor = SENSOR_MI0360SOC;
+                       sd->sensor = sd->bridge == BRIDGE_ZC301
+                                       ? SENSOR_MT9V111_1
+                                       : SENSOR_MT9V111_3;
                        break;
                case 0x14:
                        PDEBUG(D_PROBE,
@@ -6699,12 +6580,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
                                sd->chip_revision);
                        sd->sensor = SENSOR_CS2102K;
                        break;
-               case 0x15:
-                       PDEBUG(D_PROBE,
-                               "Find Sensor TAS5130CK?. Chip revision %x",
-                               sd->chip_revision);
-                       sd->sensor = SENSOR_TAS5130CK;
-                       break;
                case 0x16:
                        PDEBUG(D_PROBE, "Find Sensor ADCM2700");
                        sd->sensor = SENSOR_ADCM2700;
@@ -6741,13 +6616,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
        }
        if (sensor < 0x20) {
                if (sensor == -1 || sensor == 0x10 || sensor == 0x12)
-                       reg_w(gspca_dev->dev, 0x02, 0x0010);
+                       reg_w(gspca_dev, 0x02, 0x0010);
                reg_r(gspca_dev, 0x0010);
        }
 
        cam = &gspca_dev->cam;
-/*fixme:test*/
-       gspca_dev->nbalt--;
        switch (mode_tb[sd->sensor]) {
        case 0:
                cam->cam_mode = sif_mode;
@@ -6763,58 +6636,62 @@ static int sd_config(struct gspca_dev *gspca_dev,
                cam->nmodes = ARRAY_SIZE(broken_vga_mode);
                break;
        }
-       sd->brightness = BRIGHTNESS_DEF;
-       sd->contrast = CONTRAST_DEF;
        sd->gamma = gamma[sd->sensor];
-       sd->autogain = AUTOGAIN_DEF;
-       sd->lightfreq = FREQ_DEF;
-       sd->quality = QUALITY_DEF;
 
        switch (sd->sensor) {
-       case SENSOR_HV7131B:
-       case SENSOR_HV7131C:
        case SENSOR_OV7630C:
                gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX);
                break;
        }
 
-       return 0;
-}
-
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
-{
        /* switch off the led */
-       reg_w(gspca_dev->dev, 0x01, 0x0000);
-       return 0;
+       reg_w(gspca_dev, 0x01, 0x0000);
+       return gspca_dev->usb_err;
 }
 
 static int sd_start(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       struct usb_device *dev = gspca_dev->dev;
        int mode;
        static const struct usb_action *init_tb[SENSOR_MAX][2] = {
-               {adcm2700_Initial, adcm2700_InitialScale},      /* 0 */
-               {cs2102_Initial, cs2102_InitialScale},          /* 1 */
-               {cs2102K_Initial, cs2102K_InitialScale},        /* 2 */
-               {gc0305_Initial, gc0305_InitialScale},          /* 3 */
-               {hdcs2020b_Initial, hdcs2020b_InitialScale},    /* 4 */
-               {hv7131b_Initial, hv7131b_InitialScale},        /* 5 */
-               {hv7131r_Initial, hv7131r_InitialScale},        /* 6 */
-               {icm105a_Initial, icm105a_InitialScale},        /* 7 */
-               {mc501cb_Initial, mc501cb_InitialScale},        /* 8 */
-               {mi0360soc_Initial, mi0360soc_InitialScale},    /* 9 */
-               {ov7620_Initial, ov7620_InitialScale},          /* 10 */
-               {ov7630c_Initial, ov7630c_InitialScale},        /* 11 */
-               {pas106b_Initial, pas106b_InitialScale},        /* 12 */
-               {pas202b_Initial, pas202b_InitialScale},        /* 13 */
-               {pb0330_Initial, pb0330_InitialScale},          /* 14 */
-               {po2030_Initial, po2030_InitialScale},          /* 15 */
-               {tas5130cK_Initial, tas5130cK_InitialScale},    /* 16 */
-               {tas5130cxx_Initial, tas5130cxx_InitialScale},  /* 17 */
+       [SENSOR_ADCM2700] =
+                       {adcm2700_Initial, adcm2700_InitialScale},
+       [SENSOR_CS2102] =
+                       {cs2102_Initial, cs2102_InitialScale},
+       [SENSOR_CS2102K] =
+                       {cs2102K_Initial, cs2102K_InitialScale},
+       [SENSOR_GC0305] =
+                       {gc0305_Initial, gc0305_InitialScale},
+       [SENSOR_HDCS2020b] =
+                       {hdcs2020b_Initial, hdcs2020b_InitialScale},
+       [SENSOR_HV7131B] =
+                       {hv7131b_Initial, hv7131b_InitialScale},
+       [SENSOR_HV7131R] =
+                       {hv7131r_Initial, hv7131r_InitialScale},
+       [SENSOR_ICM105A] =
+                       {icm105a_Initial, icm105a_InitialScale},
+       [SENSOR_MC501CB] =
+                       {mc501cb_Initial, mc501cb_InitialScale},
+       [SENSOR_MT9V111_1] =
+                       {mt9v111_1_Initial, mt9v111_1_InitialScale},
+       [SENSOR_MT9V111_3] =
+                       {mt9v111_3_Initial, mt9v111_3_InitialScale},
+       [SENSOR_OV7620] =
+                       {ov7620_Initial, ov7620_InitialScale},
+       [SENSOR_OV7630C] =
+                       {ov7630c_Initial, ov7630c_InitialScale},
+       [SENSOR_PAS106] =
+                       {pas106b_Initial, pas106b_InitialScale},
+       [SENSOR_PAS202B] =
+                       {pas202b_Initial, pas202b_InitialScale},
+       [SENSOR_PB0330] =
+                       {pb0330_Initial, pb0330_InitialScale},
+       [SENSOR_PO2030] =
+                       {po2030_Initial, po2030_InitialScale},
+       [SENSOR_TAS5130C] =
+                       {tas5130c_Initial, tas5130c_InitialScale},
+       [SENSOR_TAS5130C_VF0250] =
                {tas5130c_vf0250_Initial, tas5130c_vf0250_InitialScale},
-                                                               /* 18 */
        };
 
        /* create the JPEG header */
@@ -6824,7 +6701,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
        mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
        switch (sd->sensor) {
-       case SENSOR_HV7131C:
+       case SENSOR_HV7131R:
                zcxx_probeSensor(gspca_dev);
                break;
        case SENSOR_PAS106:
@@ -6838,22 +6715,22 @@ static int sd_start(struct gspca_dev *gspca_dev)
        case SENSOR_GC0305:
        case SENSOR_OV7620:
        case SENSOR_PO2030:
-       case SENSOR_TAS5130CXX:
+       case SENSOR_TAS5130C:
        case SENSOR_TAS5130C_VF0250:
 /*             msleep(100);                     * ?? */
                reg_r(gspca_dev, 0x0002);       /* --> 0x40 */
-               reg_w(dev, 0x09, 0x01ad);       /* (from win traces) */
-               reg_w(dev, 0x15, 0x01ae);
-               if (sd->sensor == SENSOR_TAS5130CXX)
+               reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+               reg_w(gspca_dev, 0x15, 0x01ae);
+               if (sd->sensor == SENSOR_TAS5130C)
                        break;
-               reg_w(dev, 0x0d, 0x003a);
-               reg_w(dev, 0x02, 0x003b);
-               reg_w(dev, 0x00, 0x0038);
+               reg_w(gspca_dev, 0x0d, 0x003a);
+               reg_w(gspca_dev, 0x02, 0x003b);
+               reg_w(gspca_dev, 0x00, 0x0038);
                break;
        case SENSOR_PAS202B:
-               reg_w(dev, 0x03, 0x003b);
-               reg_w(dev, 0x0c, 0x003a);
-               reg_w(dev, 0x0b, 0x0039);
+               reg_w(gspca_dev, 0x03, 0x003b);
+               reg_w(gspca_dev, 0x0c, 0x003a);
+               reg_w(gspca_dev, 0x0b, 0x0039);
                break;
        }
 
@@ -6862,15 +6739,15 @@ static int sd_start(struct gspca_dev *gspca_dev)
        case SENSOR_ADCM2700:
        case SENSOR_OV7620:
                reg_r(gspca_dev, 0x0008);
-               reg_w(dev, 0x00, 0x0008);
+               reg_w(gspca_dev, 0x00, 0x0008);
                break;
        case SENSOR_PAS202B:
        case SENSOR_GC0305:
-       case SENSOR_TAS5130CXX:
+       case SENSOR_TAS5130C:
                reg_r(gspca_dev, 0x0008);
                /* fall thru */
        case SENSOR_PO2030:
-               reg_w(dev, 0x03, 0x0008);
+               reg_w(gspca_dev, 0x03, 0x0008);
                break;
        }
        setsharpness(gspca_dev);
@@ -6880,7 +6757,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
        case SENSOR_CS2102K:            /* gamma set in xxx_Initial */
        case SENSOR_HDCS2020b:
        case SENSOR_OV7630C:
-       case SENSOR_TAS5130CK:
                break;
        default:
                setcontrast(gspca_dev);
@@ -6891,7 +6767,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
        case SENSOR_OV7620:
        case SENSOR_PAS202B:
                reg_r(gspca_dev, 0x0180);       /* from win */
-               reg_w(dev, 0x00, 0x0180);
+               reg_w(gspca_dev, 0x00, 0x0180);
                break;
        default:
                setquality(gspca_dev);
@@ -6901,29 +6777,29 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
        switch (sd->sensor) {
        case SENSOR_ADCM2700:
-               reg_w(dev, 0x09, 0x01ad);       /* (from win traces) */
-               reg_w(dev, 0x15, 0x01ae);
-               reg_w(dev, 0x02, 0x0180);
+               reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+               reg_w(gspca_dev, 0x15, 0x01ae);
+               reg_w(gspca_dev, 0x02, 0x0180);
                                                /* ms-win + */
-               reg_w(dev, 0x40, 0x0117);
+               reg_w(gspca_dev, 0x40, 0x0117);
                break;
        case SENSOR_GC0305:
-       case SENSOR_TAS5130CXX:
-               reg_w(dev, 0x09, 0x01ad);       /* (from win traces) */
-               reg_w(dev, 0x15, 0x01ae);
+       case SENSOR_TAS5130C:
+               reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+               reg_w(gspca_dev, 0x15, 0x01ae);
                /* fall thru */
        case SENSOR_PAS202B:
        case SENSOR_PO2030:
-/*             reg_w(dev, 0x40, ZC3XX_R117_GGAIN);  * (from win traces) */
+/*             reg_w(gspca_dev, 0x40, ZC3XX_R117_GGAIN);  * (from win traces) */
                reg_r(gspca_dev, 0x0180);
                break;
        case SENSOR_OV7620:
-               reg_w(dev, 0x09, 0x01ad);
-               reg_w(dev, 0x15, 0x01ae);
+               reg_w(gspca_dev, 0x09, 0x01ad);
+               reg_w(gspca_dev, 0x15, 0x01ae);
                i2c_read(gspca_dev, 0x13);      /*fixme: returns 0xa3 */
                i2c_write(gspca_dev, 0x13, 0xa3, 0x00);
                                         /*fixme: returned value to send? */
-               reg_w(dev, 0x40, 0x0117);
+               reg_w(gspca_dev, 0x40, 0x0117);
                reg_r(gspca_dev, 0x0180);
                break;
        }
@@ -6932,11 +6808,11 @@ static int sd_start(struct gspca_dev *gspca_dev)
        switch (sd->sensor) {
        case SENSOR_PO2030:
                msleep(50);
-               reg_w(dev, 0x00, 0x0007);       /* (from win traces) */
-               reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING);
+               reg_w(gspca_dev, 0x00, 0x0007); /* (from win traces) */
+               reg_w(gspca_dev, 0x02, ZC3XX_R008_CLOCKSETTING);
                break;
        }
-       return 0;
+       return gspca_dev->usb_err;
 }
 
 /* called on streamoff with alt 0 and on disconnect */
@@ -6946,7 +6822,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
 
        if (!gspca_dev->present)
                return;
-       send_unknown(gspca_dev->dev, sd->sensor);
+       send_unknown(gspca_dev, sd->sensor);
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
@@ -6981,7 +6857,7 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
        sd->brightness = val;
        if (gspca_dev->streaming)
                setcontrast(gspca_dev);
-       return 0;
+       return gspca_dev->usb_err;
 }
 
 static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -6999,7 +6875,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
        sd->contrast = val;
        if (gspca_dev->streaming)
                setcontrast(gspca_dev);
-       return 0;
+       return gspca_dev->usb_err;
 }
 
 static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
@@ -7017,7 +6893,7 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
        sd->autogain = val;
        if (gspca_dev->streaming)
                setautogain(gspca_dev);
-       return 0;
+       return gspca_dev->usb_err;
 }
 
 static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -7035,7 +6911,7 @@ static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val)
        sd->gamma = val;
        if (gspca_dev->streaming)
                setcontrast(gspca_dev);
-       return 0;
+       return gspca_dev->usb_err;
 }
 
 static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val)
@@ -7053,7 +6929,7 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
        sd->lightfreq = val;
        if (gspca_dev->streaming)
                setlightfreq(gspca_dev);
-       return 0;
+       return gspca_dev->usb_err;
 }
 
 static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
@@ -7071,7 +6947,7 @@ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
        sd->sharpness = val;
        if (gspca_dev->streaming)
                setsharpness(gspca_dev);
-       return 0;
+       return gspca_dev->usb_err;
 }
 
 static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -7116,7 +6992,7 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
                sd->quality = jcomp->quality;
        if (gspca_dev->streaming)
                jpeg_set_qual(sd->jpeg_hdr, sd->quality);
-       return 0;
+       return gspca_dev->usb_err;
 }
 
 static int sd_get_jcomp(struct gspca_dev *gspca_dev,
@@ -7220,7 +7096,6 @@ static const __devinitdata struct usb_device_id device_table[] = {
        {USB_DEVICE(0x10fd, 0x8050)},
        {}                      /* end of entry */
 };
-#undef DVNAME
 MODULE_DEVICE_TABLE(usb, device_table);
 
 /* -- device connect -- */
index b588e30cbcf02bfa79fef54c09e3134a536bb730..b31ee1bceef82c71bf3b13845bc55a5f481945ba 100644 (file)
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
-#include <linux/kernel.h>
-#include <linux/slab.h>
 
 #include "ivtv-driver.h"
-#include "ivtv-cards.h"
 #include "ivtv-ioctl.h"
-#include "ivtv-routing.h"
-#include "ivtv-i2c.h"
-#include "ivtv-mailbox.h"
 #include "ivtv-controls.h"
 
-/* Must be sorted from low to high control ID! */
-static const u32 user_ctrls[] = {
-       V4L2_CID_USER_CLASS,
-       V4L2_CID_BRIGHTNESS,
-       V4L2_CID_CONTRAST,
-       V4L2_CID_SATURATION,
-       V4L2_CID_HUE,
-       V4L2_CID_AUDIO_VOLUME,
-       V4L2_CID_AUDIO_BALANCE,
-       V4L2_CID_AUDIO_BASS,
-       V4L2_CID_AUDIO_TREBLE,
-       V4L2_CID_AUDIO_MUTE,
-       V4L2_CID_AUDIO_LOUDNESS,
-       0
-};
-
-static const u32 *ctrl_classes[] = {
-       user_ctrls,
-       cx2341x_mpeg_ctrls,
-       NULL
-};
-
-
-int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
-{
-       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
-       const char *name;
-
-       qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
-       if (qctrl->id == 0)
-               return -EINVAL;
-
-       switch (qctrl->id) {
-       /* Standard V4L2 controls */
-       case V4L2_CID_USER_CLASS:
-               return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0);
-       case V4L2_CID_BRIGHTNESS:
-       case V4L2_CID_HUE:
-       case V4L2_CID_SATURATION:
-       case V4L2_CID_CONTRAST:
-               if (v4l2_subdev_call(itv->sd_video, core, queryctrl, qctrl))
-                       qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
-               return 0;
-
-       case V4L2_CID_AUDIO_VOLUME:
-       case V4L2_CID_AUDIO_MUTE:
-       case V4L2_CID_AUDIO_BALANCE:
-       case V4L2_CID_AUDIO_BASS:
-       case V4L2_CID_AUDIO_TREBLE:
-       case V4L2_CID_AUDIO_LOUDNESS:
-               if (v4l2_subdev_call(itv->sd_audio, core, queryctrl, qctrl))
-                       qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
-               return 0;
-
-       default:
-               if (cx2341x_ctrl_query(&itv->params, qctrl))
-                       qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
-               return 0;
-       }
-       strncpy(qctrl->name, name, sizeof(qctrl->name) - 1);
-       qctrl->name[sizeof(qctrl->name) - 1] = 0;
-       return 0;
-}
-
-int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu)
-{
-       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
-       struct v4l2_queryctrl qctrl;
-
-       qctrl.id = qmenu->id;
-       ivtv_queryctrl(file, fh, &qctrl);
-       return v4l2_ctrl_query_menu(qmenu, &qctrl,
-                       cx2341x_ctrl_get_menu(&itv->params, qmenu->id));
-}
-
-static int ivtv_try_ctrl(struct file *file, void *fh,
-                                       struct v4l2_ext_control *vctrl)
-{
-       struct v4l2_queryctrl qctrl;
-       const char **menu_items = NULL;
-       int err;
-
-       qctrl.id = vctrl->id;
-       err = ivtv_queryctrl(file, fh, &qctrl);
-       if (err)
-               return err;
-       if (qctrl.type == V4L2_CTRL_TYPE_MENU)
-               menu_items = v4l2_ctrl_get_menu(qctrl.id);
-       return v4l2_ctrl_check(vctrl, &qctrl, menu_items);
-}
-
-static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
-{
-       switch (vctrl->id) {
-               /* Standard V4L2 controls */
-       case V4L2_CID_BRIGHTNESS:
-       case V4L2_CID_HUE:
-       case V4L2_CID_SATURATION:
-       case V4L2_CID_CONTRAST:
-               return v4l2_subdev_call(itv->sd_video, core, s_ctrl, vctrl);
-
-       case V4L2_CID_AUDIO_VOLUME:
-       case V4L2_CID_AUDIO_MUTE:
-       case V4L2_CID_AUDIO_BALANCE:
-       case V4L2_CID_AUDIO_BASS:
-       case V4L2_CID_AUDIO_TREBLE:
-       case V4L2_CID_AUDIO_LOUDNESS:
-               return v4l2_subdev_call(itv->sd_audio, core, s_ctrl, vctrl);
-
-       default:
-               IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
+static int ivtv_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt)
 {
-       switch (vctrl->id) {
-               /* Standard V4L2 controls */
-       case V4L2_CID_BRIGHTNESS:
-       case V4L2_CID_HUE:
-       case V4L2_CID_SATURATION:
-       case V4L2_CID_CONTRAST:
-               return v4l2_subdev_call(itv->sd_video, core, g_ctrl, vctrl);
-
-       case V4L2_CID_AUDIO_VOLUME:
-       case V4L2_CID_AUDIO_MUTE:
-       case V4L2_CID_AUDIO_BALANCE:
-       case V4L2_CID_AUDIO_BASS:
-       case V4L2_CID_AUDIO_TREBLE:
-       case V4L2_CID_AUDIO_LOUDNESS:
-               return v4l2_subdev_call(itv->sd_audio, core, g_ctrl, vctrl);
-       default:
-               IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmt)
-{
-       if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
-               return -EINVAL;
-       if (atomic_read(&itv->capturing) > 0)
-               return -EBUSY;
+       struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
 
        /* First try to allocate sliced VBI buffers if needed. */
        if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) {
@@ -208,106 +59,43 @@ static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fm
        return 0;
 }
 
-int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+static int ivtv_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val)
 {
-       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
-       struct v4l2_control ctrl;
-
-       if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
-               int i;
-               int err = 0;
-
-               for (i = 0; i < c->count; i++) {
-                       ctrl.id = c->controls[i].id;
-                       ctrl.value = c->controls[i].value;
-                       err = ivtv_g_ctrl(itv, &ctrl);
-                       c->controls[i].value = ctrl.value;
-                       if (err) {
-                               c->error_idx = i;
-                               break;
-                       }
-               }
-               return err;
-       }
-       if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
-               return cx2341x_ext_ctrls(&itv->params, 0, c, VIDIOC_G_EXT_CTRLS);
-       return -EINVAL;
+       struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
+       int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+       struct v4l2_mbus_framefmt fmt;
+
+       /* fix videodecoder resolution */
+       fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
+       fmt.height = cxhdl->height;
+       fmt.code = V4L2_MBUS_FMT_FIXED;
+       v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt);
+       return 0;
 }
 
-int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+static int ivtv_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx)
 {
-       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
-       struct v4l2_control ctrl;
-
-       if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
-               int i;
-               int err = 0;
-
-               for (i = 0; i < c->count; i++) {
-                       ctrl.id = c->controls[i].id;
-                       ctrl.value = c->controls[i].value;
-                       err = ivtv_s_ctrl(itv, &ctrl);
-                       c->controls[i].value = ctrl.value;
-                       if (err) {
-                               c->error_idx = i;
-                               break;
-                       }
-               }
-               return err;
-       }
-       if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
-               static u32 freqs[3] = { 44100, 48000, 32000 };
-               struct cx2341x_mpeg_params p = itv->params;
-               int err = cx2341x_ext_ctrls(&p, atomic_read(&itv->capturing), c, VIDIOC_S_EXT_CTRLS);
-               unsigned idx;
-
-               if (err)
-                       return err;
+       static const u32 freqs[3] = { 44100, 48000, 32000 };
+       struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
 
-               if (p.video_encoding != itv->params.video_encoding) {
-                       int is_mpeg1 = p.video_encoding ==
-                               V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
-                       struct v4l2_mbus_framefmt fmt;
-
-                       /* fix videodecoder resolution */
-                       fmt.width = itv->params.width / (is_mpeg1 ? 2 : 1);
-                       fmt.height = itv->params.height;
-                       fmt.code = V4L2_MBUS_FMT_FIXED;
-                       v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt);
-               }
-               err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p);
-               if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt)
-                       err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt);
-               itv->params = p;
-               itv->dualwatch_stereo_mode = p.audio_properties & 0x0300;
-               idx = p.audio_properties & 0x03;
-               /* The audio clock of the digitizer must match the codec sample
-                  rate otherwise you get some very strange effects. */
-               if (idx < ARRAY_SIZE(freqs))
-                       ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
-               return err;
-       }
-       return -EINVAL;
+       /* The audio clock of the digitizer must match the codec sample
+          rate otherwise you get some very strange effects. */
+       if (idx < ARRAY_SIZE(freqs))
+               ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
+       return 0;
 }
 
-int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+static int ivtv_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val)
 {
-       struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+       struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
 
-       if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
-               int i;
-               int err = 0;
-
-               for (i = 0; i < c->count; i++) {
-                       err = ivtv_try_ctrl(file, fh, &c->controls[i]);
-                       if (err) {
-                               c->error_idx = i;
-                               break;
-                       }
-               }
-               return err;
-       }
-       if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
-               return cx2341x_ext_ctrls(&itv->params, atomic_read(&itv->capturing), c, VIDIOC_TRY_EXT_CTRLS);
-       return -EINVAL;
+       itv->dualwatch_stereo_mode = val;
+       return 0;
 }
+
+struct cx2341x_handler_ops ivtv_cxhdl_ops = {
+       .s_audio_mode = ivtv_s_audio_mode,
+       .s_audio_sampling_freq = ivtv_s_audio_sampling_freq,
+       .s_video_encoding = ivtv_s_video_encoding,
+       .s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt,
+};
index 1c7721e23c9ba003936ab9cb336363612f6ed7d3..d12893dd0183815b512f0e1a7cdc034558b673a6 100644 (file)
 #ifndef IVTV_CONTROLS_H
 #define IVTV_CONTROLS_H
 
-int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a);
-int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
-int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
-int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
-int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *a);
+extern struct cx2341x_handler_ops ivtv_cxhdl_ops;
 
 #endif
index 07c5c18a25cbc619bff2e8bf3cd6951dc7dcf049..e421d15b0f5c407be8efa267adb66fa381b457ea 100644 (file)
@@ -53,6 +53,7 @@
 #include "ivtv-cards.h"
 #include "ivtv-vbi.h"
 #include "ivtv-routing.h"
+#include "ivtv-controls.h"
 #include "ivtv-gpio.h"
 
 #include <media/tveeprom.h>
@@ -734,9 +735,8 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv)
        itv->open_id = 1;
 
        /* Initial settings */
-       cx2341x_fill_defaults(&itv->params);
-       itv->params.port = CX2341X_PORT_MEMORY;
-       itv->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
+       itv->cxhdl.port = CX2341X_PORT_MEMORY;
+       itv->cxhdl.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
        init_waitqueue_head(&itv->eos_waitq);
        init_waitqueue_head(&itv->event_waitq);
        init_waitqueue_head(&itv->vsync_waitq);
@@ -1006,6 +1006,13 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
                retval = -ENOMEM;
                goto err;
        }
+       retval = cx2341x_handler_init(&itv->cxhdl, 50);
+       if (retval)
+               goto err;
+       itv->v4l2_dev.ctrl_handler = &itv->cxhdl.hdl;
+       itv->cxhdl.ops = &ivtv_cxhdl_ops;
+       itv->cxhdl.priv = itv;
+       itv->cxhdl.func = ivtv_api_func;
 
        IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr);
 
@@ -1127,7 +1134,7 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
        itv->yuv_info.v4l2_src_w = itv->yuv_info.osd_full_w;
        itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h;
 
-       itv->params.video_gop_size = itv->is_60hz ? 15 : 12;
+       cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
 
        itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000;
        itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200;
@@ -1269,15 +1276,8 @@ int ivtv_init_on_first_open(struct ivtv *itv)
        IVTV_DEBUG_INFO("Getting firmware version..\n");
        ivtv_firmware_versions(itv);
 
-       if (itv->card->hw_all & IVTV_HW_CX25840) {
-               struct v4l2_control ctrl;
-
+       if (itv->card->hw_all & IVTV_HW_CX25840)
                v4l2_subdev_call(itv->sd_video, core, load_fw);
-               /* CX25840_CID_ENABLE_PVR150_WORKAROUND */
-               ctrl.id = V4L2_CID_PRIVATE_BASE;
-               ctrl.value = itv->pvr150_workaround;
-               v4l2_subdev_call(itv->sd_video, core, s_ctrl, &ctrl);
-       }
 
        vf.tuner = 0;
        vf.type = V4L2_TUNER_ANALOG_TV;
@@ -1329,6 +1329,8 @@ int ivtv_init_on_first_open(struct ivtv *itv)
        /* For cards with video out, this call needs interrupts enabled */
        ivtv_s_std(NULL, &fh, &itv->tuner_std);
 
+       /* Setup initial controls */
+       cx2341x_handler_setup(&itv->cxhdl);
        return 0;
 }
 
index 102071246218db284eed240e4b173b1dc4eef417..75803141481e30aee323ca4ee14fa34ebea88590 100644 (file)
@@ -62,6 +62,7 @@
 #include <linux/dvb/audio.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-fh.h>
 #include <media/tuner.h>
@@ -631,6 +632,8 @@ struct ivtv {
        struct ivtv_options options;    /* user options */
 
        struct v4l2_device v4l2_dev;
+       struct cx2341x_handler cxhdl;
+       struct v4l2_ctrl_handler hdl_gpio;
        struct v4l2_subdev sd_gpio;     /* GPIO sub-device */
        u16 instance;
 
@@ -648,7 +651,6 @@ struct ivtv {
        v4l2_std_id std_out;            /* current TV output standard */
        u8 audio_stereo_mode;           /* decoder setting how to handle stereo MPEG audio */
        u8 audio_bilingual_mode;        /* decoder setting how to handle bilingual MPEG audio */
-       struct cx2341x_mpeg_params params;              /* current encoder parameters */
 
 
        /* Locking */
index a6a2cdb81566f2aa6f68b614fedb8aea54f976af..d727485da886bc840136735335df30482b44dcee 100644 (file)
@@ -150,12 +150,10 @@ void ivtv_release_stream(struct ivtv_stream *s)
 static void ivtv_dualwatch(struct ivtv *itv)
 {
        struct v4l2_tuner vt;
-       u32 new_bitmap;
        u32 new_stereo_mode;
-       const u32 stereo_mask = 0x0300;
-       const u32 dual = 0x0200;
+       const u32 dual = 0x02;
 
-       new_stereo_mode = itv->params.audio_properties & stereo_mask;
+       new_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
        memset(&vt, 0, sizeof(vt));
        ivtv_call_all(itv, tuner, g_tuner, &vt);
        if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
@@ -164,16 +162,10 @@ static void ivtv_dualwatch(struct ivtv *itv)
        if (new_stereo_mode == itv->dualwatch_stereo_mode)
                return;
 
-       new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask);
-
-       IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
-                          itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
-
-       if (ivtv_vapi(itv, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new_bitmap) == 0) {
-               itv->dualwatch_stereo_mode = new_stereo_mode;
-               return;
-       }
-       IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+       IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n",
+                          itv->dualwatch_stereo_mode, new_stereo_mode);
+       if (v4l2_ctrl_s_ctrl(itv->cxhdl.audio_mode, new_stereo_mode))
+               IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
 }
 
 static void ivtv_update_pgm_info(struct ivtv *itv)
@@ -894,7 +886,8 @@ int ivtv_v4l2_close(struct file *filp)
                if (atomic_read(&itv->capturing) > 0) {
                        /* Undo video mute */
                        ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
-                               itv->params.video_mute | (itv->params.video_mute_yuv << 8));
+                               v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute) |
+                               (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
                }
                /* Done! Unmute and continue. */
                ivtv_unmute(itv);
index d8bf2b01729dc9a8bcdcfc20e63480990b4a4729..4df01947a7df04311668aba78dd0d6956b387bea 100644 (file)
@@ -248,9 +248,9 @@ void ivtv_init_mpeg_decoder(struct ivtv *itv)
        volatile u8 __iomem *mem_offset;
 
        data[0] = 0;
-       data[1] = itv->params.width;    /* YUV source width */
-       data[2] = itv->params.height;
-       data[3] = itv->params.audio_properties; /* Audio settings to use,
+       data[1] = itv->cxhdl.width;     /* YUV source width */
+       data[2] = itv->cxhdl.height;
+       data[3] = itv->cxhdl.audio_properties;  /* Audio settings to use,
                                                           bitmap. see docs. */
        if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) {
                IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n");
index aede061cae5d0b353edd7415af27575317e10578..8f0d07789053b836439a45f174399094b7dde8c1 100644 (file)
@@ -24,6 +24,7 @@
 #include "ivtv-gpio.h"
 #include "tuner-xc2028.h"
 #include <media/tuner.h>
+#include <media/v4l2-ctrls.h>
 
 /*
  * GPIO assignment of Yuan MPG600/MPG160
@@ -149,16 +150,10 @@ static inline struct ivtv *sd_to_ivtv(struct v4l2_subdev *sd)
        return container_of(sd, struct ivtv, sd_gpio);
 }
 
-static struct v4l2_queryctrl gpio_ctrl_mute = {
-       .id            = V4L2_CID_AUDIO_MUTE,
-       .type          = V4L2_CTRL_TYPE_BOOLEAN,
-       .name          = "Mute",
-       .minimum       = 0,
-       .maximum       = 1,
-       .step          = 1,
-       .default_value = 1,
-       .flags         = 0,
-};
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+       return &container_of(ctrl->handler, struct ivtv, hdl_gpio)->sd_gpio;
+}
 
 static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
 {
@@ -262,40 +257,24 @@ static int subdev_s_audio_routing(struct v4l2_subdev *sd,
        return 0;
 }
 
-static int subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int subdev_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+       struct v4l2_subdev *sd = to_sd(ctrl);
        struct ivtv *itv = sd_to_ivtv(sd);
        u16 mask, data;
 
-       if (ctrl->id != V4L2_CID_AUDIO_MUTE)
-               return -EINVAL;
-       mask = itv->card->gpio_audio_mute.mask;
-       data = itv->card->gpio_audio_mute.mute;
-       ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
-       return 0;
-}
-
-static int subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       struct ivtv *itv = sd_to_ivtv(sd);
-       u16 mask, data;
-
-       if (ctrl->id != V4L2_CID_AUDIO_MUTE)
-               return -EINVAL;
-       mask = itv->card->gpio_audio_mute.mask;
-       data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
-       if (mask)
-               write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
-       return 0;
+       switch (ctrl->id) {
+       case V4L2_CID_AUDIO_MUTE:
+               mask = itv->card->gpio_audio_mute.mask;
+               data = ctrl->val ? itv->card->gpio_audio_mute.mute : 0;
+               if (mask)
+                       write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) |
+                                       (data & mask), IVTV_REG_GPIO_OUT);
+               return 0;
+       }
+       return -EINVAL;
 }
 
-static int subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
-       if (qc->id != V4L2_CID_AUDIO_MUTE)
-               return -EINVAL;
-       *qc = gpio_ctrl_mute;
-       return 0;
-}
 
 static int subdev_log_status(struct v4l2_subdev *sd)
 {
@@ -304,6 +283,7 @@ static int subdev_log_status(struct v4l2_subdev *sd)
        IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
                        read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT),
                        read_reg(IVTV_REG_GPIO_IN));
+       v4l2_ctrl_handler_log_status(&itv->hdl_gpio, sd->name);
        return 0;
 }
 
@@ -327,11 +307,19 @@ static int subdev_s_video_routing(struct v4l2_subdev *sd,
        return 0;
 }
 
+static const struct v4l2_ctrl_ops gpio_ctrl_ops = {
+       .s_ctrl = subdev_s_ctrl,
+};
+
 static const struct v4l2_subdev_core_ops subdev_core_ops = {
        .log_status = subdev_log_status,
-       .g_ctrl = subdev_g_ctrl,
-       .s_ctrl = subdev_s_ctrl,
-       .queryctrl = subdev_queryctrl,
+       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+       .g_ctrl = v4l2_subdev_g_ctrl,
+       .s_ctrl = v4l2_subdev_s_ctrl,
+       .queryctrl = v4l2_subdev_queryctrl,
+       .querymenu = v4l2_subdev_querymenu,
 };
 
 static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = {
@@ -375,5 +363,12 @@ int ivtv_gpio_init(struct ivtv *itv)
        v4l2_subdev_init(&itv->sd_gpio, &subdev_ops);
        snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->v4l2_dev.name);
        itv->sd_gpio.grp_id = IVTV_HW_GPIO;
+       v4l2_ctrl_handler_init(&itv->hdl_gpio, 1);
+       v4l2_ctrl_new_std(&itv->hdl_gpio, &gpio_ctrl_ops,
+                       V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+       if (itv->hdl_gpio.error)
+               return itv->hdl_gpio.error;
+       itv->sd_gpio.ctrl_handler = &itv->hdl_gpio;
+       v4l2_ctrl_handler_setup(&itv->hdl_gpio);
        return v4l2_device_register_subdev(&itv->v4l2_dev, &itv->sd_gpio);
 }
index a5b92d109c6c1bd74d999961bb69febe8eebfadf..d391bbdb0b8a600f729c5efcd36aa4d8196ebeae 100644 (file)
@@ -63,6 +63,7 @@
 #include "ivtv-cards.h"
 #include "ivtv-gpio.h"
 #include "ivtv-i2c.h"
+#include <media/cx25840.h>
 
 /* i2c implementation for cx23415/6 chip, ivtv project.
  * Author: Kevin Thayer (nufan_wfk at yahoo.com)
@@ -292,6 +293,12 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
        if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) {
                sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
                                adap, mod, type, 0, I2C_ADDRS(hw_addrs[idx]));
+       } else if (hw == IVTV_HW_CX25840) {
+               struct cx25840_platform_data pdata;
+
+               pdata.pvr150_workaround = itv->pvr150_workaround;
+               sd = v4l2_i2c_new_subdev_cfg(&itv->v4l2_dev,
+                               adap, mod, type, 0, &pdata, hw_addrs[idx], NULL);
        } else {
                sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
                                adap, mod, type, hw_addrs[idx], NULL);
index 11ac2fa33ef7153855ff7f17f0a2a35a11e661df..4eed9123683e69541c6881f9a05e87897349a491 100644 (file)
@@ -162,7 +162,7 @@ int ivtv_set_speed(struct ivtv *itv, int speed)
        data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;
        data[1] = (speed < 0);
        data[2] = speed < 0 ? 3 : 7;
-       data[3] = itv->params.video_b_frames;
+       data[3] = v4l2_ctrl_g_ctrl(itv->cxhdl.video_b_frames);
        data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;
        data[5] = 0;
        data[6] = 0;
@@ -339,8 +339,8 @@ static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
        struct ivtv *itv = id->itv;
        struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
 
-       pixfmt->width = itv->params.width;
-       pixfmt->height = itv->params.height;
+       pixfmt->width = itv->cxhdl.width;
+       pixfmt->height = itv->cxhdl.height;
        pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
        pixfmt->field = V4L2_FIELD_INTERLACED;
        pixfmt->priv = 0;
@@ -568,7 +568,6 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
 {
        struct ivtv_open_id *id = fh;
        struct ivtv *itv = id->itv;
-       struct cx2341x_mpeg_params *p = &itv->params;
        struct v4l2_mbus_framefmt mbus_fmt;
        int ret = ivtv_try_fmt_vid_cap(file, fh, fmt);
        int w = fmt->fmt.pix.width;
@@ -577,15 +576,15 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
        if (ret)
                return ret;
 
-       if (p->width == w && p->height == h)
+       if (itv->cxhdl.width == w && itv->cxhdl.height == h)
                return 0;
 
        if (atomic_read(&itv->capturing) > 0)
                return -EBUSY;
 
-       p->width = w;
-       p->height = h;
-       if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+       itv->cxhdl.width = w;
+       itv->cxhdl.height = h;
+       if (v4l2_ctrl_g_ctrl(itv->cxhdl.video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
                fmt->fmt.pix.width /= 2;
        mbus_fmt.width = fmt->fmt.pix.width;
        mbus_fmt.height = h;
@@ -1114,9 +1113,10 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
 
        itv->std = *std;
        itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
-       itv->params.is_50hz = itv->is_50hz = !itv->is_60hz;
-       itv->params.width = 720;
-       itv->params.height = itv->is_50hz ? 576 : 480;
+       itv->is_50hz = !itv->is_60hz;
+       cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
+       itv->cxhdl.width = 720;
+       itv->cxhdl.height = itv->is_50hz ? 576 : 480;
        itv->vbi.count = itv->is_50hz ? 18 : 12;
        itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
        itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
@@ -1157,7 +1157,7 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
                ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
                itv->main_rect.left = itv->main_rect.top = 0;
                itv->main_rect.width = 720;
-               itv->main_rect.height = itv->params.height;
+               itv->main_rect.height = itv->cxhdl.height;
                ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
                        720, itv->main_rect.height, 0, 0);
                yi->main_rect = itv->main_rect;
@@ -1554,7 +1554,7 @@ static int ivtv_log_status(struct file *file, void *fh)
        }
        IVTV_INFO("Tuner:  %s\n",
                test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
-       cx2341x_log_status(&itv->params, itv->v4l2_dev.name);
+       v4l2_ctrl_handler_log_status(&itv->cxhdl.hdl, itv->v4l2_dev.name);
        IVTV_INFO("Status flags:    0x%08lx\n", itv->i_flags);
        for (i = 0; i < IVTV_MAX_STREAMS; i++) {
                struct ivtv_stream *s = &itv->streams[i];
@@ -1942,11 +1942,6 @@ static const struct v4l2_ioctl_ops ivtv_ioctl_ops = {
        .vidioc_s_register                  = ivtv_s_register,
 #endif
        .vidioc_default                     = ivtv_default,
-       .vidioc_queryctrl                   = ivtv_queryctrl,
-       .vidioc_querymenu                   = ivtv_querymenu,
-       .vidioc_g_ext_ctrls                 = ivtv_g_ext_ctrls,
-       .vidioc_s_ext_ctrls                 = ivtv_s_ext_ctrls,
-       .vidioc_try_ext_ctrls               = ivtv_try_ext_ctrls,
        .vidioc_subscribe_event             = ivtv_subscribe_event,
        .vidioc_unsubscribe_event           = v4l2_event_unsubscribe,
 };
index 55df4190c28d03e7b32459b359de0c302ee7570b..512607e0cda367d401a291465e51536d46378793 100644 (file)
@@ -210,6 +210,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
 
        s->vdev->num = num;
        s->vdev->v4l2_dev = &itv->v4l2_dev;
+       s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
        s->vdev->fops = ivtv_stream_info[type].fops;
        s->vdev->release = video_device_release;
        s->vdev->tvnorms = V4L2_STD_ALL;
@@ -451,7 +452,6 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
 {
        u32 data[CX2341X_MBOX_MAX_DATA];
        struct ivtv *itv = s->itv;
-       struct cx2341x_mpeg_params *p = &itv->params;
        int captype = 0, subtype = 0;
        int enable_passthrough = 0;
 
@@ -472,7 +472,7 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
                }
                itv->mpg_data_received = itv->vbi_data_inserted = 0;
                itv->dualwatch_jiffies = jiffies;
-               itv->dualwatch_stereo_mode = p->audio_properties & 0x0300;
+               itv->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
                itv->search_pack_header = 0;
                break;
 
@@ -560,12 +560,12 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
                                itv->pgm_info_offset, itv->pgm_info_num);
 
                /* Setup API for Stream */
-               cx2341x_update(itv, ivtv_api_func, NULL, p);
+               cx2341x_handler_setup(&itv->cxhdl);
 
                /* mute if capturing radio */
                if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags))
                        ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
-                               1 | (p->video_mute_yuv << 8));
+                               1 | (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
        }
 
        /* Vsync Setup */
@@ -581,6 +581,8 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
 
                clear_bit(IVTV_F_I_EOS, &itv->i_flags);
 
+               cx2341x_handler_set_busy(&itv->cxhdl, 1);
+
                /* Initialize Digitizer for Capture */
                /* Avoid tinny audio problem - ensure audio clocks are going */
                v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1);
@@ -617,7 +619,6 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
 {
        u32 data[CX2341X_MBOX_MAX_DATA];
        struct ivtv *itv = s->itv;
-       struct cx2341x_mpeg_params *p = &itv->params;
        int datatype;
        u16 width;
        u16 height;
@@ -627,8 +628,8 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
 
        IVTV_DEBUG_INFO("Setting some initial decoder settings\n");
 
-       width = p->width;
-       height = p->height;
+       width = itv->cxhdl.width;
+       height = itv->cxhdl.height;
 
        /* set audio mode to left/stereo  for dual/stereo mode. */
        ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
@@ -668,7 +669,7 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
                break;
        }
        if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype,
-                       width, height, p->audio_properties)) {
+                       width, height, itv->cxhdl.audio_properties)) {
                IVTV_DEBUG_WARN("Couldn't initialize decoder source\n");
        }
 
@@ -847,6 +848,8 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
                return 0;
        }
 
+       cx2341x_handler_set_busy(&itv->cxhdl, 0);
+
        /* Set the following Interrupt mask bits for capture */
        ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
        del_timer(&itv->dma_timer);
@@ -967,7 +970,8 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable)
 
                /* Setup capture if not already done */
                if (atomic_read(&itv->capturing) == 0) {
-                       cx2341x_update(itv, ivtv_api_func, NULL, &itv->params);
+                       cx2341x_handler_setup(&itv->cxhdl);
+                       cx2341x_handler_set_busy(&itv->cxhdl, 1);
                }
 
                /* Start Passthrough Mode */
@@ -988,6 +992,8 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable)
        clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
        clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
        itv->output_mode = OUT_NONE;
+       if (atomic_read(&itv->capturing) == 0)
+               cx2341x_handler_set_busy(&itv->cxhdl, 0);
 
        return 0;
 }
index e9df3cb02cc11c44b18c452715039401635ceb1a..0e412131da7cc68ab932b321e0d57076a7e92b99 100644 (file)
@@ -283,51 +283,6 @@ void msp_set_scart(struct i2c_client *client, int in, int out)
                msp_write_dem(client, 0x40, state->i2s_mode);
 }
 
-void msp_set_audio(struct i2c_client *client)
-{
-       struct msp_state *state = to_state(i2c_get_clientdata(client));
-       int bal = 0, bass, treble, loudness;
-       int val = 0;
-       int reallymuted = state->muted | state->scan_in_progress;
-
-       if (!reallymuted)
-               val = (state->volume * 0x7f / 65535) << 8;
-
-       v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",
-               state->muted ? "on" : "off",
-               state->scan_in_progress ? "yes" : "no",
-               state->volume);
-
-       msp_write_dsp(client, 0x0000, val);
-       msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1));
-       if (state->has_scart2_out_volume)
-               msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1));
-       if (state->has_headphones)
-               msp_write_dsp(client, 0x0006, val);
-       if (!state->has_sound_processing)
-               return;
-
-       if (val)
-               bal = (u8)((state->balance / 256) - 128);
-       bass = ((state->bass - 32768) * 0x60 / 65535) << 8;
-       treble = ((state->treble - 32768) * 0x60 / 65535) << 8;
-       loudness = state->loudness ? ((5 * 4) << 8) : 0;
-
-       v4l_dbg(1, msp_debug, client, "balance=%d bass=%d treble=%d loudness=%d\n",
-               state->balance, state->bass, state->treble, state->loudness);
-
-       msp_write_dsp(client, 0x0001, bal << 8);
-       msp_write_dsp(client, 0x0002, bass);
-       msp_write_dsp(client, 0x0003, treble);
-       msp_write_dsp(client, 0x0004, loudness);
-       if (!state->has_headphones)
-               return;
-       msp_write_dsp(client, 0x0030, bal << 8);
-       msp_write_dsp(client, 0x0031, bass);
-       msp_write_dsp(client, 0x0032, treble);
-       msp_write_dsp(client, 0x0033, loudness);
-}
-
 /* ------------------------------------------------------------------------ */
 
 static void msp_wake_thread(struct i2c_client *client)
@@ -363,98 +318,73 @@ int msp_sleep(struct msp_state *state, int timeout)
 
 /* ------------------------------------------------------------------------ */
 
-static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int msp_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct msp_state *state = to_state(sd);
+       struct msp_state *state = ctrl_to_state(ctrl);
+       struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
+       int val = ctrl->val;
 
        switch (ctrl->id) {
-       case V4L2_CID_AUDIO_VOLUME:
-               ctrl->value = state->volume;
-               break;
-
-       case V4L2_CID_AUDIO_MUTE:
-               ctrl->value = state->muted;
-               break;
-
-       case V4L2_CID_AUDIO_BALANCE:
-               if (!state->has_sound_processing)
-                       return -EINVAL;
-               ctrl->value = state->balance;
-               break;
-
-       case V4L2_CID_AUDIO_BASS:
-               if (!state->has_sound_processing)
-                       return -EINVAL;
-               ctrl->value = state->bass;
+       case V4L2_CID_AUDIO_VOLUME: {
+               /* audio volume cluster */
+               int reallymuted = state->muted->val | state->scan_in_progress;
+
+               if (!reallymuted)
+                       val = (val * 0x7f / 65535) << 8;
+
+               v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",
+                               state->muted->val ? "on" : "off",
+                               state->scan_in_progress ? "yes" : "no",
+                               state->volume->val);
+
+               msp_write_dsp(client, 0x0000, val);
+               msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1));
+               if (state->has_scart2_out_volume)
+                       msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1));
+               if (state->has_headphones)
+                       msp_write_dsp(client, 0x0006, val);
                break;
-
-       case V4L2_CID_AUDIO_TREBLE:
-               if (!state->has_sound_processing)
-                       return -EINVAL;
-               ctrl->value = state->treble;
-               break;
-
-       case V4L2_CID_AUDIO_LOUDNESS:
-               if (!state->has_sound_processing)
-                       return -EINVAL;
-               ctrl->value = state->loudness;
-               break;
-
-       default:
-               return -EINVAL;
        }
-       return 0;
-}
-
-static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       struct msp_state *state = to_state(sd);
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-
-       switch (ctrl->id) {
-       case V4L2_CID_AUDIO_VOLUME:
-               state->volume = ctrl->value;
-               if (state->volume == 0)
-                       state->balance = 32768;
-               break;
-
-       case V4L2_CID_AUDIO_MUTE:
-               if (ctrl->value < 0 || ctrl->value >= 2)
-                       return -ERANGE;
-               state->muted = ctrl->value;
-               break;
 
        case V4L2_CID_AUDIO_BASS:
-               if (!state->has_sound_processing)
-                       return -EINVAL;
-               state->bass = ctrl->value;
+               val = ((val - 32768) * 0x60 / 65535) << 8;
+               msp_write_dsp(client, 0x0002, val);
+               if (state->has_headphones)
+                       msp_write_dsp(client, 0x0031, val);
                break;
 
        case V4L2_CID_AUDIO_TREBLE:
-               if (!state->has_sound_processing)
-                       return -EINVAL;
-               state->treble = ctrl->value;
+               val = ((val - 32768) * 0x60 / 65535) << 8;
+               msp_write_dsp(client, 0x0003, val);
+               if (state->has_headphones)
+                       msp_write_dsp(client, 0x0032, val);
                break;
 
        case V4L2_CID_AUDIO_LOUDNESS:
-               if (!state->has_sound_processing)
-                       return -EINVAL;
-               state->loudness = ctrl->value;
+               val = val ? ((5 * 4) << 8) : 0;
+               msp_write_dsp(client, 0x0004, val);
+               if (state->has_headphones)
+                       msp_write_dsp(client, 0x0033, val);
                break;
 
        case V4L2_CID_AUDIO_BALANCE:
-               if (!state->has_sound_processing)
-                       return -EINVAL;
-               state->balance = ctrl->value;
+               val = (u8)((val / 256) - 128);
+               msp_write_dsp(client, 0x0001, val << 8);
+               if (state->has_headphones)
+                       msp_write_dsp(client, 0x0030, val << 8);
                break;
 
        default:
                return -EINVAL;
        }
-       msp_set_audio(client);
        return 0;
 }
 
+void msp_update_volume(struct msp_state *state)
+{
+       v4l2_ctrl_s_ctrl(state->volume, v4l2_ctrl_g_ctrl(state->volume));
+}
+
 /* --- v4l2 ioctls --- */
 static int msp_s_radio(struct v4l2_subdev *sd)
 {
@@ -472,7 +402,7 @@ static int msp_s_radio(struct v4l2_subdev *sd)
                msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
                msp3400c_set_carrier(client, MSP_CARRIER(10.7),
                                MSP_CARRIER(10.7));
-               msp_set_audio(client);
+               msp_update_volume(state);
                break;
        case OPMODE_AUTODETECT:
        case OPMODE_AUTOSELECT:
@@ -592,33 +522,6 @@ static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)
        return 0;
 }
 
-static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
-       struct msp_state *state = to_state(sd);
-
-       switch (qc->id) {
-       case V4L2_CID_AUDIO_VOLUME:
-               return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880);
-       case V4L2_CID_AUDIO_MUTE:
-               return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-       default:
-               break;
-       }
-       if (!state->has_sound_processing)
-               return -EINVAL;
-       switch (qc->id) {
-       case V4L2_CID_AUDIO_LOUDNESS:
-               return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
-       case V4L2_CID_AUDIO_BALANCE:
-       case V4L2_CID_AUDIO_BASS:
-       case V4L2_CID_AUDIO_TREBLE:
-               return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
 static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
 {
        struct msp_state *state = to_state(sd);
@@ -633,19 +536,14 @@ static int msp_log_status(struct v4l2_subdev *sd)
        struct msp_state *state = to_state(sd);
        struct i2c_client *client = v4l2_get_subdevdata(sd);
        const char *p;
+       char prefix[V4L2_SUBDEV_NAME_SIZE + 20];
 
        if (state->opmode == OPMODE_AUTOSELECT)
                msp_detect_stereo(client);
        v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
                        client->name, state->rev1, state->rev2);
-       v4l_info(client, "Audio:    volume %d%s\n",
-                       state->volume, state->muted ? " (muted)" : "");
-       if (state->has_sound_processing) {
-               v4l_info(client, "Audio:    balance %d bass %d treble %d loudness %s\n",
-                               state->balance, state->bass,
-                               state->treble,
-                               state->loudness ? "on" : "off");
-       }
+       snprintf(prefix, sizeof(prefix), "%s: Audio:    ", sd->name);
+       v4l2_ctrl_handler_log_status(&state->hdl, prefix);
        switch (state->mode) {
                case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
                case MSP_MODE_FM_RADIO: p = "FM Radio"; break;
@@ -695,12 +593,20 @@ static int msp_resume(struct i2c_client *client)
 
 /* ----------------------------------------------------------------------- */
 
+static const struct v4l2_ctrl_ops msp_ctrl_ops = {
+       .s_ctrl = msp_s_ctrl,
+};
+
 static const struct v4l2_subdev_core_ops msp_core_ops = {
        .log_status = msp_log_status,
        .g_chip_ident = msp_g_chip_ident,
-       .g_ctrl = msp_g_ctrl,
-       .s_ctrl = msp_s_ctrl,
-       .queryctrl = msp_queryctrl,
+       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+       .g_ctrl = v4l2_subdev_g_ctrl,
+       .s_ctrl = v4l2_subdev_s_ctrl,
+       .queryctrl = v4l2_subdev_queryctrl,
+       .querymenu = v4l2_subdev_querymenu,
        .s_std = msp_s_std,
 };
 
@@ -728,6 +634,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
 {
        struct msp_state *state;
        struct v4l2_subdev *sd;
+       struct v4l2_ctrl_handler *hdl;
        int (*thread_func)(void *data) = NULL;
        int msp_hard;
        int msp_family;
@@ -752,13 +659,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
        state->v4l2_std = V4L2_STD_NTSC;
        state->audmode = V4L2_TUNER_MODE_STEREO;
-       state->volume = 58880;  /* 0db gain */
-       state->balance = 32768; /* 0db gain */
-       state->bass = 32768;
-       state->treble = 32768;
-       state->loudness = 0;
        state->input = -1;
-       state->muted = 0;
        state->i2s_mode = 0;
        init_waitqueue_head(&state->wq);
        /* These are the reset input/output positions */
@@ -777,8 +678,6 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
                return -ENODEV;
        }
 
-       msp_set_audio(client);
-
        msp_family = ((state->rev1 >> 4) & 0x0f) + 3;
        msp_product = (state->rev2 >> 8) & 0xff;
        msp_prod_hi = msp_product / 10;
@@ -849,6 +748,34 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
                        state->opmode = OPMODE_MANUAL;
        }
 
+       hdl = &state->hdl;
+       v4l2_ctrl_handler_init(hdl, 6);
+       if (state->has_sound_processing) {
+               v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+                       V4L2_CID_AUDIO_BASS, 0, 65535, 65535 / 100, 32768);
+               v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+                       V4L2_CID_AUDIO_TREBLE, 0, 65535, 65535 / 100, 32768);
+               v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+                       V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 0);
+       }
+       state->volume = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+                       V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 58880);
+       v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+                       V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+       state->muted = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+                       V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+       sd->ctrl_handler = hdl;
+       if (hdl->error) {
+               int err = hdl->error;
+
+               v4l2_ctrl_handler_free(hdl);
+               kfree(state);
+               return err;
+       }
+
+       v4l2_ctrl_cluster(2, &state->volume);
+       v4l2_ctrl_handler_setup(hdl);
+
        /* hello world :-) */
        v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n",
                        msp_family, msp_product,
@@ -903,6 +830,7 @@ static int msp_remove(struct i2c_client *client)
        }
        msp_reset(client);
 
+       v4l2_ctrl_handler_free(&state->hdl);
        kfree(state);
        return 0;
 }
index d6b3e6d0eef7fd845cd5f74996c919dd4b57acab..32a478e532f352ef6d74bc3725a3608e054a1357 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <media/msp3400.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
 
 /* ---------------------------------------------------------------------- */
 
@@ -51,6 +52,7 @@ extern int msp_stereo_thresh;
 
 struct msp_state {
        struct v4l2_subdev sd;
+       struct v4l2_ctrl_handler hdl;
        int rev1, rev2;
        int ident;
        u8 has_nicam;
@@ -87,9 +89,12 @@ struct msp_state {
        int audmode;
        int rxsubchans;
 
-       int volume, muted;
-       int balance, loudness;
-       int bass, treble;
+       struct {
+               /* volume cluster */
+               struct v4l2_ctrl *volume;
+               struct v4l2_ctrl *muted;
+       };
+
        int scan_in_progress;
 
        /* thread */
@@ -104,6 +109,11 @@ static inline struct msp_state *to_state(struct v4l2_subdev *sd)
        return container_of(sd, struct msp_state, sd);
 }
 
+static inline struct msp_state *ctrl_to_state(struct v4l2_ctrl *ctrl)
+{
+       return container_of(ctrl->handler, struct msp_state, hdl);
+}
+
 /* msp3400-driver.c */
 int msp_write_dem(struct i2c_client *client, int addr, int val);
 int msp_write_dsp(struct i2c_client *client, int addr, int val);
@@ -111,7 +121,7 @@ int msp_read_dem(struct i2c_client *client, int addr);
 int msp_read_dsp(struct i2c_client *client, int addr);
 int msp_reset(struct i2c_client *client);
 void msp_set_scart(struct i2c_client *client, int in, int out);
-void msp_set_audio(struct i2c_client *client);
+void msp_update_volume(struct msp_state *state);
 int msp_sleep(struct msp_state *state, int timeout);
 
 /* msp3400-kthreads.c */
index d5a69c5ee5e4f45a370ea266b1379f96f4c071bd..b376fcdee6523e1292182b37c2e9f37e42fe81be 100644 (file)
@@ -496,13 +496,13 @@ restart:
                        v4l_dbg(1, msp_debug, client,
                                "thread: no carrier scan\n");
                        state->scan_in_progress = 0;
-                       msp_set_audio(client);
+                       msp_update_volume(state);
                        continue;
                }
 
                /* mute audio */
                state->scan_in_progress = 1;
-               msp_set_audio(client);
+               msp_update_volume(state);
 
                msp3400c_set_mode(client, MSP_MODE_AM_DETECT);
                val1 = val2 = 0;
@@ -634,7 +634,7 @@ no_second:
                /* unmute */
                state->scan_in_progress = 0;
                msp3400c_set_audmode(client);
-               msp_set_audio(client);
+               msp_update_volume(state);
 
                if (msp_debug)
                        msp3400c_print_mode(client);
@@ -679,13 +679,13 @@ restart:
                        v4l_dbg(1, msp_debug, client,
                                "thread: no carrier scan\n");
                        state->scan_in_progress = 0;
-                       msp_set_audio(client);
+                       msp_update_volume(state);
                        continue;
                }
 
                /* mute audio */
                state->scan_in_progress = 1;
-               msp_set_audio(client);
+               msp_update_volume(state);
 
                /* start autodetect. Note: autodetect is not supported for
                   NTSC-M and radio, hence we force the standard in those
@@ -797,7 +797,7 @@ restart:
                /* unmute */
                msp3400c_set_audmode(client);
                state->scan_in_progress = 0;
-               msp_set_audio(client);
+               msp_update_volume(state);
 
                /* monitor tv audio mode, the first time don't wait
                   so long to get a quick stereo/bilingual result */
@@ -974,7 +974,7 @@ restart:
                        v4l_dbg(1, msp_debug, client,
                                "thread: no carrier scan\n");
                        state->scan_in_progress = 0;
-                       msp_set_audio(client);
+                       msp_update_volume(state);
                        continue;
                }
 
@@ -1020,7 +1020,7 @@ unmute:
                }
 
                /* unmute: dispatch sound to scart output, set scart volume */
-               msp_set_audio(client);
+               msp_update_volume(state);
 
                /* restore ACB */
                if (msp_write_dsp(client, 0x13, state->acb))
index 31cc3d04bcc49b18465511faa4d910c192298497..758a4db27d65651481eec16b970f755a9036f622 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Driver for MT9M111/MT9M112 CMOS Image Sensor from Micron
+ * Driver for MT9M111/MT9M112/MT9M131 CMOS Image Sensor from Micron/Aptina
  *
  * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
  *
 #include <media/soc_camera.h>
 
 /*
- * mt9m111 and mt9m112 i2c address is 0x5d or 0x48 (depending on SAddr pin)
+ * MT9M111, MT9M112 and MT9M131:
+ * i2c address is 0x48 or 0x5d (depending on SADDR pin)
  * The platform has to define i2c_board_info and call i2c_register_board_info()
  */
 
-/* mt9m111: Sensor register addresses */
+/*
+ * Sensor core register addresses (0x000..0x0ff)
+ */
 #define MT9M111_CHIP_VERSION           0x000
 #define MT9M111_ROW_START              0x001
 #define MT9M111_COLUMN_START           0x002
@@ -72,8 +75,9 @@
 #define MT9M111_CTXT_CTRL_LED_FLASH_EN (1 << 2)
 #define MT9M111_CTXT_CTRL_VBLANK_SEL_B (1 << 1)
 #define MT9M111_CTXT_CTRL_HBLANK_SEL_B (1 << 0)
+
 /*
- * mt9m111: Colorpipe register addresses (0x100..0x1ff)
+ * Colorpipe register addresses (0x100..0x1ff)
  */
 #define MT9M111_OPER_MODE_CTRL         0x106
 #define MT9M111_OUTPUT_FORMAT_CTRL     0x108
 #define MT9M111_OUTFMT_SWAP_YCbCr_C_Y  (1 << 1)
 #define MT9M111_OUTFMT_SWAP_RGB_EVEN   (1 << 1)
 #define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr        (1 << 0)
+
 /*
- * mt9m111: Camera control register addresses (0x200..0x2ff not implemented)
+ * Camera control register addresses (0x200..0x2ff not implemented)
  */
 
 #define reg_read(reg) mt9m111_reg_read(client, MT9M111_##reg)
@@ -160,7 +165,8 @@ enum mt9m111_context {
 
 struct mt9m111 {
        struct v4l2_subdev subdev;
-       int model;      /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */
+       int model;      /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code
+                        * from v4l2-chip-ident.h */
        enum mt9m111_context context;
        struct v4l2_rect rect;
        const struct mt9m111_datafmt *fmt;
@@ -934,7 +940,7 @@ static int mt9m111_init(struct i2c_client *client)
        if (!ret)
                ret = mt9m111_set_autoexposure(client, mt9m111->autoexposure);
        if (ret)
-               dev_err(&client->dev, "mt9m11x init failed: %d\n", ret);
+               dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
        return ret;
 }
 
@@ -963,27 +969,27 @@ static int mt9m111_video_probe(struct soc_camera_device *icd,
        mt9m111->swap_rgb_even_odd = 1;
        mt9m111->swap_rgb_red_blue = 1;
 
-       ret = mt9m111_init(client);
-       if (ret)
-               goto ei2c;
-
        data = reg_read(CHIP_VERSION);
 
        switch (data) {
-       case 0x143a: /* MT9M111 */
+       case 0x143a: /* MT9M111 or MT9M131 */
                mt9m111->model = V4L2_IDENT_MT9M111;
+               dev_info(&client->dev,
+                       "Detected a MT9M111/MT9M131 chip ID %x\n", data);
                break;
        case 0x148c: /* MT9M112 */
                mt9m111->model = V4L2_IDENT_MT9M112;
+               dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
                break;
        default:
                ret = -ENODEV;
                dev_err(&client->dev,
-                       "No MT9M11x chip detected, register read %x\n", data);
+                       "No MT9M111/MT9M112/MT9M131 chip detected register read %x\n",
+                       data);
                goto ei2c;
        }
 
-       dev_info(&client->dev, "Detected a MT9M11x chip ID %x\n", data);
+       ret = mt9m111_init(client);
 
 ei2c:
        return ret;
@@ -1034,13 +1040,13 @@ static int mt9m111_probe(struct i2c_client *client,
        int ret;
 
        if (!icd) {
-               dev_err(&client->dev, "MT9M11x: missing soc-camera data!\n");
+               dev_err(&client->dev, "mt9m111: soc-camera data missing!\n");
                return -EINVAL;
        }
 
        icl = to_soc_camera_link(icd);
        if (!icl) {
-               dev_err(&client->dev, "MT9M11x driver needs platform data\n");
+               dev_err(&client->dev, "mt9m111: driver needs platform data\n");
                return -EINVAL;
        }
 
@@ -1114,6 +1120,6 @@ static void __exit mt9m111_mod_exit(void)
 module_init(mt9m111_mod_init);
 module_exit(mt9m111_mod_exit);
 
-MODULE_DESCRIPTION("Micron MT9M111/MT9M112 Camera driver");
+MODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver");
 MODULE_AUTHOR("Robert Jarzmik");
 MODULE_LICENSE("GPL");
index 026bef0ba403d1e855f804ebfd8287f2902ceff7..66ff174151b5f3d909022fbc096f8c432e6f65f4 100644 (file)
@@ -785,6 +785,8 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
        if (ret < 0)
                return ret;
 
+       if (common_flags & SOCAM_PCLK_SAMPLE_RISING)
+               csicr1 |= CSICR1_REDGE;
        if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
                csicr1 |= CSICR1_INV_PCLK;
        if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH)
@@ -1201,7 +1203,7 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
        buf = list_entry(pcdev->capture.next,
                        struct mx2_buffer, vb.queue);
 
-       buf->bufnum = bufnum;
+       buf->bufnum = !bufnum;
 
        list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
 
index e9b11e119f629f72c001945c39b6f77c38b47798..4279ebb811a11b90329c041cbc020863f54967dc 100644 (file)
@@ -94,8 +94,6 @@ static int debugifc_parse_unsigned_number(const char *buf,unsigned int count,
                                          u32 *num_ptr)
 {
        u32 result = 0;
-       u32 val;
-       int ch;
        int radix = 10;
        if ((count >= 2) && (buf[0] == '0') &&
            ((buf[1] == 'x') || (buf[1] == 'X'))) {
@@ -107,17 +105,9 @@ static int debugifc_parse_unsigned_number(const char *buf,unsigned int count,
        }
 
        while (count--) {
-               ch = *buf++;
-               if ((ch >= '0') && (ch <= '9')) {
-                       val = ch - '0';
-               } else if ((ch >= 'a') && (ch <= 'f')) {
-                       val = ch - 'a' + 10;
-               } else if ((ch >= 'A') && (ch <= 'F')) {
-                       val = ch - 'A' + 10;
-               } else {
+               int val = hex_to_bin(*buf++);
+               if (val < 0 || val >= radix)
                        return -EINVAL;
-               }
-               if (val >= radix) return -EINVAL;
                result *= radix;
                result += val;
        }
diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile
new file mode 100644 (file)
index 0000000..0d9d541
--- /dev/null
@@ -0,0 +1,3 @@
+
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) := s5p-fimc.o
+s5p-fimc-y := fimc-core.o fimc-reg.o
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
new file mode 100644 (file)
index 0000000..b151c7b
--- /dev/null
@@ -0,0 +1,1586 @@
+/*
+ * S5P camera interface (video postprocessor) driver
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation, either version 2 of the License,
+ * or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf-dma-contig.h>
+
+#include "fimc-core.h"
+
+static char *fimc_clock_name[NUM_FIMC_CLOCKS] = { "sclk_fimc", "fimc" };
+
+static struct fimc_fmt fimc_formats[] = {
+       {
+               .name   = "RGB565",
+               .fourcc = V4L2_PIX_FMT_RGB565X,
+               .depth  = 16,
+               .color  = S5P_FIMC_RGB565,
+               .buff_cnt = 1,
+               .planes_cnt = 1
+       }, {
+               .name   = "BGR666",
+               .fourcc = V4L2_PIX_FMT_BGR666,
+               .depth  = 32,
+               .color  = S5P_FIMC_RGB666,
+               .buff_cnt = 1,
+               .planes_cnt = 1
+       }, {
+               .name = "XRGB-8-8-8-8, 24 bpp",
+               .fourcc = V4L2_PIX_FMT_RGB24,
+               .depth = 32,
+               .color  = S5P_FIMC_RGB888,
+               .buff_cnt = 1,
+               .planes_cnt = 1
+       }, {
+               .name   = "YUV 4:2:2 packed, YCbYCr",
+               .fourcc = V4L2_PIX_FMT_YUYV,
+               .depth  = 16,
+               .color  = S5P_FIMC_YCBYCR422,
+               .buff_cnt = 1,
+               .planes_cnt = 1
+               }, {
+               .name   = "YUV 4:2:2 packed, CbYCrY",
+               .fourcc = V4L2_PIX_FMT_UYVY,
+               .depth  = 16,
+               .color  = S5P_FIMC_CBYCRY422,
+               .buff_cnt = 1,
+               .planes_cnt = 1
+       }, {
+               .name   = "YUV 4:2:2 packed, CrYCbY",
+               .fourcc = V4L2_PIX_FMT_VYUY,
+               .depth  = 16,
+               .color  = S5P_FIMC_CRYCBY422,
+               .buff_cnt = 1,
+               .planes_cnt = 1
+       }, {
+               .name   = "YUV 4:2:2 packed, YCrYCb",
+               .fourcc = V4L2_PIX_FMT_YVYU,
+               .depth  = 16,
+               .color  = S5P_FIMC_YCRYCB422,
+               .buff_cnt = 1,
+               .planes_cnt = 1
+       }, {
+               .name   = "YUV 4:2:2 planar, Y/Cb/Cr",
+               .fourcc = V4L2_PIX_FMT_YUV422P,
+               .depth  = 12,
+               .color  = S5P_FIMC_YCBCR422,
+               .buff_cnt = 1,
+               .planes_cnt = 3
+       }, {
+               .name   = "YUV 4:2:2 planar, Y/CbCr",
+               .fourcc = V4L2_PIX_FMT_NV16,
+               .depth  = 16,
+               .color  = S5P_FIMC_YCBCR422,
+               .buff_cnt = 1,
+               .planes_cnt = 2
+       }, {
+               .name   = "YUV 4:2:2 planar, Y/CrCb",
+               .fourcc = V4L2_PIX_FMT_NV61,
+               .depth  = 16,
+               .color  = S5P_FIMC_RGB565,
+               .buff_cnt = 1,
+               .planes_cnt = 2
+       }, {
+               .name   = "YUV 4:2:0 planar, YCbCr",
+               .fourcc = V4L2_PIX_FMT_YUV420,
+               .depth  = 12,
+               .color  = S5P_FIMC_YCBCR420,
+               .buff_cnt = 1,
+               .planes_cnt = 3
+       }, {
+               .name   = "YUV 4:2:0 planar, Y/CbCr",
+               .fourcc = V4L2_PIX_FMT_NV12,
+               .depth  = 12,
+               .color  = S5P_FIMC_YCBCR420,
+               .buff_cnt = 1,
+               .planes_cnt = 2
+       }
+ };
+
+static struct v4l2_queryctrl fimc_ctrls[] = {
+       {
+               .id             = V4L2_CID_HFLIP,
+               .type           = V4L2_CTRL_TYPE_BOOLEAN,
+               .name           = "Horizontal flip",
+               .minimum        = 0,
+               .maximum        = 1,
+               .default_value  = 0,
+       },
+       {
+               .id             = V4L2_CID_VFLIP,
+               .type           = V4L2_CTRL_TYPE_BOOLEAN,
+               .name           = "Vertical flip",
+               .minimum        = 0,
+               .maximum        = 1,
+               .default_value  = 0,
+       },
+       {
+               .id             = V4L2_CID_ROTATE,
+               .type           = V4L2_CTRL_TYPE_INTEGER,
+               .name           = "Rotation (CCW)",
+               .minimum        = 0,
+               .maximum        = 270,
+               .step           = 90,
+               .default_value  = 0,
+       },
+};
+
+
+static struct v4l2_queryctrl *get_ctrl(int id)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(fimc_ctrls); ++i)
+               if (id == fimc_ctrls[i].id)
+                       return &fimc_ctrls[i];
+       return NULL;
+}
+
+static int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f)
+{
+       if (r->width > f->width) {
+               if (f->width > (r->width * SCALER_MAX_HRATIO))
+                       return -EINVAL;
+       } else {
+               if ((f->width * SCALER_MAX_HRATIO) < r->width)
+                       return -EINVAL;
+       }
+
+       if (r->height > f->height) {
+               if (f->height > (r->height * SCALER_MAX_VRATIO))
+                       return -EINVAL;
+       } else {
+               if ((f->height * SCALER_MAX_VRATIO) < r->height)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
+{
+       if (src >= tar * 64) {
+               return -EINVAL;
+       } else if (src >= tar * 32) {
+               *ratio = 32;
+               *shift = 5;
+       } else if (src >= tar * 16) {
+               *ratio = 16;
+               *shift = 4;
+       } else if (src >= tar * 8) {
+               *ratio = 8;
+               *shift = 3;
+       } else if (src >= tar * 4) {
+               *ratio = 4;
+               *shift = 2;
+       } else if (src >= tar * 2) {
+               *ratio = 2;
+               *shift = 1;
+       } else {
+               *ratio = 1;
+               *shift = 0;
+       }
+
+       return 0;
+}
+
+static int fimc_set_scaler_info(struct fimc_ctx *ctx)
+{
+       struct fimc_scaler *sc = &ctx->scaler;
+       struct fimc_frame *s_frame = &ctx->s_frame;
+       struct fimc_frame *d_frame = &ctx->d_frame;
+       int tx, ty, sx, sy;
+       int ret;
+
+       tx = d_frame->width;
+       ty = d_frame->height;
+       if (tx <= 0 || ty <= 0) {
+               v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+                       "invalid target size: %d x %d", tx, ty);
+               return -EINVAL;
+       }
+
+       sx = s_frame->width;
+       sy = s_frame->height;
+       if (sx <= 0 || sy <= 0) {
+               err("invalid source size: %d x %d", sx, sy);
+               return -EINVAL;
+       }
+
+       sc->real_width = sx;
+       sc->real_height = sy;
+       dbg("sx= %d, sy= %d, tx= %d, ty= %d", sx, sy, tx, ty);
+
+       ret = fimc_get_scaler_factor(sx, tx, &sc->pre_hratio, &sc->hfactor);
+       if (ret)
+               return ret;
+
+       ret = fimc_get_scaler_factor(sy, ty,  &sc->pre_vratio, &sc->vfactor);
+       if (ret)
+               return ret;
+
+       sc->pre_dst_width = sx / sc->pre_hratio;
+       sc->pre_dst_height = sy / sc->pre_vratio;
+
+       sc->main_hratio = (sx << 8) / (tx << sc->hfactor);
+       sc->main_vratio = (sy << 8) / (ty << sc->vfactor);
+
+       sc->scaleup_h = (tx >= sx) ? 1 : 0;
+       sc->scaleup_v = (ty >= sy) ? 1 : 0;
+
+       /* check to see if input and output size/format differ */
+       if (s_frame->fmt->color == d_frame->fmt->color
+               && s_frame->width == d_frame->width
+               && s_frame->height == d_frame->height)
+               sc->copy_mode = 1;
+       else
+               sc->copy_mode = 0;
+
+       return 0;
+}
+
+
+static irqreturn_t fimc_isr(int irq, void *priv)
+{
+       struct fimc_vid_buffer *src_buf, *dst_buf;
+       struct fimc_dev *fimc = (struct fimc_dev *)priv;
+       struct fimc_ctx *ctx;
+
+       BUG_ON(!fimc);
+       fimc_hw_clear_irq(fimc);
+
+       spin_lock(&fimc->slock);
+
+       if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) {
+               ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev);
+               if (!ctx || !ctx->m2m_ctx)
+                       goto isr_unlock;
+               src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+               dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+               if (src_buf && dst_buf) {
+                       spin_lock(&fimc->irqlock);
+                       src_buf->vb.state = dst_buf->vb.state =  VIDEOBUF_DONE;
+                       wake_up(&src_buf->vb.done);
+                       wake_up(&dst_buf->vb.done);
+                       spin_unlock(&fimc->irqlock);
+                       v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx);
+               }
+       }
+
+isr_unlock:
+       spin_unlock(&fimc->slock);
+       return IRQ_HANDLED;
+}
+
+/* The color format (planes_cnt, buff_cnt) must be already configured. */
+static int fimc_prepare_addr(struct fimc_ctx *ctx,
+               struct fimc_vid_buffer *buf, enum v4l2_buf_type type)
+{
+       struct fimc_frame *frame;
+       struct fimc_addr *paddr;
+       u32 pix_size;
+       int ret = 0;
+
+       frame = ctx_m2m_get_frame(ctx, type);
+       if (IS_ERR(frame))
+               return PTR_ERR(frame);
+       paddr = &frame->paddr;
+
+       if (!buf)
+               return -EINVAL;
+
+       pix_size = frame->width * frame->height;
+
+       dbg("buff_cnt= %d, planes_cnt= %d, frame->size= %d, pix_size= %d",
+               frame->fmt->buff_cnt, frame->fmt->planes_cnt,
+               frame->size, pix_size);
+
+       if (frame->fmt->buff_cnt == 1) {
+               paddr->y = videobuf_to_dma_contig(&buf->vb);
+               switch (frame->fmt->planes_cnt) {
+               case 1:
+                       paddr->cb = 0;
+                       paddr->cr = 0;
+                       break;
+               case 2:
+                       /* decompose Y into Y/Cb */
+                       paddr->cb = (u32)(paddr->y + pix_size);
+                       paddr->cr = 0;
+                       break;
+               case 3:
+                       paddr->cb = (u32)(paddr->y + pix_size);
+                       /* decompose Y into Y/Cb/Cr */
+                       if (S5P_FIMC_YCBCR420 == frame->fmt->color)
+                               paddr->cr = (u32)(paddr->cb
+                                               + (pix_size >> 2));
+                       else /* 422 */
+                               paddr->cr = (u32)(paddr->cb
+                                               + (pix_size >> 1));
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       dbg("PHYS_ADDR: type= %d, y= 0x%X  cb= 0x%X cr= 0x%X ret= %d",
+       type, paddr->y, paddr->cb, paddr->cr, ret);
+
+       return ret;
+}
+
+/* Set order for 1 and 2 plane YCBCR 4:2:2 formats. */
+static void fimc_set_yuv_order(struct fimc_ctx *ctx)
+{
+       /* The one only mode supported in SoC. */
+       ctx->in_order_2p = S5P_FIMC_LSB_CRCB;
+       ctx->out_order_2p = S5P_FIMC_LSB_CRCB;
+
+       /* Set order for 1 plane input formats. */
+       switch (ctx->s_frame.fmt->color) {
+       case S5P_FIMC_YCRYCB422:
+               ctx->in_order_1p = S5P_FIMC_IN_YCRYCB;
+               break;
+       case S5P_FIMC_CBYCRY422:
+               ctx->in_order_1p = S5P_FIMC_IN_CBYCRY;
+               break;
+       case S5P_FIMC_CRYCBY422:
+               ctx->in_order_1p = S5P_FIMC_IN_CRYCBY;
+               break;
+       case S5P_FIMC_YCBYCR422:
+       default:
+               ctx->in_order_1p = S5P_FIMC_IN_YCBYCR;
+               break;
+       }
+       dbg("ctx->in_order_1p= %d", ctx->in_order_1p);
+
+       switch (ctx->d_frame.fmt->color) {
+       case S5P_FIMC_YCRYCB422:
+               ctx->out_order_1p = S5P_FIMC_OUT_YCRYCB;
+               break;
+       case S5P_FIMC_CBYCRY422:
+               ctx->out_order_1p = S5P_FIMC_OUT_CBYCRY;
+               break;
+       case S5P_FIMC_CRYCBY422:
+               ctx->out_order_1p = S5P_FIMC_OUT_CRYCBY;
+               break;
+       case S5P_FIMC_YCBYCR422:
+       default:
+               ctx->out_order_1p = S5P_FIMC_OUT_YCBYCR;
+               break;
+       }
+       dbg("ctx->out_order_1p= %d", ctx->out_order_1p);
+}
+
+/**
+ * fimc_prepare_config - check dimensions, operation and color mode
+ *                      and pre-calculate offset and the scaling coefficients.
+ *
+ * @ctx: hardware context information
+ * @flags: flags indicating which parameters to check/update
+ *
+ * Return: 0 if dimensions are valid or non zero otherwise.
+ */
+static int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags)
+{
+       struct fimc_frame *s_frame, *d_frame;
+       struct fimc_vid_buffer *buf = NULL;
+       struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+       int ret = 0;
+
+       s_frame = &ctx->s_frame;
+       d_frame = &ctx->d_frame;
+
+       if (flags & FIMC_PARAMS) {
+               if ((ctx->out_path == FIMC_DMA) &&
+                       (ctx->rotation == 90 || ctx->rotation == 270)) {
+                       swap(d_frame->f_width, d_frame->f_height);
+                       swap(d_frame->width, d_frame->height);
+               }
+
+               /* Prepare the output offset ratios for scaler. */
+               d_frame->dma_offset.y_h = d_frame->offs_h;
+               if (!variant->pix_hoff)
+                       d_frame->dma_offset.y_h *= (d_frame->fmt->depth >> 3);
+
+               d_frame->dma_offset.y_v = d_frame->offs_v;
+
+               d_frame->dma_offset.cb_h = d_frame->offs_h;
+               d_frame->dma_offset.cb_v = d_frame->offs_v;
+
+               d_frame->dma_offset.cr_h = d_frame->offs_h;
+               d_frame->dma_offset.cr_v = d_frame->offs_v;
+
+               if (!variant->pix_hoff && d_frame->fmt->planes_cnt == 3) {
+                       d_frame->dma_offset.cb_h >>= 1;
+                       d_frame->dma_offset.cb_v >>= 1;
+                       d_frame->dma_offset.cr_h >>= 1;
+                       d_frame->dma_offset.cr_v >>= 1;
+               }
+
+               dbg("out offset: color= %d, y_h= %d, y_v= %d",
+                       d_frame->fmt->color,
+                       d_frame->dma_offset.y_h, d_frame->dma_offset.y_v);
+
+               /* Prepare the input offset ratios for scaler. */
+               s_frame->dma_offset.y_h = s_frame->offs_h;
+               if (!variant->pix_hoff)
+                       s_frame->dma_offset.y_h *= (s_frame->fmt->depth >> 3);
+               s_frame->dma_offset.y_v = s_frame->offs_v;
+
+               s_frame->dma_offset.cb_h = s_frame->offs_h;
+               s_frame->dma_offset.cb_v = s_frame->offs_v;
+
+               s_frame->dma_offset.cr_h = s_frame->offs_h;
+               s_frame->dma_offset.cr_v = s_frame->offs_v;
+
+               if (!variant->pix_hoff && s_frame->fmt->planes_cnt == 3) {
+                       s_frame->dma_offset.cb_h >>= 1;
+                       s_frame->dma_offset.cb_v >>= 1;
+                       s_frame->dma_offset.cr_h >>= 1;
+                       s_frame->dma_offset.cr_v >>= 1;
+               }
+
+               dbg("in offset: color= %d, y_h= %d, y_v= %d",
+                       s_frame->fmt->color, s_frame->dma_offset.y_h,
+                       s_frame->dma_offset.y_v);
+
+               fimc_set_yuv_order(ctx);
+
+               /* Check against the scaler ratio. */
+               if (s_frame->height > (SCALER_MAX_VRATIO * d_frame->height) ||
+                   s_frame->width > (SCALER_MAX_HRATIO * d_frame->width)) {
+                       err("out of scaler range");
+                       return -EINVAL;
+               }
+       }
+
+       /* Input DMA mode is not allowed when the scaler is disabled. */
+       ctx->scaler.enabled = 1;
+
+       if (flags & FIMC_SRC_ADDR) {
+               buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+               ret = fimc_prepare_addr(ctx, buf,
+                       V4L2_BUF_TYPE_VIDEO_OUTPUT);
+               if (ret)
+                       return ret;
+       }
+
+       if (flags & FIMC_DST_ADDR) {
+               buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+               ret = fimc_prepare_addr(ctx, buf,
+                       V4L2_BUF_TYPE_VIDEO_CAPTURE);
+       }
+
+       return ret;
+}
+
+static void fimc_dma_run(void *priv)
+{
+       struct fimc_ctx *ctx = priv;
+       struct fimc_dev *fimc;
+       unsigned long flags;
+       u32 ret;
+
+       if (WARN(!ctx, "null hardware context"))
+               return;
+
+       fimc = ctx->fimc_dev;
+
+       spin_lock_irqsave(&ctx->slock, flags);
+       set_bit(ST_M2M_PEND, &fimc->state);
+
+       ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR);
+       ret = fimc_prepare_config(ctx, ctx->state);
+       if (ret) {
+               err("general configuration error");
+               goto dma_unlock;
+       }
+
+       if (fimc->m2m.ctx != ctx)
+               ctx->state |= FIMC_PARAMS;
+
+       fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr);
+
+       if (ctx->state & FIMC_PARAMS) {
+               fimc_hw_set_input_path(ctx);
+               fimc_hw_set_in_dma(ctx);
+               if (fimc_set_scaler_info(ctx)) {
+                       err("scaler configuration error");
+                       goto dma_unlock;
+               }
+               fimc_hw_set_prescaler(ctx);
+               fimc_hw_set_scaler(ctx);
+               fimc_hw_set_target_format(ctx);
+               fimc_hw_set_rotation(ctx);
+               fimc_hw_set_effect(ctx);
+       }
+
+       fimc_hw_set_output_path(ctx);
+       if (ctx->state & (FIMC_DST_ADDR | FIMC_PARAMS))
+               fimc_hw_set_output_addr(fimc, &ctx->d_frame.paddr);
+
+       if (ctx->state & FIMC_PARAMS)
+               fimc_hw_set_out_dma(ctx);
+
+       if (ctx->scaler.enabled)
+               fimc_hw_start_scaler(fimc);
+       fimc_hw_en_capture(ctx);
+
+       ctx->state = 0;
+       fimc_hw_start_in_dma(fimc);
+
+       fimc->m2m.ctx = ctx;
+
+dma_unlock:
+       spin_unlock_irqrestore(&ctx->slock, flags);
+}
+
+static void fimc_job_abort(void *priv)
+{
+       /* Nothing done in job_abort. */
+}
+
+static void fimc_buf_release(struct videobuf_queue *vq,
+                                   struct videobuf_buffer *vb)
+{
+       videobuf_dma_contig_free(vq, vb);
+       vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int fimc_buf_setup(struct videobuf_queue *vq, unsigned int *count,
+                               unsigned int *size)
+{
+       struct fimc_ctx *ctx = vq->priv_data;
+       struct fimc_frame *frame;
+
+       frame = ctx_m2m_get_frame(ctx, vq->type);
+       if (IS_ERR(frame))
+               return PTR_ERR(frame);
+
+       *size = (frame->width * frame->height * frame->fmt->depth) >> 3;
+       if (0 == *count)
+               *count = 1;
+       return 0;
+}
+
+static int fimc_buf_prepare(struct videobuf_queue *vq,
+               struct videobuf_buffer *vb, enum v4l2_field field)
+{
+       struct fimc_ctx *ctx = vq->priv_data;
+       struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev;
+       struct fimc_frame *frame;
+       int ret;
+
+       frame = ctx_m2m_get_frame(ctx, vq->type);
+       if (IS_ERR(frame))
+               return PTR_ERR(frame);
+
+       if (vb->baddr) {
+               if (vb->bsize < frame->size) {
+                       v4l2_err(v4l2_dev,
+                               "User-provided buffer too small (%d < %d)\n",
+                                vb->bsize, frame->size);
+                       WARN_ON(1);
+                       return -EINVAL;
+               }
+       } else if (vb->state != VIDEOBUF_NEEDS_INIT
+                  && vb->bsize < frame->size) {
+               return -EINVAL;
+       }
+
+       vb->width       = frame->width;
+       vb->height      = frame->height;
+       vb->bytesperline = (frame->width * frame->fmt->depth) >> 3;
+       vb->size        = frame->size;
+       vb->field       = field;
+
+       if (VIDEOBUF_NEEDS_INIT == vb->state) {
+               ret = videobuf_iolock(vq, vb, NULL);
+               if (ret) {
+                       v4l2_err(v4l2_dev, "Iolock failed\n");
+                       fimc_buf_release(vq, vb);
+                       return ret;
+               }
+       }
+       vb->state = VIDEOBUF_PREPARED;
+
+       return 0;
+}
+
+static void fimc_buf_queue(struct videobuf_queue *vq,
+                                 struct videobuf_buffer *vb)
+{
+       struct fimc_ctx *ctx = vq->priv_data;
+       v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb);
+}
+
+static struct videobuf_queue_ops fimc_qops = {
+       .buf_setup      = fimc_buf_setup,
+       .buf_prepare    = fimc_buf_prepare,
+       .buf_queue      = fimc_buf_queue,
+       .buf_release    = fimc_buf_release,
+};
+
+static int fimc_m2m_querycap(struct file *file, void *priv,
+                          struct v4l2_capability *cap)
+{
+       struct fimc_ctx *ctx = file->private_data;
+       struct fimc_dev *fimc = ctx->fimc_dev;
+
+       strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
+       strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
+       cap->bus_info[0] = 0;
+       cap->version = KERNEL_VERSION(1, 0, 0);
+       cap->capabilities = V4L2_CAP_STREAMING |
+               V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
+
+       return 0;
+}
+
+static int fimc_m2m_enum_fmt(struct file *file, void *priv,
+                               struct v4l2_fmtdesc *f)
+{
+       struct fimc_fmt *fmt;
+
+       if (f->index >= ARRAY_SIZE(fimc_formats))
+               return -EINVAL;
+
+       fmt = &fimc_formats[f->index];
+       strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+       f->pixelformat = fmt->fourcc;
+       return 0;
+}
+
+static int fimc_m2m_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+       struct fimc_ctx *ctx = priv;
+       struct fimc_frame *frame;
+
+       frame = ctx_m2m_get_frame(ctx, f->type);
+       if (IS_ERR(frame))
+               return PTR_ERR(frame);
+
+       f->fmt.pix.width        = frame->width;
+       f->fmt.pix.height       = frame->height;
+       f->fmt.pix.field        = V4L2_FIELD_NONE;
+       f->fmt.pix.pixelformat  = frame->fmt->fourcc;
+
+       return 0;
+}
+
+static struct fimc_fmt *find_format(struct v4l2_format *f)
+{
+       struct fimc_fmt *fmt;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) {
+               fmt = &fimc_formats[i];
+               if (fmt->fourcc == f->fmt.pix.pixelformat)
+                       break;
+       }
+       if (i == ARRAY_SIZE(fimc_formats))
+               return NULL;
+
+       return fmt;
+}
+
+static int fimc_m2m_try_fmt(struct file *file, void *priv,
+                            struct v4l2_format *f)
+{
+       struct fimc_fmt *fmt;
+       u32 max_width, max_height, mod_x, mod_y;
+       struct fimc_ctx *ctx = priv;
+       struct fimc_dev *fimc = ctx->fimc_dev;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct samsung_fimc_variant *variant = fimc->variant;
+
+       fmt = find_format(f);
+       if (!fmt) {
+               v4l2_err(&fimc->m2m.v4l2_dev,
+                        "Fourcc format (0x%X) invalid.\n",  pix->pixelformat);
+               return -EINVAL;
+       }
+
+       if (pix->field == V4L2_FIELD_ANY)
+               pix->field = V4L2_FIELD_NONE;
+       else if (V4L2_FIELD_NONE != pix->field)
+               return -EINVAL;
+
+       if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+               max_width = variant->scaler_dis_w;
+               max_height = variant->scaler_dis_w;
+               mod_x = variant->min_inp_pixsize;
+               mod_y = variant->min_inp_pixsize;
+       } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               max_width = variant->out_rot_dis_w;
+               max_height = variant->out_rot_dis_w;
+               mod_x = variant->min_out_pixsize;
+               mod_y = variant->min_out_pixsize;
+       } else {
+               err("Wrong stream type (%d)", f->type);
+               return -EINVAL;
+       }
+
+       dbg("max_w= %d, max_h= %d", max_width, max_height);
+
+       if (pix->height > max_height)
+               pix->height = max_height;
+       if (pix->width > max_width)
+               pix->width = max_width;
+
+       if (tiled_fmt(fmt)) {
+               mod_x = 64; /* 64x32 tile */
+               mod_y = 32;
+       }
+
+       dbg("mod_x= 0x%X, mod_y= 0x%X", mod_x, mod_y);
+
+       pix->width = (pix->width == 0) ? mod_x : ALIGN(pix->width, mod_x);
+       pix->height = (pix->height == 0) ? mod_y : ALIGN(pix->height, mod_y);
+
+       if (pix->bytesperline == 0 ||
+           pix->bytesperline * 8 / fmt->depth > pix->width)
+               pix->bytesperline = (pix->width * fmt->depth) >> 3;
+
+       if (pix->sizeimage == 0)
+               pix->sizeimage = pix->height * pix->bytesperline;
+
+       dbg("pix->bytesperline= %d, fmt->depth= %d",
+           pix->bytesperline, fmt->depth);
+
+       return 0;
+}
+
+
+static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+       struct fimc_ctx *ctx = priv;
+       struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev;
+       struct videobuf_queue *src_vq, *dst_vq;
+       struct fimc_frame *frame;
+       struct v4l2_pix_format *pix;
+       unsigned long flags;
+       int ret = 0;
+
+       BUG_ON(!ctx);
+
+       ret = fimc_m2m_try_fmt(file, priv, f);
+       if (ret)
+               return ret;
+
+       mutex_lock(&ctx->fimc_dev->lock);
+
+       src_vq = v4l2_m2m_get_src_vq(ctx->m2m_ctx);
+       dst_vq = v4l2_m2m_get_dst_vq(ctx->m2m_ctx);
+
+       mutex_lock(&src_vq->vb_lock);
+       mutex_lock(&dst_vq->vb_lock);
+
+       if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+               if (videobuf_queue_is_busy(src_vq)) {
+                       v4l2_err(v4l2_dev, "%s queue busy\n", __func__);
+                       ret = -EBUSY;
+                       goto s_fmt_out;
+               }
+               frame = &ctx->s_frame;
+               spin_lock_irqsave(&ctx->slock, flags);
+               ctx->state |= FIMC_SRC_FMT;
+               spin_unlock_irqrestore(&ctx->slock, flags);
+
+       } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               if (videobuf_queue_is_busy(dst_vq)) {
+                       v4l2_err(v4l2_dev, "%s queue busy\n", __func__);
+                       ret = -EBUSY;
+                       goto s_fmt_out;
+               }
+               frame = &ctx->d_frame;
+               spin_lock_irqsave(&ctx->slock, flags);
+               ctx->state |= FIMC_DST_FMT;
+               spin_unlock_irqrestore(&ctx->slock, flags);
+       } else {
+               v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+                        "Wrong buffer/video queue type (%d)\n", f->type);
+               return -EINVAL;
+       }
+
+       pix = &f->fmt.pix;
+       frame->fmt = find_format(f);
+       if (!frame->fmt) {
+               ret = -EINVAL;
+               goto s_fmt_out;
+       }
+
+       frame->f_width = pix->bytesperline * 8 / frame->fmt->depth;
+       frame->f_height = pix->sizeimage/pix->bytesperline;
+       frame->width = pix->width;
+       frame->height = pix->height;
+       frame->o_width = pix->width;
+       frame->o_height = pix->height;
+       frame->offs_h = 0;
+       frame->offs_v = 0;
+       frame->size = (pix->width * pix->height * frame->fmt->depth) >> 3;
+       src_vq->field = dst_vq->field = pix->field;
+       spin_lock_irqsave(&ctx->slock, flags);
+       ctx->state |= FIMC_PARAMS;
+       spin_unlock_irqrestore(&ctx->slock, flags);
+
+       dbg("f_width= %d, f_height= %d", frame->f_width, frame->f_height);
+
+s_fmt_out:
+       mutex_unlock(&dst_vq->vb_lock);
+       mutex_unlock(&src_vq->vb_lock);
+       mutex_unlock(&ctx->fimc_dev->lock);
+       return ret;
+}
+
+static int fimc_m2m_reqbufs(struct file *file, void *priv,
+                         struct v4l2_requestbuffers *reqbufs)
+{
+       struct fimc_ctx *ctx = priv;
+       return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int fimc_m2m_querybuf(struct file *file, void *priv,
+                          struct v4l2_buffer *buf)
+{
+       struct fimc_ctx *ctx = priv;
+       return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_qbuf(struct file *file, void *priv,
+                         struct v4l2_buffer *buf)
+{
+       struct fimc_ctx *ctx = priv;
+
+       return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_dqbuf(struct file *file, void *priv,
+                          struct v4l2_buffer *buf)
+{
+       struct fimc_ctx *ctx = priv;
+       return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_streamon(struct file *file, void *priv,
+                          enum v4l2_buf_type type)
+{
+       struct fimc_ctx *ctx = priv;
+       return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int fimc_m2m_streamoff(struct file *file, void *priv,
+                           enum v4l2_buf_type type)
+{
+       struct fimc_ctx *ctx = priv;
+       return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+int fimc_m2m_queryctrl(struct file *file, void *priv,
+                           struct v4l2_queryctrl *qc)
+{
+       struct v4l2_queryctrl *c;
+       c = get_ctrl(qc->id);
+       if (!c)
+               return -EINVAL;
+       *qc = *c;
+       return 0;
+}
+
+int fimc_m2m_g_ctrl(struct file *file, void *priv,
+                        struct v4l2_control *ctrl)
+{
+       struct fimc_ctx *ctx = priv;
+
+       switch (ctrl->id) {
+       case V4L2_CID_HFLIP:
+               ctrl->value = (FLIP_X_AXIS & ctx->flip) ? 1 : 0;
+               break;
+       case V4L2_CID_VFLIP:
+               ctrl->value = (FLIP_Y_AXIS & ctx->flip) ? 1 : 0;
+               break;
+       case V4L2_CID_ROTATE:
+               ctrl->value = ctx->rotation;
+               break;
+       default:
+               v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, "Invalid control\n");
+               return -EINVAL;
+       }
+       dbg("ctrl->value= %d", ctrl->value);
+       return 0;
+}
+
+static int check_ctrl_val(struct fimc_ctx *ctx,
+                         struct v4l2_control *ctrl)
+{
+       struct v4l2_queryctrl *c;
+       c = get_ctrl(ctrl->id);
+       if (!c)
+               return -EINVAL;
+
+       if (ctrl->value < c->minimum || ctrl->value > c->maximum
+               || (c->step != 0 && ctrl->value % c->step != 0)) {
+               v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+               "Invalid control value\n");
+               return -ERANGE;
+       }
+
+       return 0;
+}
+
+int fimc_m2m_s_ctrl(struct file *file, void *priv,
+                        struct v4l2_control *ctrl)
+{
+       struct fimc_ctx *ctx = priv;
+       struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+       unsigned long flags;
+       int ret = 0;
+
+       ret = check_ctrl_val(ctx, ctrl);
+       if (ret)
+               return ret;
+
+       switch (ctrl->id) {
+       case V4L2_CID_HFLIP:
+               if (ctx->rotation != 0)
+                       return 0;
+               if (ctrl->value)
+                       ctx->flip |= FLIP_X_AXIS;
+               else
+                       ctx->flip &= ~FLIP_X_AXIS;
+               break;
+
+       case V4L2_CID_VFLIP:
+               if (ctx->rotation != 0)
+                       return 0;
+               if (ctrl->value)
+                       ctx->flip |= FLIP_Y_AXIS;
+               else
+                       ctx->flip &= ~FLIP_Y_AXIS;
+               break;
+
+       case V4L2_CID_ROTATE:
+               if (ctrl->value == 90 || ctrl->value == 270) {
+                       if (ctx->out_path == FIMC_LCDFIFO &&
+                           !variant->has_inp_rot) {
+                               return -EINVAL;
+                       } else if (ctx->in_path == FIMC_DMA &&
+                                  !variant->has_out_rot) {
+                               return -EINVAL;
+                       }
+               }
+               ctx->rotation = ctrl->value;
+               if (ctrl->value == 180)
+                       ctx->flip = FLIP_XY_AXIS;
+               break;
+
+       default:
+               v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, "Invalid control\n");
+               return -EINVAL;
+       }
+       spin_lock_irqsave(&ctx->slock, flags);
+       ctx->state |= FIMC_PARAMS;
+       spin_unlock_irqrestore(&ctx->slock, flags);
+       return 0;
+}
+
+
+static int fimc_m2m_cropcap(struct file *file, void *fh,
+                            struct v4l2_cropcap *cr)
+{
+       struct fimc_frame *frame;
+       struct fimc_ctx *ctx = fh;
+
+       frame = ctx_m2m_get_frame(ctx, cr->type);
+       if (IS_ERR(frame))
+               return PTR_ERR(frame);
+
+       cr->bounds.left = 0;
+       cr->bounds.top = 0;
+       cr->bounds.width = frame->f_width;
+       cr->bounds.height = frame->f_height;
+       cr->defrect.left = frame->offs_h;
+       cr->defrect.top = frame->offs_v;
+       cr->defrect.width = frame->o_width;
+       cr->defrect.height = frame->o_height;
+       return 0;
+}
+
+static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+{
+       struct fimc_frame *frame;
+       struct fimc_ctx *ctx = file->private_data;
+
+       frame = ctx_m2m_get_frame(ctx, cr->type);
+       if (IS_ERR(frame))
+               return PTR_ERR(frame);
+
+       cr->c.left = frame->offs_h;
+       cr->c.top = frame->offs_v;
+       cr->c.width = frame->width;
+       cr->c.height = frame->height;
+
+       return 0;
+}
+
+static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+{
+       struct fimc_ctx *ctx = file->private_data;
+       struct fimc_dev *fimc = ctx->fimc_dev;
+       unsigned long flags;
+       struct fimc_frame *f;
+       u32 min_size;
+       int ret = 0;
+
+       if (cr->c.top < 0 || cr->c.left < 0) {
+               v4l2_err(&fimc->m2m.v4l2_dev,
+                       "doesn't support negative values for top & left\n");
+               return -EINVAL;
+       }
+
+       if (cr->c.width  <= 0 || cr->c.height <= 0) {
+               v4l2_err(&fimc->m2m.v4l2_dev,
+                       "crop width and height must be greater than 0\n");
+               return -EINVAL;
+       }
+
+       f = ctx_m2m_get_frame(ctx, cr->type);
+       if (IS_ERR(f))
+               return PTR_ERR(f);
+
+       /* Adjust to required pixel boundary. */
+       min_size = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ?
+               fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
+
+       cr->c.width = round_down(cr->c.width, min_size);
+       cr->c.height = round_down(cr->c.height, min_size);
+       cr->c.left = round_down(cr->c.left + 1, min_size);
+       cr->c.top = round_down(cr->c.top + 1, min_size);
+
+       if ((cr->c.left + cr->c.width > f->o_width)
+               || (cr->c.top + cr->c.height > f->o_height)) {
+               v4l2_err(&fimc->m2m.v4l2_dev, "Error in S_CROP params\n");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&ctx->slock, flags);
+       if ((ctx->state & FIMC_SRC_FMT) && (ctx->state & FIMC_DST_FMT)) {
+               /* Check for the pixel scaling ratio when cropping input img. */
+               if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+                       ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame);
+               else if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                       ret = fimc_check_scaler_ratio(&cr->c, &ctx->s_frame);
+
+               if (ret) {
+                       spin_unlock_irqrestore(&ctx->slock, flags);
+                       v4l2_err(&fimc->m2m.v4l2_dev,  "Out of scaler range");
+                       return -EINVAL;
+               }
+       }
+       ctx->state |= FIMC_PARAMS;
+       spin_unlock_irqrestore(&ctx->slock, flags);
+
+       f->offs_h = cr->c.left;
+       f->offs_v = cr->c.top;
+       f->width = cr->c.width;
+       f->height = cr->c.height;
+       return 0;
+}
+
+static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
+       .vidioc_querycap                = fimc_m2m_querycap,
+
+       .vidioc_enum_fmt_vid_cap        = fimc_m2m_enum_fmt,
+       .vidioc_enum_fmt_vid_out        = fimc_m2m_enum_fmt,
+
+       .vidioc_g_fmt_vid_cap           = fimc_m2m_g_fmt,
+       .vidioc_g_fmt_vid_out           = fimc_m2m_g_fmt,
+
+       .vidioc_try_fmt_vid_cap         = fimc_m2m_try_fmt,
+       .vidioc_try_fmt_vid_out         = fimc_m2m_try_fmt,
+
+       .vidioc_s_fmt_vid_cap           = fimc_m2m_s_fmt,
+       .vidioc_s_fmt_vid_out           = fimc_m2m_s_fmt,
+
+       .vidioc_reqbufs                 = fimc_m2m_reqbufs,
+       .vidioc_querybuf                = fimc_m2m_querybuf,
+
+       .vidioc_qbuf                    = fimc_m2m_qbuf,
+       .vidioc_dqbuf                   = fimc_m2m_dqbuf,
+
+       .vidioc_streamon                = fimc_m2m_streamon,
+       .vidioc_streamoff               = fimc_m2m_streamoff,
+
+       .vidioc_queryctrl               = fimc_m2m_queryctrl,
+       .vidioc_g_ctrl                  = fimc_m2m_g_ctrl,
+       .vidioc_s_ctrl                  = fimc_m2m_s_ctrl,
+
+       .vidioc_g_crop                  = fimc_m2m_g_crop,
+       .vidioc_s_crop                  = fimc_m2m_s_crop,
+       .vidioc_cropcap                 = fimc_m2m_cropcap
+
+};
+
+static void queue_init(void *priv, struct videobuf_queue *vq,
+                      enum v4l2_buf_type type)
+{
+       struct fimc_ctx *ctx = priv;
+       struct fimc_dev *fimc = ctx->fimc_dev;
+
+       videobuf_queue_dma_contig_init(vq, &fimc_qops,
+               fimc->m2m.v4l2_dev.dev,
+               &fimc->irqlock, type, V4L2_FIELD_NONE,
+               sizeof(struct fimc_vid_buffer), priv);
+}
+
+static int fimc_m2m_open(struct file *file)
+{
+       struct fimc_dev *fimc = video_drvdata(file);
+       struct fimc_ctx *ctx = NULL;
+       int err = 0;
+
+       mutex_lock(&fimc->lock);
+       fimc->m2m.refcnt++;
+       set_bit(ST_OUTDMA_RUN, &fimc->state);
+       mutex_unlock(&fimc->lock);
+
+
+       ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       file->private_data = ctx;
+       ctx->fimc_dev = fimc;
+       /* default format */
+       ctx->s_frame.fmt = &fimc_formats[0];
+       ctx->d_frame.fmt = &fimc_formats[0];
+       /* per user process device context initialization */
+       ctx->state = 0;
+       ctx->flags = 0;
+       ctx->effect.type = S5P_FIMC_EFFECT_ORIGINAL;
+       ctx->in_path = FIMC_DMA;
+       ctx->out_path = FIMC_DMA;
+       spin_lock_init(&ctx->slock);
+
+       ctx->m2m_ctx = v4l2_m2m_ctx_init(ctx, fimc->m2m.m2m_dev, queue_init);
+       if (IS_ERR(ctx->m2m_ctx)) {
+               err = PTR_ERR(ctx->m2m_ctx);
+               kfree(ctx);
+       }
+       return err;
+}
+
+static int fimc_m2m_release(struct file *file)
+{
+       struct fimc_ctx *ctx = file->private_data;
+       struct fimc_dev *fimc = ctx->fimc_dev;
+
+       v4l2_m2m_ctx_release(ctx->m2m_ctx);
+       kfree(ctx);
+       mutex_lock(&fimc->lock);
+       if (--fimc->m2m.refcnt <= 0)
+               clear_bit(ST_OUTDMA_RUN, &fimc->state);
+       mutex_unlock(&fimc->lock);
+       return 0;
+}
+
+static unsigned int fimc_m2m_poll(struct file *file,
+                                    struct poll_table_struct *wait)
+{
+       struct fimc_ctx *ctx = file->private_data;
+       return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+
+static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct fimc_ctx *ctx = file->private_data;
+       return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+
+static const struct v4l2_file_operations fimc_m2m_fops = {
+       .owner          = THIS_MODULE,
+       .open           = fimc_m2m_open,
+       .release        = fimc_m2m_release,
+       .poll           = fimc_m2m_poll,
+       .ioctl          = video_ioctl2,
+       .mmap           = fimc_m2m_mmap,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+       .device_run     = fimc_dma_run,
+       .job_abort      = fimc_job_abort,
+};
+
+
+static int fimc_register_m2m_device(struct fimc_dev *fimc)
+{
+       struct video_device *vfd;
+       struct platform_device *pdev;
+       struct v4l2_device *v4l2_dev;
+       int ret = 0;
+
+       if (!fimc)
+               return -ENODEV;
+
+       pdev = fimc->pdev;
+       v4l2_dev = &fimc->m2m.v4l2_dev;
+
+       /* set name if it is empty */
+       if (!v4l2_dev->name[0])
+               snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
+                        "%s.m2m", dev_name(&pdev->dev));
+
+       ret = v4l2_device_register(&pdev->dev, v4l2_dev);
+       if (ret)
+               return ret;;
+
+       vfd = video_device_alloc();
+       if (!vfd) {
+               v4l2_err(v4l2_dev, "Failed to allocate video device\n");
+               goto err_m2m_r1;
+       }
+
+       vfd->fops       = &fimc_m2m_fops;
+       vfd->ioctl_ops  = &fimc_m2m_ioctl_ops;
+       vfd->minor      = -1;
+       vfd->release    = video_device_release;
+
+       snprintf(vfd->name, sizeof(vfd->name), "%s:m2m", dev_name(&pdev->dev));
+
+       video_set_drvdata(vfd, fimc);
+       platform_set_drvdata(pdev, fimc);
+
+       fimc->m2m.vfd = vfd;
+       fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops);
+       if (IS_ERR(fimc->m2m.m2m_dev)) {
+               v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n");
+               ret = PTR_ERR(fimc->m2m.m2m_dev);
+               goto err_m2m_r2;
+       }
+
+       ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+       if (ret) {
+               v4l2_err(v4l2_dev,
+                        "%s(): failed to register video device\n", __func__);
+               goto err_m2m_r3;
+       }
+       v4l2_info(v4l2_dev,
+                 "FIMC m2m driver registered as /dev/video%d\n", vfd->num);
+
+       return 0;
+
+err_m2m_r3:
+       v4l2_m2m_release(fimc->m2m.m2m_dev);
+err_m2m_r2:
+       video_device_release(fimc->m2m.vfd);
+err_m2m_r1:
+       v4l2_device_unregister(v4l2_dev);
+
+       return ret;
+}
+
+static void fimc_unregister_m2m_device(struct fimc_dev *fimc)
+{
+       if (fimc) {
+               v4l2_m2m_release(fimc->m2m.m2m_dev);
+               video_unregister_device(fimc->m2m.vfd);
+               video_device_release(fimc->m2m.vfd);
+               v4l2_device_unregister(&fimc->m2m.v4l2_dev);
+       }
+}
+
+static void fimc_clk_release(struct fimc_dev *fimc)
+{
+       int i;
+       for (i = 0; i < NUM_FIMC_CLOCKS; i++) {
+               if (fimc->clock[i]) {
+                       clk_disable(fimc->clock[i]);
+                       clk_put(fimc->clock[i]);
+               }
+       }
+}
+
+static int fimc_clk_get(struct fimc_dev *fimc)
+{
+       int i;
+       for (i = 0; i < NUM_FIMC_CLOCKS; i++) {
+               fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clock_name[i]);
+               if (IS_ERR(fimc->clock[i])) {
+                       dev_err(&fimc->pdev->dev,
+                               "failed to get fimc clock: %s\n",
+                               fimc_clock_name[i]);
+                       return -ENXIO;
+               }
+               clk_enable(fimc->clock[i]);
+       }
+       return 0;
+}
+
+static int fimc_probe(struct platform_device *pdev)
+{
+       struct fimc_dev *fimc;
+       struct resource *res;
+       struct samsung_fimc_driverdata *drv_data;
+       int ret = 0;
+
+       dev_dbg(&pdev->dev, "%s():\n", __func__);
+
+       drv_data = (struct samsung_fimc_driverdata *)
+               platform_get_device_id(pdev)->driver_data;
+
+       if (pdev->id >= drv_data->devs_cnt) {
+               dev_err(&pdev->dev, "Invalid platform device id: %d\n",
+                       pdev->id);
+               return -EINVAL;
+       }
+
+       fimc = kzalloc(sizeof(struct fimc_dev), GFP_KERNEL);
+       if (!fimc)
+               return -ENOMEM;
+
+       fimc->id = pdev->id;
+       fimc->variant = drv_data->variant[fimc->id];
+       fimc->pdev = pdev;
+       fimc->state = ST_IDLE;
+
+       spin_lock_init(&fimc->irqlock);
+       spin_lock_init(&fimc->slock);
+
+       mutex_init(&fimc->lock);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "failed to find the registers\n");
+               ret = -ENOENT;
+               goto err_info;
+       }
+
+       fimc->regs_res = request_mem_region(res->start, resource_size(res),
+                       dev_name(&pdev->dev));
+       if (!fimc->regs_res) {
+               dev_err(&pdev->dev, "failed to obtain register region\n");
+               ret = -ENOENT;
+               goto err_info;
+       }
+
+       fimc->regs = ioremap(res->start, resource_size(res));
+       if (!fimc->regs) {
+               dev_err(&pdev->dev, "failed to map registers\n");
+               ret = -ENXIO;
+               goto err_req_region;
+       }
+
+       ret = fimc_clk_get(fimc);
+       if (ret)
+               goto err_regs_unmap;
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "failed to get IRQ resource\n");
+               ret = -ENXIO;
+               goto err_clk;
+       }
+       fimc->irq = res->start;
+
+       fimc_hw_reset(fimc);
+
+       ret = request_irq(fimc->irq, fimc_isr, 0, pdev->name, fimc);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
+               goto err_clk;
+       }
+
+       fimc->work_queue = create_workqueue(dev_name(&fimc->pdev->dev));
+       if (!fimc->work_queue)
+               goto err_irq;
+
+       ret = fimc_register_m2m_device(fimc);
+       if (ret)
+               goto err_wq;
+
+       fimc_hw_en_lastirq(fimc, true);
+
+       dev_dbg(&pdev->dev, "%s(): fimc-%d registered successfully\n",
+               __func__, fimc->id);
+
+       return 0;
+
+err_wq:
+       destroy_workqueue(fimc->work_queue);
+err_irq:
+       free_irq(fimc->irq, fimc);
+err_clk:
+       fimc_clk_release(fimc);
+err_regs_unmap:
+       iounmap(fimc->regs);
+err_req_region:
+       release_resource(fimc->regs_res);
+       kfree(fimc->regs_res);
+err_info:
+       kfree(fimc);
+       dev_err(&pdev->dev, "failed to install\n");
+       return ret;
+}
+
+static int __devexit fimc_remove(struct platform_device *pdev)
+{
+       struct fimc_dev *fimc =
+               (struct fimc_dev *)platform_get_drvdata(pdev);
+
+       v4l2_info(&fimc->m2m.v4l2_dev, "Removing %s\n", pdev->name);
+
+       free_irq(fimc->irq, fimc);
+
+       fimc_hw_reset(fimc);
+
+       fimc_unregister_m2m_device(fimc);
+       fimc_clk_release(fimc);
+       iounmap(fimc->regs);
+       release_resource(fimc->regs_res);
+       kfree(fimc->regs_res);
+       kfree(fimc);
+       return 0;
+}
+
+static struct samsung_fimc_variant fimc01_variant_s5p = {
+       .has_inp_rot    = 1,
+       .has_out_rot    = 1,
+       .min_inp_pixsize = 16,
+       .min_out_pixsize = 16,
+
+       .scaler_en_w    = 3264,
+       .scaler_dis_w   = 8192,
+       .in_rot_en_h    = 1920,
+       .in_rot_dis_w   = 8192,
+       .out_rot_en_w   = 1920,
+       .out_rot_dis_w  = 4224,
+};
+
+static struct samsung_fimc_variant fimc2_variant_s5p = {
+       .min_inp_pixsize = 16,
+       .min_out_pixsize = 16,
+
+       .scaler_en_w    = 4224,
+       .scaler_dis_w   = 8192,
+       .in_rot_en_h    = 1920,
+       .in_rot_dis_w   = 8192,
+       .out_rot_en_w   = 1920,
+       .out_rot_dis_w  = 4224,
+};
+
+static struct samsung_fimc_variant fimc01_variant_s5pv210 = {
+       .has_inp_rot    = 1,
+       .has_out_rot    = 1,
+       .min_inp_pixsize = 16,
+       .min_out_pixsize = 32,
+
+       .scaler_en_w    = 4224,
+       .scaler_dis_w   = 8192,
+       .in_rot_en_h    = 1920,
+       .in_rot_dis_w   = 8192,
+       .out_rot_en_w   = 1920,
+       .out_rot_dis_w  = 4224,
+};
+
+static struct samsung_fimc_variant fimc2_variant_s5pv210 = {
+       .min_inp_pixsize = 16,
+       .min_out_pixsize = 32,
+
+       .scaler_en_w    = 1920,
+       .scaler_dis_w   = 8192,
+       .in_rot_en_h    = 1280,
+       .in_rot_dis_w   = 8192,
+       .out_rot_en_w   = 1280,
+       .out_rot_dis_w  = 1920,
+};
+
+static struct samsung_fimc_driverdata fimc_drvdata_s5p = {
+       .variant = {
+               [0] = &fimc01_variant_s5p,
+               [1] = &fimc01_variant_s5p,
+               [2] = &fimc2_variant_s5p,
+       },
+       .devs_cnt = 3
+};
+
+static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = {
+       .variant = {
+               [0] = &fimc01_variant_s5pv210,
+               [1] = &fimc01_variant_s5pv210,
+               [2] = &fimc2_variant_s5pv210,
+       },
+       .devs_cnt = 3
+};
+
+static struct platform_device_id fimc_driver_ids[] = {
+       {
+               .name           = "s5p-fimc",
+               .driver_data    = (unsigned long)&fimc_drvdata_s5p,
+       }, {
+               .name           = "s5pv210-fimc",
+               .driver_data    = (unsigned long)&fimc_drvdata_s5pv210,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(platform, fimc_driver_ids);
+
+static struct platform_driver fimc_driver = {
+       .probe          = fimc_probe,
+       .remove = __devexit_p(fimc_remove),
+       .id_table       = fimc_driver_ids,
+       .driver = {
+               .name   = MODULE_NAME,
+               .owner  = THIS_MODULE,
+       }
+};
+
+static char banner[] __initdata = KERN_INFO
+       "S5PC Camera Interface V4L2 Driver, (c) 2010 Samsung Electronics\n";
+
+static int __init fimc_init(void)
+{
+       u32 ret;
+       printk(banner);
+
+       ret = platform_driver_register(&fimc_driver);
+       if (ret) {
+               printk(KERN_ERR "FIMC platform driver register failed\n");
+               return -1;
+       }
+       return 0;
+}
+
+static void __exit fimc_exit(void)
+{
+       platform_driver_unregister(&fimc_driver);
+}
+
+module_init(fimc_init);
+module_exit(fimc_exit);
+
+MODULE_AUTHOR("Sylwester Nawrocki, s.nawrocki@samsung.com");
+MODULE_DESCRIPTION("S3C/S5P FIMC (video postprocessor) driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h
new file mode 100644 (file)
index 0000000..6b3e0cd
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef FIMC_CORE_H_
+#define FIMC_CORE_H_
+
+#include <linux/types.h>
+#include <media/videobuf-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include <linux/videodev2.h>
+#include "regs-fimc.h"
+
+#define err(fmt, args...) \
+       printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
+
+#ifdef DEBUG
+#define dbg(fmt, args...) \
+       printk(KERN_DEBUG "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
+#else
+#define dbg(fmt, args...)
+#endif
+
+#define NUM_FIMC_CLOCKS                2
+#define MODULE_NAME            "s5p-fimc"
+#define FIMC_MAX_DEVS          3
+#define FIMC_MAX_OUT_BUFS      4
+#define SCALER_MAX_HRATIO      64
+#define SCALER_MAX_VRATIO      64
+
+enum {
+       ST_IDLE,
+       ST_OUTDMA_RUN,
+       ST_M2M_PEND,
+};
+
+#define fimc_m2m_active(dev) test_bit(ST_OUTDMA_RUN, &(dev)->state)
+#define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)
+
+enum fimc_datapath {
+       FIMC_ITU_CAM_A,
+       FIMC_ITU_CAM_B,
+       FIMC_MIPI_CAM,
+       FIMC_DMA,
+       FIMC_LCDFIFO,
+       FIMC_WRITEBACK
+};
+
+enum fimc_color_fmt {
+       S5P_FIMC_RGB565,
+       S5P_FIMC_RGB666,
+       S5P_FIMC_RGB888,
+       S5P_FIMC_YCBCR420,
+       S5P_FIMC_YCBCR422,
+       S5P_FIMC_YCBYCR422,
+       S5P_FIMC_YCRYCB422,
+       S5P_FIMC_CBYCRY422,
+       S5P_FIMC_CRYCBY422,
+       S5P_FIMC_RGB30_LOCAL,
+       S5P_FIMC_YCBCR444_LOCAL,
+       S5P_FIMC_MAX_COLOR = S5P_FIMC_YCBCR444_LOCAL,
+       S5P_FIMC_COLOR_MASK = 0x0F,
+};
+
+/* Y/Cb/Cr components order at DMA output for 1 plane YCbCr 4:2:2 formats. */
+#define        S5P_FIMC_OUT_CRYCBY     S5P_CIOCTRL_ORDER422_CRYCBY
+#define        S5P_FIMC_OUT_CBYCRY     S5P_CIOCTRL_ORDER422_YCRYCB
+#define        S5P_FIMC_OUT_YCRYCB     S5P_CIOCTRL_ORDER422_CBYCRY
+#define        S5P_FIMC_OUT_YCBYCR     S5P_CIOCTRL_ORDER422_YCBYCR
+
+/* Input Y/Cb/Cr components order for 1 plane YCbCr 4:2:2 color formats. */
+#define        S5P_FIMC_IN_CRYCBY      S5P_MSCTRL_ORDER422_CRYCBY
+#define        S5P_FIMC_IN_CBYCRY      S5P_MSCTRL_ORDER422_YCRYCB
+#define        S5P_FIMC_IN_YCRYCB      S5P_MSCTRL_ORDER422_CBYCRY
+#define        S5P_FIMC_IN_YCBYCR      S5P_MSCTRL_ORDER422_YCBYCR
+
+/* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats. */
+#define        S5P_FIMC_LSB_CRCB       S5P_CIOCTRL_ORDER422_2P_LSB_CRCB
+
+/* The embedded image effect selection */
+#define        S5P_FIMC_EFFECT_ORIGINAL        S5P_CIIMGEFF_FIN_BYPASS
+#define        S5P_FIMC_EFFECT_ARBITRARY       S5P_CIIMGEFF_FIN_ARBITRARY
+#define        S5P_FIMC_EFFECT_NEGATIVE        S5P_CIIMGEFF_FIN_NEGATIVE
+#define        S5P_FIMC_EFFECT_ARTFREEZE       S5P_CIIMGEFF_FIN_ARTFREEZE
+#define        S5P_FIMC_EFFECT_EMBOSSING       S5P_CIIMGEFF_FIN_EMBOSSING
+#define        S5P_FIMC_EFFECT_SIKHOUETTE      S5P_CIIMGEFF_FIN_SILHOUETTE
+
+/* The hardware context state. */
+#define        FIMC_PARAMS                     (1 << 0)
+#define        FIMC_SRC_ADDR                   (1 << 1)
+#define        FIMC_DST_ADDR                   (1 << 2)
+#define        FIMC_SRC_FMT                    (1 << 3)
+#define        FIMC_DST_FMT                    (1 << 4)
+
+/* Image conversion flags */
+#define        FIMC_IN_DMA_ACCESS_TILED        (1 << 0)
+#define        FIMC_IN_DMA_ACCESS_LINEAR       (0 << 0)
+#define        FIMC_OUT_DMA_ACCESS_TILED       (1 << 1)
+#define        FIMC_OUT_DMA_ACCESS_LINEAR      (0 << 1)
+#define        FIMC_SCAN_MODE_PROGRESSIVE      (0 << 2)
+#define        FIMC_SCAN_MODE_INTERLACED       (1 << 2)
+/* YCbCr data dynamic range for RGB-YUV color conversion. Y/Cb/Cr: (0 ~ 255) */
+#define        FIMC_COLOR_RANGE_WIDE           (0 << 3)
+/* Y (16 ~ 235), Cb/Cr (16 ~ 240) */
+#define        FIMC_COLOR_RANGE_NARROW         (1 << 3)
+
+#define        FLIP_NONE                       0
+#define        FLIP_X_AXIS                     1
+#define        FLIP_Y_AXIS                     2
+#define        FLIP_XY_AXIS                    (FLIP_X_AXIS | FLIP_Y_AXIS)
+
+/**
+ * struct fimc_fmt - the driver's internal color format data
+ * @name: format description
+ * @fourcc: the fourcc code for this format
+ * @color: the corresponding fimc_color_fmt
+ * @depth: number of bits per pixel
+ * @buff_cnt: number of physically non-contiguous data planes
+ * @planes_cnt: number of physically contiguous data planes
+ */
+struct fimc_fmt {
+       char    *name;
+       u32     fourcc;
+       u32     color;
+       u32     depth;
+       u16     buff_cnt;
+       u16     planes_cnt;
+};
+
+/**
+ * struct fimc_dma_offset - pixel offset information for DMA
+ * @y_h:       y value horizontal offset
+ * @y_v:       y value vertical offset
+ * @cb_h:      cb value horizontal offset
+ * @cb_v:      cb value vertical offset
+ * @cr_h:      cr value horizontal offset
+ * @cr_v:      cr value vertical offset
+ */
+struct fimc_dma_offset {
+       int     y_h;
+       int     y_v;
+       int     cb_h;
+       int     cb_v;
+       int     cr_h;
+       int     cr_v;
+};
+
+/**
+ * struct fimc_effect - the configuration data for the "Arbitrary" image effect
+ * @type:      effect type
+ * @pat_cb:    cr value when type is "arbitrary"
+ * @pat_cr:    cr value when type is "arbitrary"
+ */
+struct fimc_effect {
+       u32     type;
+       u8      pat_cb;
+       u8      pat_cr;
+};
+
+/**
+ * struct fimc_scaler - the configuration data for FIMC inetrnal scaler
+ *
+ * @enabled:           the flag set when the scaler is used
+ * @hfactor:           horizontal shift factor
+ * @vfactor:           vertical shift factor
+ * @pre_hratio:                horizontal ratio of the prescaler
+ * @pre_vratio:                vertical ratio of the prescaler
+ * @pre_dst_width:     the prescaler's destination width
+ * @pre_dst_height:    the prescaler's destination height
+ * @scaleup_h:         flag indicating scaling up horizontally
+ * @scaleup_v:         flag indicating scaling up vertically
+ * @main_hratio:       the main scaler's horizontal ratio
+ * @main_vratio:       the main scaler's vertical ratio
+ * @real_width:                source width - offset
+ * @real_height:       source height - offset
+ * @copy_mode:         flag set if one-to-one mode is used, i.e. no scaling
+ *                     and color format conversion
+ */
+struct fimc_scaler {
+       u32     enabled;
+       u32     hfactor;
+       u32     vfactor;
+       u32     pre_hratio;
+       u32     pre_vratio;
+       u32     pre_dst_width;
+       u32     pre_dst_height;
+       u32     scaleup_h;
+       u32     scaleup_v;
+       u32     main_hratio;
+       u32     main_vratio;
+       u32     real_width;
+       u32     real_height;
+       u32     copy_mode;
+};
+
+/**
+ * struct fimc_addr - the FIMC physical address set for DMA
+ *
+ * @y:  luminance plane physical address
+ * @cb:         Cb plane physical address
+ * @cr:         Cr plane physical address
+ */
+struct fimc_addr {
+       u32     y;
+       u32     cb;
+       u32     cr;
+};
+
+/**
+ * struct fimc_vid_buffer - the driver's video buffer
+ * @vb:        v4l videobuf buffer
+ */
+struct fimc_vid_buffer {
+       struct videobuf_buffer  vb;
+};
+
+/**
+ * struct fimc_frame - input/output frame format properties
+ *
+ * @f_width:   image full width (virtual screen size)
+ * @f_height:  image full height (virtual screen size)
+ * @o_width:   original image width as set by S_FMT
+ * @o_height:  original image height as set by S_FMT
+ * @offs_h:    image horizontal pixel offset
+ * @offs_v:    image vertical pixel offset
+ * @width:     image pixel width
+ * @height:    image pixel weight
+ * @paddr:     image frame buffer physical addresses
+ * @buf_cnt:   number of buffers depending on a color format
+ * @size:      image size in bytes
+ * @color:     color format
+ * @dma_offset:        DMA offset in bytes
+ */
+struct fimc_frame {
+       u32     f_width;
+       u32     f_height;
+       u32     o_width;
+       u32     o_height;
+       u32     offs_h;
+       u32     offs_v;
+       u32     width;
+       u32     height;
+       u32     size;
+       struct fimc_addr        paddr;
+       struct fimc_dma_offset  dma_offset;
+       struct fimc_fmt         *fmt;
+};
+
+/**
+ * struct fimc_m2m_device - v4l2 memory-to-memory device data
+ * @vfd: the video device node for v4l2 m2m mode
+ * @v4l2_dev: v4l2 device for m2m mode
+ * @m2m_dev: v4l2 memory-to-memory device data
+ * @ctx: hardware context data
+ * @refcnt: the reference counter
+ */
+struct fimc_m2m_device {
+       struct video_device     *vfd;
+       struct v4l2_device      v4l2_dev;
+       struct v4l2_m2m_dev     *m2m_dev;
+       struct fimc_ctx         *ctx;
+       int                     refcnt;
+};
+
+/**
+ * struct samsung_fimc_variant - camera interface variant information
+ *
+ * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes
+ * @has_inp_rot: set if has input rotator
+ * @has_out_rot: set if has output rotator
+ * @min_inp_pixsize: minimum input pixel size
+ * @min_out_pixsize: minimum output pixel size
+ * @scaler_en_w: maximum input pixel width when the scaler is enabled
+ * @scaler_dis_w: maximum input pixel width when the scaler is disabled
+ * @in_rot_en_h: maximum input width when the input rotator is used
+ * @in_rot_dis_w: maximum input width when the input rotator is used
+ * @out_rot_en_w: maximum output width for the output rotator enabled
+ * @out_rot_dis_w: maximum output width for the output rotator enabled
+ */
+struct samsung_fimc_variant {
+       unsigned int    pix_hoff:1;
+       unsigned int    has_inp_rot:1;
+       unsigned int    has_out_rot:1;
+
+       u16             min_inp_pixsize;
+       u16             min_out_pixsize;
+       u16             scaler_en_w;
+       u16             scaler_dis_w;
+       u16             in_rot_en_h;
+       u16             in_rot_dis_w;
+       u16             out_rot_en_w;
+       u16             out_rot_dis_w;
+};
+
+/**
+ * struct samsung_fimc_driverdata - per-device type driver data for init time.
+ *
+ * @variant: the variant information for this driver.
+ * @dev_cnt: number of fimc sub-devices available in SoC
+ */
+struct samsung_fimc_driverdata {
+       struct samsung_fimc_variant *variant[FIMC_MAX_DEVS];
+       int     devs_cnt;
+};
+
+struct fimc_ctx;
+
+/**
+ * struct fimc_subdev - abstraction for a FIMC entity
+ *
+ * @slock:     the spinlock protecting this data structure
+ * @lock:      the mutex protecting this data structure
+ * @pdev:      pointer to the FIMC platform device
+ * @id:                FIMC device index (0..2)
+ * @clock[]:   the clocks required for FIMC operation
+ * @regs:      the mapped hardware registers
+ * @regs_res:  the resource claimed for IO registers
+ * @irq:       interrupt number of the FIMC subdevice
+ * @irqlock:   spinlock protecting videbuffer queue
+ * @m2m:       memory-to-memory V4L2 device information
+ * @state:     the FIMC device state flags
+ */
+struct fimc_dev {
+       spinlock_t                      slock;
+       struct mutex                    lock;
+       struct platform_device          *pdev;
+       struct samsung_fimc_variant     *variant;
+       int                             id;
+       struct clk                      *clock[NUM_FIMC_CLOCKS];
+       void __iomem                    *regs;
+       struct resource                 *regs_res;
+       int                             irq;
+       spinlock_t                      irqlock;
+       struct workqueue_struct         *work_queue;
+       struct fimc_m2m_device          m2m;
+       unsigned long                   state;
+};
+
+/**
+ * fimc_ctx - the device context data
+ *
+ * @lock:              mutex protecting this data structure
+ * @s_frame:           source frame properties
+ * @d_frame:           destination frame properties
+ * @out_order_1p:      output 1-plane YCBCR order
+ * @out_order_2p:      output 2-plane YCBCR order
+ * @in_order_1p                input 1-plane YCBCR order
+ * @in_order_2p:       input 2-plane YCBCR order
+ * @in_path:           input mode (DMA or camera)
+ * @out_path:          output mode (DMA or FIFO)
+ * @scaler:            image scaler properties
+ * @effect:            image effect
+ * @rotation:          image clockwise rotation in degrees
+ * @flip:              image flip mode
+ * @flags:             an additional flags for image conversion
+ * @state:             flags to keep track of user configuration
+ * @fimc_dev:          the FIMC device this context applies to
+ * @m2m_ctx:           memory-to-memory device context
+ */
+struct fimc_ctx {
+       spinlock_t              slock;
+       struct fimc_frame       s_frame;
+       struct fimc_frame       d_frame;
+       u32                     out_order_1p;
+       u32                     out_order_2p;
+       u32                     in_order_1p;
+       u32                     in_order_2p;
+       enum fimc_datapath      in_path;
+       enum fimc_datapath      out_path;
+       struct fimc_scaler      scaler;
+       struct fimc_effect      effect;
+       int                     rotation;
+       u32                     flip;
+       u32                     flags;
+       u32                     state;
+       struct fimc_dev         *fimc_dev;
+       struct v4l2_m2m_ctx     *m2m_ctx;
+};
+
+
+static inline int tiled_fmt(struct fimc_fmt *fmt)
+{
+       return 0;
+}
+
+static inline void fimc_hw_clear_irq(struct fimc_dev *dev)
+{
+       u32 cfg = readl(dev->regs + S5P_CIGCTRL);
+       cfg |= S5P_CIGCTRL_IRQ_CLR;
+       writel(cfg, dev->regs + S5P_CIGCTRL);
+}
+
+static inline void fimc_hw_start_scaler(struct fimc_dev *dev)
+{
+       u32 cfg = readl(dev->regs + S5P_CISCCTRL);
+       cfg |= S5P_CISCCTRL_SCALERSTART;
+       writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+static inline void fimc_hw_stop_scaler(struct fimc_dev *dev)
+{
+       u32 cfg = readl(dev->regs + S5P_CISCCTRL);
+       cfg &= ~S5P_CISCCTRL_SCALERSTART;
+       writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+static inline void fimc_hw_dis_capture(struct fimc_dev *dev)
+{
+       u32 cfg = readl(dev->regs + S5P_CIIMGCPT);
+       cfg &= ~(S5P_CIIMGCPT_IMGCPTEN | S5P_CIIMGCPT_IMGCPTEN_SC);
+       writel(cfg, dev->regs + S5P_CIIMGCPT);
+}
+
+static inline void fimc_hw_start_in_dma(struct fimc_dev *dev)
+{
+       u32 cfg = readl(dev->regs + S5P_MSCTRL);
+       cfg |= S5P_MSCTRL_ENVID;
+       writel(cfg, dev->regs + S5P_MSCTRL);
+}
+
+static inline void fimc_hw_stop_in_dma(struct fimc_dev *dev)
+{
+       u32 cfg = readl(dev->regs + S5P_MSCTRL);
+       cfg &= ~S5P_MSCTRL_ENVID;
+       writel(cfg, dev->regs + S5P_MSCTRL);
+}
+
+static inline struct fimc_frame *ctx_m2m_get_frame(struct fimc_ctx *ctx,
+                                                  enum v4l2_buf_type type)
+{
+       struct fimc_frame *frame;
+
+       if (V4L2_BUF_TYPE_VIDEO_OUTPUT == type) {
+               frame = &ctx->s_frame;
+       } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == type) {
+               frame = &ctx->d_frame;
+       } else {
+               v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+                       "Wrong buffer/video queue type (%d)\n", type);
+               return ERR_PTR(-EINVAL);
+       }
+
+       return frame;
+}
+
+/* -----------------------------------------------------*/
+/* fimc-reg.c                                          */
+void fimc_hw_reset(struct fimc_dev *dev);
+void fimc_hw_set_rotation(struct fimc_ctx *ctx);
+void fimc_hw_set_target_format(struct fimc_ctx *ctx);
+void fimc_hw_set_out_dma(struct fimc_ctx *ctx);
+void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable);
+void fimc_hw_en_irq(struct fimc_dev *dev, int enable);
+void fimc_hw_set_prescaler(struct fimc_ctx *ctx);
+void fimc_hw_set_scaler(struct fimc_ctx *ctx);
+void fimc_hw_en_capture(struct fimc_ctx *ctx);
+void fimc_hw_set_effect(struct fimc_ctx *ctx);
+void fimc_hw_set_in_dma(struct fimc_ctx *ctx);
+void fimc_hw_set_input_path(struct fimc_ctx *ctx);
+void fimc_hw_set_output_path(struct fimc_ctx *ctx);
+void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr);
+void fimc_hw_set_output_addr(struct fimc_dev *dev, struct fimc_addr *paddr);
+
+#endif /* FIMC_CORE_H_ */
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c
new file mode 100644 (file)
index 0000000..5570f1c
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ * Register interface file for Samsung Camera Interface (FIMC) driver
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * Sylwester Nawrocki, s.nawrocki@samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <mach/map.h>
+
+#include "fimc-core.h"
+
+
+void fimc_hw_reset(struct fimc_dev *dev)
+{
+       u32 cfg;
+
+       cfg = readl(dev->regs + S5P_CISRCFMT);
+       cfg |= S5P_CISRCFMT_ITU601_8BIT;
+       writel(cfg, dev->regs + S5P_CISRCFMT);
+
+       /* Software reset. */
+       cfg = readl(dev->regs + S5P_CIGCTRL);
+       cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL);
+       writel(cfg, dev->regs + S5P_CIGCTRL);
+       msleep(1);
+
+       cfg = readl(dev->regs + S5P_CIGCTRL);
+       cfg &= ~S5P_CIGCTRL_SWRST;
+       writel(cfg, dev->regs + S5P_CIGCTRL);
+
+}
+
+void fimc_hw_set_rotation(struct fimc_ctx *ctx)
+{
+       u32 cfg, flip;
+       struct fimc_dev *dev = ctx->fimc_dev;
+
+       cfg = readl(dev->regs + S5P_CITRGFMT);
+       cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90);
+
+       flip = readl(dev->regs + S5P_MSCTRL);
+       flip &= ~S5P_MSCTRL_FLIP_MASK;
+
+       /*
+        * The input and output rotator cannot work simultaneously.
+        * Use the output rotator in output DMA mode or the input rotator
+        * in direct fifo output mode.
+        */
+       if (ctx->rotation == 90 || ctx->rotation == 270) {
+               if (ctx->out_path == FIMC_LCDFIFO) {
+                       cfg |= S5P_CITRGFMT_INROT90;
+                       if (ctx->rotation == 270)
+                               flip |= S5P_MSCTRL_FLIP_180;
+               } else {
+                       cfg |= S5P_CITRGFMT_OUTROT90;
+                       if (ctx->rotation == 270)
+                               cfg |= S5P_CITRGFMT_FLIP_180;
+               }
+       } else if (ctx->rotation == 180) {
+               if (ctx->out_path == FIMC_LCDFIFO)
+                       flip |= S5P_MSCTRL_FLIP_180;
+               else
+                       cfg |= S5P_CITRGFMT_FLIP_180;
+       }
+       if (ctx->rotation == 180 || ctx->rotation == 270)
+               writel(flip, dev->regs + S5P_MSCTRL);
+       writel(cfg, dev->regs + S5P_CITRGFMT);
+}
+
+static u32 fimc_hw_get_in_flip(u32 ctx_flip)
+{
+       u32 flip = S5P_MSCTRL_FLIP_NORMAL;
+
+       switch (ctx_flip) {
+       case FLIP_X_AXIS:
+               flip = S5P_MSCTRL_FLIP_X_MIRROR;
+               break;
+       case FLIP_Y_AXIS:
+               flip = S5P_MSCTRL_FLIP_Y_MIRROR;
+               break;
+       case FLIP_XY_AXIS:
+               flip = S5P_MSCTRL_FLIP_180;
+               break;
+       }
+
+       return flip;
+}
+
+static u32 fimc_hw_get_target_flip(u32 ctx_flip)
+{
+       u32 flip = S5P_CITRGFMT_FLIP_NORMAL;
+
+       switch (ctx_flip) {
+       case FLIP_X_AXIS:
+               flip = S5P_CITRGFMT_FLIP_X_MIRROR;
+               break;
+       case FLIP_Y_AXIS:
+               flip = S5P_CITRGFMT_FLIP_Y_MIRROR;
+               break;
+       case FLIP_XY_AXIS:
+               flip = S5P_CITRGFMT_FLIP_180;
+               break;
+       case FLIP_NONE:
+               break;
+
+       }
+       return flip;
+}
+
+void fimc_hw_set_target_format(struct fimc_ctx *ctx)
+{
+       u32 cfg;
+       struct fimc_dev *dev = ctx->fimc_dev;
+       struct fimc_frame *frame = &ctx->d_frame;
+
+       dbg("w= %d, h= %d color: %d", frame->width,
+               frame->height, frame->fmt->color);
+
+       cfg = readl(dev->regs + S5P_CITRGFMT);
+       cfg &= ~(S5P_CITRGFMT_FMT_MASK | S5P_CITRGFMT_HSIZE_MASK |
+                 S5P_CITRGFMT_VSIZE_MASK);
+
+       switch (frame->fmt->color) {
+       case S5P_FIMC_RGB565:
+       case S5P_FIMC_RGB666:
+       case S5P_FIMC_RGB888:
+               cfg |= S5P_CITRGFMT_RGB;
+               break;
+       case S5P_FIMC_YCBCR420:
+               cfg |= S5P_CITRGFMT_YCBCR420;
+               break;
+       case S5P_FIMC_YCBYCR422:
+       case S5P_FIMC_YCRYCB422:
+       case S5P_FIMC_CBYCRY422:
+       case S5P_FIMC_CRYCBY422:
+               if (frame->fmt->planes_cnt == 1)
+                       cfg |= S5P_CITRGFMT_YCBCR422_1P;
+               else
+                       cfg |= S5P_CITRGFMT_YCBCR422;
+               break;
+       default:
+               break;
+       }
+
+       cfg |= S5P_CITRGFMT_HSIZE(frame->width);
+       cfg |= S5P_CITRGFMT_VSIZE(frame->height);
+
+       if (ctx->rotation == 0) {
+               cfg &= ~S5P_CITRGFMT_FLIP_MASK;
+               cfg |= fimc_hw_get_target_flip(ctx->flip);
+       }
+       writel(cfg, dev->regs + S5P_CITRGFMT);
+
+       cfg = readl(dev->regs + S5P_CITAREA) & ~S5P_CITAREA_MASK;
+       cfg |= (frame->width * frame->height);
+       writel(cfg, dev->regs + S5P_CITAREA);
+}
+
+static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx)
+{
+       struct fimc_dev *dev = ctx->fimc_dev;
+       struct fimc_frame *frame = &ctx->d_frame;
+       u32 cfg = 0;
+
+       if (ctx->rotation == 90 || ctx->rotation == 270) {
+               cfg |= S5P_ORIG_SIZE_HOR(frame->f_height);
+               cfg |= S5P_ORIG_SIZE_VER(frame->f_width);
+       } else {
+               cfg |= S5P_ORIG_SIZE_HOR(frame->f_width);
+               cfg |= S5P_ORIG_SIZE_VER(frame->f_height);
+       }
+       writel(cfg, dev->regs + S5P_ORGOSIZE);
+}
+
+void fimc_hw_set_out_dma(struct fimc_ctx *ctx)
+{
+       u32 cfg;
+       struct fimc_dev *dev = ctx->fimc_dev;
+       struct fimc_frame *frame = &ctx->d_frame;
+       struct fimc_dma_offset *offset = &frame->dma_offset;
+
+       /* Set the input dma offsets. */
+       cfg = 0;
+       cfg |= S5P_CIO_OFFS_HOR(offset->y_h);
+       cfg |= S5P_CIO_OFFS_VER(offset->y_v);
+       writel(cfg, dev->regs + S5P_CIOYOFF);
+
+       cfg = 0;
+       cfg |= S5P_CIO_OFFS_HOR(offset->cb_h);
+       cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
+       writel(cfg, dev->regs + S5P_CIOCBOFF);
+
+       cfg = 0;
+       cfg |= S5P_CIO_OFFS_HOR(offset->cr_h);
+       cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
+       writel(cfg, dev->regs + S5P_CIOCROFF);
+
+       fimc_hw_set_out_dma_size(ctx);
+
+       /* Configure chroma components order. */
+       cfg = readl(dev->regs + S5P_CIOCTRL);
+
+       cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK |
+                S5P_CIOCTRL_YCBCR_PLANE_MASK);
+
+       if (frame->fmt->planes_cnt == 1)
+               cfg |= ctx->out_order_1p;
+       else if (frame->fmt->planes_cnt == 2)
+               cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE;
+       else if (frame->fmt->planes_cnt == 3)
+               cfg |= S5P_CIOCTRL_YCBCR_3PLANE;
+
+       writel(cfg, dev->regs + S5P_CIOCTRL);
+}
+
+static void fimc_hw_en_autoload(struct fimc_dev *dev, int enable)
+{
+       u32 cfg = readl(dev->regs + S5P_ORGISIZE);
+       if (enable)
+               cfg |= S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+       else
+               cfg &= ~S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+       writel(cfg, dev->regs + S5P_ORGISIZE);
+}
+
+void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable)
+{
+       unsigned long flags;
+       u32 cfg;
+
+       spin_lock_irqsave(&dev->slock, flags);
+
+       cfg = readl(dev->regs + S5P_CIOCTRL);
+       if (enable)
+               cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE;
+       else
+               cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE;
+       writel(cfg, dev->regs + S5P_CIOCTRL);
+
+       spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+void fimc_hw_set_prescaler(struct fimc_ctx *ctx)
+{
+       struct fimc_dev *dev =  ctx->fimc_dev;
+       struct fimc_scaler *sc = &ctx->scaler;
+       u32 cfg = 0, shfactor;
+
+       shfactor = 10 - (sc->hfactor + sc->vfactor);
+
+       cfg |= S5P_CISCPRERATIO_SHFACTOR(shfactor);
+       cfg |= S5P_CISCPRERATIO_HOR(sc->pre_hratio);
+       cfg |= S5P_CISCPRERATIO_VER(sc->pre_vratio);
+       writel(cfg, dev->regs + S5P_CISCPRERATIO);
+
+       cfg = 0;
+       cfg |= S5P_CISCPREDST_WIDTH(sc->pre_dst_width);
+       cfg |= S5P_CISCPREDST_HEIGHT(sc->pre_dst_height);
+       writel(cfg, dev->regs + S5P_CISCPREDST);
+}
+
+void fimc_hw_set_scaler(struct fimc_ctx *ctx)
+{
+       struct fimc_dev *dev = ctx->fimc_dev;
+       struct fimc_scaler *sc = &ctx->scaler;
+       struct fimc_frame *src_frame = &ctx->s_frame;
+       struct fimc_frame *dst_frame = &ctx->d_frame;
+       u32 cfg = 0;
+
+       if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW))
+               cfg |= (S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE);
+
+       if (!sc->enabled)
+               cfg |= S5P_CISCCTRL_SCALERBYPASS;
+
+       if (sc->scaleup_h)
+               cfg |= S5P_CISCCTRL_SCALEUP_H;
+
+       if (sc->scaleup_v)
+               cfg |= S5P_CISCCTRL_SCALEUP_V;
+
+       if (sc->copy_mode)
+               cfg |= S5P_CISCCTRL_ONE2ONE;
+
+
+       if (ctx->in_path == FIMC_DMA) {
+               if (src_frame->fmt->color == S5P_FIMC_RGB565)
+                       cfg |= S5P_CISCCTRL_INRGB_FMT_RGB565;
+               else if (src_frame->fmt->color == S5P_FIMC_RGB666)
+                       cfg |= S5P_CISCCTRL_INRGB_FMT_RGB666;
+               else if (src_frame->fmt->color == S5P_FIMC_RGB888)
+                       cfg |= S5P_CISCCTRL_INRGB_FMT_RGB888;
+       }
+
+       if (ctx->out_path == FIMC_DMA) {
+               if (dst_frame->fmt->color == S5P_FIMC_RGB565)
+                       cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB565;
+               else if (dst_frame->fmt->color == S5P_FIMC_RGB666)
+                       cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB666;
+               else if (dst_frame->fmt->color == S5P_FIMC_RGB888)
+                       cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
+       } else {
+               cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
+
+               if (ctx->flags & FIMC_SCAN_MODE_INTERLACED)
+                       cfg |= S5P_CISCCTRL_INTERLACE;
+       }
+
+       dbg("main_hratio= 0x%X  main_vratio= 0x%X",
+               sc->main_hratio, sc->main_vratio);
+
+       cfg |= S5P_CISCCTRL_SC_HORRATIO(sc->main_hratio);
+       cfg |= S5P_CISCCTRL_SC_VERRATIO(sc->main_vratio);
+
+       writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+void fimc_hw_en_capture(struct fimc_ctx *ctx)
+{
+       struct fimc_dev *dev = ctx->fimc_dev;
+       u32 cfg;
+
+       cfg = readl(dev->regs + S5P_CIIMGCPT);
+       /* One shot mode for output DMA or freerun for FIFO. */
+       if (ctx->out_path == FIMC_DMA)
+               cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE;
+       else
+               cfg &= ~S5P_CIIMGCPT_CPT_FREN_ENABLE;
+
+       if (ctx->scaler.enabled)
+               cfg |= S5P_CIIMGCPT_IMGCPTEN_SC;
+
+       writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT);
+}
+
+void fimc_hw_set_effect(struct fimc_ctx *ctx)
+{
+       struct fimc_dev *dev = ctx->fimc_dev;
+       struct fimc_effect *effect = &ctx->effect;
+       u32 cfg = (S5P_CIIMGEFF_IE_ENABLE | S5P_CIIMGEFF_IE_SC_AFTER);
+
+       cfg |= effect->type;
+
+       if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
+               cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
+               cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
+       }
+
+       writel(cfg, dev->regs + S5P_CIIMGEFF);
+}
+
+static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx)
+{
+       struct fimc_dev *dev = ctx->fimc_dev;
+       struct fimc_frame *frame = &ctx->s_frame;
+       u32 cfg_o = 0;
+       u32 cfg_r = 0;
+
+       if (FIMC_LCDFIFO == ctx->out_path)
+               cfg_r |=  S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+
+       cfg_o |= S5P_ORIG_SIZE_HOR(frame->f_width);
+       cfg_o |= S5P_ORIG_SIZE_VER(frame->f_height);
+       cfg_r |= S5P_CIREAL_ISIZE_WIDTH(frame->width);
+       cfg_r |= S5P_CIREAL_ISIZE_HEIGHT(frame->height);
+
+       writel(cfg_o, dev->regs + S5P_ORGISIZE);
+       writel(cfg_r, dev->regs + S5P_CIREAL_ISIZE);
+}
+
+void fimc_hw_set_in_dma(struct fimc_ctx *ctx)
+{
+       struct fimc_dev *dev = ctx->fimc_dev;
+       struct fimc_frame *frame = &ctx->s_frame;
+       struct fimc_dma_offset *offset = &frame->dma_offset;
+       u32 cfg = 0;
+
+       /* Set the pixel offsets. */
+       cfg |= S5P_CIO_OFFS_HOR(offset->y_h);
+       cfg |= S5P_CIO_OFFS_VER(offset->y_v);
+       writel(cfg, dev->regs + S5P_CIIYOFF);
+
+       cfg = 0;
+       cfg |= S5P_CIO_OFFS_HOR(offset->cb_h);
+       cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
+       writel(cfg, dev->regs + S5P_CIICBOFF);
+
+       cfg = 0;
+       cfg |= S5P_CIO_OFFS_HOR(offset->cr_h);
+       cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
+       writel(cfg, dev->regs + S5P_CIICROFF);
+
+       /* Input original and real size. */
+       fimc_hw_set_in_dma_size(ctx);
+
+       /* Autoload is used currently only in FIFO mode. */
+       fimc_hw_en_autoload(dev, ctx->out_path == FIMC_LCDFIFO);
+
+       /* Set the input DMA to process single frame only. */
+       cfg = readl(dev->regs + S5P_MSCTRL);
+       cfg &= ~(S5P_MSCTRL_FLIP_MASK
+               | S5P_MSCTRL_INFORMAT_MASK
+               | S5P_MSCTRL_IN_BURST_COUNT_MASK
+               | S5P_MSCTRL_INPUT_MASK
+               | S5P_MSCTRL_C_INT_IN_MASK
+               | S5P_MSCTRL_2P_IN_ORDER_MASK);
+
+       cfg |= (S5P_MSCTRL_FRAME_COUNT(1) | S5P_MSCTRL_INPUT_MEMORY);
+
+       switch (frame->fmt->color) {
+       case S5P_FIMC_RGB565:
+       case S5P_FIMC_RGB666:
+       case S5P_FIMC_RGB888:
+               cfg |= S5P_MSCTRL_INFORMAT_RGB;
+               break;
+       case S5P_FIMC_YCBCR420:
+               cfg |= S5P_MSCTRL_INFORMAT_YCBCR420;
+
+               if (frame->fmt->planes_cnt == 2)
+                       cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE;
+               else
+                       cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
+
+               break;
+       case S5P_FIMC_YCBYCR422:
+       case S5P_FIMC_YCRYCB422:
+       case S5P_FIMC_CBYCRY422:
+       case S5P_FIMC_CRYCBY422:
+               if (frame->fmt->planes_cnt == 1) {
+                       cfg |= ctx->in_order_1p
+                               | S5P_MSCTRL_INFORMAT_YCBCR422_1P;
+               } else {
+                       cfg |= S5P_MSCTRL_INFORMAT_YCBCR422;
+
+                       if (frame->fmt->planes_cnt == 2)
+                               cfg |= ctx->in_order_2p
+                                       | S5P_MSCTRL_C_INT_IN_2PLANE;
+                       else
+                               cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
+               }
+               break;
+       default:
+               break;
+       }
+
+       /*
+        * Input DMA flip mode (and rotation).
+        * Do not allow simultaneous rotation and flipping.
+        */
+       if (!ctx->rotation && ctx->out_path == FIMC_LCDFIFO)
+               cfg |= fimc_hw_get_in_flip(ctx->flip);
+
+       writel(cfg, dev->regs + S5P_MSCTRL);
+
+       /* Input/output DMA linear/tiled mode. */
+       cfg = readl(dev->regs + S5P_CIDMAPARAM);
+       cfg &= ~S5P_CIDMAPARAM_TILE_MASK;
+
+       if (tiled_fmt(ctx->s_frame.fmt))
+               cfg |= S5P_CIDMAPARAM_R_64X32;
+
+       if (tiled_fmt(ctx->d_frame.fmt))
+               cfg |= S5P_CIDMAPARAM_W_64X32;
+
+       writel(cfg, dev->regs + S5P_CIDMAPARAM);
+}
+
+
+void fimc_hw_set_input_path(struct fimc_ctx *ctx)
+{
+       struct fimc_dev *dev = ctx->fimc_dev;
+
+       u32 cfg = readl(dev->regs + S5P_MSCTRL);
+       cfg &= ~S5P_MSCTRL_INPUT_MASK;
+
+       if (ctx->in_path == FIMC_DMA)
+               cfg |= S5P_MSCTRL_INPUT_MEMORY;
+       else
+               cfg |= S5P_MSCTRL_INPUT_EXTCAM;
+
+       writel(cfg, dev->regs + S5P_MSCTRL);
+}
+
+void fimc_hw_set_output_path(struct fimc_ctx *ctx)
+{
+       struct fimc_dev *dev = ctx->fimc_dev;
+
+       u32 cfg = readl(dev->regs + S5P_CISCCTRL);
+       cfg &= ~S5P_CISCCTRL_LCDPATHEN_FIFO;
+       if (ctx->out_path == FIMC_LCDFIFO)
+               cfg |= S5P_CISCCTRL_LCDPATHEN_FIFO;
+       writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr)
+{
+       u32 cfg = 0;
+
+       cfg = readl(dev->regs + S5P_CIREAL_ISIZE);
+       cfg |= S5P_CIREAL_ISIZE_ADDR_CH_DIS;
+       writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
+
+       writel(paddr->y, dev->regs + S5P_CIIYSA0);
+       writel(paddr->cb, dev->regs + S5P_CIICBSA0);
+       writel(paddr->cr, dev->regs + S5P_CIICRSA0);
+
+       cfg &= ~S5P_CIREAL_ISIZE_ADDR_CH_DIS;
+       writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
+}
+
+void fimc_hw_set_output_addr(struct fimc_dev *dev, struct fimc_addr *paddr)
+{
+       int i;
+       /* Set all the output register sets to point to single video buffer. */
+       for (i = 0; i < FIMC_MAX_OUT_BUFS; i++) {
+               writel(paddr->y, dev->regs + S5P_CIOYSA(i));
+               writel(paddr->cb, dev->regs + S5P_CIOCBSA(i));
+               writel(paddr->cr, dev->regs + S5P_CIOCRSA(i));
+       }
+}
diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h
new file mode 100644 (file)
index 0000000..a3cfe82
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * Register definition file for Samsung Camera Interface (FIMC) driver
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * 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 REGS_FIMC_H_
+#define REGS_FIMC_H_
+
+#define S5P_CIOYSA(__x)                        (0x18 + (__x) * 4)
+#define S5P_CIOCBSA(__x)               (0x28 + (__x) * 4)
+#define S5P_CIOCRSA(__x)               (0x38 + (__x) * 4)
+
+/* Input source format */
+#define S5P_CISRCFMT                   0x00
+#define S5P_CISRCFMT_ITU601_8BIT       (1 << 31)
+#define S5P_CISRCFMT_ITU601_16BIT      (1 << 29)
+#define S5P_CISRCFMT_ORDER422_YCBYCR   (0 << 14)
+#define S5P_CISRCFMT_ORDER422_YCRYCB   (1 << 14)
+#define S5P_CISRCFMT_ORDER422_CBYCRY   (2 << 14)
+#define S5P_CISRCFMT_ORDER422_CRYCBY   (3 << 14)
+#define S5P_CISRCFMT_HSIZE(x)          ((x) << 16)
+#define S5P_CISRCFMT_VSIZE(x)          ((x) << 0)
+
+/* Window offset */
+#define S5P_CIWDOFST                   0x04
+#define S5P_CIWDOFST_WINOFSEN          (1 << 31)
+#define S5P_CIWDOFST_CLROVFIY          (1 << 30)
+#define S5P_CIWDOFST_CLROVRLB          (1 << 29)
+#define S5P_CIWDOFST_WINHOROFST_MASK   (0x7ff << 16)
+#define S5P_CIWDOFST_CLROVFICB         (1 << 15)
+#define S5P_CIWDOFST_CLROVFICR         (1 << 14)
+#define S5P_CIWDOFST_WINHOROFST(x)     ((x) << 16)
+#define S5P_CIWDOFST_WINVEROFST(x)     ((x) << 0)
+#define S5P_CIWDOFST_WINVEROFST_MASK   (0xfff << 0)
+
+/* Global control */
+#define S5P_CIGCTRL                    0x08
+#define S5P_CIGCTRL_SWRST              (1 << 31)
+#define S5P_CIGCTRL_CAMRST_A           (1 << 30)
+#define S5P_CIGCTRL_SELCAM_ITU_A       (1 << 29)
+#define S5P_CIGCTRL_SELCAM_ITU_MASK    (1 << 29)
+#define S5P_CIGCTRL_TESTPAT_NORMAL     (0 << 27)
+#define S5P_CIGCTRL_TESTPAT_COLOR_BAR  (1 << 27)
+#define S5P_CIGCTRL_TESTPAT_HOR_INC    (2 << 27)
+#define S5P_CIGCTRL_TESTPAT_VER_INC    (3 << 27)
+#define S5P_CIGCTRL_TESTPAT_MASK       (3 << 27)
+#define S5P_CIGCTRL_TESTPAT_SHIFT      (27)
+#define S5P_CIGCTRL_INVPOLPCLK         (1 << 26)
+#define S5P_CIGCTRL_INVPOLVSYNC                (1 << 25)
+#define S5P_CIGCTRL_INVPOLHREF         (1 << 24)
+#define S5P_CIGCTRL_IRQ_OVFEN          (1 << 22)
+#define S5P_CIGCTRL_HREF_MASK          (1 << 21)
+#define S5P_CIGCTRL_IRQ_LEVEL          (1 << 20)
+#define S5P_CIGCTRL_IRQ_CLR            (1 << 19)
+#define S5P_CIGCTRL_IRQ_ENABLE         (1 << 16)
+#define S5P_CIGCTRL_SHDW_DISABLE       (1 << 12)
+#define S5P_CIGCTRL_SELCAM_MIPI_A      (1 << 7)
+#define S5P_CIGCTRL_CAMIF_SELWB                (1 << 6)
+#define S5P_CIGCTRL_INVPOLHSYNC                (1 << 4)
+#define S5P_CIGCTRL_SELCAM_MIPI                (1 << 3)
+#define S5P_CIGCTRL_INTERLACE          (1 << 0)
+
+/* Window offset 2 */
+#define S5P_CIWDOFST2                  0x14
+#define S5P_CIWDOFST2_HOROFF_MASK      (0xfff << 16)
+#define S5P_CIWDOFST2_VEROFF_MASK      (0xfff << 0)
+#define S5P_CIWDOFST2_HOROFF(x)                ((x) << 16)
+#define S5P_CIWDOFST2_VEROFF(x)                ((x) << 0)
+
+/* Output DMA Y plane start address */
+#define S5P_CIOYSA1                    0x18
+#define S5P_CIOYSA2                    0x1c
+#define S5P_CIOYSA3                    0x20
+#define S5P_CIOYSA4                    0x24
+
+/* Output DMA Cb plane start address */
+#define S5P_CIOCBSA1                   0x28
+#define S5P_CIOCBSA2                   0x2c
+#define S5P_CIOCBSA3                   0x30
+#define S5P_CIOCBSA4                   0x34
+
+/* Output DMA Cr plane start address */
+#define S5P_CIOCRSA1                   0x38
+#define S5P_CIOCRSA2                   0x3c
+#define S5P_CIOCRSA3                   0x40
+#define S5P_CIOCRSA4                   0x44
+
+/* Target image format */
+#define S5P_CITRGFMT                   0x48
+#define S5P_CITRGFMT_INROT90           (1 << 31)
+#define S5P_CITRGFMT_YCBCR420          (0 << 29)
+#define S5P_CITRGFMT_YCBCR422          (1 << 29)
+#define S5P_CITRGFMT_YCBCR422_1P       (2 << 29)
+#define S5P_CITRGFMT_RGB               (3 << 29)
+#define S5P_CITRGFMT_FMT_MASK          (3 << 29)
+#define S5P_CITRGFMT_HSIZE_MASK                (0xfff << 16)
+#define S5P_CITRGFMT_FLIP_SHIFT                (14)
+#define S5P_CITRGFMT_FLIP_NORMAL       (0 << 14)
+#define S5P_CITRGFMT_FLIP_X_MIRROR     (1 << 14)
+#define S5P_CITRGFMT_FLIP_Y_MIRROR     (2 << 14)
+#define S5P_CITRGFMT_FLIP_180          (3 << 14)
+#define S5P_CITRGFMT_FLIP_MASK         (3 << 14)
+#define S5P_CITRGFMT_OUTROT90          (1 << 13)
+#define S5P_CITRGFMT_VSIZE_MASK                (0xfff << 0)
+#define S5P_CITRGFMT_HSIZE(x)          ((x) << 16)
+#define S5P_CITRGFMT_VSIZE(x)          ((x) << 0)
+
+/* Output DMA control */
+#define S5P_CIOCTRL                    0x4c
+#define S5P_CIOCTRL_ORDER422_MASK      (3 << 0)
+#define S5P_CIOCTRL_ORDER422_CRYCBY    (0 << 0)
+#define S5P_CIOCTRL_ORDER422_YCRYCB    (1 << 0)
+#define S5P_CIOCTRL_ORDER422_CBYCRY    (2 << 0)
+#define S5P_CIOCTRL_ORDER422_YCBYCR    (3 << 0)
+#define S5P_CIOCTRL_LASTIRQ_ENABLE     (1 << 2)
+#define S5P_CIOCTRL_YCBCR_3PLANE       (0 << 3)
+#define S5P_CIOCTRL_YCBCR_2PLANE       (1 << 3)
+#define S5P_CIOCTRL_YCBCR_PLANE_MASK   (1 << 3)
+#define S5P_CIOCTRL_ORDER2P_SHIFT      (24)
+#define S5P_CIOCTRL_ORDER2P_MASK       (3 << 24)
+#define S5P_CIOCTRL_ORDER422_2P_LSB_CRCB (0 << 24)
+
+/* Pre-scaler control 1 */
+#define S5P_CISCPRERATIO               0x50
+#define S5P_CISCPRERATIO_SHFACTOR(x)   ((x) << 28)
+#define S5P_CISCPRERATIO_HOR(x)                ((x) << 16)
+#define S5P_CISCPRERATIO_VER(x)                ((x) << 0)
+
+#define S5P_CISCPREDST                 0x54
+#define S5P_CISCPREDST_WIDTH(x)                ((x) << 16)
+#define S5P_CISCPREDST_HEIGHT(x)       ((x) << 0)
+
+/* Main scaler control */
+#define S5P_CISCCTRL                   0x58
+#define S5P_CISCCTRL_SCALERBYPASS      (1 << 31)
+#define S5P_CISCCTRL_SCALEUP_H         (1 << 30)
+#define S5P_CISCCTRL_SCALEUP_V         (1 << 29)
+#define S5P_CISCCTRL_CSCR2Y_WIDE       (1 << 28)
+#define S5P_CISCCTRL_CSCY2R_WIDE       (1 << 27)
+#define S5P_CISCCTRL_LCDPATHEN_FIFO    (1 << 26)
+#define S5P_CISCCTRL_INTERLACE         (1 << 25)
+#define S5P_CISCCTRL_SCALERSTART       (1 << 15)
+#define S5P_CISCCTRL_INRGB_FMT_RGB565  (0 << 13)
+#define S5P_CISCCTRL_INRGB_FMT_RGB666  (1 << 13)
+#define S5P_CISCCTRL_INRGB_FMT_RGB888  (2 << 13)
+#define S5P_CISCCTRL_INRGB_FMT_MASK    (3 << 13)
+#define S5P_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11)
+#define S5P_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11)
+#define S5P_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11)
+#define S5P_CISCCTRL_OUTRGB_FMT_MASK   (3 << 11)
+#define S5P_CISCCTRL_RGB_EXT           (1 << 10)
+#define S5P_CISCCTRL_ONE2ONE           (1 << 9)
+#define S5P_CISCCTRL_SC_HORRATIO(x)    ((x) << 16)
+#define S5P_CISCCTRL_SC_VERRATIO(x)    ((x) << 0)
+
+/* Target area */
+#define S5P_CITAREA                    0x5c
+#define S5P_CITAREA_MASK               0x0fffffff
+
+/* General status */
+#define S5P_CISTATUS                   0x64
+#define S5P_CISTATUS_OVFIY             (1 << 31)
+#define S5P_CISTATUS_OVFICB            (1 << 30)
+#define S5P_CISTATUS_OVFICR            (1 << 29)
+#define S5P_CISTATUS_VSYNC             (1 << 28)
+#define S5P_CISTATUS_WINOFF_EN         (1 << 25)
+#define S5P_CISTATUS_IMGCPT_EN         (1 << 22)
+#define S5P_CISTATUS_IMGCPT_SCEN       (1 << 21)
+#define S5P_CISTATUS_VSYNC_A           (1 << 20)
+#define S5P_CISTATUS_VSYNC_B           (1 << 19)
+#define S5P_CISTATUS_OVRLB             (1 << 18)
+#define S5P_CISTATUS_FRAME_END         (1 << 17)
+#define S5P_CISTATUS_LASTCAPT_END      (1 << 16)
+#define S5P_CISTATUS_VVALID_A          (1 << 15)
+#define S5P_CISTATUS_VVALID_B          (1 << 14)
+
+/* Image capture control */
+#define S5P_CIIMGCPT                   0xc0
+#define S5P_CIIMGCPT_IMGCPTEN          (1 << 31)
+#define S5P_CIIMGCPT_IMGCPTEN_SC       (1 << 30)
+#define S5P_CIIMGCPT_CPT_FREN_ENABLE   (1 << 25)
+#define S5P_CIIMGCPT_CPT_FRMOD_CNT     (1 << 18)
+
+/* Frame capture sequence */
+#define S5P_CICPTSEQ                   0xc4
+
+/* Image effect */
+#define S5P_CIIMGEFF                   0xd0
+#define S5P_CIIMGEFF_IE_DISABLE                (0 << 30)
+#define S5P_CIIMGEFF_IE_ENABLE         (1 << 30)
+#define S5P_CIIMGEFF_IE_SC_BEFORE      (0 << 29)
+#define S5P_CIIMGEFF_IE_SC_AFTER       (1 << 29)
+#define S5P_CIIMGEFF_FIN_BYPASS                (0 << 26)
+#define S5P_CIIMGEFF_FIN_ARBITRARY     (1 << 26)
+#define S5P_CIIMGEFF_FIN_NEGATIVE      (2 << 26)
+#define S5P_CIIMGEFF_FIN_ARTFREEZE     (3 << 26)
+#define S5P_CIIMGEFF_FIN_EMBOSSING     (4 << 26)
+#define S5P_CIIMGEFF_FIN_SILHOUETTE    (5 << 26)
+#define S5P_CIIMGEFF_FIN_MASK          (7 << 26)
+#define S5P_CIIMGEFF_PAT_CBCR_MASK     ((0xff < 13) | (0xff < 0))
+#define S5P_CIIMGEFF_PAT_CB(x)         ((x) << 13)
+#define S5P_CIIMGEFF_PAT_CR(x)         ((x) << 0)
+
+/* Input DMA Y/Cb/Cr plane start address 0 */
+#define S5P_CIIYSA0                    0xd4
+#define S5P_CIICBSA0                   0xd8
+#define S5P_CIICRSA0                   0xdc
+
+/* Real input DMA image size */
+#define S5P_CIREAL_ISIZE               0xf8
+#define S5P_CIREAL_ISIZE_AUTOLOAD_EN   (1 << 31)
+#define S5P_CIREAL_ISIZE_ADDR_CH_DIS   (1 << 30)
+#define S5P_CIREAL_ISIZE_HEIGHT(x)     ((x) << 16)
+#define S5P_CIREAL_ISIZE_WIDTH(x)      ((x) << 0)
+
+
+/* Input DMA control */
+#define S5P_MSCTRL                     0xfc
+#define S5P_MSCTRL_IN_BURST_COUNT_MASK (3 << 24)
+#define S5P_MSCTRL_2P_IN_ORDER_MASK    (3 << 16)
+#define S5P_MSCTRL_2P_IN_ORDER_SHIFT   16
+#define S5P_MSCTRL_C_INT_IN_3PLANE     (0 << 15)
+#define S5P_MSCTRL_C_INT_IN_2PLANE     (1 << 15)
+#define S5P_MSCTRL_C_INT_IN_MASK       (1 << 15)
+#define S5P_MSCTRL_FLIP_SHIFT          13
+#define S5P_MSCTRL_FLIP_MASK           (3 << 13)
+#define S5P_MSCTRL_FLIP_NORMAL         (0 << 13)
+#define S5P_MSCTRL_FLIP_X_MIRROR       (1 << 13)
+#define S5P_MSCTRL_FLIP_Y_MIRROR       (2 << 13)
+#define S5P_MSCTRL_FLIP_180            (3 << 13)
+#define S5P_MSCTRL_ORDER422_SHIFT      4
+#define S5P_MSCTRL_ORDER422_CRYCBY     (0 << 4)
+#define S5P_MSCTRL_ORDER422_YCRYCB     (1 << 4)
+#define S5P_MSCTRL_ORDER422_CBYCRY     (2 << 4)
+#define S5P_MSCTRL_ORDER422_YCBYCR     (3 << 4)
+#define S5P_MSCTRL_ORDER422_MASK       (3 << 4)
+#define S5P_MSCTRL_INPUT_EXTCAM                (0 << 3)
+#define S5P_MSCTRL_INPUT_MEMORY                (1 << 3)
+#define S5P_MSCTRL_INPUT_MASK          (1 << 3)
+#define S5P_MSCTRL_INFORMAT_YCBCR420   (0 << 1)
+#define S5P_MSCTRL_INFORMAT_YCBCR422   (1 << 1)
+#define S5P_MSCTRL_INFORMAT_YCBCR422_1P        (2 << 1)
+#define S5P_MSCTRL_INFORMAT_RGB                (3 << 1)
+#define S5P_MSCTRL_INFORMAT_MASK       (3 << 1)
+#define S5P_MSCTRL_ENVID               (1 << 0)
+#define S5P_MSCTRL_FRAME_COUNT(x)      ((x) << 24)
+
+/* Input DMA Y/Cb/Cr plane start address 1 */
+#define S5P_CIIYSA1                    0x144
+#define S5P_CIICBSA1                   0x148
+#define S5P_CIICRSA1                   0x14c
+
+/* Output DMA Y/Cb/Cr offset */
+#define S5P_CIOYOFF                    0x168
+#define S5P_CIOCBOFF                   0x16c
+#define S5P_CIOCROFF                   0x170
+
+/* Input DMA Y/Cb/Cr offset */
+#define S5P_CIIYOFF                    0x174
+#define S5P_CIICBOFF                   0x178
+#define S5P_CIICROFF                   0x17c
+
+#define S5P_CIO_OFFS_VER(x)            ((x) << 16)
+#define S5P_CIO_OFFS_HOR(x)            ((x) << 0)
+
+/* Input DMA original image size */
+#define S5P_ORGISIZE                   0x180
+
+/* Output DMA original image size */
+#define S5P_ORGOSIZE                   0x184
+
+#define S5P_ORIG_SIZE_VER(x)           ((x) << 16)
+#define S5P_ORIG_SIZE_HOR(x)           ((x) << 0)
+
+/* Real output DMA image size (extension register) */
+#define S5P_CIEXTEN                    0x188
+
+#define S5P_CIDMAPARAM                 0x18c
+#define S5P_CIDMAPARAM_R_LINEAR                (0 << 29)
+#define S5P_CIDMAPARAM_R_64X32         (3 << 29)
+#define S5P_CIDMAPARAM_W_LINEAR                (0 << 13)
+#define S5P_CIDMAPARAM_W_64X32         (3 << 13)
+#define S5P_CIDMAPARAM_TILE_MASK       ((3 << 29) | (3 << 13))
+
+/* MIPI CSI image format */
+#define S5P_CSIIMGFMT                  0x194
+
+#endif /* REGS_FIMC_H_ */
index 76da74368680f9a10f747fc0c8322a56f2c630bd..ee963f4d01bcf770d6e9ae6b711ed41ec1ed4668 100644 (file)
@@ -45,6 +45,7 @@
 #include <linux/i2c.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-i2c-drv.h>
 #include <media/saa7115.h>
@@ -65,16 +66,19 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
 
 struct saa711x_state {
        struct v4l2_subdev sd;
+       struct v4l2_ctrl_handler hdl;
+
+       struct {
+               /* chroma gain control cluster */
+               struct v4l2_ctrl *agc;
+               struct v4l2_ctrl *gain;
+       };
+
        v4l2_std_id std;
        int input;
        int output;
        int enable;
        int radio;
-       int bright;
-       int contrast;
-       int hue;
-       int sat;
-       int chroma_agc;
        int width;
        int height;
        u32 ident;
@@ -90,6 +94,11 @@ static inline struct saa711x_state *to_state(struct v4l2_subdev *sd)
        return container_of(sd, struct saa711x_state, sd);
 }
 
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+       return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd;
+}
+
 /* ----------------------------------------------------------------------- */
 
 static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value)
@@ -741,96 +750,53 @@ static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
        return 0;
 }
 
-static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 {
+       struct v4l2_subdev *sd = to_sd(ctrl);
        struct saa711x_state *state = to_state(sd);
-       u8 val;
 
        switch (ctrl->id) {
-       case V4L2_CID_BRIGHTNESS:
-               if (ctrl->value < 0 || ctrl->value > 255) {
-                       v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
-                       return -ERANGE;
-               }
-
-               state->bright = ctrl->value;
-               saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright);
-               break;
-
-       case V4L2_CID_CONTRAST:
-               if (ctrl->value < 0 || ctrl->value > 127) {
-                       v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
-                       return -ERANGE;
-               }
-
-               state->contrast = ctrl->value;
-               saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast);
-               break;
-
-       case V4L2_CID_SATURATION:
-               if (ctrl->value < 0 || ctrl->value > 127) {
-                       v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
-                       return -ERANGE;
-               }
-
-               state->sat = ctrl->value;
-               saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat);
-               break;
-
-       case V4L2_CID_HUE:
-               if (ctrl->value < -128 || ctrl->value > 127) {
-                       v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
-                       return -ERANGE;
-               }
-
-               state->hue = ctrl->value;
-               saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue);
-               break;
        case V4L2_CID_CHROMA_AGC:
-               val = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL);
-               state->chroma_agc = ctrl->value;
-               if (ctrl->value)
-                       val &= 0x7f;
-               else
-                       val |= 0x80;
-               saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, val);
+               /* chroma gain cluster */
+               if (state->agc->cur.val)
+                       state->gain->cur.val =
+                               saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
                break;
-       case V4L2_CID_CHROMA_GAIN:
-               /* Chroma gain cannot be set when AGC is enabled */
-               if (state->chroma_agc == 1)
-                       return -EINVAL;
-               saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, ctrl->value | 0x80);
-               break;
-       default:
-               return -EINVAL;
        }
-
        return 0;
 }
 
-static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+       struct v4l2_subdev *sd = to_sd(ctrl);
        struct saa711x_state *state = to_state(sd);
 
        switch (ctrl->id) {
        case V4L2_CID_BRIGHTNESS:
-               ctrl->value = state->bright;
+               saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val);
                break;
+
        case V4L2_CID_CONTRAST:
-               ctrl->value = state->contrast;
+               saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val);
                break;
+
        case V4L2_CID_SATURATION:
-               ctrl->value = state->sat;
+               saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val);
                break;
+
        case V4L2_CID_HUE:
-               ctrl->value = state->hue;
+               saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val);
                break;
+
        case V4L2_CID_CHROMA_AGC:
-               ctrl->value = state->chroma_agc;
-               break;
-       case V4L2_CID_CHROMA_GAIN:
-               ctrl->value = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
+               /* chroma gain cluster */
+               if (state->agc->val)
+                       saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val);
+               else
+                       saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80);
+               v4l2_ctrl_activate(state->gain, !state->agc->val);
                break;
+
        default:
                return -EINVAL;
        }
@@ -1223,25 +1189,6 @@ static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
        return 0;
 }
 
-static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
-       switch (qc->id) {
-       case V4L2_CID_BRIGHTNESS:
-               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
-       case V4L2_CID_CONTRAST:
-       case V4L2_CID_SATURATION:
-               return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
-       case V4L2_CID_HUE:
-               return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
-       case V4L2_CID_CHROMA_AGC:
-               return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
-       case V4L2_CID_CHROMA_GAIN:
-               return v4l2_ctrl_query_fill(qc, 0, 127, 1, 48);
-       default:
-               return -EINVAL;
-       }
-}
-
 static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
 {
        struct saa711x_state *state = to_state(sd);
@@ -1518,17 +1465,27 @@ static int saa711x_log_status(struct v4l2_subdev *sd)
                break;
        }
        v4l2_info(sd, "Width, Height:   %d, %d\n", state->width, state->height);
+       v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
        return 0;
 }
 
 /* ----------------------------------------------------------------------- */
 
+static const struct v4l2_ctrl_ops saa711x_ctrl_ops = {
+       .s_ctrl = saa711x_s_ctrl,
+       .g_volatile_ctrl = saa711x_g_volatile_ctrl,
+};
+
 static const struct v4l2_subdev_core_ops saa711x_core_ops = {
        .log_status = saa711x_log_status,
        .g_chip_ident = saa711x_g_chip_ident,
-       .g_ctrl = saa711x_g_ctrl,
-       .s_ctrl = saa711x_s_ctrl,
-       .queryctrl = saa711x_queryctrl,
+       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+       .g_ctrl = v4l2_subdev_g_ctrl,
+       .s_ctrl = v4l2_subdev_s_ctrl,
+       .queryctrl = v4l2_subdev_queryctrl,
+       .querymenu = v4l2_subdev_querymenu,
        .s_std = saa711x_s_std,
        .reset = saa711x_reset,
        .s_gpio = saa711x_s_gpio,
@@ -1579,8 +1536,9 @@ static int saa711x_probe(struct i2c_client *client,
 {
        struct saa711x_state *state;
        struct v4l2_subdev *sd;
-       int     i;
-       char    name[17];
+       struct v4l2_ctrl_handler *hdl;
+       int i;
+       char name[17];
        char chip_id;
        int autodetect = !id || id->driver_data == 1;
 
@@ -1619,15 +1577,38 @@ static int saa711x_probe(struct i2c_client *client,
                return -ENOMEM;
        sd = &state->sd;
        v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
+
+       hdl = &state->hdl;
+       v4l2_ctrl_handler_init(hdl, 6);
+       /* add in ascending ID order */
+       v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+       v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 127, 1, 64);
+       v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 127, 1, 64);
+       v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+                       V4L2_CID_HUE, -128, 127, 1, 0);
+       state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+                       V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
+       state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+                       V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40);
+       state->gain->is_volatile = 1;
+       sd->ctrl_handler = hdl;
+       if (hdl->error) {
+               int err = hdl->error;
+
+               v4l2_ctrl_handler_free(hdl);
+               kfree(state);
+               return err;
+       }
+       state->agc->flags |= V4L2_CTRL_FLAG_UPDATE;
+       v4l2_ctrl_cluster(2, &state->agc);
+
        state->input = -1;
        state->output = SAA7115_IPORT_ON;
        state->enable = 1;
        state->radio = 0;
-       state->bright = 128;
-       state->contrast = 64;
-       state->hue = 0;
-       state->sat = 64;
-       state->chroma_agc = 1;
        switch (chip_id) {
        case '1':
                state->ident = V4L2_IDENT_SAA7111;
@@ -1675,6 +1656,7 @@ static int saa711x_probe(struct i2c_client *client,
        if (state->ident > V4L2_IDENT_SAA7111A)
                saa711x_writeregs(sd, saa7115_init_misc);
        saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
+       v4l2_ctrl_handler_setup(hdl);
 
        v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
                saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
@@ -1689,6 +1671,7 @@ static int saa711x_remove(struct i2c_client *client)
        struct v4l2_subdev *sd = i2c_get_clientdata(client);
 
        v4l2_device_unregister_subdev(sd);
+       v4l2_ctrl_handler_free(sd->ctrl_handler);
        kfree(to_state(sd));
        return 0;
 }
index 78d69950c00abe1bc4ee70bd000e1ed2c3e051a3..45f8bfc1342e331d13adae8c2a11ba357b1d4c5c 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/videodev2.h>
 #include <linux/i2c.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver");
@@ -55,14 +56,11 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
 
 struct saa717x_state {
        struct v4l2_subdev sd;
+       struct v4l2_ctrl_handler hdl;
        v4l2_std_id std;
        int input;
        int enable;
        int radio;
-       int bright;
-       int contrast;
-       int hue;
-       int sat;
        int playback;
        int audio;
        int tuner_audio_mode;
@@ -81,6 +79,11 @@ static inline struct saa717x_state *to_state(struct v4l2_subdev *sd)
        return container_of(sd, struct saa717x_state, sd);
 }
 
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+       return &container_of(ctrl->handler, struct saa717x_state, hdl)->sd;
+}
+
 /* ----------------------------------------------------------------------- */
 
 /* for audio mode */
@@ -774,29 +777,6 @@ static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode)
        saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]);
 }
 
-/* write regs to video output level (bright,contrast,hue,sat) */
-static void set_video_output_level_regs(struct v4l2_subdev *sd,
-               struct saa717x_state *decoder)
-{
-       /* brightness ffh (bright) - 80h (ITU level) - 00h (dark) */
-       saa717x_write(sd, 0x10a, decoder->bright);
-
-       /* contrast 7fh (max: 1.984) - 44h (ITU) - 40h (1.0) -
-          0h (luminance off) 40: i2c dump
-          c0h (-1.0 inverse chrominance)
-          80h (-2.0 inverse chrominance) */
-       saa717x_write(sd, 0x10b, decoder->contrast);
-
-       /* saturation? 7fh(max)-40h(ITU)-0h(color off)
-          c0h (-1.0 inverse chrominance)
-          80h (-2.0 inverse chrominance) */
-       saa717x_write(sd, 0x10c, decoder->sat);
-
-       /* color hue (phase) control
-          7fh (+178.6) - 0h (0 normal) - 80h (-180.0) */
-       saa717x_write(sd, 0x10d, decoder->hue);
-}
-
 /* write regs to set audio volume, bass and treble */
 static int set_audio_regs(struct v4l2_subdev *sd,
                struct saa717x_state *decoder)
@@ -829,9 +809,9 @@ static int set_audio_regs(struct v4l2_subdev *sd,
 
        saa717x_write(sd, 0x480, val);
 
-       /* bass and treble; go to another function */
        /* set bass and treble */
-       val = decoder->audio_main_bass | (decoder->audio_main_treble << 8);
+       val = decoder->audio_main_bass & 0x1f;
+       val |= (decoder->audio_main_treble & 0x1f) << 5;
        saa717x_write(sd, 0x488, val);
        return 0;
 }
@@ -893,218 +873,55 @@ static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale)
        saa717x_write(sd, 0x71 + task_shift, yscale >> 8);
 }
 
-static int saa717x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       struct saa717x_state *state = to_state(sd);
-
-       switch (ctrl->id) {
-       case V4L2_CID_BRIGHTNESS:
-               if (ctrl->value < 0 || ctrl->value > 255) {
-                       v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
-                       return -ERANGE;
-               }
-
-               state->bright = ctrl->value;
-               v4l2_dbg(1, debug, sd, "bright:%d\n", state->bright);
-               saa717x_write(sd, 0x10a, state->bright);
-               break;
-
-       case V4L2_CID_CONTRAST:
-               if (ctrl->value < 0 || ctrl->value > 127) {
-                       v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
-                       return -ERANGE;
-               }
-
-               state->contrast = ctrl->value;
-               v4l2_dbg(1, debug, sd, "contrast:%d\n", state->contrast);
-               saa717x_write(sd, 0x10b, state->contrast);
-               break;
-
-       case V4L2_CID_SATURATION:
-               if (ctrl->value < 0 || ctrl->value > 127) {
-                       v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
-                       return -ERANGE;
-               }
-
-               state->sat = ctrl->value;
-               v4l2_dbg(1, debug, sd, "sat:%d\n", state->sat);
-               saa717x_write(sd, 0x10c, state->sat);
-               break;
-
-       case V4L2_CID_HUE:
-               if (ctrl->value < -128 || ctrl->value > 127) {
-                       v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
-                       return -ERANGE;
-               }
-
-               state->hue = ctrl->value;
-               v4l2_dbg(1, debug, sd, "hue:%d\n", state->hue);
-               saa717x_write(sd, 0x10d, state->hue);
-               break;
-
-       case V4L2_CID_AUDIO_MUTE:
-               state->audio_main_mute = ctrl->value;
-               set_audio_regs(sd, state);
-               break;
-
-       case V4L2_CID_AUDIO_VOLUME:
-               state->audio_main_volume = ctrl->value;
-               set_audio_regs(sd, state);
-               break;
-
-       case V4L2_CID_AUDIO_BALANCE:
-               state->audio_main_balance = ctrl->value;
-               set_audio_regs(sd, state);
-               break;
-
-       case V4L2_CID_AUDIO_TREBLE:
-               state->audio_main_treble = ctrl->value;
-               set_audio_regs(sd, state);
-               break;
-
-       case V4L2_CID_AUDIO_BASS:
-               state->audio_main_bass = ctrl->value;
-               set_audio_regs(sd, state);
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int saa717x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int saa717x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+       struct v4l2_subdev *sd = to_sd(ctrl);
        struct saa717x_state *state = to_state(sd);
 
        switch (ctrl->id) {
        case V4L2_CID_BRIGHTNESS:
-               ctrl->value = state->bright;
-               break;
+               saa717x_write(sd, 0x10a, ctrl->val);
+               return 0;
 
        case V4L2_CID_CONTRAST:
-               ctrl->value = state->contrast;
-               break;
+               saa717x_write(sd, 0x10b, ctrl->val);
+               return 0;
 
        case V4L2_CID_SATURATION:
-               ctrl->value = state->sat;
-               break;
+               saa717x_write(sd, 0x10c, ctrl->val);
+               return 0;
 
        case V4L2_CID_HUE:
-               ctrl->value = state->hue;
-               break;
+               saa717x_write(sd, 0x10d, ctrl->val);
+               return 0;
 
        case V4L2_CID_AUDIO_MUTE:
-               ctrl->value = state->audio_main_mute;
+               state->audio_main_mute = ctrl->val;
                break;
 
        case V4L2_CID_AUDIO_VOLUME:
-               ctrl->value = state->audio_main_volume;
+               state->audio_main_volume = ctrl->val;
                break;
 
        case V4L2_CID_AUDIO_BALANCE:
-               ctrl->value = state->audio_main_balance;
+               state->audio_main_balance = ctrl->val;
                break;
 
        case V4L2_CID_AUDIO_TREBLE:
-               ctrl->value = state->audio_main_treble;
+               state->audio_main_treble = ctrl->val;
                break;
 
        case V4L2_CID_AUDIO_BASS:
-               ctrl->value = state->audio_main_bass;
+               state->audio_main_bass = ctrl->val;
                break;
 
        default:
-               return -EINVAL;
+               return 0;
        }
-
+       set_audio_regs(sd, state);
        return 0;
 }
 
-static struct v4l2_queryctrl saa717x_qctrl[] = {
-       {
-               .id            = V4L2_CID_BRIGHTNESS,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-               .name          = "Brightness",
-               .minimum       = 0,
-               .maximum       = 255,
-               .step          = 1,
-               .default_value = 128,
-               .flags         = 0,
-       }, {
-               .id            = V4L2_CID_CONTRAST,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-               .name          = "Contrast",
-               .minimum       = 0,
-               .maximum       = 255,
-               .step          = 1,
-               .default_value = 64,
-               .flags         = 0,
-       }, {
-               .id            = V4L2_CID_SATURATION,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-               .name          = "Saturation",
-               .minimum       = 0,
-               .maximum       = 255,
-               .step          = 1,
-               .default_value = 64,
-               .flags         = 0,
-       }, {
-               .id            = V4L2_CID_HUE,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-               .name          = "Hue",
-               .minimum       = -128,
-               .maximum       = 127,
-               .step          = 1,
-               .default_value = 0,
-               .flags         = 0,
-       }, {
-               .id            = V4L2_CID_AUDIO_VOLUME,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-               .name          = "Volume",
-               .minimum       = 0,
-               .maximum       = 65535,
-               .step          = 65535 / 100,
-               .default_value = 58880,
-               .flags         = 0,
-       }, {
-               .id            = V4L2_CID_AUDIO_BALANCE,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-               .name          = "Balance",
-               .minimum       = 0,
-               .maximum       = 65535,
-               .step          = 65535 / 100,
-               .default_value = 32768,
-               .flags         = 0,
-       }, {
-               .id            = V4L2_CID_AUDIO_MUTE,
-               .type          = V4L2_CTRL_TYPE_BOOLEAN,
-               .name          = "Mute",
-               .minimum       = 0,
-               .maximum       = 1,
-               .step          = 1,
-               .default_value = 1,
-               .flags         = 0,
-       }, {
-               .id            = V4L2_CID_AUDIO_BASS,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-               .name          = "Bass",
-               .minimum       = 0,
-               .maximum       = 65535,
-               .step          = 65535 / 100,
-               .default_value = 32768,
-       }, {
-               .id            = V4L2_CID_AUDIO_TREBLE,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-               .name          = "Treble",
-               .minimum       = 0,
-               .maximum       = 65535,
-               .step          = 65535 / 100,
-               .default_value = 32768,
-       },
-};
-
 static int saa717x_s_video_routing(struct v4l2_subdev *sd,
                                   u32 input, u32 output, u32 config)
 {
@@ -1158,18 +975,6 @@ static int saa717x_s_video_routing(struct v4l2_subdev *sd,
        return 0;
 }
 
-static int saa717x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++)
-               if (qc->id && qc->id == saa717x_qctrl[i].id) {
-                       memcpy(qc, &saa717x_qctrl[i], sizeof(*qc));
-                       return 0;
-               }
-       return -EINVAL;
-}
-
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
 {
@@ -1386,17 +1191,34 @@ static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
        return 0;
 }
 
+static int saa717x_log_status(struct v4l2_subdev *sd)
+{
+       struct saa717x_state *state = to_state(sd);
+
+       v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
+       return 0;
+}
+
 /* ----------------------------------------------------------------------- */
 
+static const struct v4l2_ctrl_ops saa717x_ctrl_ops = {
+       .s_ctrl = saa717x_s_ctrl,
+};
+
 static const struct v4l2_subdev_core_ops saa717x_core_ops = {
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        .g_register = saa717x_g_register,
        .s_register = saa717x_s_register,
 #endif
-       .queryctrl = saa717x_queryctrl,
-       .g_ctrl = saa717x_g_ctrl,
-       .s_ctrl = saa717x_s_ctrl,
        .s_std = saa717x_s_std,
+       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+       .g_ctrl = v4l2_subdev_g_ctrl,
+       .s_ctrl = v4l2_subdev_s_ctrl,
+       .queryctrl = v4l2_subdev_queryctrl,
+       .querymenu = v4l2_subdev_querymenu,
+       .log_status = saa717x_log_status,
 };
 
 static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = {
@@ -1432,6 +1254,7 @@ static int saa717x_probe(struct i2c_client *client,
                         const struct i2c_device_id *did)
 {
        struct saa717x_state *decoder;
+       struct v4l2_ctrl_handler *hdl;
        struct v4l2_subdev *sd;
        u8 id = 0;
        char *p = "";
@@ -1467,16 +1290,41 @@ static int saa717x_probe(struct i2c_client *client,
                p = "saa7171";
        v4l2_info(sd, "%s found @ 0x%x (%s)\n", p,
                        client->addr << 1, client->adapter->name);
+
+       hdl = &decoder->hdl;
+       v4l2_ctrl_handler_init(hdl, 9);
+       /* add in ascending ID order */
+       v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+       v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 255, 1, 68);
+       v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 255, 1, 64);
+       v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+                       V4L2_CID_HUE, -128, 127, 1, 0);
+       v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+                       V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 42000);
+       v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+                       V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+       v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+                       V4L2_CID_AUDIO_BASS, -16, 15, 1, 0);
+       v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+                       V4L2_CID_AUDIO_TREBLE, -16, 15, 1, 0);
+       v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+                       V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+       sd->ctrl_handler = hdl;
+       if (hdl->error) {
+               int err = hdl->error;
+
+               v4l2_ctrl_handler_free(hdl);
+               kfree(decoder);
+               return err;
+       }
+
        decoder->std = V4L2_STD_NTSC;
        decoder->input = -1;
        decoder->enable = 1;
 
-       /* tune these parameters */
-       decoder->bright = 0x80;
-       decoder->contrast = 0x44;
-       decoder->sat = 0x40;
-       decoder->hue = 0x00;
-
        /* FIXME!! */
        decoder->playback = 0;  /* initially capture mode used */
        decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */
@@ -1487,23 +1335,13 @@ static int saa717x_probe(struct i2c_client *client,
        /* set volume, bass and treble */
        decoder->audio_main_vol_l = 6;
        decoder->audio_main_vol_r = 6;
-       decoder->audio_main_bass = 0;
-       decoder->audio_main_treble = 0;
-       decoder->audio_main_mute = 0;
-       decoder->audio_main_balance = 32768;
-       /* normalize (24 to -40 (not -84) -> 65535 to 0) */
-       decoder->audio_main_volume =
-               (decoder->audio_main_vol_r + 41) * 65535 / (24 - (-40));
 
        v4l2_dbg(1, debug, sd, "writing init values\n");
 
        /* FIXME!! */
        saa717x_write_regs(sd, reg_init_initialize);
-       set_video_output_level_regs(sd, decoder);
-       /* set bass,treble to 0db 20041101 K.Ohta */
-       decoder->audio_main_bass = 0;
-       decoder->audio_main_treble = 0;
-       set_audio_regs(sd, decoder);
+
+       v4l2_ctrl_handler_setup(hdl);
 
        set_current_state(TASK_INTERRUPTIBLE);
        schedule_timeout(2*HZ);
@@ -1515,6 +1353,7 @@ static int saa717x_remove(struct i2c_client *client)
        struct v4l2_subdev *sd = i2c_get_clientdata(client);
 
        v4l2_device_unregister_subdev(sd);
+       v4l2_ctrl_handler_free(sd->ctrl_handler);
        kfree(to_state(sd));
        return 0;
 }
index f2032939fd4b728d0d7465d03da5ec587087c52f..a499cacec1f37897c08de6d7bf21b1210723aca0 100644 (file)
@@ -779,9 +779,12 @@ static int soc_camera_s_crop(struct file *file, void *fh,
        ret = ici->ops->get_crop(icd, &current_crop);
 
        /* Prohibit window size change with initialised buffers */
-       if (icf->vb_vidq.bufs[0] && !ret &&
-           (a->c.width != current_crop.c.width ||
-            a->c.height != current_crop.c.height)) {
+       if (ret < 0) {
+               dev_err(&icd->dev,
+                       "S_CROP denied: getting current crop failed\n");
+       } else if (icf->vb_vidq.bufs[0] &&
+                  (a->c.width != current_crop.c.width ||
+                   a->c.height != current_crop.c.height)) {
                dev_err(&icd->dev,
                        "S_CROP denied: queue initialised and sizes differ\n");
                ret = -EBUSY;
index 8085ac392446e9bc5f57f12b5d9211aeb0454b8a..48f5c76ab5210c44dbdb27cac71fe5712b568c9c 100644 (file)
@@ -179,7 +179,7 @@ static const struct i2c_reg_value tvp7002_init_default[] = {
 /* Register parameters for 480P */
 static const struct i2c_reg_value tvp7002_parms_480P[] = {
        { TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE },
-       { TVP7002_HPLL_FDBK_DIV_LSBS, 0x0a, TVP7002_WRITE },
+       { TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE },
        { TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE },
        { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
        { TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE },
@@ -223,7 +223,7 @@ static const struct i2c_reg_value tvp7002_parms_576P[] = {
 /* Register parameters for 1080I60 */
 static const struct i2c_reg_value tvp7002_parms_1080I60[] = {
        { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
-       { TVP7002_HPLL_FDBK_DIV_LSBS, 0x08, TVP7002_WRITE },
+       { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE },
        { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
        { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
        { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
@@ -245,7 +245,7 @@ static const struct i2c_reg_value tvp7002_parms_1080I60[] = {
 /* Register parameters for 1080P60 */
 static const struct i2c_reg_value tvp7002_parms_1080P60[] = {
        { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
-       { TVP7002_HPLL_FDBK_DIV_LSBS, 0x08, TVP7002_WRITE },
+       { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE },
        { TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE },
        { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
        { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
@@ -289,7 +289,7 @@ static const struct i2c_reg_value tvp7002_parms_1080I50[] = {
 /* Register parameters for 720P60 */
 static const struct i2c_reg_value tvp7002_parms_720P60[] = {
        { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE },
-       { TVP7002_HPLL_FDBK_DIV_LSBS, 0x02, TVP7002_WRITE },
+       { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE },
        { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE },
        { TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
        { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
@@ -311,7 +311,7 @@ static const struct i2c_reg_value tvp7002_parms_720P60[] = {
 /* Register parameters for 720P50 */
 static const struct i2c_reg_value tvp7002_parms_720P50[] = {
        { TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE },
-       { TVP7002_HPLL_FDBK_DIV_LSBS, 0x0c, TVP7002_WRITE },
+       { TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE },
        { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
        { TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
        { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
index 5ac37c6c43131503dc95cbf4e496bee1ee2d1460..f1fcf9744961fde9f2aca7e6f3ac1018f9f22c57 100644 (file)
@@ -282,19 +282,15 @@ static void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame,
        };
        unsigned short digit;
        int ix, iy;
+       int value;
 
        if ((uvd == NULL) || (frame == NULL))
                return;
 
-       if (ch >= '0' && ch <= '9')
-               ch -= '0';
-       else if (ch >= 'A' && ch <= 'F')
-               ch = 10 + (ch - 'A');
-       else if (ch >= 'a' && ch <= 'f')
-               ch = 10 + (ch - 'a');
-       else
+       value = hex_to_bin(ch);
+       if (value < 0)
                return;
-       digit = digits[ch];
+       digit = digits[value];
 
        for (iy=0; iy < 5; iy++) {
                for (ix=0; ix < 3; ix++) {
index 7eaf99b22a48d9001f844772fcfa43483fbe9b9f..8bdd940f32e689c5b51a94ec8975014567d9f00c 100644 (file)
@@ -2145,6 +2145,15 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_STREAM_NO_FID },
+       /* Miricle 307K */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x17dc,
+         .idProduct            = 0x0202,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_STREAM_NO_FID },
        /* Lenovo Thinkpad SL400/SL500 */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
index 133c78d113ac2d23cbaa19b993c8d95feb466a9f..e9928a415086ef88af00406e6d81bd0e9735806c 100644 (file)
  *
  */
 
-void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
+void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
+                   int drop_corrupted)
 {
        mutex_init(&queue->mutex);
        spin_lock_init(&queue->irqlock);
        INIT_LIST_HEAD(&queue->mainqueue);
        INIT_LIST_HEAD(&queue->irqqueue);
+       queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0;
        queue->type = type;
 }
 
@@ -435,8 +437,10 @@ int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
                uvc_queue_cancel(queue, 0);
                INIT_LIST_HEAD(&queue->mainqueue);
 
-               for (i = 0; i < queue->count; ++i)
+               for (i = 0; i < queue->count; ++i) {
+                       queue->buffer[i].error = 0;
                        queue->buffer[i].state = UVC_BUF_STATE_IDLE;
+               }
 
                queue->flags &= ~UVC_QUEUE_STREAMING;
        }
@@ -488,8 +492,8 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
        struct uvc_buffer *nextbuf;
        unsigned long flags;
 
-       if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
-           buf->buf.length != buf->buf.bytesused) {
+       if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) {
+               buf->error = 0;
                buf->state = UVC_BUF_STATE_QUEUED;
                buf->buf.bytesused = 0;
                return buf;
@@ -497,6 +501,7 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
 
        spin_lock_irqsave(&queue->irqlock, flags);
        list_del(&buf->queue);
+       buf->error = 0;
        buf->state = UVC_BUF_STATE_DONE;
        if (!list_empty(&queue->irqqueue))
                nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
index 53f3ef4635eb3a9d1cf53f1fb7cdb7e57f54d83d..e27cf0d3b6d90d08e156f99c2bbe9d2b3703ec76 100644 (file)
@@ -555,6 +555,9 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
                if (urb->iso_frame_desc[i].status < 0) {
                        uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
                                "lost (%d).\n", urb->iso_frame_desc[i].status);
+                       /* Mark the buffer as faulty. */
+                       if (buf != NULL)
+                               buf->error = 1;
                        continue;
                }
 
@@ -579,8 +582,14 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
                uvc_video_decode_end(stream, buf, mem,
                        urb->iso_frame_desc[i].actual_length);
 
-               if (buf->state == UVC_BUF_STATE_READY)
+               if (buf->state == UVC_BUF_STATE_READY) {
+                       if (buf->buf.length != buf->buf.bytesused &&
+                           !(stream->cur_format->flags &
+                             UVC_FMT_FLAG_COMPRESSED))
+                               buf->error = 1;
+
                        buf = uvc_queue_next_buffer(&stream->queue, buf);
+               }
        }
 }
 
@@ -1104,7 +1113,7 @@ int uvc_video_init(struct uvc_streaming *stream)
        atomic_set(&stream->active, 0);
 
        /* Initialize the video buffers queue. */
-       uvc_queue_init(&stream->queue, stream->type);
+       uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);
 
        /* Alternate setting 0 should be the default, yet the XBox Live Vision
         * Cam (and possibly other devices) crash or otherwise misbehave if
@@ -1197,12 +1206,6 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable)
                return 0;
        }
 
-       if ((stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) ||
-           uvc_no_drop_param)
-               stream->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
-       else
-               stream->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
-
        ret = uvc_queue_enable(&stream->queue, 1);
        if (ret < 0)
                return ret;
index ac272456fbfdf997f177f99842fdfaef6529a6db..bdacf3beabf54fcbe1f9f901692a0134e6b48ed1 100644 (file)
@@ -379,11 +379,12 @@ struct uvc_buffer {
        struct list_head queue;
        wait_queue_head_t wait;
        enum uvc_buffer_state state;
+       unsigned int error;
 };
 
 #define UVC_QUEUE_STREAMING            (1 << 0)
 #define UVC_QUEUE_DISCONNECTED         (1 << 1)
-#define UVC_QUEUE_DROP_INCOMPLETE      (1 << 2)
+#define UVC_QUEUE_DROP_CORRUPTED       (1 << 2)
 
 struct uvc_video_queue {
        enum v4l2_buf_type type;
@@ -562,7 +563,7 @@ extern struct uvc_driver uvc_driver;
 
 /* Video buffers queue management. */
 extern void uvc_queue_init(struct uvc_video_queue *queue,
-               enum v4l2_buf_type type);
+               enum v4l2_buf_type type, int drop_corrupted);
 extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
                unsigned int nbuffers, unsigned int buflength);
 extern int uvc_free_buffers(struct uvc_video_queue *queue);
index 4e53b0b3339ceda15b4ab0a2a6f017970d082bf6..3ce7c64e57897aec0fbe81f911d742604d95872d 100644 (file)
@@ -62,6 +62,7 @@
 #define __OLD_VIDIOC_ /* To allow fixing old calls*/
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-chip-ident.h>
 
 #include <linux/videodev2.h>
@@ -172,487 +173,17 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
 }
 EXPORT_SYMBOL(v4l2_ctrl_check);
 
-/* Returns NULL or a character pointer array containing the menu for
-   the given control ID. The pointer array ends with a NULL pointer.
-   An empty string signifies a menu entry that is invalid. This allows
-   drivers to disable certain options if it is not supported. */
-const char **v4l2_ctrl_get_menu(u32 id)
-{
-       static const char *mpeg_audio_sampling_freq[] = {
-               "44.1 kHz",
-               "48 kHz",
-               "32 kHz",
-               NULL
-       };
-       static const char *mpeg_audio_encoding[] = {
-               "MPEG-1/2 Layer I",
-               "MPEG-1/2 Layer II",
-               "MPEG-1/2 Layer III",
-               "MPEG-2/4 AAC",
-               "AC-3",
-               NULL
-       };
-       static const char *mpeg_audio_l1_bitrate[] = {
-               "32 kbps",
-               "64 kbps",
-               "96 kbps",
-               "128 kbps",
-               "160 kbps",
-               "192 kbps",
-               "224 kbps",
-               "256 kbps",
-               "288 kbps",
-               "320 kbps",
-               "352 kbps",
-               "384 kbps",
-               "416 kbps",
-               "448 kbps",
-               NULL
-       };
-       static const char *mpeg_audio_l2_bitrate[] = {
-               "32 kbps",
-               "48 kbps",
-               "56 kbps",
-               "64 kbps",
-               "80 kbps",
-               "96 kbps",
-               "112 kbps",
-               "128 kbps",
-               "160 kbps",
-               "192 kbps",
-               "224 kbps",
-               "256 kbps",
-               "320 kbps",
-               "384 kbps",
-               NULL
-       };
-       static const char *mpeg_audio_l3_bitrate[] = {
-               "32 kbps",
-               "40 kbps",
-               "48 kbps",
-               "56 kbps",
-               "64 kbps",
-               "80 kbps",
-               "96 kbps",
-               "112 kbps",
-               "128 kbps",
-               "160 kbps",
-               "192 kbps",
-               "224 kbps",
-               "256 kbps",
-               "320 kbps",
-               NULL
-       };
-       static const char *mpeg_audio_ac3_bitrate[] = {
-               "32 kbps",
-               "40 kbps",
-               "48 kbps",
-               "56 kbps",
-               "64 kbps",
-               "80 kbps",
-               "96 kbps",
-               "112 kbps",
-               "128 kbps",
-               "160 kbps",
-               "192 kbps",
-               "224 kbps",
-               "256 kbps",
-               "320 kbps",
-               "384 kbps",
-               "448 kbps",
-               "512 kbps",
-               "576 kbps",
-               "640 kbps",
-               NULL
-       };
-       static const char *mpeg_audio_mode[] = {
-               "Stereo",
-               "Joint Stereo",
-               "Dual",
-               "Mono",
-               NULL
-       };
-       static const char *mpeg_audio_mode_extension[] = {
-               "Bound 4",
-               "Bound 8",
-               "Bound 12",
-               "Bound 16",
-               NULL
-       };
-       static const char *mpeg_audio_emphasis[] = {
-               "No Emphasis",
-               "50/15 us",
-               "CCITT J17",
-               NULL
-       };
-       static const char *mpeg_audio_crc[] = {
-               "No CRC",
-               "16-bit CRC",
-               NULL
-       };
-       static const char *mpeg_video_encoding[] = {
-               "MPEG-1",
-               "MPEG-2",
-               "MPEG-4 AVC",
-               NULL
-       };
-       static const char *mpeg_video_aspect[] = {
-               "1x1",
-               "4x3",
-               "16x9",
-               "2.21x1",
-               NULL
-       };
-       static const char *mpeg_video_bitrate_mode[] = {
-               "Variable Bitrate",
-               "Constant Bitrate",
-               NULL
-       };
-       static const char *mpeg_stream_type[] = {
-               "MPEG-2 Program Stream",
-               "MPEG-2 Transport Stream",
-               "MPEG-1 System Stream",
-               "MPEG-2 DVD-compatible Stream",
-               "MPEG-1 VCD-compatible Stream",
-               "MPEG-2 SVCD-compatible Stream",
-               NULL
-       };
-       static const char *mpeg_stream_vbi_fmt[] = {
-               "No VBI",
-               "Private packet, IVTV format",
-               NULL
-       };
-       static const char *camera_power_line_frequency[] = {
-               "Disabled",
-               "50 Hz",
-               "60 Hz",
-               NULL
-       };
-       static const char *camera_exposure_auto[] = {
-               "Auto Mode",
-               "Manual Mode",
-               "Shutter Priority Mode",
-               "Aperture Priority Mode",
-               NULL
-       };
-       static const char *colorfx[] = {
-               "None",
-               "Black & White",
-               "Sepia",
-               "Negative",
-               "Emboss",
-               "Sketch",
-               "Sky blue",
-               "Grass green",
-               "Skin whiten",
-               "Vivid",
-               NULL
-       };
-       static const char *tune_preemphasis[] = {
-               "No preemphasis",
-               "50 useconds",
-               "75 useconds",
-               NULL,
-       };
-
-       switch (id) {
-               case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
-                       return mpeg_audio_sampling_freq;
-               case V4L2_CID_MPEG_AUDIO_ENCODING:
-                       return mpeg_audio_encoding;
-               case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
-                       return mpeg_audio_l1_bitrate;
-               case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
-                       return mpeg_audio_l2_bitrate;
-               case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
-                       return mpeg_audio_l3_bitrate;
-               case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
-                       return mpeg_audio_ac3_bitrate;
-               case V4L2_CID_MPEG_AUDIO_MODE:
-                       return mpeg_audio_mode;
-               case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
-                       return mpeg_audio_mode_extension;
-               case V4L2_CID_MPEG_AUDIO_EMPHASIS:
-                       return mpeg_audio_emphasis;
-               case V4L2_CID_MPEG_AUDIO_CRC:
-                       return mpeg_audio_crc;
-               case V4L2_CID_MPEG_VIDEO_ENCODING:
-                       return mpeg_video_encoding;
-               case V4L2_CID_MPEG_VIDEO_ASPECT:
-                       return mpeg_video_aspect;
-               case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
-                       return mpeg_video_bitrate_mode;
-               case V4L2_CID_MPEG_STREAM_TYPE:
-                       return mpeg_stream_type;
-               case V4L2_CID_MPEG_STREAM_VBI_FMT:
-                       return mpeg_stream_vbi_fmt;
-               case V4L2_CID_POWER_LINE_FREQUENCY:
-                       return camera_power_line_frequency;
-               case V4L2_CID_EXPOSURE_AUTO:
-                       return camera_exposure_auto;
-               case V4L2_CID_COLORFX:
-                       return colorfx;
-               case V4L2_CID_TUNE_PREEMPHASIS:
-                       return tune_preemphasis;
-               default:
-                       return NULL;
-       }
-}
-EXPORT_SYMBOL(v4l2_ctrl_get_menu);
-
-/* Return the control name. */
-const char *v4l2_ctrl_get_name(u32 id)
-{
-       switch (id) {
-       /* USER controls */
-       case V4L2_CID_USER_CLASS:               return "User Controls";
-       case V4L2_CID_BRIGHTNESS:               return "Brightness";
-       case V4L2_CID_CONTRAST:                 return "Contrast";
-       case V4L2_CID_SATURATION:               return "Saturation";
-       case V4L2_CID_HUE:                      return "Hue";
-       case V4L2_CID_AUDIO_VOLUME:             return "Volume";
-       case V4L2_CID_AUDIO_BALANCE:            return "Balance";
-       case V4L2_CID_AUDIO_BASS:               return "Bass";
-       case V4L2_CID_AUDIO_TREBLE:             return "Treble";
-       case V4L2_CID_AUDIO_MUTE:               return "Mute";
-       case V4L2_CID_AUDIO_LOUDNESS:           return "Loudness";
-       case V4L2_CID_BLACK_LEVEL:              return "Black Level";
-       case V4L2_CID_AUTO_WHITE_BALANCE:       return "White Balance, Automatic";
-       case V4L2_CID_DO_WHITE_BALANCE:         return "Do White Balance";
-       case V4L2_CID_RED_BALANCE:              return "Red Balance";
-       case V4L2_CID_BLUE_BALANCE:             return "Blue Balance";
-       case V4L2_CID_GAMMA:                    return "Gamma";
-       case V4L2_CID_EXPOSURE:                 return "Exposure";
-       case V4L2_CID_AUTOGAIN:                 return "Gain, Automatic";
-       case V4L2_CID_GAIN:                     return "Gain";
-       case V4L2_CID_HFLIP:                    return "Horizontal Flip";
-       case V4L2_CID_VFLIP:                    return "Vertical Flip";
-       case V4L2_CID_HCENTER:                  return "Horizontal Center";
-       case V4L2_CID_VCENTER:                  return "Vertical Center";
-       case V4L2_CID_POWER_LINE_FREQUENCY:     return "Power Line Frequency";
-       case V4L2_CID_HUE_AUTO:                 return "Hue, Automatic";
-       case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
-       case V4L2_CID_SHARPNESS:                return "Sharpness";
-       case V4L2_CID_BACKLIGHT_COMPENSATION:   return "Backlight Compensation";
-       case V4L2_CID_CHROMA_AGC:               return "Chroma AGC";
-       case V4L2_CID_CHROMA_GAIN:              return "Chroma Gain";
-       case V4L2_CID_COLOR_KILLER:             return "Color Killer";
-       case V4L2_CID_COLORFX:                  return "Color Effects";
-       case V4L2_CID_AUTOBRIGHTNESS:           return "Brightness, Automatic";
-       case V4L2_CID_BAND_STOP_FILTER:         return "Band-Stop Filter";
-       case V4L2_CID_ROTATE:                   return "Rotate";
-       case V4L2_CID_BG_COLOR:                 return "Background Color";
-
-       /* MPEG controls */
-       case V4L2_CID_MPEG_CLASS:               return "MPEG Encoder Controls";
-       case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency";
-       case V4L2_CID_MPEG_AUDIO_ENCODING:      return "Audio Encoding";
-       case V4L2_CID_MPEG_AUDIO_L1_BITRATE:    return "Audio Layer I Bitrate";
-       case V4L2_CID_MPEG_AUDIO_L2_BITRATE:    return "Audio Layer II Bitrate";
-       case V4L2_CID_MPEG_AUDIO_L3_BITRATE:    return "Audio Layer III Bitrate";
-       case V4L2_CID_MPEG_AUDIO_AAC_BITRATE:   return "Audio AAC Bitrate";
-       case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:   return "Audio AC-3 Bitrate";
-       case V4L2_CID_MPEG_AUDIO_MODE:          return "Audio Stereo Mode";
-       case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension";
-       case V4L2_CID_MPEG_AUDIO_EMPHASIS:      return "Audio Emphasis";
-       case V4L2_CID_MPEG_AUDIO_CRC:           return "Audio CRC";
-       case V4L2_CID_MPEG_AUDIO_MUTE:          return "Audio Mute";
-       case V4L2_CID_MPEG_VIDEO_ENCODING:      return "Video Encoding";
-       case V4L2_CID_MPEG_VIDEO_ASPECT:        return "Video Aspect";
-       case V4L2_CID_MPEG_VIDEO_B_FRAMES:      return "Video B Frames";
-       case V4L2_CID_MPEG_VIDEO_GOP_SIZE:      return "Video GOP Size";
-       case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:   return "Video GOP Closure";
-       case V4L2_CID_MPEG_VIDEO_PULLDOWN:      return "Video Pulldown";
-       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:  return "Video Bitrate Mode";
-       case V4L2_CID_MPEG_VIDEO_BITRATE:       return "Video Bitrate";
-       case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:  return "Video Peak Bitrate";
-       case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
-       case V4L2_CID_MPEG_VIDEO_MUTE:          return "Video Mute";
-       case V4L2_CID_MPEG_VIDEO_MUTE_YUV:      return "Video Mute YUV";
-       case V4L2_CID_MPEG_STREAM_TYPE:         return "Stream Type";
-       case V4L2_CID_MPEG_STREAM_PID_PMT:      return "Stream PMT Program ID";
-       case V4L2_CID_MPEG_STREAM_PID_AUDIO:    return "Stream Audio Program ID";
-       case V4L2_CID_MPEG_STREAM_PID_VIDEO:    return "Stream Video Program ID";
-       case V4L2_CID_MPEG_STREAM_PID_PCR:      return "Stream PCR Program ID";
-       case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
-       case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
-       case V4L2_CID_MPEG_STREAM_VBI_FMT:      return "Stream VBI Format";
-
-       /* CAMERA controls */
-       case V4L2_CID_CAMERA_CLASS:             return "Camera Controls";
-       case V4L2_CID_EXPOSURE_AUTO:            return "Auto Exposure";
-       case V4L2_CID_EXPOSURE_ABSOLUTE:        return "Exposure Time, Absolute";
-       case V4L2_CID_EXPOSURE_AUTO_PRIORITY:   return "Exposure, Dynamic Framerate";
-       case V4L2_CID_PAN_RELATIVE:             return "Pan, Relative";
-       case V4L2_CID_TILT_RELATIVE:            return "Tilt, Relative";
-       case V4L2_CID_PAN_RESET:                return "Pan, Reset";
-       case V4L2_CID_TILT_RESET:               return "Tilt, Reset";
-       case V4L2_CID_PAN_ABSOLUTE:             return "Pan, Absolute";
-       case V4L2_CID_TILT_ABSOLUTE:            return "Tilt, Absolute";
-       case V4L2_CID_FOCUS_ABSOLUTE:           return "Focus, Absolute";
-       case V4L2_CID_FOCUS_RELATIVE:           return "Focus, Relative";
-       case V4L2_CID_FOCUS_AUTO:               return "Focus, Automatic";
-       case V4L2_CID_IRIS_ABSOLUTE:            return "Iris, Absolute";
-       case V4L2_CID_IRIS_RELATIVE:            return "Iris, Relative";
-       case V4L2_CID_ZOOM_ABSOLUTE:            return "Zoom, Absolute";
-       case V4L2_CID_ZOOM_RELATIVE:            return "Zoom, Relative";
-       case V4L2_CID_ZOOM_CONTINUOUS:          return "Zoom, Continuous";
-       case V4L2_CID_PRIVACY:                  return "Privacy";
-
-       /* FM Radio Modulator control */
-       case V4L2_CID_FM_TX_CLASS:              return "FM Radio Modulator Controls";
-       case V4L2_CID_RDS_TX_DEVIATION:         return "RDS Signal Deviation";
-       case V4L2_CID_RDS_TX_PI:                return "RDS Program ID";
-       case V4L2_CID_RDS_TX_PTY:               return "RDS Program Type";
-       case V4L2_CID_RDS_TX_PS_NAME:           return "RDS PS Name";
-       case V4L2_CID_RDS_TX_RADIO_TEXT:        return "RDS Radio Text";
-       case V4L2_CID_AUDIO_LIMITER_ENABLED:    return "Audio Limiter Feature Enabled";
-       case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
-       case V4L2_CID_AUDIO_LIMITER_DEVIATION:  return "Audio Limiter Deviation";
-       case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Feature Enabled";
-       case V4L2_CID_AUDIO_COMPRESSION_GAIN:   return "Audio Compression Gain";
-       case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold";
-       case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time";
-       case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time";
-       case V4L2_CID_PILOT_TONE_ENABLED:       return "Pilot Tone Feature Enabled";
-       case V4L2_CID_PILOT_TONE_DEVIATION:     return "Pilot Tone Deviation";
-       case V4L2_CID_PILOT_TONE_FREQUENCY:     return "Pilot Tone Frequency";
-       case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-emphasis settings";
-       case V4L2_CID_TUNE_POWER_LEVEL:         return "Tune Power Level";
-       case V4L2_CID_TUNE_ANTENNA_CAPACITOR:   return "Tune Antenna Capacitor";
-
-       default:
-               return NULL;
-       }
-}
-EXPORT_SYMBOL(v4l2_ctrl_get_name);
-
 /* Fill in a struct v4l2_queryctrl */
 int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def)
 {
-       const char *name = v4l2_ctrl_get_name(qctrl->id);
+       const char *name;
+
+       v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type,
+                      &min, &max, &step, &def, &qctrl->flags);
 
-       qctrl->flags = 0;
        if (name == NULL)
                return -EINVAL;
 
-       switch (qctrl->id) {
-       case V4L2_CID_AUDIO_MUTE:
-       case V4L2_CID_AUDIO_LOUDNESS:
-       case V4L2_CID_AUTO_WHITE_BALANCE:
-       case V4L2_CID_AUTOGAIN:
-       case V4L2_CID_HFLIP:
-       case V4L2_CID_VFLIP:
-       case V4L2_CID_HUE_AUTO:
-       case V4L2_CID_CHROMA_AGC:
-       case V4L2_CID_COLOR_KILLER:
-       case V4L2_CID_MPEG_AUDIO_MUTE:
-       case V4L2_CID_MPEG_VIDEO_MUTE:
-       case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
-       case V4L2_CID_MPEG_VIDEO_PULLDOWN:
-       case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
-       case V4L2_CID_FOCUS_AUTO:
-       case V4L2_CID_PRIVACY:
-       case V4L2_CID_AUDIO_LIMITER_ENABLED:
-       case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
-       case V4L2_CID_PILOT_TONE_ENABLED:
-               qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
-               min = 0;
-               max = step = 1;
-               break;
-       case V4L2_CID_PAN_RESET:
-       case V4L2_CID_TILT_RESET:
-               qctrl->type = V4L2_CTRL_TYPE_BUTTON;
-               qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
-               min = max = step = def = 0;
-               break;
-       case V4L2_CID_POWER_LINE_FREQUENCY:
-       case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
-       case V4L2_CID_MPEG_AUDIO_ENCODING:
-       case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
-       case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
-       case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
-       case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
-       case V4L2_CID_MPEG_AUDIO_MODE:
-       case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
-       case V4L2_CID_MPEG_AUDIO_EMPHASIS:
-       case V4L2_CID_MPEG_AUDIO_CRC:
-       case V4L2_CID_MPEG_VIDEO_ENCODING:
-       case V4L2_CID_MPEG_VIDEO_ASPECT:
-       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
-       case V4L2_CID_MPEG_STREAM_TYPE:
-       case V4L2_CID_MPEG_STREAM_VBI_FMT:
-       case V4L2_CID_EXPOSURE_AUTO:
-       case V4L2_CID_COLORFX:
-       case V4L2_CID_TUNE_PREEMPHASIS:
-               qctrl->type = V4L2_CTRL_TYPE_MENU;
-               step = 1;
-               break;
-       case V4L2_CID_RDS_TX_PS_NAME:
-       case V4L2_CID_RDS_TX_RADIO_TEXT:
-               qctrl->type = V4L2_CTRL_TYPE_STRING;
-               break;
-       case V4L2_CID_USER_CLASS:
-       case V4L2_CID_CAMERA_CLASS:
-       case V4L2_CID_MPEG_CLASS:
-       case V4L2_CID_FM_TX_CLASS:
-               qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS;
-               qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
-               min = max = step = def = 0;
-               break;
-       case V4L2_CID_BG_COLOR:
-               qctrl->type = V4L2_CTRL_TYPE_INTEGER;
-               step = 1;
-               min = 0;
-               /* Max is calculated as RGB888 that is 2^24 */
-               max = 0xFFFFFF;
-               break;
-       default:
-               qctrl->type = V4L2_CTRL_TYPE_INTEGER;
-               break;
-       }
-       switch (qctrl->id) {
-       case V4L2_CID_MPEG_AUDIO_ENCODING:
-       case V4L2_CID_MPEG_AUDIO_MODE:
-       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
-       case V4L2_CID_MPEG_VIDEO_B_FRAMES:
-       case V4L2_CID_MPEG_STREAM_TYPE:
-               qctrl->flags |= V4L2_CTRL_FLAG_UPDATE;
-               break;
-       case V4L2_CID_AUDIO_VOLUME:
-       case V4L2_CID_AUDIO_BALANCE:
-       case V4L2_CID_AUDIO_BASS:
-       case V4L2_CID_AUDIO_TREBLE:
-       case V4L2_CID_BRIGHTNESS:
-       case V4L2_CID_CONTRAST:
-       case V4L2_CID_SATURATION:
-       case V4L2_CID_HUE:
-       case V4L2_CID_RED_BALANCE:
-       case V4L2_CID_BLUE_BALANCE:
-       case V4L2_CID_GAMMA:
-       case V4L2_CID_SHARPNESS:
-       case V4L2_CID_CHROMA_GAIN:
-       case V4L2_CID_RDS_TX_DEVIATION:
-       case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
-       case V4L2_CID_AUDIO_LIMITER_DEVIATION:
-       case V4L2_CID_AUDIO_COMPRESSION_GAIN:
-       case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
-       case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
-       case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
-       case V4L2_CID_PILOT_TONE_DEVIATION:
-       case V4L2_CID_PILOT_TONE_FREQUENCY:
-       case V4L2_CID_TUNE_POWER_LEVEL:
-       case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
-               qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
-               break;
-       case V4L2_CID_PAN_RELATIVE:
-       case V4L2_CID_TILT_RELATIVE:
-       case V4L2_CID_FOCUS_RELATIVE:
-       case V4L2_CID_IRIS_RELATIVE:
-       case V4L2_CID_ZOOM_RELATIVE:
-               qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
-               break;
-       }
        qctrl->minimum = min;
        qctrl->maximum = max;
        qctrl->step = step;
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
new file mode 100644 (file)
index 0000000..84c1a53
--- /dev/null
@@ -0,0 +1,1851 @@
+/*
+    V4L2 controls framework implementation.
+
+    Copyright (C) 2010  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/ctype.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+
+/* Internal temporary helper struct, one for each v4l2_ext_control */
+struct ctrl_helper {
+       /* The control corresponding to the v4l2_ext_control ID field. */
+       struct v4l2_ctrl *ctrl;
+       /* Used internally to mark whether this control was already
+          processed. */
+       bool handled;
+};
+
+/* Returns NULL or a character pointer array containing the menu for
+   the given control ID. The pointer array ends with a NULL pointer.
+   An empty string signifies a menu entry that is invalid. This allows
+   drivers to disable certain options if it is not supported. */
+const char **v4l2_ctrl_get_menu(u32 id)
+{
+       static const char *mpeg_audio_sampling_freq[] = {
+               "44.1 kHz",
+               "48 kHz",
+               "32 kHz",
+               NULL
+       };
+       static const char *mpeg_audio_encoding[] = {
+               "MPEG-1/2 Layer I",
+               "MPEG-1/2 Layer II",
+               "MPEG-1/2 Layer III",
+               "MPEG-2/4 AAC",
+               "AC-3",
+               NULL
+       };
+       static const char *mpeg_audio_l1_bitrate[] = {
+               "32 kbps",
+               "64 kbps",
+               "96 kbps",
+               "128 kbps",
+               "160 kbps",
+               "192 kbps",
+               "224 kbps",
+               "256 kbps",
+               "288 kbps",
+               "320 kbps",
+               "352 kbps",
+               "384 kbps",
+               "416 kbps",
+               "448 kbps",
+               NULL
+       };
+       static const char *mpeg_audio_l2_bitrate[] = {
+               "32 kbps",
+               "48 kbps",
+               "56 kbps",
+               "64 kbps",
+               "80 kbps",
+               "96 kbps",
+               "112 kbps",
+               "128 kbps",
+               "160 kbps",
+               "192 kbps",
+               "224 kbps",
+               "256 kbps",
+               "320 kbps",
+               "384 kbps",
+               NULL
+       };
+       static const char *mpeg_audio_l3_bitrate[] = {
+               "32 kbps",
+               "40 kbps",
+               "48 kbps",
+               "56 kbps",
+               "64 kbps",
+               "80 kbps",
+               "96 kbps",
+               "112 kbps",
+               "128 kbps",
+               "160 kbps",
+               "192 kbps",
+               "224 kbps",
+               "256 kbps",
+               "320 kbps",
+               NULL
+       };
+       static const char *mpeg_audio_ac3_bitrate[] = {
+               "32 kbps",
+               "40 kbps",
+               "48 kbps",
+               "56 kbps",
+               "64 kbps",
+               "80 kbps",
+               "96 kbps",
+               "112 kbps",
+               "128 kbps",
+               "160 kbps",
+               "192 kbps",
+               "224 kbps",
+               "256 kbps",
+               "320 kbps",
+               "384 kbps",
+               "448 kbps",
+               "512 kbps",
+               "576 kbps",
+               "640 kbps",
+               NULL
+       };
+       static const char *mpeg_audio_mode[] = {
+               "Stereo",
+               "Joint Stereo",
+               "Dual",
+               "Mono",
+               NULL
+       };
+       static const char *mpeg_audio_mode_extension[] = {
+               "Bound 4",
+               "Bound 8",
+               "Bound 12",
+               "Bound 16",
+               NULL
+       };
+       static const char *mpeg_audio_emphasis[] = {
+               "No Emphasis",
+               "50/15 us",
+               "CCITT J17",
+               NULL
+       };
+       static const char *mpeg_audio_crc[] = {
+               "No CRC",
+               "16-bit CRC",
+               NULL
+       };
+       static const char *mpeg_video_encoding[] = {
+               "MPEG-1",
+               "MPEG-2",
+               "MPEG-4 AVC",
+               NULL
+       };
+       static const char *mpeg_video_aspect[] = {
+               "1x1",
+               "4x3",
+               "16x9",
+               "2.21x1",
+               NULL
+       };
+       static const char *mpeg_video_bitrate_mode[] = {
+               "Variable Bitrate",
+               "Constant Bitrate",
+               NULL
+       };
+       static const char *mpeg_stream_type[] = {
+               "MPEG-2 Program Stream",
+               "MPEG-2 Transport Stream",
+               "MPEG-1 System Stream",
+               "MPEG-2 DVD-compatible Stream",
+               "MPEG-1 VCD-compatible Stream",
+               "MPEG-2 SVCD-compatible Stream",
+               NULL
+       };
+       static const char *mpeg_stream_vbi_fmt[] = {
+               "No VBI",
+               "Private packet, IVTV format",
+               NULL
+       };
+       static const char *camera_power_line_frequency[] = {
+               "Disabled",
+               "50 Hz",
+               "60 Hz",
+               NULL
+       };
+       static const char *camera_exposure_auto[] = {
+               "Auto Mode",
+               "Manual Mode",
+               "Shutter Priority Mode",
+               "Aperture Priority Mode",
+               NULL
+       };
+       static const char *colorfx[] = {
+               "None",
+               "Black & White",
+               "Sepia",
+               "Negative",
+               "Emboss",
+               "Sketch",
+               "Sky blue",
+               "Grass green",
+               "Skin whiten",
+               "Vivid",
+               NULL
+       };
+       static const char *tune_preemphasis[] = {
+               "No preemphasis",
+               "50 useconds",
+               "75 useconds",
+               NULL,
+       };
+
+       switch (id) {
+       case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+               return mpeg_audio_sampling_freq;
+       case V4L2_CID_MPEG_AUDIO_ENCODING:
+               return mpeg_audio_encoding;
+       case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+               return mpeg_audio_l1_bitrate;
+       case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+               return mpeg_audio_l2_bitrate;
+       case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+               return mpeg_audio_l3_bitrate;
+       case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+               return mpeg_audio_ac3_bitrate;
+       case V4L2_CID_MPEG_AUDIO_MODE:
+               return mpeg_audio_mode;
+       case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+               return mpeg_audio_mode_extension;
+       case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+               return mpeg_audio_emphasis;
+       case V4L2_CID_MPEG_AUDIO_CRC:
+               return mpeg_audio_crc;
+       case V4L2_CID_MPEG_VIDEO_ENCODING:
+               return mpeg_video_encoding;
+       case V4L2_CID_MPEG_VIDEO_ASPECT:
+               return mpeg_video_aspect;
+       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+               return mpeg_video_bitrate_mode;
+       case V4L2_CID_MPEG_STREAM_TYPE:
+               return mpeg_stream_type;
+       case V4L2_CID_MPEG_STREAM_VBI_FMT:
+               return mpeg_stream_vbi_fmt;
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               return camera_power_line_frequency;
+       case V4L2_CID_EXPOSURE_AUTO:
+               return camera_exposure_auto;
+       case V4L2_CID_COLORFX:
+               return colorfx;
+       case V4L2_CID_TUNE_PREEMPHASIS:
+               return tune_preemphasis;
+       default:
+               return NULL;
+       }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_menu);
+
+/* Return the control name. */
+const char *v4l2_ctrl_get_name(u32 id)
+{
+       switch (id) {
+       /* USER controls */
+       /* Keep the order of the 'case's the same as in videodev2.h! */
+       case V4L2_CID_USER_CLASS:               return "User Controls";
+       case V4L2_CID_BRIGHTNESS:               return "Brightness";
+       case V4L2_CID_CONTRAST:                 return "Contrast";
+       case V4L2_CID_SATURATION:               return "Saturation";
+       case V4L2_CID_HUE:                      return "Hue";
+       case V4L2_CID_AUDIO_VOLUME:             return "Volume";
+       case V4L2_CID_AUDIO_BALANCE:            return "Balance";
+       case V4L2_CID_AUDIO_BASS:               return "Bass";
+       case V4L2_CID_AUDIO_TREBLE:             return "Treble";
+       case V4L2_CID_AUDIO_MUTE:               return "Mute";
+       case V4L2_CID_AUDIO_LOUDNESS:           return "Loudness";
+       case V4L2_CID_BLACK_LEVEL:              return "Black Level";
+       case V4L2_CID_AUTO_WHITE_BALANCE:       return "White Balance, Automatic";
+       case V4L2_CID_DO_WHITE_BALANCE:         return "Do White Balance";
+       case V4L2_CID_RED_BALANCE:              return "Red Balance";
+       case V4L2_CID_BLUE_BALANCE:             return "Blue Balance";
+       case V4L2_CID_GAMMA:                    return "Gamma";
+       case V4L2_CID_EXPOSURE:                 return "Exposure";
+       case V4L2_CID_AUTOGAIN:                 return "Gain, Automatic";
+       case V4L2_CID_GAIN:                     return "Gain";
+       case V4L2_CID_HFLIP:                    return "Horizontal Flip";
+       case V4L2_CID_VFLIP:                    return "Vertical Flip";
+       case V4L2_CID_HCENTER:                  return "Horizontal Center";
+       case V4L2_CID_VCENTER:                  return "Vertical Center";
+       case V4L2_CID_POWER_LINE_FREQUENCY:     return "Power Line Frequency";
+       case V4L2_CID_HUE_AUTO:                 return "Hue, Automatic";
+       case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
+       case V4L2_CID_SHARPNESS:                return "Sharpness";
+       case V4L2_CID_BACKLIGHT_COMPENSATION:   return "Backlight Compensation";
+       case V4L2_CID_CHROMA_AGC:               return "Chroma AGC";
+       case V4L2_CID_COLOR_KILLER:             return "Color Killer";
+       case V4L2_CID_COLORFX:                  return "Color Effects";
+       case V4L2_CID_AUTOBRIGHTNESS:           return "Brightness, Automatic";
+       case V4L2_CID_BAND_STOP_FILTER:         return "Band-Stop Filter";
+       case V4L2_CID_ROTATE:                   return "Rotate";
+       case V4L2_CID_BG_COLOR:                 return "Background Color";
+       case V4L2_CID_CHROMA_GAIN:              return "Chroma Gain";
+
+       /* MPEG controls */
+       /* Keep the order of the 'case's the same as in videodev2.h! */
+       case V4L2_CID_MPEG_CLASS:               return "MPEG Encoder Controls";
+       case V4L2_CID_MPEG_STREAM_TYPE:         return "Stream Type";
+       case V4L2_CID_MPEG_STREAM_PID_PMT:      return "Stream PMT Program ID";
+       case V4L2_CID_MPEG_STREAM_PID_AUDIO:    return "Stream Audio Program ID";
+       case V4L2_CID_MPEG_STREAM_PID_VIDEO:    return "Stream Video Program ID";
+       case V4L2_CID_MPEG_STREAM_PID_PCR:      return "Stream PCR Program ID";
+       case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
+       case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
+       case V4L2_CID_MPEG_STREAM_VBI_FMT:      return "Stream VBI Format";
+       case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency";
+       case V4L2_CID_MPEG_AUDIO_ENCODING:      return "Audio Encoding";
+       case V4L2_CID_MPEG_AUDIO_L1_BITRATE:    return "Audio Layer I Bitrate";
+       case V4L2_CID_MPEG_AUDIO_L2_BITRATE:    return "Audio Layer II Bitrate";
+       case V4L2_CID_MPEG_AUDIO_L3_BITRATE:    return "Audio Layer III Bitrate";
+       case V4L2_CID_MPEG_AUDIO_MODE:          return "Audio Stereo Mode";
+       case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension";
+       case V4L2_CID_MPEG_AUDIO_EMPHASIS:      return "Audio Emphasis";
+       case V4L2_CID_MPEG_AUDIO_CRC:           return "Audio CRC";
+       case V4L2_CID_MPEG_AUDIO_MUTE:          return "Audio Mute";
+       case V4L2_CID_MPEG_AUDIO_AAC_BITRATE:   return "Audio AAC Bitrate";
+       case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:   return "Audio AC-3 Bitrate";
+       case V4L2_CID_MPEG_VIDEO_ENCODING:      return "Video Encoding";
+       case V4L2_CID_MPEG_VIDEO_ASPECT:        return "Video Aspect";
+       case V4L2_CID_MPEG_VIDEO_B_FRAMES:      return "Video B Frames";
+       case V4L2_CID_MPEG_VIDEO_GOP_SIZE:      return "Video GOP Size";
+       case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:   return "Video GOP Closure";
+       case V4L2_CID_MPEG_VIDEO_PULLDOWN:      return "Video Pulldown";
+       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:  return "Video Bitrate Mode";
+       case V4L2_CID_MPEG_VIDEO_BITRATE:       return "Video Bitrate";
+       case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:  return "Video Peak Bitrate";
+       case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
+       case V4L2_CID_MPEG_VIDEO_MUTE:          return "Video Mute";
+       case V4L2_CID_MPEG_VIDEO_MUTE_YUV:      return "Video Mute YUV";
+
+       /* CAMERA controls */
+       /* Keep the order of the 'case's the same as in videodev2.h! */
+       case V4L2_CID_CAMERA_CLASS:             return "Camera Controls";
+       case V4L2_CID_EXPOSURE_AUTO:            return "Auto Exposure";
+       case V4L2_CID_EXPOSURE_ABSOLUTE:        return "Exposure Time, Absolute";
+       case V4L2_CID_EXPOSURE_AUTO_PRIORITY:   return "Exposure, Dynamic Framerate";
+       case V4L2_CID_PAN_RELATIVE:             return "Pan, Relative";
+       case V4L2_CID_TILT_RELATIVE:            return "Tilt, Relative";
+       case V4L2_CID_PAN_RESET:                return "Pan, Reset";
+       case V4L2_CID_TILT_RESET:               return "Tilt, Reset";
+       case V4L2_CID_PAN_ABSOLUTE:             return "Pan, Absolute";
+       case V4L2_CID_TILT_ABSOLUTE:            return "Tilt, Absolute";
+       case V4L2_CID_FOCUS_ABSOLUTE:           return "Focus, Absolute";
+       case V4L2_CID_FOCUS_RELATIVE:           return "Focus, Relative";
+       case V4L2_CID_FOCUS_AUTO:               return "Focus, Automatic";
+       case V4L2_CID_ZOOM_ABSOLUTE:            return "Zoom, Absolute";
+       case V4L2_CID_ZOOM_RELATIVE:            return "Zoom, Relative";
+       case V4L2_CID_ZOOM_CONTINUOUS:          return "Zoom, Continuous";
+       case V4L2_CID_PRIVACY:                  return "Privacy";
+       case V4L2_CID_IRIS_ABSOLUTE:            return "Iris, Absolute";
+       case V4L2_CID_IRIS_RELATIVE:            return "Iris, Relative";
+
+       /* FM Radio Modulator control */
+       /* Keep the order of the 'case's the same as in videodev2.h! */
+       case V4L2_CID_FM_TX_CLASS:              return "FM Radio Modulator Controls";
+       case V4L2_CID_RDS_TX_DEVIATION:         return "RDS Signal Deviation";
+       case V4L2_CID_RDS_TX_PI:                return "RDS Program ID";
+       case V4L2_CID_RDS_TX_PTY:               return "RDS Program Type";
+       case V4L2_CID_RDS_TX_PS_NAME:           return "RDS PS Name";
+       case V4L2_CID_RDS_TX_RADIO_TEXT:        return "RDS Radio Text";
+       case V4L2_CID_AUDIO_LIMITER_ENABLED:    return "Audio Limiter Feature Enabled";
+       case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
+       case V4L2_CID_AUDIO_LIMITER_DEVIATION:  return "Audio Limiter Deviation";
+       case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Feature Enabled";
+       case V4L2_CID_AUDIO_COMPRESSION_GAIN:   return "Audio Compression Gain";
+       case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold";
+       case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time";
+       case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time";
+       case V4L2_CID_PILOT_TONE_ENABLED:       return "Pilot Tone Feature Enabled";
+       case V4L2_CID_PILOT_TONE_DEVIATION:     return "Pilot Tone Deviation";
+       case V4L2_CID_PILOT_TONE_FREQUENCY:     return "Pilot Tone Frequency";
+       case V4L2_CID_TUNE_PREEMPHASIS:         return "Pre-emphasis settings";
+       case V4L2_CID_TUNE_POWER_LEVEL:         return "Tune Power Level";
+       case V4L2_CID_TUNE_ANTENNA_CAPACITOR:   return "Tune Antenna Capacitor";
+
+       default:
+               return NULL;
+       }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_name);
+
+void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
+                   s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
+{
+       *name = v4l2_ctrl_get_name(id);
+       *flags = 0;
+
+       switch (id) {
+       case V4L2_CID_AUDIO_MUTE:
+       case V4L2_CID_AUDIO_LOUDNESS:
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+       case V4L2_CID_AUTOGAIN:
+       case V4L2_CID_HFLIP:
+       case V4L2_CID_VFLIP:
+       case V4L2_CID_HUE_AUTO:
+       case V4L2_CID_CHROMA_AGC:
+       case V4L2_CID_COLOR_KILLER:
+       case V4L2_CID_MPEG_AUDIO_MUTE:
+       case V4L2_CID_MPEG_VIDEO_MUTE:
+       case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+       case V4L2_CID_MPEG_VIDEO_PULLDOWN:
+       case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
+       case V4L2_CID_FOCUS_AUTO:
+       case V4L2_CID_PRIVACY:
+       case V4L2_CID_AUDIO_LIMITER_ENABLED:
+       case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
+       case V4L2_CID_PILOT_TONE_ENABLED:
+               *type = V4L2_CTRL_TYPE_BOOLEAN;
+               *min = 0;
+               *max = *step = 1;
+               break;
+       case V4L2_CID_PAN_RESET:
+       case V4L2_CID_TILT_RESET:
+               *type = V4L2_CTRL_TYPE_BUTTON;
+               *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+               *min = *max = *step = *def = 0;
+               break;
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+       case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+       case V4L2_CID_MPEG_AUDIO_ENCODING:
+       case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+       case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+       case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+       case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+       case V4L2_CID_MPEG_AUDIO_MODE:
+       case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+       case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+       case V4L2_CID_MPEG_AUDIO_CRC:
+       case V4L2_CID_MPEG_VIDEO_ENCODING:
+       case V4L2_CID_MPEG_VIDEO_ASPECT:
+       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+       case V4L2_CID_MPEG_STREAM_TYPE:
+       case V4L2_CID_MPEG_STREAM_VBI_FMT:
+       case V4L2_CID_EXPOSURE_AUTO:
+       case V4L2_CID_COLORFX:
+       case V4L2_CID_TUNE_PREEMPHASIS:
+               *type = V4L2_CTRL_TYPE_MENU;
+               break;
+       case V4L2_CID_RDS_TX_PS_NAME:
+       case V4L2_CID_RDS_TX_RADIO_TEXT:
+               *type = V4L2_CTRL_TYPE_STRING;
+               break;
+       case V4L2_CID_USER_CLASS:
+       case V4L2_CID_CAMERA_CLASS:
+       case V4L2_CID_MPEG_CLASS:
+       case V4L2_CID_FM_TX_CLASS:
+               *type = V4L2_CTRL_TYPE_CTRL_CLASS;
+               /* You can neither read not write these */
+               *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
+               *min = *max = *step = *def = 0;
+               break;
+       case V4L2_CID_BG_COLOR:
+               *type = V4L2_CTRL_TYPE_INTEGER;
+               *step = 1;
+               *min = 0;
+               /* Max is calculated as RGB888 that is 2^24 */
+               *max = 0xFFFFFF;
+               break;
+       default:
+               *type = V4L2_CTRL_TYPE_INTEGER;
+               break;
+       }
+       switch (id) {
+       case V4L2_CID_MPEG_AUDIO_ENCODING:
+       case V4L2_CID_MPEG_AUDIO_MODE:
+       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+       case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+       case V4L2_CID_MPEG_STREAM_TYPE:
+               *flags |= V4L2_CTRL_FLAG_UPDATE;
+               break;
+       case V4L2_CID_AUDIO_VOLUME:
+       case V4L2_CID_AUDIO_BALANCE:
+       case V4L2_CID_AUDIO_BASS:
+       case V4L2_CID_AUDIO_TREBLE:
+       case V4L2_CID_BRIGHTNESS:
+       case V4L2_CID_CONTRAST:
+       case V4L2_CID_SATURATION:
+       case V4L2_CID_HUE:
+       case V4L2_CID_RED_BALANCE:
+       case V4L2_CID_BLUE_BALANCE:
+       case V4L2_CID_GAMMA:
+       case V4L2_CID_SHARPNESS:
+       case V4L2_CID_CHROMA_GAIN:
+       case V4L2_CID_RDS_TX_DEVIATION:
+       case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
+       case V4L2_CID_AUDIO_LIMITER_DEVIATION:
+       case V4L2_CID_AUDIO_COMPRESSION_GAIN:
+       case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
+       case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
+       case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
+       case V4L2_CID_PILOT_TONE_DEVIATION:
+       case V4L2_CID_PILOT_TONE_FREQUENCY:
+       case V4L2_CID_TUNE_POWER_LEVEL:
+       case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
+               *flags |= V4L2_CTRL_FLAG_SLIDER;
+               break;
+       case V4L2_CID_PAN_RELATIVE:
+       case V4L2_CID_TILT_RELATIVE:
+       case V4L2_CID_FOCUS_RELATIVE:
+       case V4L2_CID_IRIS_RELATIVE:
+       case V4L2_CID_ZOOM_RELATIVE:
+               *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+               break;
+       }
+}
+EXPORT_SYMBOL(v4l2_ctrl_fill);
+
+/* Helper function to determine whether the control type is compatible with
+   VIDIOC_G/S_CTRL. */
+static bool type_is_int(const struct v4l2_ctrl *ctrl)
+{
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_INTEGER64:
+       case V4L2_CTRL_TYPE_STRING:
+               /* Nope, these need v4l2_ext_control */
+               return false;
+       default:
+               return true;
+       }
+}
+
+/* Helper function: copy the current control value back to the caller */
+static int cur_to_user(struct v4l2_ext_control *c,
+                      struct v4l2_ctrl *ctrl)
+{
+       u32 len;
+
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_STRING:
+               len = strlen(ctrl->cur.string);
+               if (c->size < len + 1) {
+                       c->size = len + 1;
+                       return -ENOSPC;
+               }
+               return copy_to_user(c->string, ctrl->cur.string,
+                                               len + 1) ? -EFAULT : 0;
+       case V4L2_CTRL_TYPE_INTEGER64:
+               c->value64 = ctrl->cur.val64;
+               break;
+       default:
+               c->value = ctrl->cur.val;
+               break;
+       }
+       return 0;
+}
+
+/* Helper function: copy the caller-provider value as the new control value */
+static int user_to_new(struct v4l2_ext_control *c,
+                      struct v4l2_ctrl *ctrl)
+{
+       int ret;
+       u32 size;
+
+       ctrl->has_new = 1;
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_INTEGER64:
+               ctrl->val64 = c->value64;
+               break;
+       case V4L2_CTRL_TYPE_STRING:
+               size = c->size;
+               if (size == 0)
+                       return -ERANGE;
+               if (size > ctrl->maximum + 1)
+                       size = ctrl->maximum + 1;
+               ret = copy_from_user(ctrl->string, c->string, size);
+               if (!ret) {
+                       char last = ctrl->string[size - 1];
+
+                       ctrl->string[size - 1] = 0;
+                       /* If the string was longer than ctrl->maximum,
+                          then return an error. */
+                       if (strlen(ctrl->string) == ctrl->maximum && last)
+                               return -ERANGE;
+               }
+               return ret ? -EFAULT : 0;
+       default:
+               ctrl->val = c->value;
+               break;
+       }
+       return 0;
+}
+
+/* Helper function: copy the new control value back to the caller */
+static int new_to_user(struct v4l2_ext_control *c,
+                      struct v4l2_ctrl *ctrl)
+{
+       u32 len;
+
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_STRING:
+               len = strlen(ctrl->string);
+               if (c->size < len + 1) {
+                       c->size = ctrl->maximum + 1;
+                       return -ENOSPC;
+               }
+               return copy_to_user(c->string, ctrl->string,
+                                               len + 1) ? -EFAULT : 0;
+       case V4L2_CTRL_TYPE_INTEGER64:
+               c->value64 = ctrl->val64;
+               break;
+       default:
+               c->value = ctrl->val;
+               break;
+       }
+       return 0;
+}
+
+/* Copy the new value to the current value. */
+static void new_to_cur(struct v4l2_ctrl *ctrl)
+{
+       if (ctrl == NULL)
+               return;
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_STRING:
+               /* strings are always 0-terminated */
+               strcpy(ctrl->cur.string, ctrl->string);
+               break;
+       case V4L2_CTRL_TYPE_INTEGER64:
+               ctrl->cur.val64 = ctrl->val64;
+               break;
+       default:
+               ctrl->cur.val = ctrl->val;
+               break;
+       }
+}
+
+/* Copy the current value to the new value */
+static void cur_to_new(struct v4l2_ctrl *ctrl)
+{
+       if (ctrl == NULL)
+               return;
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_STRING:
+               /* strings are always 0-terminated */
+               strcpy(ctrl->string, ctrl->cur.string);
+               break;
+       case V4L2_CTRL_TYPE_INTEGER64:
+               ctrl->val64 = ctrl->cur.val64;
+               break;
+       default:
+               ctrl->val = ctrl->cur.val;
+               break;
+       }
+}
+
+/* Return non-zero if one or more of the controls in the cluster has a new
+   value that differs from the current value. */
+static int cluster_changed(struct v4l2_ctrl *master)
+{
+       int diff = 0;
+       int i;
+
+       for (i = 0; !diff && i < master->ncontrols; i++) {
+               struct v4l2_ctrl *ctrl = master->cluster[i];
+
+               if (ctrl == NULL)
+                       continue;
+               switch (ctrl->type) {
+               case V4L2_CTRL_TYPE_BUTTON:
+                       /* Button controls are always 'different' */
+                       return 1;
+               case V4L2_CTRL_TYPE_STRING:
+                       /* strings are always 0-terminated */
+                       diff = strcmp(ctrl->string, ctrl->cur.string);
+                       break;
+               case V4L2_CTRL_TYPE_INTEGER64:
+                       diff = ctrl->val64 != ctrl->cur.val64;
+                       break;
+               default:
+                       diff = ctrl->val != ctrl->cur.val;
+                       break;
+               }
+       }
+       return diff;
+}
+
+/* Validate a new control */
+static int validate_new(struct v4l2_ctrl *ctrl)
+{
+       s32 val = ctrl->val;
+       char *s = ctrl->string;
+       u32 offset;
+       size_t len;
+
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_INTEGER:
+               /* Round towards the closest legal value */
+               val += ctrl->step / 2;
+               if (val < ctrl->minimum)
+                       val = ctrl->minimum;
+               if (val > ctrl->maximum)
+                       val = ctrl->maximum;
+               offset = val - ctrl->minimum;
+               offset = ctrl->step * (offset / ctrl->step);
+               val = ctrl->minimum + offset;
+               ctrl->val = val;
+               return 0;
+
+       case V4L2_CTRL_TYPE_BOOLEAN:
+               ctrl->val = !!ctrl->val;
+               return 0;
+
+       case V4L2_CTRL_TYPE_MENU:
+               if (val < ctrl->minimum || val > ctrl->maximum)
+                       return -ERANGE;
+               if (ctrl->qmenu[val][0] == '\0' ||
+                   (ctrl->menu_skip_mask & (1 << val)))
+                       return -EINVAL;
+               return 0;
+
+       case V4L2_CTRL_TYPE_BUTTON:
+       case V4L2_CTRL_TYPE_CTRL_CLASS:
+               ctrl->val64 = 0;
+               return 0;
+
+       case V4L2_CTRL_TYPE_INTEGER64:
+               return 0;
+
+       case V4L2_CTRL_TYPE_STRING:
+               len = strlen(s);
+               if (len < ctrl->minimum)
+                       return -ERANGE;
+               if ((len - ctrl->minimum) % ctrl->step)
+                       return -ERANGE;
+               return 0;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static inline u32 node2id(struct list_head *node)
+{
+       return list_entry(node, struct v4l2_ctrl_ref, node)->ctrl->id;
+}
+
+/* Set the handler's error code if it wasn't set earlier already */
+static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err)
+{
+       if (hdl->error == 0)
+               hdl->error = err;
+       return err;
+}
+
+/* Initialize the handler */
+int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
+                          unsigned nr_of_controls_hint)
+{
+       mutex_init(&hdl->lock);
+       INIT_LIST_HEAD(&hdl->ctrls);
+       INIT_LIST_HEAD(&hdl->ctrl_refs);
+       hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
+       hdl->buckets = kzalloc(sizeof(hdl->buckets[0]) * hdl->nr_of_buckets,
+                                                               GFP_KERNEL);
+       hdl->error = hdl->buckets ? 0 : -ENOMEM;
+       return hdl->error;
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_init);
+
+/* Free all controls and control refs */
+void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
+{
+       struct v4l2_ctrl_ref *ref, *next_ref;
+       struct v4l2_ctrl *ctrl, *next_ctrl;
+
+       if (hdl == NULL || hdl->buckets == NULL)
+               return;
+
+       mutex_lock(&hdl->lock);
+       /* Free all nodes */
+       list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
+               list_del(&ref->node);
+               kfree(ref);
+       }
+       /* Free all controls owned by the handler */
+       list_for_each_entry_safe(ctrl, next_ctrl, &hdl->ctrls, node) {
+               list_del(&ctrl->node);
+               kfree(ctrl);
+       }
+       kfree(hdl->buckets);
+       hdl->buckets = NULL;
+       hdl->cached = NULL;
+       hdl->error = 0;
+       mutex_unlock(&hdl->lock);
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_free);
+
+/* For backwards compatibility: V4L2_CID_PRIVATE_BASE should no longer
+   be used except in G_CTRL, S_CTRL, QUERYCTRL and QUERYMENU when dealing
+   with applications that do not use the NEXT_CTRL flag.
+
+   We just find the n-th private user control. It's O(N), but that should not
+   be an issue in this particular case. */
+static struct v4l2_ctrl_ref *find_private_ref(
+               struct v4l2_ctrl_handler *hdl, u32 id)
+{
+       struct v4l2_ctrl_ref *ref;
+
+       id -= V4L2_CID_PRIVATE_BASE;
+       list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+               /* Search for private user controls that are compatible with
+                  VIDIOC_G/S_CTRL. */
+               if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER &&
+                   V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) {
+                       if (!type_is_int(ref->ctrl))
+                               continue;
+                       if (id == 0)
+                               return ref;
+                       id--;
+               }
+       }
+       return NULL;
+}
+
+/* Find a control with the given ID. */
+static struct v4l2_ctrl_ref *find_ref(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+       struct v4l2_ctrl_ref *ref;
+       int bucket;
+
+       id &= V4L2_CTRL_ID_MASK;
+
+       /* Old-style private controls need special handling */
+       if (id >= V4L2_CID_PRIVATE_BASE)
+               return find_private_ref(hdl, id);
+       bucket = id % hdl->nr_of_buckets;
+
+       /* Simple optimization: cache the last control found */
+       if (hdl->cached && hdl->cached->ctrl->id == id)
+               return hdl->cached;
+
+       /* Not in cache, search the hash */
+       ref = hdl->buckets ? hdl->buckets[bucket] : NULL;
+       while (ref && ref->ctrl->id != id)
+               ref = ref->next;
+
+       if (ref)
+               hdl->cached = ref; /* cache it! */
+       return ref;
+}
+
+/* Find a control with the given ID. Take the handler's lock first. */
+static struct v4l2_ctrl_ref *find_ref_lock(
+               struct v4l2_ctrl_handler *hdl, u32 id)
+{
+       struct v4l2_ctrl_ref *ref = NULL;
+
+       if (hdl) {
+               mutex_lock(&hdl->lock);
+               ref = find_ref(hdl, id);
+               mutex_unlock(&hdl->lock);
+       }
+       return ref;
+}
+
+/* Find a control with the given ID. */
+struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+       struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
+
+       return ref ? ref->ctrl : NULL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_find);
+
+/* Allocate a new v4l2_ctrl_ref and hook it into the handler. */
+static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
+                          struct v4l2_ctrl *ctrl)
+{
+       struct v4l2_ctrl_ref *ref;
+       struct v4l2_ctrl_ref *new_ref;
+       u32 id = ctrl->id;
+       u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1;
+       int bucket = id % hdl->nr_of_buckets;   /* which bucket to use */
+
+       /* Automatically add the control class if it is not yet present. */
+       if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL)
+               if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0))
+                       return hdl->error;
+
+       if (hdl->error)
+               return hdl->error;
+
+       new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
+       if (!new_ref)
+               return handler_set_err(hdl, -ENOMEM);
+       new_ref->ctrl = ctrl;
+       if (ctrl->handler == hdl) {
+               /* By default each control starts in a cluster of its own.
+                  new_ref->ctrl is basically a cluster array with one
+                  element, so that's perfect to use as the cluster pointer.
+                  But only do this for the handler that owns the control. */
+               ctrl->cluster = &new_ref->ctrl;
+               ctrl->ncontrols = 1;
+       }
+
+       INIT_LIST_HEAD(&new_ref->node);
+
+       mutex_lock(&hdl->lock);
+
+       /* Add immediately at the end of the list if the list is empty, or if
+          the last element in the list has a lower ID.
+          This ensures that when elements are added in ascending order the
+          insertion is an O(1) operation. */
+       if (list_empty(&hdl->ctrl_refs) || id > node2id(hdl->ctrl_refs.prev)) {
+               list_add_tail(&new_ref->node, &hdl->ctrl_refs);
+               goto insert_in_hash;
+       }
+
+       /* Find insert position in sorted list */
+       list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+               if (ref->ctrl->id < id)
+                       continue;
+               /* Don't add duplicates */
+               if (ref->ctrl->id == id) {
+                       kfree(new_ref);
+                       goto unlock;
+               }
+               list_add(&new_ref->node, ref->node.prev);
+               break;
+       }
+
+insert_in_hash:
+       /* Insert the control node in the hash */
+       new_ref->next = hdl->buckets[bucket];
+       hdl->buckets[bucket] = new_ref;
+
+unlock:
+       mutex_unlock(&hdl->lock);
+       return 0;
+}
+
+/* Add a new control */
+static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
+                       const struct v4l2_ctrl_ops *ops,
+                       u32 id, const char *name, enum v4l2_ctrl_type type,
+                       s32 min, s32 max, u32 step, s32 def,
+                       u32 flags, const char **qmenu, void *priv)
+{
+       struct v4l2_ctrl *ctrl;
+       unsigned sz_extra = 0;
+
+       if (hdl->error)
+               return NULL;
+
+       /* Sanity checks */
+       if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
+           def < min || def > max || max < min ||
+           (type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
+           (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
+           (type == V4L2_CTRL_TYPE_STRING && max == 0)) {
+               handler_set_err(hdl, -ERANGE);
+               return NULL;
+       }
+
+       if (type == V4L2_CTRL_TYPE_BUTTON)
+               flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+       else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
+               flags |= V4L2_CTRL_FLAG_READ_ONLY;
+       else if (type == V4L2_CTRL_TYPE_STRING)
+               sz_extra += 2 * (max + 1);
+
+       ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
+       if (ctrl == NULL) {
+               handler_set_err(hdl, -ENOMEM);
+               return NULL;
+       }
+
+       INIT_LIST_HEAD(&ctrl->node);
+       ctrl->handler = hdl;
+       ctrl->ops = ops;
+       ctrl->id = id;
+       ctrl->name = name;
+       ctrl->type = type;
+       ctrl->flags = flags;
+       ctrl->minimum = min;
+       ctrl->maximum = max;
+       ctrl->step = step;
+       ctrl->qmenu = qmenu;
+       ctrl->priv = priv;
+       ctrl->cur.val = ctrl->val = ctrl->default_value = def;
+
+       if (ctrl->type == V4L2_CTRL_TYPE_STRING) {
+               ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1);
+               ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1);
+               if (ctrl->minimum)
+                       memset(ctrl->cur.string, ' ', ctrl->minimum);
+       }
+       if (handler_new_ref(hdl, ctrl)) {
+               kfree(ctrl);
+               return NULL;
+       }
+       mutex_lock(&hdl->lock);
+       list_add_tail(&ctrl->node, &hdl->ctrls);
+       mutex_unlock(&hdl->lock);
+       return ctrl;
+}
+
+struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+                       const struct v4l2_ctrl_config *cfg, void *priv)
+{
+       bool is_menu;
+       struct v4l2_ctrl *ctrl;
+       const char *name = cfg->name;
+       const char **qmenu = cfg->qmenu;
+       enum v4l2_ctrl_type type = cfg->type;
+       u32 flags = cfg->flags;
+       s32 min = cfg->min;
+       s32 max = cfg->max;
+       u32 step = cfg->step;
+       s32 def = cfg->def;
+
+       if (name == NULL)
+               v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step,
+                                                               &def, &flags);
+
+       is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU);
+       if (is_menu)
+               WARN_ON(step);
+       else
+               WARN_ON(cfg->menu_skip_mask);
+       if (is_menu && qmenu == NULL)
+               qmenu = v4l2_ctrl_get_menu(cfg->id);
+
+       ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name,
+                       type, min, max,
+                       is_menu ? cfg->menu_skip_mask : step,
+                       def, flags, qmenu, priv);
+       if (ctrl) {
+               ctrl->is_private = cfg->is_private;
+               ctrl->is_volatile = cfg->is_volatile;
+       }
+       return ctrl;
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_custom);
+
+/* Helper function for standard non-menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+                       const struct v4l2_ctrl_ops *ops,
+                       u32 id, s32 min, s32 max, u32 step, s32 def)
+{
+       const char *name;
+       enum v4l2_ctrl_type type;
+       u32 flags;
+
+       v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+       if (type == V4L2_CTRL_TYPE_MENU) {
+               handler_set_err(hdl, -EINVAL);
+               return NULL;
+       }
+       return v4l2_ctrl_new(hdl, ops, id, name, type,
+                                   min, max, step, def, flags, NULL, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_std);
+
+/* Helper function for standard menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
+                       const struct v4l2_ctrl_ops *ops,
+                       u32 id, s32 max, s32 mask, s32 def)
+{
+       const char **qmenu = v4l2_ctrl_get_menu(id);
+       const char *name;
+       enum v4l2_ctrl_type type;
+       s32 min;
+       s32 step;
+       u32 flags;
+
+       v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+       if (type != V4L2_CTRL_TYPE_MENU) {
+               handler_set_err(hdl, -EINVAL);
+               return NULL;
+       }
+       return v4l2_ctrl_new(hdl, ops, id, name, type,
+                                   0, max, mask, def, flags, qmenu, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
+
+/* Add a control from another handler to this handler */
+struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
+                                         struct v4l2_ctrl *ctrl)
+{
+       if (hdl == NULL || hdl->error)
+               return NULL;
+       if (ctrl == NULL) {
+               handler_set_err(hdl, -EINVAL);
+               return NULL;
+       }
+       if (ctrl->handler == hdl)
+               return ctrl;
+       return handler_new_ref(hdl, ctrl) ? NULL : ctrl;
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_ctrl);
+
+/* Add the controls from another handler to our own. */
+int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
+                         struct v4l2_ctrl_handler *add)
+{
+       struct v4l2_ctrl *ctrl;
+       int ret = 0;
+
+       /* Do nothing if either handler is NULL or if they are the same */
+       if (!hdl || !add || hdl == add)
+               return 0;
+       if (hdl->error)
+               return hdl->error;
+       mutex_lock(&add->lock);
+       list_for_each_entry(ctrl, &add->ctrls, node) {
+               /* Skip handler-private controls. */
+               if (ctrl->is_private)
+                       continue;
+               ret = handler_new_ref(hdl, ctrl);
+               if (ret)
+                       break;
+       }
+       mutex_unlock(&add->lock);
+       return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_handler);
+
+/* Cluster controls */
+void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
+{
+       int i;
+
+       /* The first control is the master control and it must not be NULL */
+       BUG_ON(controls[0] == NULL);
+
+       for (i = 0; i < ncontrols; i++) {
+               if (controls[i]) {
+                       controls[i]->cluster = controls;
+                       controls[i]->ncontrols = ncontrols;
+               }
+       }
+}
+EXPORT_SYMBOL(v4l2_ctrl_cluster);
+
+/* Activate/deactivate a control. */
+void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active)
+{
+       if (ctrl == NULL)
+               return;
+
+       if (!active)
+               /* set V4L2_CTRL_FLAG_INACTIVE */
+               set_bit(4, &ctrl->flags);
+       else
+               /* clear V4L2_CTRL_FLAG_INACTIVE */
+               clear_bit(4, &ctrl->flags);
+}
+EXPORT_SYMBOL(v4l2_ctrl_activate);
+
+/* Grab/ungrab a control.
+   Typically used when streaming starts and you want to grab controls,
+   preventing the user from changing them.
+
+   Just call this and the framework will block any attempts to change
+   these controls. */
+void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed)
+{
+       if (ctrl == NULL)
+               return;
+
+       if (grabbed)
+               /* set V4L2_CTRL_FLAG_GRABBED */
+               set_bit(1, &ctrl->flags);
+       else
+               /* clear V4L2_CTRL_FLAG_GRABBED */
+               clear_bit(1, &ctrl->flags);
+}
+EXPORT_SYMBOL(v4l2_ctrl_grab);
+
+/* Log the control name and value */
+static void log_ctrl(const struct v4l2_ctrl *ctrl,
+                    const char *prefix, const char *colon)
+{
+       int fl_inact = ctrl->flags & V4L2_CTRL_FLAG_INACTIVE;
+       int fl_grabbed = ctrl->flags & V4L2_CTRL_FLAG_GRABBED;
+
+       if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY))
+               return;
+       if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+               return;
+
+       printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name);
+
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_INTEGER:
+               printk(KERN_CONT "%d", ctrl->cur.val);
+               break;
+       case V4L2_CTRL_TYPE_BOOLEAN:
+               printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false");
+               break;
+       case V4L2_CTRL_TYPE_MENU:
+               printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
+               break;
+       case V4L2_CTRL_TYPE_INTEGER64:
+               printk(KERN_CONT "%lld", ctrl->cur.val64);
+               break;
+       case V4L2_CTRL_TYPE_STRING:
+               printk(KERN_CONT "%s", ctrl->cur.string);
+               break;
+       default:
+               printk(KERN_CONT "unknown type %d", ctrl->type);
+               break;
+       }
+       if (fl_inact && fl_grabbed)
+               printk(KERN_CONT " (inactive, grabbed)\n");
+       else if (fl_inact)
+               printk(KERN_CONT " (inactive)\n");
+       else if (fl_grabbed)
+               printk(KERN_CONT " (grabbed)\n");
+       else
+               printk(KERN_CONT "\n");
+}
+
+/* Log all controls owned by the handler */
+void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
+                                 const char *prefix)
+{
+       struct v4l2_ctrl *ctrl;
+       const char *colon = "";
+       int len;
+
+       if (hdl == NULL)
+               return;
+       if (prefix == NULL)
+               prefix = "";
+       len = strlen(prefix);
+       if (len && prefix[len - 1] != ' ')
+               colon = ": ";
+       mutex_lock(&hdl->lock);
+       list_for_each_entry(ctrl, &hdl->ctrls, node)
+               if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED))
+                       log_ctrl(ctrl, prefix, colon);
+       mutex_unlock(&hdl->lock);
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_log_status);
+
+/* Call s_ctrl for all controls owned by the handler */
+int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
+{
+       struct v4l2_ctrl *ctrl;
+       int ret = 0;
+
+       if (hdl == NULL)
+               return 0;
+       mutex_lock(&hdl->lock);
+       list_for_each_entry(ctrl, &hdl->ctrls, node)
+               ctrl->done = false;
+
+       list_for_each_entry(ctrl, &hdl->ctrls, node) {
+               struct v4l2_ctrl *master = ctrl->cluster[0];
+               int i;
+
+               /* Skip if this control was already handled by a cluster. */
+               if (ctrl->done)
+                       continue;
+
+               for (i = 0; i < master->ncontrols; i++)
+                       cur_to_new(master->cluster[i]);
+
+               /* Skip button controls and read-only controls. */
+               if (ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+                   (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
+                       continue;
+               ret = master->ops->s_ctrl(master);
+               if (ret)
+                       break;
+               for (i = 0; i < master->ncontrols; i++)
+                       if (master->cluster[i])
+                               master->cluster[i]->done = true;
+       }
+       mutex_unlock(&hdl->lock);
+       return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_setup);
+
+/* Implement VIDIOC_QUERYCTRL */
+int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
+{
+       u32 id = qc->id & V4L2_CTRL_ID_MASK;
+       struct v4l2_ctrl_ref *ref;
+       struct v4l2_ctrl *ctrl;
+
+       if (hdl == NULL)
+               return -EINVAL;
+
+       mutex_lock(&hdl->lock);
+
+       /* Try to find it */
+       ref = find_ref(hdl, id);
+
+       if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) {
+               /* Find the next control with ID > qc->id */
+
+               /* Did we reach the end of the control list? */
+               if (id >= node2id(hdl->ctrl_refs.prev)) {
+                       ref = NULL; /* Yes, so there is no next control */
+               } else if (ref) {
+                       /* We found a control with the given ID, so just get
+                          the next one in the list. */
+                       ref = list_entry(ref->node.next, typeof(*ref), node);
+               } else {
+                       /* No control with the given ID exists, so start
+                          searching for the next largest ID. We know there
+                          is one, otherwise the first 'if' above would have
+                          been true. */
+                       list_for_each_entry(ref, &hdl->ctrl_refs, node)
+                               if (id < ref->ctrl->id)
+                                       break;
+               }
+       }
+       mutex_unlock(&hdl->lock);
+       if (!ref)
+               return -EINVAL;
+
+       ctrl = ref->ctrl;
+       memset(qc, 0, sizeof(*qc));
+       qc->id = ctrl->id;
+       strlcpy(qc->name, ctrl->name, sizeof(qc->name));
+       qc->minimum = ctrl->minimum;
+       qc->maximum = ctrl->maximum;
+       qc->default_value = ctrl->default_value;
+       if (qc->type == V4L2_CTRL_TYPE_MENU)
+               qc->step = 1;
+       else
+               qc->step = ctrl->step;
+       qc->flags = ctrl->flags;
+       qc->type = ctrl->type;
+       return 0;
+}
+EXPORT_SYMBOL(v4l2_queryctrl);
+
+int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+       return v4l2_queryctrl(sd->ctrl_handler, qc);
+}
+EXPORT_SYMBOL(v4l2_subdev_queryctrl);
+
+/* Implement VIDIOC_QUERYMENU */
+int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
+{
+       struct v4l2_ctrl *ctrl;
+       u32 i = qm->index;
+
+       ctrl = v4l2_ctrl_find(hdl, qm->id);
+       if (!ctrl)
+               return -EINVAL;
+
+       qm->reserved = 0;
+       /* Sanity checks */
+       if (ctrl->qmenu == NULL ||
+           i < ctrl->minimum || i > ctrl->maximum)
+               return -EINVAL;
+       /* Use mask to see if this menu item should be skipped */
+       if (ctrl->menu_skip_mask & (1 << i))
+               return -EINVAL;
+       /* Empty menu items should also be skipped */
+       if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0')
+               return -EINVAL;
+       strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name));
+       return 0;
+}
+EXPORT_SYMBOL(v4l2_querymenu);
+
+int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm)
+{
+       return v4l2_querymenu(sd->ctrl_handler, qm);
+}
+EXPORT_SYMBOL(v4l2_subdev_querymenu);
+
+
+
+/* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS:
+
+   It is not a fully atomic operation, just best-effort only. After all, if
+   multiple controls have to be set through multiple i2c writes (for example)
+   then some initial writes may succeed while others fail. Thus leaving the
+   system in an inconsistent state. The question is how much effort you are
+   willing to spend on trying to make something atomic that really isn't.
+
+   From the point of view of an application the main requirement is that
+   when you call VIDIOC_S_EXT_CTRLS and some values are invalid then an
+   error should be returned without actually affecting any controls.
+
+   If all the values are correct, then it is acceptable to just give up
+   in case of low-level errors.
+
+   It is important though that the application can tell when only a partial
+   configuration was done. The way we do that is through the error_idx field
+   of struct v4l2_ext_controls: if that is equal to the count field then no
+   controls were affected. Otherwise all controls before that index were
+   successful in performing their 'get' or 'set' operation, the control at
+   the given index failed, and you don't know what happened with the controls
+   after the failed one. Since if they were part of a control cluster they
+   could have been successfully processed (if a cluster member was encountered
+   at index < error_idx), they could have failed (if a cluster member was at
+   error_idx), or they may not have been processed yet (if the first cluster
+   member appeared after error_idx).
+
+   It is all fairly theoretical, though. In practice all you can do is to
+   bail out. If error_idx == count, then it is an application bug. If
+   error_idx < count then it is only an application bug if the error code was
+   EBUSY. That usually means that something started streaming just when you
+   tried to set the controls. In all other cases it is a driver/hardware
+   problem and all you can do is to retry or bail out.
+
+   Note that these rules do not apply to VIDIOC_TRY_EXT_CTRLS: since that
+   never modifies controls the error_idx is just set to whatever control
+   has an invalid value.
+ */
+
+/* Prepare for the extended g/s/try functions.
+   Find the controls in the control array and do some basic checks. */
+static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+                            struct v4l2_ext_controls *cs,
+                            struct ctrl_helper *helpers,
+                            bool try)
+{
+       u32 i;
+
+       for (i = 0; i < cs->count; i++) {
+               struct v4l2_ext_control *c = &cs->controls[i];
+               struct v4l2_ctrl *ctrl;
+               u32 id = c->id & V4L2_CTRL_ID_MASK;
+
+               if (try)
+                       cs->error_idx = i;
+
+               if (cs->ctrl_class && V4L2_CTRL_ID2CLASS(id) != cs->ctrl_class)
+                       return -EINVAL;
+
+               /* Old-style private controls are not allowed for
+                  extended controls */
+               if (id >= V4L2_CID_PRIVATE_BASE)
+                       return -EINVAL;
+               ctrl = v4l2_ctrl_find(hdl, id);
+               if (ctrl == NULL)
+                       return -EINVAL;
+               if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
+                       return -EINVAL;
+
+               helpers[i].ctrl = ctrl;
+               helpers[i].handled = false;
+       }
+       return 0;
+}
+
+typedef int (*cluster_func)(struct v4l2_ext_control *c,
+                           struct v4l2_ctrl *ctrl);
+
+/* Walk over all controls in v4l2_ext_controls belonging to the same cluster
+   and call the provided function. */
+static int cluster_walk(unsigned from,
+                       struct v4l2_ext_controls *cs,
+                       struct ctrl_helper *helpers,
+                       cluster_func f)
+{
+       struct v4l2_ctrl **cluster = helpers[from].ctrl->cluster;
+       int ret = 0;
+       int i;
+
+       /* Find any controls from the same cluster and call the function */
+       for (i = from; !ret && i < cs->count; i++) {
+               struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+
+               if (!helpers[i].handled && ctrl->cluster == cluster)
+                       ret = f(&cs->controls[i], ctrl);
+       }
+       return ret;
+}
+
+static void cluster_done(unsigned from,
+                        struct v4l2_ext_controls *cs,
+                        struct ctrl_helper *helpers)
+{
+       struct v4l2_ctrl **cluster = helpers[from].ctrl->cluster;
+       int i;
+
+       /* Find any controls from the same cluster and mark them as handled */
+       for (i = from; i < cs->count; i++)
+               if (helpers[i].ctrl->cluster == cluster)
+                       helpers[i].handled = true;
+}
+
+/* Handles the corner case where cs->count == 0. It checks whether the
+   specified control class exists. If that class ID is 0, then it checks
+   whether there are any controls at all. */
+static int class_check(struct v4l2_ctrl_handler *hdl, u32 ctrl_class)
+{
+       if (ctrl_class == 0)
+               return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0;
+       return find_ref_lock(hdl, ctrl_class | 1) ? 0 : -EINVAL;
+}
+
+
+
+/* Get extended controls. Allocates the helpers array if needed. */
+int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+       struct ctrl_helper helper[4];
+       struct ctrl_helper *helpers = helper;
+       int ret;
+       int i;
+
+       cs->error_idx = cs->count;
+       cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+       if (hdl == NULL)
+               return -EINVAL;
+
+       if (cs->count == 0)
+               return class_check(hdl, cs->ctrl_class);
+
+       if (cs->count > ARRAY_SIZE(helper)) {
+               helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL);
+               if (helpers == NULL)
+                       return -ENOMEM;
+       }
+
+       ret = prepare_ext_ctrls(hdl, cs, helpers, false);
+
+       for (i = 0; !ret && i < cs->count; i++)
+               if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+                       ret = -EACCES;
+
+       for (i = 0; !ret && i < cs->count; i++) {
+               struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+               struct v4l2_ctrl *master = ctrl->cluster[0];
+
+               if (helpers[i].handled)
+                       continue;
+
+               cs->error_idx = i;
+
+               v4l2_ctrl_lock(master);
+               /* g_volatile_ctrl will update the current control values */
+               if (ctrl->is_volatile && master->ops->g_volatile_ctrl)
+                       ret = master->ops->g_volatile_ctrl(master);
+               /* If OK, then copy the current control values to the caller */
+               if (!ret)
+                       ret = cluster_walk(i, cs, helpers, cur_to_user);
+               v4l2_ctrl_unlock(master);
+               cluster_done(i, cs, helpers);
+       }
+
+       if (cs->count > ARRAY_SIZE(helper))
+               kfree(helpers);
+       return ret;
+}
+EXPORT_SYMBOL(v4l2_g_ext_ctrls);
+
+int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+       return v4l2_g_ext_ctrls(sd->ctrl_handler, cs);
+}
+EXPORT_SYMBOL(v4l2_subdev_g_ext_ctrls);
+
+/* Helper function to get a single control */
+static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
+{
+       struct v4l2_ctrl *master = ctrl->cluster[0];
+       int ret = 0;
+
+       if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+               return -EACCES;
+
+       v4l2_ctrl_lock(master);
+       /* g_volatile_ctrl will update the current control values */
+       if (ctrl->is_volatile && master->ops->g_volatile_ctrl)
+               ret = master->ops->g_volatile_ctrl(master);
+       *val = ctrl->cur.val;
+       v4l2_ctrl_unlock(master);
+       return ret;
+}
+
+int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
+{
+       struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
+
+       if (ctrl == NULL || !type_is_int(ctrl))
+               return -EINVAL;
+       return get_ctrl(ctrl, &control->value);
+}
+EXPORT_SYMBOL(v4l2_g_ctrl);
+
+int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+       return v4l2_g_ctrl(sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_subdev_g_ctrl);
+
+s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+       s32 val = 0;
+
+       /* It's a driver bug if this happens. */
+       WARN_ON(!type_is_int(ctrl));
+       get_ctrl(ctrl, &val);
+       return val;
+}
+EXPORT_SYMBOL(v4l2_ctrl_g_ctrl);
+
+
+/* Core function that calls try/s_ctrl and ensures that the new value is
+   copied to the current value on a set.
+   Must be called with ctrl->handler->lock held. */
+static int try_or_set_control_cluster(struct v4l2_ctrl *master, bool set)
+{
+       bool try = !set;
+       int ret = 0;
+       int i;
+
+       /* Go through the cluster and either validate the new value or
+          (if no new value was set), copy the current value to the new
+          value, ensuring a consistent view for the control ops when
+          called. */
+       for (i = 0; !ret && i < master->ncontrols; i++) {
+               struct v4l2_ctrl *ctrl = master->cluster[i];
+
+               if (ctrl == NULL)
+                       continue;
+
+               if (ctrl->has_new) {
+                       /* Double check this: it may have changed since the
+                          last check in try_or_set_ext_ctrls(). */
+                       if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+                               return -EBUSY;
+
+                       /* Validate if required */
+                       if (!set)
+                               ret = validate_new(ctrl);
+                       continue;
+               }
+               /* No new value was set, so copy the current and force
+                  a call to try_ctrl later, since the values for the cluster
+                  may now have changed and the end result might be invalid. */
+               try = true;
+               cur_to_new(ctrl);
+       }
+
+       /* For larger clusters you have to call try_ctrl again to
+          verify that the controls are still valid after the
+          'cur_to_new' above. */
+       if (!ret && master->ops->try_ctrl && try)
+               ret = master->ops->try_ctrl(master);
+
+       /* Don't set if there is no change */
+       if (!ret && set && cluster_changed(master)) {
+               ret = master->ops->s_ctrl(master);
+               /* If OK, then make the new values permanent. */
+               if (!ret)
+                       for (i = 0; i < master->ncontrols; i++)
+                               new_to_cur(master->cluster[i]);
+       }
+       return ret;
+}
+
+/* Try or set controls. */
+static int try_or_set_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+                               struct v4l2_ext_controls *cs,
+                               struct ctrl_helper *helpers,
+                               bool set)
+{
+       unsigned i, j;
+       int ret = 0;
+
+       cs->error_idx = cs->count;
+       for (i = 0; i < cs->count; i++) {
+               struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+
+               if (!set)
+                       cs->error_idx = i;
+
+               if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+                       return -EACCES;
+               /* This test is also done in try_set_control_cluster() which
+                  is called in atomic context, so that has the final say,
+                  but it makes sense to do an up-front check as well. Once
+                  an error occurs in try_set_control_cluster() some other
+                  controls may have been set already and we want to do a
+                  best-effort to avoid that. */
+               if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+                       return -EBUSY;
+       }
+
+       for (i = 0; !ret && i < cs->count; i++) {
+               struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+               struct v4l2_ctrl *master = ctrl->cluster[0];
+
+               cs->error_idx = i;
+
+               if (helpers[i].handled)
+                       continue;
+
+               v4l2_ctrl_lock(ctrl);
+
+               /* Reset the 'has_new' flags of the cluster */
+               for (j = 0; j < master->ncontrols; j++)
+                       if (master->cluster[j])
+                               master->cluster[j]->has_new = 0;
+
+               /* Copy the new caller-supplied control values.
+                  user_to_new() sets 'has_new' to 1. */
+               ret = cluster_walk(i, cs, helpers, user_to_new);
+
+               if (!ret)
+                       ret = try_or_set_control_cluster(master, set);
+
+               /* Copy the new values back to userspace. */
+               if (!ret)
+                       ret = cluster_walk(i, cs, helpers, new_to_user);
+
+               v4l2_ctrl_unlock(ctrl);
+               cluster_done(i, cs, helpers);
+       }
+       return ret;
+}
+
+/* Try or try-and-set controls */
+static int try_set_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+                            struct v4l2_ext_controls *cs,
+                            bool set)
+{
+       struct ctrl_helper helper[4];
+       struct ctrl_helper *helpers = helper;
+       int ret;
+       int i;
+
+       cs->error_idx = cs->count;
+       cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+       if (hdl == NULL)
+               return -EINVAL;
+
+       if (cs->count == 0)
+               return class_check(hdl, cs->ctrl_class);
+
+       if (cs->count > ARRAY_SIZE(helper)) {
+               helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL);
+               if (!helpers)
+                       return -ENOMEM;
+       }
+       ret = prepare_ext_ctrls(hdl, cs, helpers, !set);
+       if (ret)
+               goto free;
+
+       /* First 'try' all controls and abort on error */
+       ret = try_or_set_ext_ctrls(hdl, cs, helpers, false);
+       /* If this is a 'set' operation and the initial 'try' failed,
+          then set error_idx to count to tell the application that no
+          controls changed value yet. */
+       if (set)
+               cs->error_idx = cs->count;
+       if (!ret && set) {
+               /* Reset 'handled' state */
+               for (i = 0; i < cs->count; i++)
+                       helpers[i].handled = false;
+               ret = try_or_set_ext_ctrls(hdl, cs, helpers, true);
+       }
+
+free:
+       if (cs->count > ARRAY_SIZE(helper))
+               kfree(helpers);
+       return ret;
+}
+
+int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+       return try_set_ext_ctrls(hdl, cs, false);
+}
+EXPORT_SYMBOL(v4l2_try_ext_ctrls);
+
+int v4l2_s_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+       return try_set_ext_ctrls(hdl, cs, true);
+}
+EXPORT_SYMBOL(v4l2_s_ext_ctrls);
+
+int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+       return try_set_ext_ctrls(sd->ctrl_handler, cs, false);
+}
+EXPORT_SYMBOL(v4l2_subdev_try_ext_ctrls);
+
+int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+       return try_set_ext_ctrls(sd->ctrl_handler, cs, true);
+}
+EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls);
+
+/* Helper function for VIDIOC_S_CTRL compatibility */
+static int set_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
+{
+       struct v4l2_ctrl *master = ctrl->cluster[0];
+       int ret;
+       int i;
+
+       v4l2_ctrl_lock(ctrl);
+
+       /* Reset the 'has_new' flags of the cluster */
+       for (i = 0; i < master->ncontrols; i++)
+               if (master->cluster[i])
+                       master->cluster[i]->has_new = 0;
+
+       ctrl->val = *val;
+       ctrl->has_new = 1;
+       ret = try_or_set_control_cluster(master, false);
+       if (!ret)
+               ret = try_or_set_control_cluster(master, true);
+       *val = ctrl->cur.val;
+       v4l2_ctrl_unlock(ctrl);
+       return ret;
+}
+
+int v4l2_s_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
+{
+       struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
+
+       if (ctrl == NULL || !type_is_int(ctrl))
+               return -EINVAL;
+
+       return set_ctrl(ctrl, &control->value);
+}
+EXPORT_SYMBOL(v4l2_s_ctrl);
+
+int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+       return v4l2_s_ctrl(sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_subdev_s_ctrl);
+
+int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
+{
+       /* It's a driver bug if this happens. */
+       WARN_ON(!type_is_int(ctrl));
+       return set_ctrl(ctrl, &val);
+}
+EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
index 249af6a1d56defcfdbccbee4591479d44b03cea9..cb77197d480e07b2b939bd8679a5c55c5e7ed976 100644 (file)
@@ -428,8 +428,12 @@ static int __video_register_device(struct video_device *vdev, int type, int nr,
 
        vdev->vfl_type = type;
        vdev->cdev = NULL;
-       if (vdev->v4l2_dev && vdev->v4l2_dev->dev)
-               vdev->parent = vdev->v4l2_dev->dev;
+       if (vdev->v4l2_dev) {
+               if (vdev->v4l2_dev->dev)
+                       vdev->parent = vdev->v4l2_dev->dev;
+               if (vdev->ctrl_handler == NULL)
+                       vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
+       }
 
        /* Part 2: find a free minor, device node number and device index. */
 #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
index 5a7dc4afe92a2e4ca7872df5f06cab3443ff79f8..0b08f96b74a5ed5c750fdcc7541d2e38f069d79f 100644 (file)
@@ -26,6 +26,7 @@
 #endif
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
 
 int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
 {
@@ -115,6 +116,8 @@ EXPORT_SYMBOL_GPL(v4l2_device_unregister);
 int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
                                                struct v4l2_subdev *sd)
 {
+       int err;
+
        /* Check for valid input */
        if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
                return -EINVAL;
@@ -122,6 +125,10 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
        WARN_ON(sd->v4l2_dev != NULL);
        if (!try_module_get(sd->owner))
                return -ENODEV;
+       /* This just returns 0 if either of the two args is NULL */
+       err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);
+       if (err)
+               return err;
        sd->v4l2_dev = v4l2_dev;
        spin_lock(&v4l2_dev->lock);
        list_add_tail(&sd->list, &v4l2_dev->subdevs);
index 0eeceae503293da68debf793fb40e10906f17acb..dd9283fcb5643584d44628d447ec0791b86654a9 100644 (file)
@@ -26,6 +26,7 @@
 #endif
 #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-chip-ident.h>
@@ -1259,9 +1260,12 @@ static long __video_do_ioctl(struct file *file,
        {
                struct v4l2_queryctrl *p = arg;
 
-               if (!ops->vidioc_queryctrl)
+               if (vfd->ctrl_handler)
+                       ret = v4l2_queryctrl(vfd->ctrl_handler, p);
+               else if (ops->vidioc_queryctrl)
+                       ret = ops->vidioc_queryctrl(file, fh, p);
+               else
                        break;
-               ret = ops->vidioc_queryctrl(file, fh, p);
                if (!ret)
                        dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, "
                                        "step=%d, default=%d, flags=0x%08x\n",
@@ -1276,7 +1280,9 @@ static long __video_do_ioctl(struct file *file,
        {
                struct v4l2_control *p = arg;
 
-               if (ops->vidioc_g_ctrl)
+               if (vfd->ctrl_handler)
+                       ret = v4l2_g_ctrl(vfd->ctrl_handler, p);
+               else if (ops->vidioc_g_ctrl)
                        ret = ops->vidioc_g_ctrl(file, fh, p);
                else if (ops->vidioc_g_ext_ctrls) {
                        struct v4l2_ext_controls ctrls;
@@ -1306,11 +1312,16 @@ static long __video_do_ioctl(struct file *file,
                struct v4l2_ext_controls ctrls;
                struct v4l2_ext_control ctrl;
 
-               if (!ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
+               if (!vfd->ctrl_handler &&
+                       !ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
                        break;
 
                dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
 
+               if (vfd->ctrl_handler) {
+                       ret = v4l2_s_ctrl(vfd->ctrl_handler, p);
+                       break;
+               }
                if (ops->vidioc_s_ctrl) {
                        ret = ops->vidioc_s_ctrl(file, fh, p);
                        break;
@@ -1332,10 +1343,12 @@ static long __video_do_ioctl(struct file *file,
                struct v4l2_ext_controls *p = arg;
 
                p->error_idx = p->count;
-               if (!ops->vidioc_g_ext_ctrls)
-                       break;
-               if (check_ext_ctrls(p, 0))
+               if (vfd->ctrl_handler)
+                       ret = v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
+               else if (ops->vidioc_g_ext_ctrls && check_ext_ctrls(p, 0))
                        ret = ops->vidioc_g_ext_ctrls(file, fh, p);
+               else
+                       break;
                v4l_print_ext_ctrls(cmd, vfd, p, !ret);
                break;
        }
@@ -1344,10 +1357,12 @@ static long __video_do_ioctl(struct file *file,
                struct v4l2_ext_controls *p = arg;
 
                p->error_idx = p->count;
-               if (!ops->vidioc_s_ext_ctrls)
+               if (!vfd->ctrl_handler && !ops->vidioc_s_ext_ctrls)
                        break;
                v4l_print_ext_ctrls(cmd, vfd, p, 1);
-               if (check_ext_ctrls(p, 0))
+               if (vfd->ctrl_handler)
+                       ret = v4l2_s_ext_ctrls(vfd->ctrl_handler, p);
+               else if (check_ext_ctrls(p, 0))
                        ret = ops->vidioc_s_ext_ctrls(file, fh, p);
                break;
        }
@@ -1356,10 +1371,12 @@ static long __video_do_ioctl(struct file *file,
                struct v4l2_ext_controls *p = arg;
 
                p->error_idx = p->count;
-               if (!ops->vidioc_try_ext_ctrls)
+               if (!vfd->ctrl_handler && !ops->vidioc_try_ext_ctrls)
                        break;
                v4l_print_ext_ctrls(cmd, vfd, p, 1);
-               if (check_ext_ctrls(p, 0))
+               if (vfd->ctrl_handler)
+                       ret = v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
+               else if (check_ext_ctrls(p, 0))
                        ret = ops->vidioc_try_ext_ctrls(file, fh, p);
                break;
        }
@@ -1367,9 +1384,12 @@ static long __video_do_ioctl(struct file *file,
        {
                struct v4l2_querymenu *p = arg;
 
-               if (!ops->vidioc_querymenu)
+               if (vfd->ctrl_handler)
+                       ret = v4l2_querymenu(vfd->ctrl_handler, p);
+               else if (ops->vidioc_querymenu)
+                       ret = ops->vidioc_querymenu(file, fh, p);
+               else
                        break;
-               ret = ops->vidioc_querymenu(file, fh, p);
                if (!ret)
                        dbgarg(cmd, "id=0x%x, index=%d, name=%s\n",
                                p->id, p->index, p->name);
index a11b99b4226b86c864595bfbe09a0c8fd78228d8..d5965543ecab72af2bfc1874c172137c2ab76b93 100644 (file)
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-i2c-drv.h>
+#include <media/v4l2-ctrls.h>
 
 MODULE_DESCRIPTION("wm8739 driver");
 MODULE_AUTHOR("T. Adachi, Hans Verkuil");
@@ -54,12 +54,14 @@ enum {
 
 struct wm8739_state {
        struct v4l2_subdev sd;
+       struct v4l2_ctrl_handler hdl;
+       struct {
+               /* audio cluster */
+               struct v4l2_ctrl *volume;
+               struct v4l2_ctrl *mute;
+               struct v4l2_ctrl *balance;
+       };
        u32 clock_freq;
-       u8 muted;
-       u16 volume;
-       u16 balance;
-       u8 vol_l;               /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
-       u8 vol_r;               /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
 };
 
 static inline struct wm8739_state *to_state(struct v4l2_subdev *sd)
@@ -67,6 +69,11 @@ static inline struct wm8739_state *to_state(struct v4l2_subdev *sd)
        return container_of(sd, struct wm8739_state, sd);
 }
 
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+       return &container_of(ctrl->handler, struct wm8739_state, hdl)->sd;
+}
+
 /* ------------------------------------------------------------------------ */
 
 static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val)
@@ -89,58 +96,17 @@ static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val)
        return -1;
 }
 
-/* write regs to set audio volume etc */
-static void wm8739_set_audio(struct v4l2_subdev *sd)
-{
-       struct wm8739_state *state = to_state(sd);
-       u16 mute = state->muted ? 0x80 : 0;
-
-       /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB
-        * Default setting: 0x17 = 0 dB
-        */
-       wm8739_write(sd, R0, (state->vol_l & 0x1f) | mute);
-       wm8739_write(sd, R1, (state->vol_r & 0x1f) | mute);
-}
-
-static int wm8739_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       struct wm8739_state *state = to_state(sd);
-
-       switch (ctrl->id) {
-       case V4L2_CID_AUDIO_MUTE:
-               ctrl->value = state->muted;
-               break;
-
-       case V4L2_CID_AUDIO_VOLUME:
-               ctrl->value = state->volume;
-               break;
-
-       case V4L2_CID_AUDIO_BALANCE:
-               ctrl->value = state->balance;
-               break;
-
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int wm8739_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+       struct v4l2_subdev *sd = to_sd(ctrl);
        struct wm8739_state *state = to_state(sd);
        unsigned int work_l, work_r;
+       u8 vol_l;       /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
+       u8 vol_r;       /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
+       u16 mute;
 
        switch (ctrl->id) {
-       case V4L2_CID_AUDIO_MUTE:
-               state->muted = ctrl->value;
-               break;
-
        case V4L2_CID_AUDIO_VOLUME:
-               state->volume = ctrl->value;
-               break;
-
-       case V4L2_CID_AUDIO_BALANCE:
-               state->balance = ctrl->value;
                break;
 
        default:
@@ -148,52 +114,25 @@ static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
        }
 
        /* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */
-       work_l = (min(65536 - state->balance, 32768) * state->volume) / 32768;
-       work_r = (min(state->balance, (u16)32768) * state->volume) / 32768;
+       work_l = (min(65536 - state->balance->val, 32768) * state->volume->val) / 32768;
+       work_r = (min(state->balance->val, 32768) * state->volume->val) / 32768;
 
-       state->vol_l = (long)work_l * 31 / 65535;
-       state->vol_r = (long)work_r * 31 / 65535;
+       vol_l = (long)work_l * 31 / 65535;
+       vol_r = (long)work_r * 31 / 65535;
 
        /* set audio volume etc. */
-       wm8739_set_audio(sd);
+       mute = state->mute->val ? 0x80 : 0;
+
+       /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB
+        * Default setting: 0x17 = 0 dB
+        */
+       wm8739_write(sd, R0, (vol_l & 0x1f) | mute);
+       wm8739_write(sd, R1, (vol_r & 0x1f) | mute);
        return 0;
 }
 
 /* ------------------------------------------------------------------------ */
 
-static struct v4l2_queryctrl wm8739_qctrl[] = {
-       {
-               .id            = V4L2_CID_AUDIO_VOLUME,
-               .name          = "Volume",
-               .minimum       = 0,
-               .maximum       = 65535,
-               .step          = 65535/100,
-               .default_value = 58880,
-               .flags         = 0,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-       }, {
-               .id            = V4L2_CID_AUDIO_MUTE,
-               .name          = "Mute",
-               .minimum       = 0,
-               .maximum       = 1,
-               .step          = 1,
-               .default_value = 1,
-               .flags         = 0,
-               .type          = V4L2_CTRL_TYPE_BOOLEAN,
-       }, {
-               .id            = V4L2_CID_AUDIO_BALANCE,
-               .name          = "Balance",
-               .minimum       = 0,
-               .maximum       = 65535,
-               .step          = 65535/100,
-               .default_value = 32768,
-               .flags         = 0,
-               .type          = V4L2_CTRL_TYPE_INTEGER,
-       }
-};
-
-/* ------------------------------------------------------------------------ */
-
 static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq)
 {
        struct wm8739_state *state = to_state(sd);
@@ -222,18 +161,6 @@ static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq)
        return 0;
 }
 
-static int wm8739_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++)
-               if (qc->id && qc->id == wm8739_qctrl[i].id) {
-                       memcpy(qc, &wm8739_qctrl[i], sizeof(*qc));
-                       return 0;
-               }
-       return -EINVAL;
-}
-
 static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
 {
        struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -246,21 +173,26 @@ static int wm8739_log_status(struct v4l2_subdev *sd)
        struct wm8739_state *state = to_state(sd);
 
        v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq);
-       v4l2_info(sd, "Volume L:  %02x%s\n", state->vol_l & 0x1f,
-                       state->muted ? " (muted)" : "");
-       v4l2_info(sd, "Volume R:  %02x%s\n", state->vol_r & 0x1f,
-                       state->muted ? " (muted)" : "");
+       v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
        return 0;
 }
 
 /* ----------------------------------------------------------------------- */
 
+static const struct v4l2_ctrl_ops wm8739_ctrl_ops = {
+       .s_ctrl = wm8739_s_ctrl,
+};
+
 static const struct v4l2_subdev_core_ops wm8739_core_ops = {
        .log_status = wm8739_log_status,
        .g_chip_ident = wm8739_g_chip_ident,
-       .queryctrl = wm8739_queryctrl,
-       .g_ctrl = wm8739_g_ctrl,
-       .s_ctrl = wm8739_s_ctrl,
+       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+       .g_ctrl = v4l2_subdev_g_ctrl,
+       .s_ctrl = v4l2_subdev_s_ctrl,
+       .queryctrl = v4l2_subdev_queryctrl,
+       .querymenu = v4l2_subdev_querymenu,
 };
 
 static const struct v4l2_subdev_audio_ops wm8739_audio_ops = {
@@ -289,17 +221,28 @@ static int wm8739_probe(struct i2c_client *client,
        v4l_info(client, "chip found @ 0x%x (%s)\n",
                        client->addr << 1, client->adapter->name);
 
-       state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL);
+       state = kzalloc(sizeof(struct wm8739_state), GFP_KERNEL);
        if (state == NULL)
                return -ENOMEM;
        sd = &state->sd;
        v4l2_i2c_subdev_init(sd, client, &wm8739_ops);
-       state->vol_l = 0x17; /* 0dB */
-       state->vol_r = 0x17; /* 0dB */
-       state->muted = 0;
-       state->balance = 32768;
-       /* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */
-       state->volume = ((long)state->vol_l + 1) * 65535 / 31;
+       v4l2_ctrl_handler_init(&state->hdl, 2);
+       state->volume = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+                       V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 50736);
+       state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+                       V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+       state->balance = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+                       V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+       sd->ctrl_handler = &state->hdl;
+       if (state->hdl.error) {
+               int err = state->hdl.error;
+
+               v4l2_ctrl_handler_free(&state->hdl);
+               kfree(state);
+               return err;
+       }
+       v4l2_ctrl_cluster(3, &state->volume);
+
        state->clock_freq = 48000;
 
        /* Initialize wm8739 */
@@ -318,15 +261,17 @@ static int wm8739_probe(struct i2c_client *client,
        /* activate */
        wm8739_write(sd, R9, 0x001);
        /* set volume/mute */
-       wm8739_set_audio(sd);
+       v4l2_ctrl_handler_setup(&state->hdl);
        return 0;
 }
 
 static int wm8739_remove(struct i2c_client *client)
 {
        struct v4l2_subdev *sd = i2c_get_clientdata(client);
+       struct wm8739_state *state = to_state(sd);
 
        v4l2_device_unregister_subdev(sd);
+       v4l2_ctrl_handler_free(&state->hdl);
        kfree(to_state(sd));
        return 0;
 }
index 5c2ba599c0c731735ce5dc08170ae8217723b665..23bad3fd6dc5fdef5d773819c9d6b482a27593cd 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-i2c-drv.h>
 
 MODULE_DESCRIPTION("wm8775 driver");
@@ -53,8 +54,9 @@ enum {
 
 struct wm8775_state {
        struct v4l2_subdev sd;
+       struct v4l2_ctrl_handler hdl;
+       struct v4l2_ctrl *mute;
        u8 input;               /* Last selected input (0-0xf) */
-       u8 muted;
 };
 
 static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
@@ -62,6 +64,11 @@ static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
        return container_of(sd, struct wm8775_state, sd);
 }
 
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+       return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd;
+}
+
 static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
 {
        struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -95,7 +102,7 @@ static int wm8775_s_routing(struct v4l2_subdev *sd,
                return -EINVAL;
        }
        state->input = input;
-       if (state->muted)
+       if (!v4l2_ctrl_g_ctrl(state->mute))
                return 0;
        wm8775_write(sd, R21, 0x0c0);
        wm8775_write(sd, R14, 0x1d4);
@@ -104,29 +111,21 @@ static int wm8775_s_routing(struct v4l2_subdev *sd,
        return 0;
 }
 
-static int wm8775_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+       struct v4l2_subdev *sd = to_sd(ctrl);
        struct wm8775_state *state = to_state(sd);
 
-       if (ctrl->id != V4L2_CID_AUDIO_MUTE)
-               return -EINVAL;
-       ctrl->value = state->muted;
-       return 0;
-}
-
-static int wm8775_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-       struct wm8775_state *state = to_state(sd);
-
-       if (ctrl->id != V4L2_CID_AUDIO_MUTE)
-               return -EINVAL;
-       state->muted = ctrl->value;
-       wm8775_write(sd, R21, 0x0c0);
-       wm8775_write(sd, R14, 0x1d4);
-       wm8775_write(sd, R15, 0x1d4);
-       if (!state->muted)
-               wm8775_write(sd, R21, 0x100 + state->input);
-       return 0;
+       switch (ctrl->id) {
+       case V4L2_CID_AUDIO_MUTE:
+               wm8775_write(sd, R21, 0x0c0);
+               wm8775_write(sd, R14, 0x1d4);
+               wm8775_write(sd, R15, 0x1d4);
+               if (!ctrl->val)
+                       wm8775_write(sd, R21, 0x100 + state->input);
+               return 0;
+       }
+       return -EINVAL;
 }
 
 static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
@@ -140,8 +139,8 @@ static int wm8775_log_status(struct v4l2_subdev *sd)
 {
        struct wm8775_state *state = to_state(sd);
 
-       v4l2_info(sd, "Input: %d%s\n", state->input,
-                       state->muted ? " (muted)" : "");
+       v4l2_info(sd, "Input: %d\n", state->input);
+       v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
        return 0;
 }
 
@@ -162,11 +161,20 @@ static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fre
 
 /* ----------------------------------------------------------------------- */
 
+static const struct v4l2_ctrl_ops wm8775_ctrl_ops = {
+       .s_ctrl = wm8775_s_ctrl,
+};
+
 static const struct v4l2_subdev_core_ops wm8775_core_ops = {
        .log_status = wm8775_log_status,
        .g_chip_ident = wm8775_g_chip_ident,
-       .g_ctrl = wm8775_g_ctrl,
-       .s_ctrl = wm8775_s_ctrl,
+       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+       .g_ctrl = v4l2_subdev_g_ctrl,
+       .s_ctrl = v4l2_subdev_s_ctrl,
+       .queryctrl = v4l2_subdev_queryctrl,
+       .querymenu = v4l2_subdev_querymenu,
 };
 
 static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
@@ -205,13 +213,24 @@ static int wm8775_probe(struct i2c_client *client,
        v4l_info(client, "chip found @ 0x%02x (%s)\n",
                        client->addr << 1, client->adapter->name);
 
-       state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL);
+       state = kzalloc(sizeof(struct wm8775_state), GFP_KERNEL);
        if (state == NULL)
                return -ENOMEM;
        sd = &state->sd;
        v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
        state->input = 2;
-       state->muted = 0;
+
+       v4l2_ctrl_handler_init(&state->hdl, 1);
+       state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
+                       V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+       sd->ctrl_handler = &state->hdl;
+       if (state->hdl.error) {
+               int err = state->hdl.error;
+
+               v4l2_ctrl_handler_free(&state->hdl);
+               kfree(state);
+               return err;
+       }
 
        /* Initialize wm8775 */
 
@@ -248,9 +267,11 @@ static int wm8775_probe(struct i2c_client *client,
 static int wm8775_remove(struct i2c_client *client)
 {
        struct v4l2_subdev *sd = i2c_get_clientdata(client);
+       struct wm8775_state *state = to_state(sd);
 
        v4l2_device_unregister_subdev(sd);
-       kfree(to_state(sd));
+       v4l2_ctrl_handler_free(&state->hdl);
+       kfree(state);
        return 0;
 }
 
index 968c2adee06bf2b9d9018b801d0206c6eced71c0..100c4d4b812540fcff47946fd5398b5540681fad 100644 (file)
@@ -3,6 +3,7 @@
 #
 menuconfig LIRC_STAGING
        bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
+       depends on LIRC
        help
          Say Y here, and all supported Linux Infrared Remote Control IR and
          RF receiver and transmitter drivers will be displayed. When paired
@@ -13,21 +14,13 @@ if LIRC_STAGING
 
 config LIRC_BT829
         tristate "BT829 based hardware"
-       depends on LIRC_STAGING
+       depends on LIRC_STAGING && PCI
        help
          Driver for the IR interface on BT829-based hardware
 
-config LIRC_ENE0100
-       tristate "ENE KB3924/ENE0100 CIR Port Reciever"
-       depends on LIRC_STAGING
-       help
-         This is a driver for CIR port handled by ENE KB3924 embedded
-         controller found on some notebooks.
-         It appears on PNP list as ENE0100.
-
 config LIRC_I2C
        tristate "I2C Based IR Receivers"
-       depends on LIRC_STAGING
+       depends on LIRC_STAGING && I2C
        help
          Driver for I2C-based IR receivers, such as those commonly
          found onboard Hauppauge PVR-150/250/350 video capture cards
@@ -40,7 +33,7 @@ config LIRC_IGORPLUGUSB
 
 config LIRC_IMON
        tristate "Legacy SoundGraph iMON Receiver and Display"
-       depends on LIRC_STAGING
+       depends on LIRC_STAGING && USB
        help
          Driver for the original SoundGraph iMON IR Receiver and Display
 
@@ -48,7 +41,7 @@ config LIRC_IMON
 
 config LIRC_IT87
        tristate "ITE IT87XX CIR Port Receiver"
-       depends on LIRC_STAGING
+       depends on LIRC_STAGING && PNP
        help
          Driver for the ITE IT87xx IR Receiver
 
@@ -60,13 +53,13 @@ config LIRC_ITE8709
 
 config LIRC_PARALLEL
        tristate "Homebrew Parallel Port Receiver"
-       depends on LIRC_STAGING && !SMP
+       depends on LIRC_STAGING && PARPORT && !SMP
        help
          Driver for Homebrew Parallel Port Receivers
 
 config LIRC_SASEM
        tristate "Sasem USB IR Remote"
-       depends on LIRC_STAGING
+       depends on LIRC_STAGING && USB
        help
          Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
 
@@ -89,12 +82,6 @@ config LIRC_SIR
        help
          Driver for the SIR IrDA port
 
-config LIRC_STREAMZAP
-       tristate "Streamzap PC Receiver"
-       depends on LIRC_STAGING
-       help
-         Driver for the Streamzap PC Receiver
-
 config LIRC_TTUSBIR
        tristate "Technotrend USB IR Receiver"
        depends on LIRC_STAGING && USB
@@ -103,7 +90,7 @@ config LIRC_TTUSBIR
 
 config LIRC_ZILOG
        tristate "Zilog/Hauppauge IR Transmitter"
-       depends on LIRC_STAGING
+       depends on LIRC_STAGING && I2C
        help
          Driver for the Zilog/Hauppauge IR Transmitter, found on
          PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards
index a019182a7a3887a0d404f68d2b13eb334b825778..4da1f3397a16734de06bb38cc58d9a8f663b8d4a 100644 (file)
@@ -4,7 +4,6 @@
 # Each configuration option enables a list of files.
 
 obj-$(CONFIG_LIRC_BT829)       += lirc_bt829.o
-obj-$(CONFIG_LIRC_ENE0100)     += lirc_ene0100.o
 obj-$(CONFIG_LIRC_I2C)         += lirc_i2c.o
 obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o
 obj-$(CONFIG_LIRC_IMON)                += lirc_imon.o
@@ -14,6 +13,5 @@ obj-$(CONFIG_LIRC_PARALLEL)   += lirc_parallel.o
 obj-$(CONFIG_LIRC_SASEM)       += lirc_sasem.o
 obj-$(CONFIG_LIRC_SERIAL)      += lirc_serial.o
 obj-$(CONFIG_LIRC_SIR)         += lirc_sir.o
-obj-$(CONFIG_LIRC_STREAMZAP)   += lirc_streamzap.o
 obj-$(CONFIG_LIRC_TTUSBIR)     += lirc_ttusbir.o
 obj-$(CONFIG_LIRC_ZILOG)       += lirc_zilog.o
diff --git a/drivers/staging/lirc/lirc_ene0100.c b/drivers/staging/lirc/lirc_ene0100.c
deleted file mode 100644 (file)
index a152c52..0000000
+++ /dev/null
@@ -1,646 +0,0 @@
-/*
- * driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
- *
- * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pnp.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-#include "lirc_ene0100.h"
-
-static int sample_period = 75;
-static int enable_idle = 1;
-static int enable_learning;
-
-static void ene_set_idle(struct ene_device *dev, int idle);
-static void ene_set_inputs(struct ene_device *dev, int enable);
-
-/* read a hardware register */
-static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
-{
-       outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
-       outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
-       return inb(dev->hw_io + ENE_IO);
-}
-
-/* write a hardware register */
-static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value)
-{
-       outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
-       outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
-       outb(value, dev->hw_io + ENE_IO);
-}
-
-/* change specific bits in hardware register */
-static void ene_hw_write_reg_mask(struct ene_device *dev,
-                                 u16 reg, u8 value, u8 mask)
-{
-       u8 regvalue;
-
-       outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
-       outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
-
-       regvalue = inb(dev->hw_io + ENE_IO) & ~mask;
-       regvalue |= (value & mask);
-       outb(regvalue, dev->hw_io + ENE_IO);
-}
-
-/* read irq status and ack it */
-static int ene_hw_irq_status(struct ene_device *dev, int *buffer_pointer)
-{
-       u8 irq_status;
-       u8 fw_flags1, fw_flags2;
-
-       fw_flags2 = ene_hw_read_reg(dev, ENE_FW2);
-
-       if (buffer_pointer)
-               *buffer_pointer = 4 * (fw_flags2 & ENE_FW2_BUF_HIGH);
-
-       if (dev->hw_revision < ENE_HW_C) {
-               irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS);
-
-               if (!(irq_status & ENEB_IRQ_STATUS_IR))
-                       return 0;
-               ene_hw_write_reg(dev, ENEB_IRQ_STATUS,
-                                irq_status & ~ENEB_IRQ_STATUS_IR);
-
-               /* rev B support only recieving */
-               return ENE_IRQ_RX;
-       }
-
-       irq_status = ene_hw_read_reg(dev, ENEC_IRQ);
-
-       if (!(irq_status & ENEC_IRQ_STATUS))
-               return 0;
-
-       /* original driver does that twice - a workaround ? */
-       ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
-       ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
-
-       /* clear unknown flag in F8F9 */
-       if (fw_flags2 & ENE_FW2_IRQ_CLR)
-               ene_hw_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_IRQ_CLR);
-
-       /* check if this is a TX interrupt */
-       fw_flags1 = ene_hw_read_reg(dev, ENE_FW1);
-
-       if (fw_flags1 & ENE_FW1_TXIRQ) {
-               ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ);
-               return ENE_IRQ_TX;
-       } else
-               return ENE_IRQ_RX;
-}
-
-static int ene_hw_detect(struct ene_device *dev)
-{
-       u8 chip_major, chip_minor;
-       u8 hw_revision, old_ver;
-       u8 tmp;
-       u8 fw_capabilities;
-
-       tmp = ene_hw_read_reg(dev, ENE_HW_UNK);
-       ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR);
-
-       chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR);
-       chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR);
-
-       ene_hw_write_reg(dev, ENE_HW_UNK, tmp);
-       hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION);
-       old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD);
-
-       if (hw_revision == 0xFF) {
-
-               ene_printk(KERN_WARNING, "device seems to be disabled\n");
-               ene_printk(KERN_WARNING,
-                       "send a mail to lirc-list@lists.sourceforge.net\n");
-               ene_printk(KERN_WARNING, "please attach output of acpidump\n");
-
-               return -ENODEV;
-       }
-
-       if (chip_major == 0x33) {
-               ene_printk(KERN_WARNING, "chips 0x33xx aren't supported yet\n");
-               return -ENODEV;
-       }
-
-       if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
-               dev->hw_revision = ENE_HW_C;
-               ene_printk(KERN_WARNING,
-                      "KB3926C detected, driver support is not complete!\n");
-
-       } else if (old_ver == 0x24 && hw_revision == 0xC0) {
-               dev->hw_revision = ENE_HW_B;
-               ene_printk(KERN_NOTICE, "KB3926B detected\n");
-       } else {
-               dev->hw_revision = ENE_HW_D;
-               ene_printk(KERN_WARNING,
-                       "unknown ENE chip detected, assuming KB3926D\n");
-               ene_printk(KERN_WARNING, "driver support incomplete");
-
-       }
-
-       ene_printk(KERN_DEBUG, "chip is 0x%02x%02x - 0x%02x, 0x%02x\n",
-               chip_major, chip_minor, old_ver, hw_revision);
-
-
-       /* detect features hardware supports */
-
-       if (dev->hw_revision < ENE_HW_C)
-               return 0;
-
-       fw_capabilities = ene_hw_read_reg(dev, ENE_FW2);
-
-       dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN;
-       dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING;
-
-       dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable &&
-           fw_capabilities & ENE_FW2_FAN_AS_NRML_IN;
-
-       ene_printk(KERN_NOTICE, "hardware features:\n");
-       ene_printk(KERN_NOTICE,
-               "learning and tx %s, gpio40_learn %s, fan_in %s\n",
-              dev->hw_learning_and_tx_capable ? "on" : "off",
-              dev->hw_gpio40_learning ? "on" : "off",
-              dev->hw_fan_as_normal_input ? "on" : "off");
-
-       if (!dev->hw_learning_and_tx_capable && enable_learning)
-               enable_learning = 0;
-
-       if (dev->hw_learning_and_tx_capable) {
-               ene_printk(KERN_WARNING,
-               "Device supports transmitting, but the driver doesn't\n");
-               ene_printk(KERN_WARNING,
-               "due to lack of hardware to test against.\n");
-               ene_printk(KERN_WARNING,
-               "Send a mail to: lirc-list@lists.sourceforge.net\n");
-       }
-       return 0;
-}
-
-/* hardware initialization */
-static int ene_hw_init(void *data)
-{
-       u8 reg_value;
-       struct ene_device *dev = (struct ene_device *)data;
-       dev->in_use = 1;
-
-       if (dev->hw_revision < ENE_HW_C) {
-               ene_hw_write_reg(dev, ENEB_IRQ, dev->irq << 1);
-               ene_hw_write_reg(dev, ENEB_IRQ_UNK1, 0x01);
-       } else {
-               reg_value = ene_hw_read_reg(dev, ENEC_IRQ) & 0xF0;
-               reg_value |= ENEC_IRQ_UNK_EN;
-               reg_value &= ~ENEC_IRQ_STATUS;
-               reg_value |= (dev->irq & ENEC_IRQ_MASK);
-               ene_hw_write_reg(dev, ENEC_IRQ, reg_value);
-               ene_hw_write_reg(dev, ENE_TX_UNK1, 0x63);
-       }
-
-       ene_hw_write_reg(dev, ENE_CIR_CONF2, 0x00);
-       ene_set_inputs(dev, enable_learning);
-
-       /* set sampling period */
-       ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period);
-
-       /* ack any pending irqs - just in case */
-       ene_hw_irq_status(dev, NULL);
-
-       /* enter idle mode */
-       ene_set_idle(dev, 1);
-
-       /* enable firmware bits */
-       ene_hw_write_reg_mask(dev, ENE_FW1,
-                             ENE_FW1_ENABLE | ENE_FW1_IRQ,
-                             ENE_FW1_ENABLE | ENE_FW1_IRQ);
-       /* clear stats */
-       dev->sample = 0;
-       return 0;
-}
-
-/* this enables gpio40 signal, used if connected to wide band input*/
-static void ene_enable_gpio40(struct ene_device *dev, int enable)
-{
-       ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, enable ?
-                             0 : ENE_CIR_CONF2_GPIO40DIS,
-                             ENE_CIR_CONF2_GPIO40DIS);
-}
-
-/* this enables the classic sampler */
-static void ene_enable_normal_recieve(struct ene_device *dev, int enable)
-{
-       ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_ADC_ON : 0);
-}
-
-/* this enables recieve via fan input */
-static void ene_enable_fan_recieve(struct ene_device *dev, int enable)
-{
-       if (!enable)
-               ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0);
-       else {
-               ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
-               ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
-       }
-       dev->fan_input_inuse = enable;
-}
-
-/* determine which input to use*/
-static void ene_set_inputs(struct ene_device *dev, int learning_enable)
-{
-       ene_enable_normal_recieve(dev, 1);
-
-       /* old hardware doesn't support learning mode for sure */
-       if (dev->hw_revision <= ENE_HW_B)
-               return;
-
-       /* reciever not learning capable, still set gpio40 correctly */
-       if (!dev->hw_learning_and_tx_capable) {
-               ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
-               return;
-       }
-
-       /* enable learning mode */
-       if (learning_enable) {
-               ene_enable_gpio40(dev, dev->hw_gpio40_learning);
-
-               /* fan input is not used for learning */
-               if (dev->hw_fan_as_normal_input)
-                       ene_enable_fan_recieve(dev, 0);
-
-       /* disable learning mode */
-       } else {
-               if (dev->hw_fan_as_normal_input) {
-                       ene_enable_fan_recieve(dev, 1);
-                       ene_enable_normal_recieve(dev, 0);
-               } else
-                       ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
-       }
-
-       /* set few additional settings for this mode */
-       ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_enable ?
-                             ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1);
-
-       ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_enable ?
-                             ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2);
-}
-
-/* deinitialization */
-static void ene_hw_deinit(void *data)
-{
-       struct ene_device *dev = (struct ene_device *)data;
-
-       /* disable samplers */
-       ene_enable_normal_recieve(dev, 0);
-
-       if (dev->hw_fan_as_normal_input)
-               ene_enable_fan_recieve(dev, 0);
-
-       /* disable hardware IRQ and firmware flag */
-       ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ);
-
-       ene_set_idle(dev, 1);
-       dev->in_use = 0;
-}
-
-/*  sends current sample to userspace */
-static void send_sample(struct ene_device *dev)
-{
-       int value = abs(dev->sample) & PULSE_MASK;
-
-       if (dev->sample > 0)
-               value |= PULSE_BIT;
-
-       if (!lirc_buffer_full(dev->lirc_driver->rbuf)) {
-               lirc_buffer_write(dev->lirc_driver->rbuf, (void *)&value);
-               wake_up(&dev->lirc_driver->rbuf->wait_poll);
-       }
-       dev->sample = 0;
-}
-
-/*  this updates current sample */
-static void update_sample(struct ene_device *dev, int sample)
-{
-       if (!dev->sample)
-               dev->sample = sample;
-       else if (same_sign(dev->sample, sample))
-               dev->sample += sample;
-       else {
-               send_sample(dev);
-               dev->sample = sample;
-       }
-}
-
-/* enable or disable idle mode */
-static void ene_set_idle(struct ene_device *dev, int idle)
-{
-       struct timeval now;
-       int disable = idle && enable_idle && (dev->hw_revision < ENE_HW_C);
-
-       ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD,
-                             disable ? 0 : ENE_CIR_SAMPLE_OVERFLOW,
-                             ENE_CIR_SAMPLE_OVERFLOW);
-       dev->idle = idle;
-
-       /* remember when we have entered the idle mode */
-       if (idle) {
-               do_gettimeofday(&dev->gap_start);
-               return;
-       }
-
-       /* send the gap between keypresses now */
-       do_gettimeofday(&now);
-
-       if (now.tv_sec - dev->gap_start.tv_sec > 16)
-               dev->sample = space(PULSE_MASK);
-       else
-               dev->sample = dev->sample +
-                   space(1000000ull * (now.tv_sec - dev->gap_start.tv_sec))
-                   + space(now.tv_usec - dev->gap_start.tv_usec);
-
-       if (abs(dev->sample) > PULSE_MASK)
-               dev->sample = space(PULSE_MASK);
-       send_sample(dev);
-}
-
-/* interrupt handler */
-static irqreturn_t ene_hw_irq(int irq, void *data)
-{
-       u16 hw_value;
-       int i, hw_sample;
-       int space;
-       int buffer_pointer;
-       int irq_status;
-
-       struct ene_device *dev = (struct ene_device *)data;
-       irq_status = ene_hw_irq_status(dev, &buffer_pointer);
-
-       if (!irq_status)
-               return IRQ_NONE;
-
-       /* TODO: only RX for now */
-       if (irq_status == ENE_IRQ_TX)
-               return IRQ_HANDLED;
-
-       for (i = 0; i < ENE_SAMPLES_SIZE; i++) {
-
-               hw_value = ene_hw_read_reg(dev,
-                               ENE_SAMPLE_BUFFER + buffer_pointer + i);
-
-               if (dev->fan_input_inuse) {
-                       /* read high part of the sample */
-                       hw_value |= ene_hw_read_reg(dev,
-                           ENE_SAMPLE_BUFFER_FAN + buffer_pointer + i) << 8;
-
-                       /* test for _space_ bit */
-                       space = !(hw_value & ENE_FAN_SMPL_PULS_MSK);
-
-                       /* clear space bit, and other unused bits */
-                       hw_value &= ENE_FAN_VALUE_MASK;
-                       hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN;
-
-               } else {
-                       space = hw_value & ENE_SAMPLE_SPC_MASK;
-                       hw_value &= ENE_SAMPLE_VALUE_MASK;
-                       hw_sample = hw_value * sample_period;
-               }
-
-               /* no more data */
-               if (!(hw_value))
-                       break;
-
-               if (space)
-                       hw_sample *= -1;
-
-               /* overflow sample recieved, handle it */
-
-               if (!dev->fan_input_inuse && hw_value == ENE_SAMPLE_OVERFLOW) {
-
-                       if (dev->idle)
-                               continue;
-
-                       if (dev->sample > 0 || abs(dev->sample) <= ENE_MAXGAP)
-                               update_sample(dev, hw_sample);
-                       else
-                               ene_set_idle(dev, 1);
-
-                       continue;
-               }
-
-               /* normal first sample recieved */
-               if (!dev->fan_input_inuse && dev->idle) {
-                       ene_set_idle(dev, 0);
-
-                       /* discard first recieved value, its random
-                          since its the time signal was off before
-                          first pulse if idle mode is enabled, HW
-                          does that for us */
-
-                       if (!enable_idle)
-                               continue;
-               }
-               update_sample(dev, hw_sample);
-               send_sample(dev);
-       }
-       return IRQ_HANDLED;
-}
-
-static int ene_probe(struct pnp_dev *pnp_dev,
-                    const struct pnp_device_id *dev_id)
-{
-       struct ene_device *dev;
-       struct lirc_driver *lirc_driver;
-       int error = -ENOMEM;
-
-       dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
-
-       if (!dev)
-               goto err1;
-
-       dev->pnp_dev = pnp_dev;
-       pnp_set_drvdata(pnp_dev, dev);
-
-
-       /* prepare lirc interface */
-       error = -ENOMEM;
-       lirc_driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
-
-       if (!lirc_driver)
-               goto err2;
-
-       dev->lirc_driver = lirc_driver;
-
-       strcpy(lirc_driver->name, ENE_DRIVER_NAME);
-       lirc_driver->minor = -1;
-       lirc_driver->code_length = sizeof(int) * 8;
-       lirc_driver->features = LIRC_CAN_REC_MODE2;
-       lirc_driver->data = dev;
-       lirc_driver->set_use_inc = ene_hw_init;
-       lirc_driver->set_use_dec = ene_hw_deinit;
-       lirc_driver->dev = &pnp_dev->dev;
-       lirc_driver->owner = THIS_MODULE;
-
-       lirc_driver->rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
-
-       if (!lirc_driver->rbuf)
-               goto err3;
-
-       if (lirc_buffer_init(lirc_driver->rbuf, sizeof(int), sizeof(int) * 256))
-               goto err4;
-
-       error = -ENODEV;
-       if (lirc_register_driver(lirc_driver))
-               goto err5;
-
-       /* validate resources */
-       if (!pnp_port_valid(pnp_dev, 0) ||
-           pnp_port_len(pnp_dev, 0) < ENE_MAX_IO)
-               goto err6;
-
-       if (!pnp_irq_valid(pnp_dev, 0))
-               goto err6;
-
-       dev->hw_io = pnp_port_start(pnp_dev, 0);
-       dev->irq = pnp_irq(pnp_dev, 0);
-
-       /* claim the resources */
-       error = -EBUSY;
-       if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME))
-               goto err6;
-
-       if (request_irq(dev->irq, ene_hw_irq,
-                       IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev))
-               goto err7;
-
-       /* detect hardware version and features */
-       error = ene_hw_detect(dev);
-       if (error)
-               goto err8;
-
-       ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n");
-       return 0;
-
-err8:
-       free_irq(dev->irq, dev);
-err7:
-       release_region(dev->hw_io, ENE_MAX_IO);
-err6:
-       lirc_unregister_driver(lirc_driver->minor);
-err5:
-       lirc_buffer_free(lirc_driver->rbuf);
-err4:
-       kfree(lirc_driver->rbuf);
-err3:
-       kfree(lirc_driver);
-err2:
-       kfree(dev);
-err1:
-       return error;
-}
-
-static void ene_remove(struct pnp_dev *pnp_dev)
-{
-       struct ene_device *dev = pnp_get_drvdata(pnp_dev);
-       ene_hw_deinit(dev);
-       free_irq(dev->irq, dev);
-       release_region(dev->hw_io, ENE_MAX_IO);
-       lirc_unregister_driver(dev->lirc_driver->minor);
-       lirc_buffer_free(dev->lirc_driver->rbuf);
-       kfree(dev->lirc_driver);
-       kfree(dev);
-}
-
-#ifdef CONFIG_PM
-
-/* TODO: make 'wake on IR' configurable and add .shutdown */
-/* currently impossible due to lack of kernel support */
-
-static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
-{
-       struct ene_device *dev = pnp_get_drvdata(pnp_dev);
-       ene_hw_write_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, ENE_FW1_WAKE);
-       return 0;
-}
-
-static int ene_resume(struct pnp_dev *pnp_dev)
-{
-       struct ene_device *dev = pnp_get_drvdata(pnp_dev);
-       if (dev->in_use)
-               ene_hw_init(dev);
-
-       ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_WAKE);
-       return 0;
-}
-
-#endif
-
-static const struct pnp_device_id ene_ids[] = {
-       {.id = "ENE0100",},
-       {},
-};
-
-static struct pnp_driver ene_driver = {
-       .name = ENE_DRIVER_NAME,
-       .id_table = ene_ids,
-       .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
-
-       .probe = ene_probe,
-       .remove = __devexit_p(ene_remove),
-
-#ifdef CONFIG_PM
-       .suspend = ene_suspend,
-       .resume = ene_resume,
-#endif
-};
-
-static int __init ene_init(void)
-{
-       if (sample_period < 5) {
-               ene_printk(KERN_ERR, "sample period must be at\n");
-               ene_printk(KERN_ERR, "least 5 us, (at least 30 recommended)\n");
-               return -EINVAL;
-       }
-       return pnp_register_driver(&ene_driver);
-}
-
-static void ene_exit(void)
-{
-       pnp_unregister_driver(&ene_driver);
-}
-
-module_param(sample_period, int, S_IRUGO);
-MODULE_PARM_DESC(sample_period, "Hardware sample period (75 us default)");
-
-module_param(enable_idle, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(enable_idle,
-       "Enables turning off signal sampling after long inactivity time; "
-       "if disabled might help detecting input signal (default: enabled)");
-
-module_param(enable_learning, bool, S_IRUGO);
-MODULE_PARM_DESC(enable_learning, "Use wide band (learning) reciever");
-
-MODULE_DEVICE_TABLE(pnp, ene_ids);
-MODULE_DESCRIPTION
-    ("LIRC driver for KB3926B/KB3926C/KB3926D (aka ENE0100) CIR port");
-MODULE_AUTHOR("Maxim Levitsky");
-MODULE_LICENSE("GPL");
-
-module_init(ene_init);
-module_exit(ene_exit);
index 09f36961c6d29279f24aa350112df15c6e3678cc..ec11c0e949a059476ff252c6ac7daeff4e75d414 100644 (file)
@@ -109,6 +109,7 @@ static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
 
 static DEFINE_SPINLOCK(hardware_lock);
 static DEFINE_SPINLOCK(dev_lock);
+static bool device_open;
 
 static int rx_buf[RBUF_LEN];
 unsigned int rx_tail, rx_head;
@@ -147,10 +148,11 @@ static void drop_port(void);
 static int lirc_open(struct inode *inode, struct file *file)
 {
        spin_lock(&dev_lock);
-       if (module_refcount(THIS_MODULE)) {
+       if (device_open) {
                spin_unlock(&dev_lock);
                return -EBUSY;
        }
+       device_open = true;
        spin_unlock(&dev_lock);
        return 0;
 }
@@ -158,6 +160,9 @@ static int lirc_open(struct inode *inode, struct file *file)
 
 static int lirc_close(struct inode *inode, struct file *file)
 {
+       spin_lock(&dev_lock);
+       device_open = false;
+       spin_unlock(&dev_lock);
        return 0;
 }
 
@@ -363,7 +368,6 @@ static struct lirc_driver driver = {
 };
 
 
-#ifdef MODULE
 static int init_chrdev(void)
 {
        driver.minor = lirc_register_driver(&driver);
@@ -380,7 +384,6 @@ static void drop_chrdev(void)
 {
        lirc_unregister_driver(driver.minor);
 }
-#endif
 
 
 /* SECTION: Hardware */
index a1ebd071640f7fe3393d7dda28b6c6ea421d1730..6da4a8c6ebc3b3367d23af5c31d3129d8af6f118 100644 (file)
@@ -240,7 +240,7 @@ static void irq_handler(void *blah)
        unsigned int level, newlevel;
        unsigned int timeout;
 
-       if (!module_refcount(THIS_MODULE))
+       if (!is_open)
                return;
 
        if (!is_claimed)
@@ -515,7 +515,7 @@ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
 
 static int lirc_open(struct inode *node, struct file *filep)
 {
-       if (module_refcount(THIS_MODULE) || !lirc_claim())
+       if (is_open || !lirc_claim())
                return -EBUSY;
 
        parport_enable_irq(pport);
diff --git a/drivers/staging/lirc/lirc_streamzap.c b/drivers/staging/lirc/lirc_streamzap.c
deleted file mode 100644 (file)
index be09c10..0000000
+++ /dev/null
@@ -1,821 +0,0 @@
-/*
- * Streamzap Remote Control driver
- *
- * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
- *
- * This driver was based on the work of Greg Wickham and Adrian
- * Dewhurst. It was substantially rewritten to support correct signal
- * gaps and now maintains a delay buffer, which is used to present
- * consistent timing behaviour to user space applications. Without the
- * delay buffer an ugly hack would be required in lircd, which can
- * cause sluggish signal decoding in certain situations.
- *
- * This driver is based on the USB skeleton driver packaged with the
- * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com)
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/smp_lock.h>
-#include <linux/completion.h>
-#include <linux/uaccess.h>
-#include <linux/usb.h>
-
-#include <media/lirc.h>
-#include <media/lirc_dev.h>
-
-#define DRIVER_VERSION "1.28"
-#define DRIVER_NAME    "lirc_streamzap"
-#define DRIVER_DESC    "Streamzap Remote Control driver"
-
-static int debug;
-
-#define USB_STREAMZAP_VENDOR_ID                0x0e9c
-#define USB_STREAMZAP_PRODUCT_ID       0x0000
-
-/* Use our own dbg macro */
-#define dprintk(fmt, args...)                                  \
-       do {                                                    \
-               if (debug)                                      \
-                       printk(KERN_DEBUG DRIVER_NAME "[%d]: "  \
-                              fmt "\n", ## args);              \
-       } while (0)
-
-/* table of devices that work with this driver */
-static struct usb_device_id streamzap_table[] = {
-       /* Streamzap Remote Control */
-       { USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
-       /* Terminating entry */
-       { }
-};
-
-MODULE_DEVICE_TABLE(usb, streamzap_table);
-
-#define STREAMZAP_PULSE_MASK 0xf0
-#define STREAMZAP_SPACE_MASK 0x0f
-#define STREAMZAP_TIMEOUT    0xff
-#define STREAMZAP_RESOLUTION 256
-
-/* number of samples buffered */
-#define STREAMZAP_BUF_LEN 128
-
-enum StreamzapDecoderState {
-       PulseSpace,
-       FullPulse,
-       FullSpace,
-       IgnorePulse
-};
-
-/* Structure to hold all of our device specific stuff
- *
- * some remarks regarding locking:
- * theoretically this struct can be accessed from three threads:
- *
- * - from lirc_dev through set_use_inc/set_use_dec
- *
- * - from the USB layer throuh probe/disconnect/irq
- *
- *   Careful placement of lirc_register_driver/lirc_unregister_driver
- *   calls will prevent conflicts. lirc_dev makes sure that
- *   set_use_inc/set_use_dec are not being executed and will not be
- *   called after lirc_unregister_driver returns.
- *
- * - by the timer callback
- *
- *   The timer is only running when the device is connected and the
- *   LIRC device is open. Making sure the timer is deleted by
- *   set_use_dec will make conflicts impossible.
- */
-struct usb_streamzap {
-
-       /* usb */
-       /* save off the usb device pointer */
-       struct usb_device       *udev;
-       /* the interface for this device */
-       struct usb_interface    *interface;
-
-       /* buffer & dma */
-       unsigned char           *buf_in;
-       dma_addr_t              dma_in;
-       unsigned int            buf_in_len;
-
-       struct usb_endpoint_descriptor *endpoint;
-
-       /* IRQ */
-       struct urb              *urb_in;
-
-       /* lirc */
-       struct lirc_driver      *driver;
-       struct lirc_buffer      *delay_buf;
-
-       /* timer used to support delay buffering */
-       struct timer_list       delay_timer;
-       int                     timer_running;
-       spinlock_t              timer_lock;
-
-       /* tracks whether we are currently receiving some signal */
-       int                     idle;
-       /* sum of signal lengths received since signal start */
-       unsigned long           sum;
-       /* start time of signal; necessary for gap tracking */
-       struct timeval          signal_last;
-       struct timeval          signal_start;
-       enum StreamzapDecoderState decoder_state;
-       struct timer_list       flush_timer;
-       int                     flush;
-       int                     in_use;
-       int                     timeout_enabled;
-};
-
-
-/* local function prototypes */
-static int streamzap_probe(struct usb_interface *interface,
-                          const struct usb_device_id *id);
-static void streamzap_disconnect(struct usb_interface *interface);
-static void usb_streamzap_irq(struct urb *urb);
-static int streamzap_use_inc(void *data);
-static void streamzap_use_dec(void *data);
-static long streamzap_ioctl(struct file *filep, unsigned int cmd,
-                           unsigned long arg);
-static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
-static int streamzap_resume(struct usb_interface *intf);
-
-/* usb specific object needed to register this driver with the usb subsystem */
-
-static struct usb_driver streamzap_driver = {
-       .name =         DRIVER_NAME,
-       .probe =        streamzap_probe,
-       .disconnect =   streamzap_disconnect,
-       .suspend =      streamzap_suspend,
-       .resume =       streamzap_resume,
-       .id_table =     streamzap_table,
-};
-
-static void stop_timer(struct usb_streamzap *sz)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&sz->timer_lock, flags);
-       if (sz->timer_running) {
-               sz->timer_running = 0;
-               spin_unlock_irqrestore(&sz->timer_lock, flags);
-               del_timer_sync(&sz->delay_timer);
-       } else {
-               spin_unlock_irqrestore(&sz->timer_lock, flags);
-       }
-}
-
-static void flush_timeout(unsigned long arg)
-{
-       struct usb_streamzap *sz = (struct usb_streamzap *) arg;
-
-       /* finally start accepting data */
-       sz->flush = 0;
-}
-static void delay_timeout(unsigned long arg)
-{
-       unsigned long flags;
-       /* deliver data every 10 ms */
-       static unsigned long timer_inc =
-               (10000/(1000000/HZ)) == 0 ? 1 : (10000/(1000000/HZ));
-       struct usb_streamzap *sz = (struct usb_streamzap *) arg;
-       int data;
-
-       spin_lock_irqsave(&sz->timer_lock, flags);
-
-       if (!lirc_buffer_empty(sz->delay_buf) &&
-           !lirc_buffer_full(sz->driver->rbuf)) {
-               lirc_buffer_read(sz->delay_buf, (unsigned char *) &data);
-               lirc_buffer_write(sz->driver->rbuf, (unsigned char *) &data);
-       }
-       if (!lirc_buffer_empty(sz->delay_buf)) {
-               while (lirc_buffer_available(sz->delay_buf) <
-                      STREAMZAP_BUF_LEN / 2 &&
-                      !lirc_buffer_full(sz->driver->rbuf)) {
-                       lirc_buffer_read(sz->delay_buf,
-                                          (unsigned char *) &data);
-                       lirc_buffer_write(sz->driver->rbuf,
-                                           (unsigned char *) &data);
-               }
-               if (sz->timer_running) {
-                       sz->delay_timer.expires = jiffies + timer_inc;
-                       add_timer(&sz->delay_timer);
-               }
-       } else {
-               sz->timer_running = 0;
-       }
-
-       if (!lirc_buffer_empty(sz->driver->rbuf))
-               wake_up(&sz->driver->rbuf->wait_poll);
-
-       spin_unlock_irqrestore(&sz->timer_lock, flags);
-}
-
-static void flush_delay_buffer(struct usb_streamzap *sz)
-{
-       int data;
-       int empty = 1;
-
-       while (!lirc_buffer_empty(sz->delay_buf)) {
-               empty = 0;
-               lirc_buffer_read(sz->delay_buf, (unsigned char *) &data);
-               if (!lirc_buffer_full(sz->driver->rbuf)) {
-                       lirc_buffer_write(sz->driver->rbuf,
-                                           (unsigned char *) &data);
-               } else {
-                       dprintk("buffer overflow", sz->driver->minor);
-               }
-       }
-       if (!empty)
-               wake_up(&sz->driver->rbuf->wait_poll);
-}
-
-static void push(struct usb_streamzap *sz, unsigned char *data)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&sz->timer_lock, flags);
-       if (lirc_buffer_full(sz->delay_buf)) {
-               int read_data;
-
-               lirc_buffer_read(sz->delay_buf,
-                                  (unsigned char *) &read_data);
-               if (!lirc_buffer_full(sz->driver->rbuf)) {
-                       lirc_buffer_write(sz->driver->rbuf,
-                                           (unsigned char *) &read_data);
-               } else {
-                       dprintk("buffer overflow", sz->driver->minor);
-               }
-       }
-
-       lirc_buffer_write(sz->delay_buf, data);
-
-       if (!sz->timer_running) {
-               sz->delay_timer.expires = jiffies + HZ/10;
-               add_timer(&sz->delay_timer);
-               sz->timer_running = 1;
-       }
-
-       spin_unlock_irqrestore(&sz->timer_lock, flags);
-}
-
-static void push_full_pulse(struct usb_streamzap *sz,
-                           unsigned char value)
-{
-       int pulse;
-
-       if (sz->idle) {
-               long deltv;
-               int tmp;
-
-               sz->signal_last = sz->signal_start;
-               do_gettimeofday(&sz->signal_start);
-
-               deltv = sz->signal_start.tv_sec-sz->signal_last.tv_sec;
-               if (deltv > 15) {
-                       /* really long time */
-                       tmp = LIRC_SPACE(LIRC_VALUE_MASK);
-               } else {
-                       tmp = (int) (deltv*1000000+
-                                       sz->signal_start.tv_usec -
-                                       sz->signal_last.tv_usec);
-                       tmp -= sz->sum;
-                       tmp = LIRC_SPACE(tmp);
-               }
-               dprintk("ls %u", sz->driver->minor, tmp);
-               push(sz, (char *)&tmp);
-
-               sz->idle = 0;
-               sz->sum = 0;
-       }
-
-       pulse = ((int) value) * STREAMZAP_RESOLUTION;
-       pulse += STREAMZAP_RESOLUTION / 2;
-       sz->sum += pulse;
-       pulse = LIRC_PULSE(pulse);
-
-       dprintk("p %u", sz->driver->minor, pulse & PULSE_MASK);
-       push(sz, (char *)&pulse);
-}
-
-static void push_half_pulse(struct usb_streamzap *sz,
-                           unsigned char value)
-{
-       push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK)>>4);
-}
-
-static void push_full_space(struct usb_streamzap *sz,
-                           unsigned char value)
-{
-       int space;
-
-       space = ((int) value)*STREAMZAP_RESOLUTION;
-       space += STREAMZAP_RESOLUTION/2;
-       sz->sum += space;
-       space = LIRC_SPACE(space);
-       dprintk("s %u", sz->driver->minor, space);
-       push(sz, (char *)&space);
-}
-
-static void push_half_space(struct usb_streamzap *sz,
-                           unsigned char value)
-{
-       push_full_space(sz, value & STREAMZAP_SPACE_MASK);
-}
-
-/**
- * usb_streamzap_irq - IRQ handler
- *
- * This procedure is invoked on reception of data from
- * the usb remote.
- */
-static void usb_streamzap_irq(struct urb *urb)
-{
-       struct usb_streamzap *sz;
-       int             len;
-       unsigned int    i = 0;
-
-       if (!urb)
-               return;
-
-       sz = urb->context;
-       len = urb->actual_length;
-
-       switch (urb->status) {
-       case -ECONNRESET:
-       case -ENOENT:
-       case -ESHUTDOWN:
-               /*
-                * this urb is terminated, clean up.
-                * sz might already be invalid at this point
-                */
-               dprintk("urb status: %d", -1, urb->status);
-               return;
-       default:
-               break;
-       }
-
-       dprintk("received %d", sz->driver->minor, urb->actual_length);
-       if (!sz->flush) {
-               for (i = 0; i < urb->actual_length; i++) {
-                       dprintk("%d: %x", sz->driver->minor,
-                               i, (unsigned char) sz->buf_in[i]);
-                       switch (sz->decoder_state) {
-                       case PulseSpace:
-                               if ((sz->buf_in[i]&STREAMZAP_PULSE_MASK) ==
-                                   STREAMZAP_PULSE_MASK) {
-                                       sz->decoder_state = FullPulse;
-                                       continue;
-                               } else if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK)
-                                          == STREAMZAP_SPACE_MASK) {
-                                       push_half_pulse(sz, sz->buf_in[i]);
-                                       sz->decoder_state = FullSpace;
-                                       continue;
-                               } else {
-                                       push_half_pulse(sz, sz->buf_in[i]);
-                                       push_half_space(sz, sz->buf_in[i]);
-                               }
-                               break;
-                       case FullPulse:
-                               push_full_pulse(sz, sz->buf_in[i]);
-                               sz->decoder_state = IgnorePulse;
-                               break;
-                       case FullSpace:
-                               if (sz->buf_in[i] == STREAMZAP_TIMEOUT) {
-                                       sz->idle = 1;
-                                       stop_timer(sz);
-                                       if (sz->timeout_enabled) {
-                                               int timeout =
-                                                       LIRC_TIMEOUT
-                                                       (STREAMZAP_TIMEOUT *
-                                                       STREAMZAP_RESOLUTION);
-                                               push(sz, (char *)&timeout);
-                                       }
-                                       flush_delay_buffer(sz);
-                               } else
-                                       push_full_space(sz, sz->buf_in[i]);
-                               sz->decoder_state = PulseSpace;
-                               break;
-                       case IgnorePulse:
-                               if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
-                                   STREAMZAP_SPACE_MASK) {
-                                       sz->decoder_state = FullSpace;
-                                       continue;
-                               }
-                               push_half_space(sz, sz->buf_in[i]);
-                               sz->decoder_state = PulseSpace;
-                               break;
-                       }
-               }
-       }
-
-       usb_submit_urb(urb, GFP_ATOMIC);
-
-       return;
-}
-
-static const struct file_operations streamzap_fops = {
-       .owner          = THIS_MODULE,
-       .unlocked_ioctl = streamzap_ioctl,
-       .read           = lirc_dev_fop_read,
-       .write          = lirc_dev_fop_write,
-       .poll           = lirc_dev_fop_poll,
-       .open           = lirc_dev_fop_open,
-       .release        = lirc_dev_fop_close,
-};
-
-
-/**
- *     streamzap_probe
- *
- *     Called by usb-core to associated with a candidate device
- *     On any failure the return value is the ERROR
- *     On success return 0
- */
-static int streamzap_probe(struct usb_interface *interface,
-                          const struct usb_device_id *id)
-{
-       struct usb_device *udev = interface_to_usbdev(interface);
-       struct usb_host_interface *iface_host;
-       struct usb_streamzap *sz;
-       struct lirc_driver *driver;
-       struct lirc_buffer *lirc_buf;
-       struct lirc_buffer *delay_buf;
-       char buf[63], name[128] = "";
-       int retval = -ENOMEM;
-       int minor = 0;
-
-       /* Allocate space for device driver specific data */
-       sz = kzalloc(sizeof(struct usb_streamzap), GFP_KERNEL);
-       if (sz == NULL)
-               return -ENOMEM;
-
-       sz->udev = udev;
-       sz->interface = interface;
-
-       /* Check to ensure endpoint information matches requirements */
-       iface_host = interface->cur_altsetting;
-
-       if (iface_host->desc.bNumEndpoints != 1) {
-               err("%s: Unexpected desc.bNumEndpoints (%d)", __func__,
-                   iface_host->desc.bNumEndpoints);
-               retval = -ENODEV;
-               goto free_sz;
-       }
-
-       sz->endpoint = &(iface_host->endpoint[0].desc);
-       if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
-           != USB_DIR_IN) {
-               err("%s: endpoint doesn't match input device 02%02x",
-                   __func__, sz->endpoint->bEndpointAddress);
-               retval = -ENODEV;
-               goto free_sz;
-       }
-
-       if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-           != USB_ENDPOINT_XFER_INT) {
-               err("%s: endpoint attributes don't match xfer 02%02x",
-                   __func__, sz->endpoint->bmAttributes);
-               retval = -ENODEV;
-               goto free_sz;
-       }
-
-       if (sz->endpoint->wMaxPacketSize == 0) {
-               err("%s: endpoint message size==0? ", __func__);
-               retval = -ENODEV;
-               goto free_sz;
-       }
-
-       /* Allocate the USB buffer and IRQ URB */
-
-       sz->buf_in_len = sz->endpoint->wMaxPacketSize;
-       sz->buf_in = usb_alloc_coherent(sz->udev, sz->buf_in_len,
-                                     GFP_ATOMIC, &sz->dma_in);
-       if (sz->buf_in == NULL)
-               goto free_sz;
-
-       sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
-       if (sz->urb_in == NULL)
-               goto free_sz;
-
-       /* Connect this device to the LIRC sub-system */
-       driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
-       if (!driver)
-               goto free_sz;
-
-       lirc_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
-       if (!lirc_buf)
-               goto free_driver;
-       if (lirc_buffer_init(lirc_buf, sizeof(int), STREAMZAP_BUF_LEN))
-               goto kfree_lirc_buf;
-
-       delay_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
-       if (!delay_buf)
-               goto free_lirc_buf;
-       if (lirc_buffer_init(delay_buf, sizeof(int), STREAMZAP_BUF_LEN))
-               goto kfree_delay_buf;
-
-       sz->driver = driver;
-       strcpy(sz->driver->name, DRIVER_NAME);
-       sz->driver->minor = -1;
-       sz->driver->sample_rate = 0;
-       sz->driver->code_length = sizeof(int) * 8;
-       sz->driver->features = LIRC_CAN_REC_MODE2 |
-               LIRC_CAN_GET_REC_RESOLUTION |
-               LIRC_CAN_SET_REC_TIMEOUT;
-       sz->driver->data = sz;
-       sz->driver->min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION;
-       sz->driver->max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION;
-       sz->driver->rbuf = lirc_buf;
-       sz->delay_buf = delay_buf;
-       sz->driver->set_use_inc = &streamzap_use_inc;
-       sz->driver->set_use_dec = &streamzap_use_dec;
-       sz->driver->fops = &streamzap_fops;
-       sz->driver->dev = &interface->dev;
-       sz->driver->owner = THIS_MODULE;
-
-       sz->idle = 1;
-       sz->decoder_state = PulseSpace;
-       init_timer(&sz->delay_timer);
-       sz->delay_timer.function = delay_timeout;
-       sz->delay_timer.data = (unsigned long) sz;
-       sz->timer_running = 0;
-       spin_lock_init(&sz->timer_lock);
-
-       init_timer(&sz->flush_timer);
-       sz->flush_timer.function = flush_timeout;
-       sz->flush_timer.data = (unsigned long) sz;
-       /* Complete final initialisations */
-
-       usb_fill_int_urb(sz->urb_in, udev,
-               usb_rcvintpipe(udev, sz->endpoint->bEndpointAddress),
-               sz->buf_in, sz->buf_in_len, usb_streamzap_irq, sz,
-               sz->endpoint->bInterval);
-       sz->urb_in->transfer_dma = sz->dma_in;
-       sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
-       if (udev->descriptor.iManufacturer
-           && usb_string(udev, udev->descriptor.iManufacturer,
-                         buf, sizeof(buf)) > 0)
-               strlcpy(name, buf, sizeof(name));
-
-       if (udev->descriptor.iProduct
-           && usb_string(udev, udev->descriptor.iProduct,
-                         buf, sizeof(buf)) > 0)
-               snprintf(name + strlen(name), sizeof(name) - strlen(name),
-                        " %s", buf);
-
-       minor = lirc_register_driver(driver);
-
-       if (minor < 0)
-               goto free_delay_buf;
-
-       sz->driver->minor = minor;
-
-       usb_set_intfdata(interface, sz);
-
-       printk(KERN_INFO DRIVER_NAME "[%d]: %s on usb%d:%d attached\n",
-              sz->driver->minor, name,
-              udev->bus->busnum, sz->udev->devnum);
-
-       return 0;
-
-free_delay_buf:
-       lirc_buffer_free(sz->delay_buf);
-kfree_delay_buf:
-       kfree(delay_buf);
-free_lirc_buf:
-       lirc_buffer_free(sz->driver->rbuf);
-kfree_lirc_buf:
-       kfree(lirc_buf);
-free_driver:
-       kfree(driver);
-free_sz:
-       if (retval == -ENOMEM)
-               err("Out of memory");
-
-       if (sz) {
-               usb_free_urb(sz->urb_in);
-               usb_free_coherent(udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
-               kfree(sz);
-       }
-
-       return retval;
-}
-
-static int streamzap_use_inc(void *data)
-{
-       struct usb_streamzap *sz = data;
-
-       if (!sz) {
-               dprintk("%s called with no context", -1, __func__);
-               return -EINVAL;
-       }
-       dprintk("set use inc", sz->driver->minor);
-
-       lirc_buffer_clear(sz->driver->rbuf);
-       lirc_buffer_clear(sz->delay_buf);
-
-       sz->flush_timer.expires = jiffies + HZ;
-       sz->flush = 1;
-       add_timer(&sz->flush_timer);
-
-       sz->urb_in->dev = sz->udev;
-       if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
-               dprintk("open result = -EIO error submitting urb",
-                       sz->driver->minor);
-               return -EIO;
-       }
-       sz->in_use++;
-
-       return 0;
-}
-
-static void streamzap_use_dec(void *data)
-{
-       struct usb_streamzap *sz = data;
-
-       if (!sz) {
-               dprintk("%s called with no context", -1, __func__);
-               return;
-       }
-       dprintk("set use dec", sz->driver->minor);
-
-       if (sz->flush) {
-               sz->flush = 0;
-               del_timer_sync(&sz->flush_timer);
-       }
-
-       usb_kill_urb(sz->urb_in);
-
-       stop_timer(sz);
-
-       sz->in_use--;
-}
-
-static long streamzap_ioctl(struct file *filep, unsigned int cmd,
-                           unsigned long arg)
-{
-       int result = 0;
-       int val;
-       struct usb_streamzap *sz = lirc_get_pdata(filep);
-
-       switch (cmd) {
-       case LIRC_GET_REC_RESOLUTION:
-               result = put_user(STREAMZAP_RESOLUTION, (unsigned int *) arg);
-               break;
-       case LIRC_SET_REC_TIMEOUT:
-               result = get_user(val, (int *)arg);
-               if (result == 0) {
-                       if (val == STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION)
-                               sz->timeout_enabled = 1;
-                       else if (val == 0)
-                               sz->timeout_enabled = 0;
-                       else
-                               result = -EINVAL;
-               }
-               break;
-       default:
-               return lirc_dev_fop_ioctl(filep, cmd, arg);
-       }
-       return result;
-}
-
-/**
- * streamzap_disconnect
- *
- * Called by the usb core when the device is removed from the system.
- *
- * This routine guarantees that the driver will not submit any more urbs
- * by clearing dev->udev.  It is also supposed to terminate any currently
- * active urbs.  Unfortunately, usb_bulk_msg(), used in streamzap_read(),
- * does not provide any way to do this.
- */
-static void streamzap_disconnect(struct usb_interface *interface)
-{
-       struct usb_streamzap *sz;
-       int errnum;
-       int minor;
-
-       sz = usb_get_intfdata(interface);
-
-       /* unregister from the LIRC sub-system */
-
-       errnum = lirc_unregister_driver(sz->driver->minor);
-       if (errnum != 0)
-               dprintk("error in lirc_unregister: (returned %d)",
-                       sz->driver->minor, errnum);
-
-       lirc_buffer_free(sz->delay_buf);
-       lirc_buffer_free(sz->driver->rbuf);
-
-       /* unregister from the USB sub-system */
-
-       usb_free_urb(sz->urb_in);
-
-       usb_free_coherent(sz->udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
-
-       minor = sz->driver->minor;
-       kfree(sz->driver->rbuf);
-       kfree(sz->driver);
-       kfree(sz->delay_buf);
-       kfree(sz);
-
-       printk(KERN_INFO DRIVER_NAME "[%d]: disconnected\n", minor);
-}
-
-static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
-{
-       struct usb_streamzap *sz = usb_get_intfdata(intf);
-
-       printk(KERN_INFO DRIVER_NAME "[%d]: suspend\n", sz->driver->minor);
-       if (sz->in_use) {
-               if (sz->flush) {
-                       sz->flush = 0;
-                       del_timer_sync(&sz->flush_timer);
-               }
-
-               stop_timer(sz);
-
-               usb_kill_urb(sz->urb_in);
-       }
-       return 0;
-}
-
-static int streamzap_resume(struct usb_interface *intf)
-{
-       struct usb_streamzap *sz = usb_get_intfdata(intf);
-
-       lirc_buffer_clear(sz->driver->rbuf);
-       lirc_buffer_clear(sz->delay_buf);
-
-       if (sz->in_use) {
-               sz->flush_timer.expires = jiffies + HZ;
-               sz->flush = 1;
-               add_timer(&sz->flush_timer);
-
-               sz->urb_in->dev = sz->udev;
-               if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
-                       dprintk("open result = -EIO error submitting urb",
-                               sz->driver->minor);
-                       return -EIO;
-               }
-       }
-       return 0;
-}
-
-/**
- *     usb_streamzap_init
- */
-static int __init usb_streamzap_init(void)
-{
-       int result;
-
-       /* register this driver with the USB subsystem */
-       result = usb_register(&streamzap_driver);
-
-       if (result) {
-               err("usb_register failed. Error number %d",
-                   result);
-               return result;
-       }
-
-       printk(KERN_INFO DRIVER_NAME " " DRIVER_VERSION " registered\n");
-       return 0;
-}
-
-/**
- *     usb_streamzap_exit
- */
-static void __exit usb_streamzap_exit(void)
-{
-       usb_deregister(&streamzap_driver);
-}
-
-
-module_init(usb_streamzap_init);
-module_exit(usb_streamzap_exit);
-
-MODULE_AUTHOR("Christoph Bartelmus, Greg Wickham, Adrian Dewhurst");
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable debugging messages");
index 047f7e6edb86f12dc38b55dc94e274fb38ef30bf..61490c6dcdbdd74cd77ab0411563a6b4da3b71e1 100644 (file)
@@ -277,6 +277,7 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_RGB565  v4l2_fourcc('R', 'G', 'B', 'P') /* 16  RGB-5-6-5     */
 #define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16  RGB-5-5-5 BE  */
 #define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16  RGB-5-6-5 BE  */
+#define V4L2_PIX_FMT_BGR666  v4l2_fourcc('B', 'G', 'R', 'H') /* 18  BGR-6-6-6    */
 #define V4L2_PIX_FMT_BGR24   v4l2_fourcc('B', 'G', 'R', '3') /* 24  BGR-8-8-8     */
 #define V4L2_PIX_FMT_RGB24   v4l2_fourcc('R', 'G', 'B', '3') /* 24  RGB-8-8-8     */
 #define V4L2_PIX_FMT_BGR32   v4l2_fourcc('B', 'G', 'R', '4') /* 32  BGR-8-8-8-8   */
index 9ebe8558b9b69f0cd5d5e3f69d426e803ce4103f..8d08ebfe20b76db1aa920c8487430ecd670f6ece 100644 (file)
@@ -19,6 +19,8 @@
 #ifndef CX2341X_H
 #define CX2341X_H
 
+#include <media/v4l2-ctrls.h>
+
 enum cx2341x_port {
        CX2341X_PORT_MEMORY    = 0,
        CX2341X_PORT_STREAMING = 1,
@@ -99,6 +101,101 @@ int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy,
 void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p);
 void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix);
 
+struct cx2341x_handler;
+
+struct cx2341x_handler_ops {
+       /* needed for the video clock freq */
+       int (*s_audio_sampling_freq)(struct cx2341x_handler *hdl, u32 val);
+       /* needed for dualwatch */
+       int (*s_audio_mode)(struct cx2341x_handler *hdl, u32 val);
+       /* needed for setting up the video resolution */
+       int (*s_video_encoding)(struct cx2341x_handler *hdl, u32 val);
+       /* needed for setting up the sliced vbi insertion data structures */
+       int (*s_stream_vbi_fmt)(struct cx2341x_handler *hdl, u32 val);
+};
+
+struct cx2341x_handler {
+       u32 capabilities;
+       enum cx2341x_port port;
+       u16 width;
+       u16 height;
+       u16 is_50hz;
+       u32 audio_properties;
+
+       struct v4l2_ctrl_handler hdl;
+       void *priv;
+       cx2341x_mbox_func func;
+       const struct cx2341x_handler_ops *ops;
+
+       struct v4l2_ctrl *stream_vbi_fmt;
+
+       struct {
+               /* audio cluster */
+               struct v4l2_ctrl *audio_sampling_freq;
+               struct v4l2_ctrl *audio_encoding;
+               struct v4l2_ctrl *audio_l2_bitrate;
+               struct v4l2_ctrl *audio_mode;
+               struct v4l2_ctrl *audio_mode_extension;
+               struct v4l2_ctrl *audio_emphasis;
+               struct v4l2_ctrl *audio_crc;
+               struct v4l2_ctrl *audio_ac3_bitrate;
+       };
+
+       struct {
+               /* video gop cluster */
+               struct v4l2_ctrl *video_b_frames;
+               struct v4l2_ctrl *video_gop_size;
+       };
+
+       struct {
+               /* stream type cluster */
+               struct v4l2_ctrl *stream_type;
+               struct v4l2_ctrl *video_encoding;
+               struct v4l2_ctrl *video_bitrate_mode;
+               struct v4l2_ctrl *video_bitrate;
+               struct v4l2_ctrl *video_bitrate_peak;
+       };
+
+       struct {
+               /* video mute cluster */
+               struct v4l2_ctrl *video_mute;
+               struct v4l2_ctrl *video_mute_yuv;
+       };
+
+       struct {
+               /* video filter mode cluster */
+               struct v4l2_ctrl *video_spatial_filter_mode;
+               struct v4l2_ctrl *video_temporal_filter_mode;
+               struct v4l2_ctrl *video_median_filter_type;
+       };
+
+       struct {
+               /* video filter type cluster */
+               struct v4l2_ctrl *video_luma_spatial_filter_type;
+               struct v4l2_ctrl *video_chroma_spatial_filter_type;
+       };
+
+       struct  {
+               /* video filter cluster */
+               struct v4l2_ctrl *video_spatial_filter;
+               struct v4l2_ctrl *video_temporal_filter;
+       };
+
+       struct {
+               /* video median cluster */
+               struct v4l2_ctrl *video_luma_median_filter_top;
+               struct v4l2_ctrl *video_luma_median_filter_bottom;
+               struct v4l2_ctrl *video_chroma_median_filter_top;
+               struct v4l2_ctrl *video_chroma_median_filter_bottom;
+       };
+};
+
+int cx2341x_handler_init(struct cx2341x_handler *cxhdl,
+                        unsigned nr_of_controls_hint);
+void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz);
+int cx2341x_handler_setup(struct cx2341x_handler *cxhdl);
+void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy);
+
 /* Firmware names */
 #define CX2341X_FIRM_ENC_FILENAME "v4l-cx2341x-enc.fw"
 /* Decoder firmware for the cx23415 only */
index 0b0cb177679663e8ae8c5405fde80dc398fc24fa..46d1a141208ef5fc9e768615ce21ba5a05c1d510 100644 (file)
@@ -97,4 +97,91 @@ enum cx25840_audio_input {
        CX25840_AUDIO8,
 };
 
+enum cx25840_io_pin {
+       CX25840_PIN_DVALID_PRGM0 = 0,
+       CX25840_PIN_FIELD_PRGM1,
+       CX25840_PIN_HRESET_PRGM2,
+       CX25840_PIN_VRESET_HCTL_PRGM3,
+       CX25840_PIN_IRQ_N_PRGM4,
+       CX25840_PIN_IR_TX_PRGM6,
+       CX25840_PIN_IR_RX_PRGM5,
+       CX25840_PIN_GPIO0_PRGM8,
+       CX25840_PIN_GPIO1_PRGM9,
+       CX25840_PIN_SA_SDIN,            /* Alternate GP Input only */
+       CX25840_PIN_SA_SDOUT,           /* Alternate GP Input only */
+       CX25840_PIN_PLL_CLK_PRGM7,
+       CX25840_PIN_CHIP_SEL_VIPCLK,    /* Output only */
+};
+
+enum cx25840_io_pad {
+       /* Output pads */
+       CX25840_PAD_DEFAULT = 0,
+       CX25840_PAD_ACTIVE,
+       CX25840_PAD_VACTIVE,
+       CX25840_PAD_CBFLAG,
+       CX25840_PAD_VID_DATA_EXT0,
+       CX25840_PAD_VID_DATA_EXT1,
+       CX25840_PAD_GPO0,
+       CX25840_PAD_GPO1,
+       CX25840_PAD_GPO2,
+       CX25840_PAD_GPO3,
+       CX25840_PAD_IRQ_N,
+       CX25840_PAD_AC_SYNC,
+       CX25840_PAD_AC_SDOUT,
+       CX25840_PAD_PLL_CLK,
+       CX25840_PAD_VRESET,
+       CX25840_PAD_RESERVED,
+       /* Pads for PLL_CLK output only */
+       CX25840_PAD_XTI_X5_DLL,
+       CX25840_PAD_AUX_PLL,
+       CX25840_PAD_VID_PLL,
+       CX25840_PAD_XTI,
+       /* Input Pads */
+       CX25840_PAD_GPI0,
+       CX25840_PAD_GPI1,
+       CX25840_PAD_GPI2,
+       CX25840_PAD_GPI3,
+};
+
+enum cx25840_io_pin_strength {
+       CX25840_PIN_DRIVE_MEDIUM = 0,
+       CX25840_PIN_DRIVE_SLOW,
+       CX25840_PIN_DRIVE_FAST,
+};
+
+enum cx23885_io_pin {
+       CX23885_PIN_IR_RX_GPIO19,
+       CX23885_PIN_IR_TX_GPIO20,
+       CX23885_PIN_I2S_SDAT_GPIO21,
+       CX23885_PIN_I2S_WCLK_GPIO22,
+       CX23885_PIN_I2S_BCLK_GPIO23,
+       CX23885_PIN_IRQ_N_GPIO16,
+};
+
+enum cx23885_io_pad {
+       CX23885_PAD_IR_RX,
+       CX23885_PAD_GPIO19,
+       CX23885_PAD_IR_TX,
+       CX23885_PAD_GPIO20,
+       CX23885_PAD_I2S_SDAT,
+       CX23885_PAD_GPIO21,
+       CX23885_PAD_I2S_WCLK,
+       CX23885_PAD_GPIO22,
+       CX23885_PAD_I2S_BCLK,
+       CX23885_PAD_GPIO23,
+       CX23885_PAD_IRQ_N,
+       CX23885_PAD_GPIO16,
+};
+
+/* pvr150_workaround activates a workaround for a hardware bug that is
+   present in Hauppauge PVR-150 (and possibly PVR-500) cards that have
+   certain NTSC tuners (tveeprom tuner model numbers 85, 99 and 112). The
+   audio autodetect fails on some channels for these models and the workaround
+   is to select the audio standard explicitly. Many thanks to Hauppauge for
+   providing this information.
+   This platform data only needs to be supplied by the ivtv driver. */
+struct cx25840_platform_data {
+       int pvr150_workaround;
+};
+
 #endif
index 513e60dd101018f265622521fde93606aa8dbd12..eb7fddf8f6075f22ab21fff06007a6cf84df5220 100644 (file)
@@ -41,6 +41,11 @@ enum rc_driver_type {
  *     anything with it. Yet, as the same keycode table can be used with other
  *     devices, a mask is provided to allow its usage. Drivers should generally
  *     leave this field in blank
+ * @timeout: optional time after which device stops sending data
+ * @min_timeout: minimum timeout supported by device
+ * @max_timeout: maximum timeout supported by device
+ * @rx_resolution : resolution (in ns) of input sampler
+ * @tx_resolution: resolution (in ns) of output sampler
  * @priv: driver-specific data, to be used on the callbacks
  * @change_protocol: allow changing the protocol used on hardware decoders
  * @open: callback to allow drivers to enable polling/irq when IR input device
@@ -49,19 +54,36 @@ enum rc_driver_type {
  *     is opened.
  * @s_tx_mask: set transmitter mask (for devices with multiple tx outputs)
  * @s_tx_carrier: set transmit carrier frequency
+ * @s_tx_duty_cycle: set transmit duty cycle (0% - 100%)
+ * @s_rx_carrier: inform driver about carrier it is expected to handle
  * @tx_ir: transmit IR
+ * @s_idle: optional: enable/disable hardware idle mode, upon which,
+       device doesn't interrupt host until it sees IR pulses
+ * @s_learning_mode: enable wide band receiver used for learning
  */
 struct ir_dev_props {
        enum rc_driver_type     driver_type;
        unsigned long           allowed_protos;
        u32                     scanmask;
+
+       u32                     timeout;
+       u32                     min_timeout;
+       u32                     max_timeout;
+
+       u32                     rx_resolution;
+       u32                     tx_resolution;
+
        void                    *priv;
        int                     (*change_protocol)(void *priv, u64 ir_type);
        int                     (*open)(void *priv);
        void                    (*close)(void *priv);
        int                     (*s_tx_mask)(void *priv, u32 mask);
        int                     (*s_tx_carrier)(void *priv, u32 carrier);
+       int                     (*s_tx_duty_cycle)(void *priv, u32 duty_cycle);
+       int                     (*s_rx_carrier_range)(void *priv, u32 min, u32 max);
        int                     (*tx_ir)(void *priv, int *txbuf, u32 n);
+       void                    (*s_idle)(void *priv, int enable);
+       int                     (*s_learning_mode)(void *priv, int enable);
 };
 
 struct ir_input_dev {
@@ -69,9 +91,10 @@ struct ir_input_dev {
        char                            *driver_name;   /* Name of the driver module */
        struct ir_scancode_table        rc_tab;         /* scan/key table */
        unsigned long                   devno;          /* device number */
-       const struct ir_dev_props       *props;         /* Device properties */
+       struct ir_dev_props             *props;         /* Device properties */
        struct ir_raw_event_ctrl        *raw;           /* for raw pulse/space events */
        struct input_dev                *input_dev;     /* the input device associated with this device */
+       bool                            idle;
 
        /* key info - needed by IR keycode handlers */
        spinlock_t                      keylock;        /* protects the below members */
@@ -95,12 +118,12 @@ enum raw_event_type {
 /* From ir-keytable.c */
 int __ir_input_register(struct input_dev *dev,
                      const struct ir_scancode_table *ir_codes,
-                     const struct ir_dev_props *props,
+                     struct ir_dev_props *props,
                      const char *driver_name);
 
 static inline int ir_input_register(struct input_dev *dev,
                      const char *map_name,
-                     const struct ir_dev_props *props,
+                     struct ir_dev_props *props,
                      const char *driver_name) {
        struct ir_scancode_table *ir_codes;
        struct ir_input_dev *ir_dev;
@@ -110,8 +133,12 @@ static inline int ir_input_register(struct input_dev *dev,
                return -EINVAL;
 
        ir_codes = get_rc_map(map_name);
-       if (!ir_codes)
-               return -EINVAL;
+       if (!ir_codes) {
+               ir_codes = get_rc_map(RC_MAP_EMPTY);
+
+               if (!ir_codes)
+                       return -EINVAL;
+       }
 
        rc = __ir_input_register(dev, ir_codes, props, driver_name);
        if (rc < 0)
@@ -144,6 +171,10 @@ struct ir_raw_event {
 void ir_raw_event_handle(struct input_dev *input_dev);
 int ir_raw_event_store(struct input_dev *input_dev, struct ir_raw_event *ev);
 int ir_raw_event_store_edge(struct input_dev *input_dev, enum raw_event_type type);
+int ir_raw_event_store_with_filter(struct input_dev *input_dev,
+                               struct ir_raw_event *ev);
+void ir_raw_event_set_idle(struct input_dev *input_dev, int idle);
+
 static inline void ir_raw_event_reset(struct input_dev *input_dev)
 {
        struct ir_raw_event ev = { .pulse = false, .duration = 0 };
index 42c467c50519a9011f2ac37d47b262a8d05975ce..6678a169fd9e2643b814b10d0a90cc2fa1df709a 100644 (file)
@@ -77,6 +77,7 @@
 #define LIRC_CAN_SET_REC_FILTER           0x08000000
 
 #define LIRC_CAN_MEASURE_CARRIER          0x02000000
+#define LIRC_CAN_USE_WIDEBAND_RECEIVER    0x04000000
 
 #define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK)
 #define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK)
  * if enabled from the next key press on the driver will send
  * LIRC_MODE2_FREQUENCY packets
  */
-#define LIRC_SET_MEASURE_CARRIER_MODE  _IOW('i', 0x0000001d, __u32)
+#define LIRC_SET_MEASURE_CARRIER_MODE  _IOW('i', 0x0000001d, __u32)
 
 /*
  * to set a range use
 #define LIRC_SETUP_START               _IO('i', 0x00000021)
 #define LIRC_SETUP_END                 _IO('i', 0x00000022)
 
+#define LIRC_SET_WIDEBAND_RECEIVER     _IOW('i', 0x00000023, __u32)
+
 #endif
index 9569d0863f8b98514d17b316b8ccce2d4d9379e5..a9c041d49662592e022d409e4b60de9f07e43300 100644 (file)
@@ -114,6 +114,7 @@ void rc_map_init(void);
 #define RC_MAP_PURPLETV                  "rc-purpletv"
 #define RC_MAP_PV951                     "rc-pv951"
 #define RC_MAP_RC5_HAUPPAUGE_NEW         "rc-rc5-hauppauge-new"
+#define RC_MAP_RC5_STREAMZAP             "rc-rc5-streamzap"
 #define RC_MAP_RC5_TV                    "rc-rc5-tv"
 #define RC_MAP_RC6_MCE                   "rc-rc6-mce"
 #define RC_MAP_REAL_AUDIO_220_32_KEYS    "rc-real-audio-220-32-keys"
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
new file mode 100644 (file)
index 0000000..9b7bea9
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+    V4L2 controls support header.
+
+    Copyright (C) 2010  Hans Verkuil <hverkuil@xs4all.nl>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _V4L2_CTRLS_H
+#define _V4L2_CTRLS_H
+
+#include <linux/list.h>
+#include <linux/device.h>
+
+/* forward references */
+struct v4l2_ctrl_handler;
+struct v4l2_ctrl;
+struct video_device;
+struct v4l2_subdev;
+
+/** struct v4l2_ctrl_ops - The control operations that the driver has to provide.
+  * @g_volatile_ctrl: Get a new value for this control. Generally only relevant
+  *            for volatile (and usually read-only) controls such as a control
+  *            that returns the current signal strength which changes
+  *            continuously.
+  *            If not set, then the currently cached value will be returned.
+  * @try_ctrl: Test whether the control's value is valid. Only relevant when
+  *            the usual min/max/step checks are not sufficient.
+  * @s_ctrl:   Actually set the new control value. s_ctrl is compulsory. The
+  *            ctrl->handler->lock is held when these ops are called, so no
+  *            one else can access controls owned by that handler.
+  */
+struct v4l2_ctrl_ops {
+       int (*g_volatile_ctrl)(struct v4l2_ctrl *ctrl);
+       int (*try_ctrl)(struct v4l2_ctrl *ctrl);
+       int (*s_ctrl)(struct v4l2_ctrl *ctrl);
+};
+
+/** struct v4l2_ctrl - The control structure.
+  * @node:     The list node.
+  * @handler:  The handler that owns the control.
+  * @cluster:  Point to start of cluster array.
+  * @ncontrols:        Number of controls in cluster array.
+  * @has_new:  Internal flag: set when there is a valid new value.
+  * @done:     Internal flag: set for each processed control.
+  * @is_private: If set, then this control is private to its handler and it
+  *            will not be added to any other handlers. Drivers can set
+  *            this flag.
+  * @is_volatile: If set, then this control is volatile. This means that the
+  *            control's current value cannot be cached and needs to be
+  *            retrieved through the g_volatile_ctrl op. Drivers can set
+  *            this flag.
+  * @ops:      The control ops.
+  * @id:       The control ID.
+  * @name:     The control name.
+  * @type:     The control type.
+  * @minimum:  The control's minimum value.
+  * @maximum:  The control's maximum value.
+  * @default_value: The control's default value.
+  * @step:     The control's step value for non-menu controls.
+  * @menu_skip_mask: The control's skip mask for menu controls. This makes it
+  *            easy to skip menu items that are not valid. If bit X is set,
+  *            then menu item X is skipped. Of course, this only works for
+  *            menus with <= 32 menu items. There are no menus that come
+  *            close to that number, so this is OK. Should we ever need more,
+  *            then this will have to be extended to a u64 or a bit array.
+  * @qmenu:    A const char * array for all menu items. Array entries that are
+  *            empty strings ("") correspond to non-existing menu items (this
+  *            is in addition to the menu_skip_mask above). The last entry
+  *            must be NULL.
+  * @flags:    The control's flags.
+  * @cur:      The control's current value.
+  * @val:      The control's new s32 value.
+  * @val64:    The control's new s64 value.
+  * @string:   The control's new string value.
+  * @priv:     The control's private pointer. For use by the driver. It is
+  *            untouched by the control framework. Note that this pointer is
+  *            not freed when the control is deleted. Should this be needed
+  *            then a new internal bitfield can be added to tell the framework
+  *            to free this pointer.
+  */
+struct v4l2_ctrl {
+       /* Administrative fields */
+       struct list_head node;
+       struct v4l2_ctrl_handler *handler;
+       struct v4l2_ctrl **cluster;
+       unsigned ncontrols;
+       unsigned int has_new:1;
+       unsigned int done:1;
+
+       unsigned int is_private:1;
+       unsigned int is_volatile:1;
+
+       const struct v4l2_ctrl_ops *ops;
+       u32 id;
+       const char *name;
+       enum v4l2_ctrl_type type;
+       s32 minimum, maximum, default_value;
+       union {
+               u32 step;
+               u32 menu_skip_mask;
+       };
+       const char **qmenu;
+       unsigned long flags;
+       union {
+               s32 val;
+               s64 val64;
+               char *string;
+       } cur;
+       union {
+               s32 val;
+               s64 val64;
+               char *string;
+       };
+       void *priv;
+};
+
+/** struct v4l2_ctrl_ref - The control reference.
+  * @node:     List node for the sorted list.
+  * @next:     Single-link list node for the hash.
+  * @ctrl:     The actual control information.
+  *
+  * Each control handler has a list of these refs. The list_head is used to
+  * keep a sorted-by-control-ID list of all controls, while the next pointer
+  * is used to link the control in the hash's bucket.
+  */
+struct v4l2_ctrl_ref {
+       struct list_head node;
+       struct v4l2_ctrl_ref *next;
+       struct v4l2_ctrl *ctrl;
+};
+
+/** struct v4l2_ctrl_handler - The control handler keeps track of all the
+  * controls: both the controls owned by the handler and those inherited
+  * from other handlers.
+  * @lock:     Lock to control access to this handler and its controls.
+  * @ctrls:    The list of controls owned by this handler.
+  * @ctrl_refs:        The list of control references.
+  * @cached:   The last found control reference. It is common that the same
+  *            control is needed multiple times, so this is a simple
+  *            optimization.
+  * @buckets:  Buckets for the hashing. Allows for quick control lookup.
+  * @nr_of_buckets: Total number of buckets in the array.
+  * @error:    The error code of the first failed control addition.
+  */
+struct v4l2_ctrl_handler {
+       struct mutex lock;
+       struct list_head ctrls;
+       struct list_head ctrl_refs;
+       struct v4l2_ctrl_ref *cached;
+       struct v4l2_ctrl_ref **buckets;
+       u16 nr_of_buckets;
+       int error;
+};
+
+/** struct v4l2_ctrl_config - Control configuration structure.
+  * @ops:      The control ops.
+  * @id:       The control ID.
+  * @name:     The control name.
+  * @type:     The control type.
+  * @min:      The control's minimum value.
+  * @max:      The control's maximum value.
+  * @step:     The control's step value for non-menu controls.
+  * @def:      The control's default value.
+  * @flags:    The control's flags.
+  * @menu_skip_mask: The control's skip mask for menu controls. This makes it
+  *            easy to skip menu items that are not valid. If bit X is set,
+  *            then menu item X is skipped. Of course, this only works for
+  *            menus with <= 32 menu items. There are no menus that come
+  *            close to that number, so this is OK. Should we ever need more,
+  *            then this will have to be extended to a u64 or a bit array.
+  * @qmenu:    A const char * array for all menu items. Array entries that are
+  *            empty strings ("") correspond to non-existing menu items (this
+  *            is in addition to the menu_skip_mask above). The last entry
+  *            must be NULL.
+  * @is_private: If set, then this control is private to its handler and it
+  *            will not be added to any other handlers.
+  * @is_volatile: If set, then this control is volatile. This means that the
+  *            control's current value cannot be cached and needs to be
+  *            retrieved through the g_volatile_ctrl op.
+  */
+struct v4l2_ctrl_config {
+       const struct v4l2_ctrl_ops *ops;
+       u32 id;
+       const char *name;
+       enum v4l2_ctrl_type type;
+       s32 min;
+       s32 max;
+       u32 step;
+       s32 def;
+       u32 flags;
+       u32 menu_skip_mask;
+       const char **qmenu;
+       unsigned int is_private:1;
+       unsigned int is_volatile:1;
+};
+
+/** v4l2_ctrl_fill() - Fill in the control fields based on the control ID.
+  *
+  * This works for all standard V4L2 controls.
+  * For non-standard controls it will only fill in the given arguments
+  * and @name will be NULL.
+  *
+  * This function will overwrite the contents of @name, @type and @flags.
+  * The contents of @min, @max, @step and @def may be modified depending on
+  * the type.
+  *
+  * Do not use in drivers! It is used internally for backwards compatibility
+  * control handling only. Once all drivers are converted to use the new
+  * control framework this function will no longer be exported.
+  */
+void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
+                   s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags);
+
+
+/** v4l2_ctrl_handler_init() - Initialize the control handler.
+  * @hdl:      The control handler.
+  * @nr_of_controls_hint: A hint of how many controls this handler is
+  *            expected to refer to. This is the total number, so including
+  *            any inherited controls. It doesn't have to be precise, but if
+  *            it is way off, then you either waste memory (too many buckets
+  *            are allocated) or the control lookup becomes slower (not enough
+  *            buckets are allocated, so there are more slow list lookups).
+  *            It will always work, though.
+  *
+  * Returns an error if the buckets could not be allocated. This error will
+  * also be stored in @hdl->error.
+  */
+int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
+                          unsigned nr_of_controls_hint);
+
+/** v4l2_ctrl_handler_free() - Free all controls owned by the handler and free
+  * the control list.
+  * @hdl:      The control handler.
+  *
+  * Does nothing if @hdl == NULL.
+  */
+void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl);
+
+/** v4l2_ctrl_handler_setup() - Call the s_ctrl op for all controls belonging
+  * to the handler to initialize the hardware to the current control values.
+  * @hdl:      The control handler.
+  *
+  * Button controls will be skipped, as are read-only controls.
+  *
+  * If @hdl == NULL, then this just returns 0.
+  */
+int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl);
+
+/** v4l2_ctrl_handler_log_status() - Log all controls owned by the handler.
+  * @hdl:      The control handler.
+  * @prefix:   The prefix to use when logging the control values. If the
+  *            prefix does not end with a space, then ": " will be added
+  *            after the prefix. If @prefix == NULL, then no prefix will be
+  *            used.
+  *
+  * For use with VIDIOC_LOG_STATUS.
+  *
+  * Does nothing if @hdl == NULL.
+  */
+void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
+                                 const char *prefix);
+
+/** v4l2_ctrl_new_custom() - Allocate and initialize a new custom V4L2
+  * control.
+  * @hdl:      The control handler.
+  * @cfg:      The control's configuration data.
+  * @priv:     The control's driver-specific private data.
+  *
+  * If the &v4l2_ctrl struct could not be allocated then NULL is returned
+  * and @hdl->error is set to the error code (if it wasn't set already).
+  */
+struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+                       const struct v4l2_ctrl_config *cfg, void *priv);
+
+/** v4l2_ctrl_new_std() - Allocate and initialize a new standard V4L2 non-menu control.
+  * @hdl:      The control handler.
+  * @ops:      The control ops.
+  * @id:       The control ID.
+  * @min:      The control's minimum value.
+  * @max:      The control's maximum value.
+  * @step:     The control's step value
+  * @def:      The control's default value.
+  *
+  * If the &v4l2_ctrl struct could not be allocated, or the control
+  * ID is not known, then NULL is returned and @hdl->error is set to the
+  * appropriate error code (if it wasn't set already).
+  *
+  * If @id refers to a menu control, then this function will return NULL.
+  *
+  * Use v4l2_ctrl_new_std_menu() when adding menu controls.
+  */
+struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+                       const struct v4l2_ctrl_ops *ops,
+                       u32 id, s32 min, s32 max, u32 step, s32 def);
+
+/** v4l2_ctrl_new_std_menu() - Allocate and initialize a new standard V4L2 menu control.
+  * @hdl:      The control handler.
+  * @ops:      The control ops.
+  * @id:       The control ID.
+  * @max:      The control's maximum value.
+  * @mask:     The control's skip mask for menu controls. This makes it
+  *            easy to skip menu items that are not valid. If bit X is set,
+  *            then menu item X is skipped. Of course, this only works for
+  *            menus with <= 32 menu items. There are no menus that come
+  *            close to that number, so this is OK. Should we ever need more,
+  *            then this will have to be extended to a u64 or a bit array.
+  * @def:      The control's default value.
+  *
+  * Same as v4l2_ctrl_new_std(), but @min is set to 0 and the @mask value
+  * determines which menu items are to be skipped.
+  *
+  * If @id refers to a non-menu control, then this function will return NULL.
+  */
+struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
+                       const struct v4l2_ctrl_ops *ops,
+                       u32 id, s32 max, s32 mask, s32 def);
+
+/** v4l2_ctrl_add_ctrl() - Add a control from another handler to this handler.
+  * @hdl:      The control handler.
+  * @ctrl:     The control to add.
+  *
+  * It will return NULL if it was unable to add the control reference.
+  * If the control already belonged to the handler, then it will do
+  * nothing and just return @ctrl.
+  */
+struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
+                                         struct v4l2_ctrl *ctrl);
+
+/** v4l2_ctrl_add_handler() - Add all controls from handler @add to
+  * handler @hdl.
+  * @hdl:      The control handler.
+  * @add:      The control handler whose controls you want to add to
+  *            the @hdl control handler.
+  *
+  * Does nothing if either of the two is a NULL pointer.
+  * In case of an error @hdl->error will be set to the error code (if it
+  * wasn't set already).
+  */
+int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
+                         struct v4l2_ctrl_handler *add);
+
+
+/** v4l2_ctrl_cluster() - Mark all controls in the cluster as belonging to that cluster.
+  * @ncontrols:        The number of controls in this cluster.
+  * @controls:         The cluster control array of size @ncontrols.
+  */
+void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls);
+
+
+/** v4l2_ctrl_find() - Find a control with the given ID.
+  * @hdl:      The control handler.
+  * @id:       The control ID to find.
+  *
+  * If @hdl == NULL this will return NULL as well. Will lock the handler so
+  * do not use from inside &v4l2_ctrl_ops.
+  */
+struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id);
+
+/** v4l2_ctrl_activate() - Make the control active or inactive.
+  * @ctrl:     The control to (de)activate.
+  * @active:   True if the control should become active.
+  *
+  * This sets or clears the V4L2_CTRL_FLAG_INACTIVE flag atomically.
+  * Does nothing if @ctrl == NULL.
+  * This will usually be called from within the s_ctrl op.
+  *
+  * This function can be called regardless of whether the control handler
+  * is locked or not.
+  */
+void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active);
+
+/** v4l2_ctrl_grab() - Mark the control as grabbed or not grabbed.
+  * @ctrl:     The control to (de)activate.
+  * @grabbed:  True if the control should become grabbed.
+  *
+  * This sets or clears the V4L2_CTRL_FLAG_GRABBED flag atomically.
+  * Does nothing if @ctrl == NULL.
+  * This will usually be called when starting or stopping streaming in the
+  * driver.
+  *
+  * This function can be called regardless of whether the control handler
+  * is locked or not.
+  */
+void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed);
+
+/** v4l2_ctrl_lock() - Helper function to lock the handler
+  * associated with the control.
+  * @ctrl:     The control to lock.
+  */
+static inline void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl)
+{
+       mutex_lock(&ctrl->handler->lock);
+}
+
+/** v4l2_ctrl_lock() - Helper function to unlock the handler
+  * associated with the control.
+  * @ctrl:     The control to unlock.
+  */
+static inline void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl)
+{
+       mutex_unlock(&ctrl->handler->lock);
+}
+
+/** v4l2_ctrl_g_ctrl() - Helper function to get the control's value from within a driver.
+  * @ctrl:     The control.
+  *
+  * This returns the control's value safely by going through the control
+  * framework. This function will lock the control's handler, so it cannot be
+  * used from within the &v4l2_ctrl_ops functions.
+  *
+  * This function is for integer type controls only.
+  */
+s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
+
+/** v4l2_ctrl_s_ctrl() - Helper function to set the control's value from within a driver.
+  * @ctrl:     The control.
+  * @val:      The new value.
+  *
+  * This set the control's new value safely by going through the control
+  * framework. This function will lock the control's handler, so it cannot be
+  * used from within the &v4l2_ctrl_ops functions.
+  *
+  * This function is for integer type controls only.
+  */
+int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
+
+
+/* Helpers for ioctl_ops. If hdl == NULL then they will all return -EINVAL. */
+int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc);
+int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm);
+int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *ctrl);
+int v4l2_s_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *ctrl);
+int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *c);
+int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *c);
+int v4l2_s_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *c);
+
+/* Helpers for subdevices. If the associated ctrl_handler == NULL then they
+   will all return -EINVAL. */
+int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc);
+int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm);
+int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
+int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
+int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
+int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+
+#endif
index bebe44b03e0fef9cad1da48cd1084792c8d3f322..1efcacbed01a055bf577ff20b48045e86f3beaba 100644 (file)
@@ -27,6 +27,7 @@
 struct v4l2_ioctl_callbacks;
 struct video_device;
 struct v4l2_device;
+struct v4l2_ctrl_handler;
 
 /* Flag to mark the video_device struct as registered.
    Drivers can clear this flag if they want to block all future
@@ -67,6 +68,9 @@ struct video_device
        struct device *parent;          /* device parent */
        struct v4l2_device *v4l2_dev;   /* v4l2_device parent */
 
+       /* Control handler associated with this device node. May be NULL. */
+       struct v4l2_ctrl_handler *ctrl_handler;
+
        /* device info */
        char name[32];
        int vfl_type;
index 5d5d550e63ad0e2c7a0c1811a42e1f031743068a..8bcbd7a0271cbb6c5143c1f40de2a211d6845baf 100644 (file)
@@ -32,6 +32,8 @@
 
 #define V4L2_DEVICE_NAME_SIZE (20 + 16)
 
+struct v4l2_ctrl_handler;
+
 struct v4l2_device {
        /* dev->driver_data points to this struct.
           Note: dev might be NULL if there is no parent device
@@ -47,6 +49,8 @@ struct v4l2_device {
        /* notify callback called by some sub-devices. */
        void (*notify)(struct v4l2_subdev *sd,
                        unsigned int notification, void *arg);
+       /* The control handler. May be NULL. */
+       struct v4l2_ctrl_handler *ctrl_handler;
 };
 
 /* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev.
index 02c6f4d11ed3654d76e2908983b26353361d2b1a..4a97d7341a945754407704d1063df3d536cc71e1 100644 (file)
@@ -35,6 +35,7 @@
 #define V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ     0x00000001
 
 struct v4l2_device;
+struct v4l2_ctrl_handler;
 struct v4l2_subdev;
 struct tuner_setup;
 
@@ -90,10 +91,31 @@ struct v4l2_decode_vbi_line {
    not yet implemented) since ops provide proper type-checking.
  */
 
+/* Subdevice external IO pin configuration */
+#define V4L2_SUBDEV_IO_PIN_DISABLE     (1 << 0) /* ENABLE assumed */
+#define V4L2_SUBDEV_IO_PIN_OUTPUT      (1 << 1)
+#define V4L2_SUBDEV_IO_PIN_INPUT       (1 << 2)
+#define V4L2_SUBDEV_IO_PIN_SET_VALUE   (1 << 3) /* Set output value */
+#define V4L2_SUBDEV_IO_PIN_ACTIVE_LOW  (1 << 4) /* ACTIVE HIGH assumed */
+
+struct v4l2_subdev_io_pin_config {
+       u32 flags;      /* V4L2_SUBDEV_IO_PIN_* flags for this pin's config */
+       u8 pin;         /* Chip external IO pin to configure */
+       u8 function;    /* Internal signal pad/function to route to IO pin */
+       u8 value;       /* Initial value for pin - e.g. GPIO output value */
+       u8 strength;    /* Pin drive strength */
+};
+
 /* s_config: if set, then it is always called by the v4l2_i2c_new_subdev*
        functions after the v4l2_subdev was registered. It is used to pass
        platform data to the subdev which can be used during initialization.
 
+   s_io_pin_config: configure one or more chip I/O pins for chips that
+       multiplex different internal signal pads out to IO pins.  This function
+       takes a pointer to an array of 'n' pin configuration entries, one for
+       each pin being configured.  This function could be called at times
+       other than just subdevice initialization.
+
    init: initialize the sensor registors to some sort of reasonable default
        values. Do not use for new drivers and should be removed in existing
        drivers.
@@ -110,11 +132,18 @@ struct v4l2_decode_vbi_line {
 
    s_power: puts subdevice in power saving mode (on == 0) or normal operation
        mode (on == 1).
+
+   interrupt_service_routine: Called by the bridge chip's interrupt service
+       handler, when an interrupt status has be raised due to this subdev,
+       so that this subdev can handle the details.  It may schedule work to be
+       performed later.  It must not sleep.  *Called from an IRQ context*.
  */
 struct v4l2_subdev_core_ops {
        int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);
        int (*log_status)(struct v4l2_subdev *sd);
        int (*s_config)(struct v4l2_subdev *sd, int irq, void *platform_data);
+       int (*s_io_pin_config)(struct v4l2_subdev *sd, size_t n,
+                                     struct v4l2_subdev_io_pin_config *pincfg);
        int (*init)(struct v4l2_subdev *sd, u32 val);
        int (*load_fw)(struct v4l2_subdev *sd);
        int (*reset)(struct v4l2_subdev *sd, u32 val);
@@ -133,6 +162,8 @@ struct v4l2_subdev_core_ops {
        int (*s_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg);
 #endif
        int (*s_power)(struct v4l2_subdev *sd, int on);
+       int (*interrupt_service_routine)(struct v4l2_subdev *sd,
+                                               u32 status, bool *handled);
 };
 
 /* s_mode: switch the tuner to a specific tuner mode. Replacement of s_radio.
@@ -307,11 +338,6 @@ struct v4l2_subdev_sensor_ops {
 };
 
 /*
-   interrupt_service_routine: Called by the bridge chip's interrupt service
-       handler, when an IR interrupt status has be raised due to this subdev,
-       so that this subdev can handle the details.  It may schedule work to be
-       performed later.  It must not sleep.  *Called from an IRQ context*.
-
    [rt]x_g_parameters: Get the current operating parameters and state of the
        the IR receiver or transmitter.
 
@@ -335,14 +361,9 @@ struct v4l2_subdev_sensor_ops {
  */
 
 enum v4l2_subdev_ir_mode {
-       V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, /* space & mark widths in nanosecs */
+       V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, /* uses struct ir_raw_event records */
 };
 
-/* Data format of data read or written for V4L2_SUBDEV_IR_MODE_PULSE_WIDTH */
-#define V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS      0x7fffffff
-#define V4L2_SUBDEV_IR_PULSE_LEVEL_MASK                0x80000000
-#define V4L2_SUBDEV_IR_PULSE_RX_SEQ_END                0xffffffff
-
 struct v4l2_subdev_ir_parameters {
        /* Either Rx or Tx */
        unsigned int bytes_per_data_element; /* of data in read or write call */
@@ -356,7 +377,10 @@ struct v4l2_subdev_ir_parameters {
        u32 max_pulse_width;       /* ns,      valid only for baseband signal */
        unsigned int carrier_freq; /* Hz,      valid only for modulated signal*/
        unsigned int duty_cycle;   /* percent, valid only for modulated signal*/
-       bool invert;               /* logically invert sense of mark/space */
+       bool invert_level;         /* invert signal level */
+
+       /* Tx only */
+       bool invert_carrier_sense; /* Send 0/space as a carrier burst */
 
        /* Rx only */
        u32 noise_filter_min_width;       /* ns, min time of a valid pulse */
@@ -366,10 +390,6 @@ struct v4l2_subdev_ir_parameters {
 };
 
 struct v4l2_subdev_ir_ops {
-       /* Common to receiver and transmitter */
-       int (*interrupt_service_routine)(struct v4l2_subdev *sd,
-                                               u32 status, bool *handled);
-
        /* Receiver */
        int (*rx_read)(struct v4l2_subdev *sd, u8 *buf, size_t count,
                                ssize_t *num);
@@ -415,6 +435,8 @@ struct v4l2_subdev {
        u32 flags;
        struct v4l2_device *v4l2_dev;
        const struct v4l2_subdev_ops *ops;
+       /* The control handler of this subdev. May be NULL. */
+       struct v4l2_ctrl_handler *ctrl_handler;
        /* name must be unique */
        char name[V4L2_SUBDEV_NAME_SIZE];
        /* can be used to group similar subdevs, value is driver-specific */