Browse Source

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

* 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (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
  ...
Linus Torvalds 15 years ago
parent
commit
7ae0dea900
100 changed files with 12423 additions and 4346 deletions
  1. 16 0
      Documentation/DocBook/v4l/lirc_device_interface.xml
  2. 78 0
      Documentation/DocBook/v4l/pixfmt-packed-rgb.xml
  3. 648 0
      Documentation/video4linux/v4l2-controls.txt
  4. 6 0
      MAINTAINERS
  5. 33 3
      drivers/media/IR/Kconfig
  6. 2 0
      drivers/media/IR/Makefile
  7. 1023 0
      drivers/media/IR/ene_ir.c
  8. 235 0
      drivers/media/IR/ene_ir.h
  9. 1 19
      drivers/media/IR/imon.c
  10. 8 5
      drivers/media/IR/ir-core-priv.h
  11. 13 1
      drivers/media/IR/ir-jvc-decoder.c
  12. 11 2
      drivers/media/IR/ir-keytable.c
  13. 100 24
      drivers/media/IR/ir-lirc-codec.c
  14. 22 3
      drivers/media/IR/ir-nec-decoder.c
  15. 133 26
      drivers/media/IR/ir-raw-event.c
  16. 2 0
      drivers/media/IR/ir-sysfs.c
  17. 1 1
      drivers/media/IR/keymaps/Makefile
  18. 0 44
      drivers/media/IR/keymaps/rc-empty.c
  19. 81 0
      drivers/media/IR/keymaps/rc-rc5-streamzap.c
  20. 2 0
      drivers/media/IR/keymaps/rc-rc6-mce.c
  21. 3 18
      drivers/media/IR/mceusb.c
  22. 23 0
      drivers/media/IR/rc-map.c
  23. 741 0
      drivers/media/IR/streamzap.c
  24. 1 1
      drivers/media/common/tuners/Kconfig
  25. 9 1
      drivers/media/dvb/bt8xx/dst.c
  26. 1 1
      drivers/media/dvb/frontends/Kconfig
  27. 4 2
      drivers/media/dvb/siano/smscoreapi.c
  28. 13 3
      drivers/media/video/Kconfig
  29. 2 1
      drivers/media/video/Makefile
  30. 68 39
      drivers/media/video/cs53l32a.c
  31. 635 112
      drivers/media/video/cx2341x.c
  32. 1 1
      drivers/media/video/cx23885/Kconfig
  33. 3 2
      drivers/media/video/cx23885/Makefile
  34. 35 0
      drivers/media/video/cx23885/cx23885-av.c
  35. 27 0
      drivers/media/video/cx23885/cx23885-av.h
  36. 107 7
      drivers/media/video/cx23885/cx23885-cards.c
  37. 110 14
      drivers/media/video/cx23885/cx23885-core.c
  38. 13 14
      drivers/media/video/cx23885/cx23885-i2c.c
  39. 50 22
      drivers/media/video/cx23885/cx23885-input.c
  40. 20 4
      drivers/media/video/cx23885/cx23885-ir.c
  41. 1 0
      drivers/media/video/cx23885/cx23885-reg.h
  42. 1 1
      drivers/media/video/cx23885/cx23885-vbi.c
  43. 20 3
      drivers/media/video/cx23885/cx23885-video.c
  44. 8 1
      drivers/media/video/cx23885/cx23885.h
  45. 88 54
      drivers/media/video/cx23885/cx23888-ir.c
  46. 1 1
      drivers/media/video/cx25840/Makefile
  47. 17 127
      drivers/media/video/cx25840/cx25840-audio.c
  48. 398 142
      drivers/media/video/cx25840/cx25840-core.c
  49. 41 11
      drivers/media/video/cx25840/cx25840-core.h
  50. 1279 0
      drivers/media/video/cx25840/cx25840-ir.c
  51. 16 5
      drivers/media/video/gspca/gspca.c
  52. 5 5
      drivers/media/video/gspca/sonixj.c
  53. 74 273
      drivers/media/video/gspca/sq930x.c
  54. 2 2
      drivers/media/video/gspca/t613.c
  55. 301 59
      drivers/media/video/gspca/vc032x.c
  56. 498 240
      drivers/media/video/gspca/zc3xx.c
  57. 32 244
      drivers/media/video/ivtv/ivtv-controls.c
  58. 1 5
      drivers/media/video/ivtv/ivtv-controls.h
  59. 14 12
      drivers/media/video/ivtv/ivtv-driver.c
  60. 3 1
      drivers/media/video/ivtv/ivtv-driver.h
  61. 8 15
      drivers/media/video/ivtv/ivtv-fileops.c
  62. 3 3
      drivers/media/video/ivtv/ivtv-firmware.c
  63. 36 41
      drivers/media/video/ivtv/ivtv-gpio.c
  64. 7 0
      drivers/media/video/ivtv/ivtv-i2c.c
  65. 13 18
      drivers/media/video/ivtv/ivtv-ioctl.c
  66. 15 9
      drivers/media/video/ivtv/ivtv-streams.c
  67. 88 160
      drivers/media/video/msp3400-driver.c
  68. 14 4
      drivers/media/video/msp3400-driver.h
  69. 8 8
      drivers/media/video/msp3400-kthreads.c
  70. 23 17
      drivers/media/video/mt9m111.c
  71. 3 1
      drivers/media/video/mx2_camera.c
  72. 2 12
      drivers/media/video/pvrusb2/pvrusb2-debugifc.c
  73. 3 0
      drivers/media/video/s5p-fimc/Makefile
  74. 1586 0
      drivers/media/video/s5p-fimc/fimc-core.c
  75. 471 0
      drivers/media/video/s5p-fimc/fimc-core.h
  76. 527 0
      drivers/media/video/s5p-fimc/fimc-reg.c
  77. 293 0
      drivers/media/video/s5p-fimc/regs-fimc.h
  78. 83 100
      drivers/media/video/saa7115.c
  79. 81 242
      drivers/media/video/saa717x.c
  80. 6 3
      drivers/media/video/soc_camera.c
  81. 5 5
      drivers/media/video/tvp7002.c
  82. 4 8
      drivers/media/video/usbvideo/usbvideo.c
  83. 9 0
      drivers/media/video/uvc/uvc_driver.c
  84. 9 4
      drivers/media/video/uvc/uvc_queue.c
  85. 11 8
      drivers/media/video/uvc/uvc_video.c
  86. 3 2
      drivers/media/video/uvc/uvcvideo.h
  87. 5 474
      drivers/media/video/v4l2-common.c
  88. 1851 0
      drivers/media/video/v4l2-ctrls.c
  89. 6 2
      drivers/media/video/v4l2-dev.c
  90. 7 0
      drivers/media/video/v4l2-device.c
  91. 33 13
      drivers/media/video/v4l2-ioctl.c
  92. 62 117
      drivers/media/video/wm8739.c
  93. 50 29
      drivers/media/video/wm8775.c
  94. 8 21
      drivers/staging/lirc/Kconfig
  95. 0 2
      drivers/staging/lirc/Makefile
  96. 0 646
      drivers/staging/lirc/lirc_ene0100.c
  97. 6 3
      drivers/staging/lirc/lirc_it87.c
  98. 2 2
      drivers/staging/lirc/lirc_parallel.c
  99. 0 821
      drivers/staging/lirc/lirc_streamzap.c
  100. 1 0
      include/linux/videodev2.h

+ 16 - 0
Documentation/DocBook/v4l/lirc_device_interface.xml

@@ -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>
       and LIRC_SETUP_END. Drivers can also choose to ignore these ioctls.</para>
     </listitem>
     </listitem>
   </varlistentry>
   </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>
 </variablelist>
 
 
 </section>
 </section>

+ 78 - 0
Documentation/DocBook/v4l/pixfmt-packed-rgb.xml

@@ -240,6 +240,45 @@ colorspace <constant>V4L2_COLORSPACE_SRGB</constant>.</para>
 	    <entry>r<subscript>1</subscript></entry>
 	    <entry>r<subscript>1</subscript></entry>
 	    <entry>r<subscript>0</subscript></entry>
 	    <entry>r<subscript>0</subscript></entry>
 	  </row>
 	  </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">
 	  <row id="V4L2-PIX-FMT-BGR24">
 	    <entry><constant>V4L2_PIX_FMT_BGR24</constant></entry>
 	    <entry><constant>V4L2_PIX_FMT_BGR24</constant></entry>
 	    <entry>'BGR3'</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>1</subscript></entry>
 	    <entry>b<subscript>0</subscript></entry>
 	    <entry>b<subscript>0</subscript></entry>
 	  </row>
 	  </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" -->
 	  <row><!-- id="V4L2-PIX-FMT-BGR24" -->
 	    <entry><constant>V4L2_PIX_FMT_BGR24</constant></entry>
 	    <entry><constant>V4L2_PIX_FMT_BGR24</constant></entry>
 	    <entry>'BGR3'</entry>
 	    <entry>'BGR3'</entry>

+ 648 - 0
Documentation/video4linux/v4l2-controls.txt

@@ -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.

+ 6 - 0
MAINTAINERS

@@ -2203,6 +2203,12 @@ F:	drivers/misc/cb710/
 F:	drivers/mmc/host/cb710-mmc.*
 F:	drivers/mmc/host/cb710-mmc.*
 F:	include/linux/cb710.h
 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
 EPSON 1355 FRAMEBUFFER DRIVER
 M:	Christopher Hoover <ch@murgatroid.com>
 M:	Christopher Hoover <ch@murgatroid.com>
 M:	Christopher Hoover <ch@hpl.hp.com>
 M:	Christopher Hoover <ch@hpl.hp.com>

+ 33 - 3
drivers/media/IR/Kconfig

@@ -1,8 +1,10 @@
-config IR_CORE
-	tristate
+menuconfig IR_CORE
+	tristate "Infrared remote controller adapters"
 	depends on INPUT
 	depends on INPUT
 	default INPUT
 	default INPUT
 
 
+if IR_CORE
+
 config VIDEO_IR
 config VIDEO_IR
 	tristate
 	tristate
 	depends on IR_CORE
 	depends on IR_CORE
@@ -16,7 +18,7 @@ config LIRC
 	   Enable this option to build the Linux Infrared Remote
 	   Enable this option to build the Linux Infrared Remote
 	   Control (LIRC) core device interface driver. The LIRC
 	   Control (LIRC) core device interface driver. The LIRC
 	   interface passes raw IR to and from userspace, where the
 	   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").
 	   encoding for IR transmitting (aka "blasting").
 
 
 source "drivers/media/IR/keymaps/Kconfig"
 source "drivers/media/IR/keymaps/Kconfig"
@@ -103,3 +105,31 @@ config IR_MCEUSB
 
 
 	   To compile this driver as a module, choose M here: the
 	   To compile this driver as a module, choose M here: the
 	   module will be called mceusb.
 	   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

+ 2 - 0
drivers/media/IR/Makefile

@@ -16,3 +16,5 @@ obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
 # stand-alone IR receivers/transmitters
 # stand-alone IR receivers/transmitters
 obj-$(CONFIG_IR_IMON) += imon.o
 obj-$(CONFIG_IR_IMON) += imon.o
 obj-$(CONFIG_IR_MCEUSB) += mceusb.o
 obj-$(CONFIG_IR_MCEUSB) += mceusb.o
+obj-$(CONFIG_IR_ENE) += ene_ir.o
+obj-$(CONFIG_IR_STREAMZAP) += streamzap.o

+ 1023 - 0
drivers/media/IR/ene_ir.c

@@ -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);

+ 235 - 0
drivers/media/IR/ene_ir.h

@@ -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;
+};

+ 1 - 19
drivers/media/IR/imon.c

@@ -87,7 +87,6 @@ static ssize_t lcd_write(struct file *file, const char *buf,
 struct imon_context {
 struct imon_context {
 	struct device *dev;
 	struct device *dev;
 	struct ir_dev_props *props;
 	struct ir_dev_props *props;
-	struct ir_input_dev *ir;
 	/* Newer devices have two interfaces */
 	/* Newer devices have two interfaces */
 	struct usb_device *usbdev_intf0;
 	struct usb_device *usbdev_intf0;
 	struct usb_device *usbdev_intf1;
 	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 input_dev *idev;
 	struct ir_dev_props *props;
 	struct ir_dev_props *props;
-	struct ir_input_dev *ir;
 	int ret, i;
 	int ret, i;
 
 
 	idev = input_allocate_device();
 	idev = input_allocate_device();
@@ -1671,12 +1669,6 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
 		goto props_alloc_failed;
 		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),
 	snprintf(ictx->name_idev, sizeof(ictx->name_idev),
 		 "iMON Remote (%04x:%04x)", ictx->vendor, ictx->product);
 		 "iMON Remote (%04x:%04x)", ictx->vendor, ictx->product);
 	idev->name = ictx->name_idev;
 	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;
 	props->change_protocol = imon_ir_change_protocol;
 	ictx->props = props;
 	ictx->props = props;
 
 
-	ictx->ir = ir;
-	memcpy(&ir->dev, ictx->dev, sizeof(struct device));
-
 	usb_to_input_id(ictx->usbdev_intf0, &idev->id);
 	usb_to_input_id(ictx->usbdev_intf0, &idev->id);
 	idev->dev.parent = ictx->dev;
 	idev->dev.parent = ictx->dev;
 
 
-	input_set_drvdata(idev, ir);
-
 	ret = ir_input_register(idev, RC_MAP_IMON_PAD, props, MOD_NAME);
 	ret = ir_input_register(idev, RC_MAP_IMON_PAD, props, MOD_NAME);
 	if (ret < 0) {
 	if (ret < 0) {
 		dev_err(ictx->dev, "remote input dev register failed\n");
 		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;
 	return idev;
 
 
 idev_register_failed:
 idev_register_failed:
-	kfree(ir);
-ir_dev_alloc_failed:
 	kfree(props);
 	kfree(props);
 props_alloc_failed:
 props_alloc_failed:
 	input_free_device(idev);
 	input_free_device(idev);
@@ -1944,7 +1929,6 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
 
 
 urb_submit_failed:
 urb_submit_failed:
 	ir_input_unregister(ictx->idev);
 	ir_input_unregister(ictx->idev);
-	input_free_device(ictx->idev);
 idev_setup_failed:
 idev_setup_failed:
 find_endpoint_failed:
 find_endpoint_failed:
 	mutex_unlock(&ictx->lock);
 	mutex_unlock(&ictx->lock);
@@ -2014,10 +1998,8 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
 	return ictx;
 	return ictx;
 
 
 urb_submit_failed:
 urb_submit_failed:
-	if (ictx->touch) {
+	if (ictx->touch)
 		input_unregister_device(ictx->touch);
 		input_unregister_device(ictx->touch);
-		input_free_device(ictx->touch);
-	}
 touch_setup_failed:
 touch_setup_failed:
 find_endpoint_failed:
 find_endpoint_failed:
 	mutex_unlock(&ictx->lock);
 	mutex_unlock(&ictx->lock);

+ 8 - 5
drivers/media/IR/ir-core-priv.h

@@ -32,7 +32,7 @@ struct ir_raw_handler {
 
 
 struct ir_raw_event_ctrl {
 struct ir_raw_event_ctrl {
 	struct list_head		list;		/* to keep track of raw clients */
 	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 */
 	struct kfifo			kfifo;		/* fifo for the pulse/space durations */
 	ktime_t				last_event;	/* when last event occurred */
 	ktime_t				last_event;	/* when last event occurred */
 	enum raw_event_type		last_type;	/* last event type */
 	enum raw_event_type		last_type;	/* last event type */
@@ -41,10 +41,13 @@ struct ir_raw_event_ctrl {
 
 
 	/* raw decoder state follows */
 	/* raw decoder state follows */
 	struct ir_raw_event prev_ev;
 	struct ir_raw_event prev_ev;
+	struct ir_raw_event this_ev;
 	struct nec_dec {
 	struct nec_dec {
 		int state;
 		int state;
 		unsigned count;
 		unsigned count;
 		u32 bits;
 		u32 bits;
+		bool is_nec_x;
+		bool necx_repeat;
 	} nec;
 	} nec;
 	struct rc5_dec {
 	struct rc5_dec {
 		int state;
 		int state;
@@ -76,7 +79,7 @@ struct ir_raw_event_ctrl {
 	struct lirc_codec {
 	struct lirc_codec {
 		struct ir_input_dev *ir_dev;
 		struct ir_input_dev *ir_dev;
 		struct lirc_driver *drv;
 		struct lirc_driver *drv;
-		int lircdata;
+		int carrier_low;
 	} lirc;
 	} lirc;
 };
 };
 
 
@@ -104,10 +107,9 @@ static inline void decrease_duration(struct ir_raw_event *ev, unsigned duration)
 		ev->duration -= 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 TO_STR(is_pulse)		((is_pulse) ? "pulse" : "space")
 #define IS_RESET(ev)			(ev.duration == 0)
 #define IS_RESET(ev)			(ev.duration == 0)
-
 /*
 /*
  * Routines from ir-sysfs.c - Meant to be called only internally inside
  * Routines from ir-sysfs.c - Meant to be called only internally inside
  * ir-core
  * 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_handler_unregister(struct ir_raw_handler *ir_raw_handler);
 void ir_raw_init(void);
 void ir_raw_init(void);
 
 
-
+int ir_rcmap_init(void);
+void ir_rcmap_cleanup(void);
 /*
 /*
  * Decoder initialization code
  * Decoder initialization code
  *
  *

+ 13 - 1
drivers/media/IR/ir-jvc-decoder.c

@@ -32,6 +32,7 @@ enum jvc_state {
 	STATE_BIT_SPACE,
 	STATE_BIT_SPACE,
 	STATE_TRAILER_PULSE,
 	STATE_TRAILER_PULSE,
 	STATE_TRAILER_SPACE,
 	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",
 	IR_dprintk(2, "JVC decode started at state %d (%uus %s)\n",
 		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
 		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
 
 
+again:
 	switch (data->state) {
 	switch (data->state) {
 
 
 	case STATE_INACTIVE:
 	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->count = 0;
-		data->state = STATE_BIT_PULSE;
+		data->state = STATE_CHECK_REPEAT;
 		return 0;
 		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:
 out:

+ 11 - 2
drivers/media/IR/ir-keytable.c

@@ -339,6 +339,8 @@ void ir_repeat(struct input_dev *dev)
 
 
 	spin_lock_irqsave(&ir->keylock, flags);
 	spin_lock_irqsave(&ir->keylock, flags);
 
 
+	input_event(dev, EV_MSC, MSC_SCAN, ir->last_scancode);
+
 	if (!ir->keypressed)
 	if (!ir->keypressed)
 		goto out;
 		goto out;
 
 
@@ -370,6 +372,8 @@ void ir_keydown(struct input_dev *dev, int scancode, u8 toggle)
 
 
 	spin_lock_irqsave(&ir->keylock, flags);
 	spin_lock_irqsave(&ir->keylock, flags);
 
 
+	input_event(dev, EV_MSC, MSC_SCAN, scancode);
+
 	/* Repeat event? */
 	/* Repeat event? */
 	if (ir->keypressed &&
 	if (ir->keypressed &&
 	    ir->last_scancode == scancode &&
 	    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_toggle = toggle;
 	ir->last_keycode = keycode;
 	ir->last_keycode = keycode;
 
 
+
 	if (keycode == KEY_RESERVED)
 	if (keycode == KEY_RESERVED)
 		goto out;
 		goto out;
 
 
+
 	/* Register a keypress */
 	/* Register a keypress */
 	ir->keypressed = true;
 	ir->keypressed = true;
 	IR_dprintk(1, "%s: key down event, key 0x%04x, scancode 0x%04x\n",
 	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,
 int __ir_input_register(struct input_dev *input_dev,
 		      const struct ir_scancode_table *rc_tab,
 		      const struct ir_scancode_table *rc_tab,
-		      const struct ir_dev_props *props,
+		      struct ir_dev_props *props,
 		      const char *driver_name)
 		      const char *driver_name)
 {
 {
 	struct ir_input_dev *ir_dev;
 	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_KEY, input_dev->evbit);
 	set_bit(EV_REP, 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)) {
 	if (ir_setkeytable(input_dev, &ir_dev->rc_tab, rc_tab)) {
 		rc = -ENOMEM;
 		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",
 	IR_dprintk(1, "Registered input device on %s for %s remote%s.\n",
 		   driver_name, rc_tab->name,
 		   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;
 	return 0;
 
 

+ 100 - 24
drivers/media/IR/ir-lirc-codec.c

@@ -32,6 +32,7 @@
 static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
 static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
 {
 {
 	struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
 	struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+	int sample;
 
 
 	if (!(ir_dev->raw->enabled_protocols & IR_TYPE_LIRC))
 	if (!(ir_dev->raw->enabled_protocols & IR_TYPE_LIRC))
 		return 0;
 		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)
 	if (!ir_dev->raw->lirc.drv || !ir_dev->raw->lirc.drv->rbuf)
 		return -EINVAL;
 		return -EINVAL;
 
 
+	if (IS_RESET(ev))
+		return 0;
+
 	IR_dprintk(2, "LIRC data transfer started (%uus %s)\n",
 	IR_dprintk(2, "LIRC data transfer started (%uus %s)\n",
 		   TO_US(ev.duration), TO_STR(ev.pulse));
 		   TO_US(ev.duration), TO_STR(ev.pulse));
 
 
-	ir_dev->raw->lirc.lircdata += ev.duration / 1000;
+	sample = ev.duration / 1000;
 	if (ev.pulse)
 	if (ev.pulse)
-		ir_dev->raw->lirc.lircdata |= PULSE_BIT;
+		sample |= PULSE_BIT;
 
 
 	lirc_buffer_write(ir_dev->raw->lirc.drv->rbuf,
 	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);
 	wake_up(&ir_dev->raw->lirc.drv->rbuf->wait_poll);
 
 
-	ir_dev->raw->lirc.lircdata = 0;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -92,13 +95,14 @@ out:
 	return ret;
 	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 lirc_codec *lirc;
 	struct ir_input_dev *ir_dev;
 	struct ir_input_dev *ir_dev;
 	int ret = 0;
 	int ret = 0;
 	void *drv_data;
 	void *drv_data;
-	unsigned long val;
+	unsigned long val = 0;
 
 
 	lirc = lirc_get_pdata(filep);
 	lirc = lirc_get_pdata(filep);
 	if (!lirc)
 	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;
 	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);
 		ret = get_user(val, (unsigned long *)arg);
 		if (ret)
 		if (ret)
 			return 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);
 			ret = ir_dev->props->s_tx_mask(drv_data, (u32)val);
 		else
 		else
 			return -EINVAL;
 			return -EINVAL;
 		break;
 		break;
 
 
 	case LIRC_SET_SEND_CARRIER:
 	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);
 			ir_dev->props->s_tx_carrier(drv_data, (u32)val);
 		else
 		else
 			return -EINVAL;
 			return -EINVAL;
 		break;
 		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;
 		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;
 			return -EINVAL;
+		ir_dev->props->timeout = val * 1000;
 		break;
 		break;
 
 
 	default:
 	default:
 		return lirc_dev_fop_ioctl(filep, cmd, arg);
 		return lirc_dev_fop_ioctl(filep, cmd, arg);
 	}
 	}
 
 
+	if (_IOC_DIR(cmd) & _IOC_READ)
+		ret = put_user(val, (unsigned long *)arg);
+
 	return ret;
 	return ret;
 }
 }
 
 
@@ -196,13 +259,28 @@ static int ir_lirc_register(struct input_dev *input_dev)
 
 
 	features = LIRC_CAN_REC_MODE2;
 	features = LIRC_CAN_REC_MODE2;
 	if (ir_dev->props->tx_ir) {
 	if (ir_dev->props->tx_ir) {
+
 		features |= LIRC_CAN_SEND_PULSE;
 		features |= LIRC_CAN_SEND_PULSE;
 		if (ir_dev->props->s_tx_mask)
 		if (ir_dev->props->s_tx_mask)
 			features |= LIRC_CAN_SET_TRANSMITTER_MASK;
 			features |= LIRC_CAN_SET_TRANSMITTER_MASK;
 		if (ir_dev->props->s_tx_carrier)
 		if (ir_dev->props->s_tx_carrier)
 			features |= LIRC_CAN_SET_SEND_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)",
 	snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)",
 		 ir_dev->driver_name);
 		 ir_dev->driver_name);
 	drv->minor = -1;
 	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.drv = drv;
 	ir_dev->raw->lirc.ir_dev = ir_dev;
 	ir_dev->raw->lirc.ir_dev = ir_dev;
-	ir_dev->raw->lirc.lircdata = PULSE_MASK;
-
 	return 0;
 	return 0;
 
 
 lirc_register_failed:
 lirc_register_failed:

+ 22 - 3
drivers/media/IR/ir-nec-decoder.c

@@ -20,12 +20,13 @@
 #define NEC_HEADER_PULSE	(16 * NEC_UNIT)
 #define NEC_HEADER_PULSE	(16 * NEC_UNIT)
 #define NECX_HEADER_PULSE	(8  * NEC_UNIT) /* Less common NEC variant */
 #define NECX_HEADER_PULSE	(8  * NEC_UNIT) /* Less common NEC variant */
 #define NEC_HEADER_SPACE	(8  * NEC_UNIT)
 #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_PULSE		(1  * NEC_UNIT)
 #define NEC_BIT_0_SPACE		(1  * NEC_UNIT)
 #define NEC_BIT_0_SPACE		(1  * NEC_UNIT)
 #define NEC_BIT_1_SPACE		(3  * NEC_UNIT)
 #define NEC_BIT_1_SPACE		(3  * NEC_UNIT)
 #define	NEC_TRAILER_PULSE	(1  * NEC_UNIT)
 #define	NEC_TRAILER_PULSE	(1  * NEC_UNIT)
 #define	NEC_TRAILER_SPACE	(10 * NEC_UNIT) /* even longer in reality */
 #define	NEC_TRAILER_SPACE	(10 * NEC_UNIT) /* even longer in reality */
+#define NECX_REPEAT_BITS	1
 
 
 enum nec_state {
 enum nec_state {
 	STATE_INACTIVE,
 	STATE_INACTIVE,
@@ -67,8 +68,12 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
 		if (!ev.pulse)
 		if (!ev.pulse)
 			break;
 			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;
 			break;
 
 
 		data->count = 0;
 		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)
 		if (ev.pulse)
 			break;
 			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;
 		data->bits <<= 1;
 		if (eq_margin(ev.duration, NEC_BIT_1_SPACE, NEC_UNIT / 2))
 		if (eq_margin(ev.duration, NEC_BIT_1_SPACE, NEC_UNIT / 2))
 			data->bits |= 1;
 			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);
 			IR_dprintk(1, "NEC scancode 0x%04x\n", scancode);
 		}
 		}
 
 
+		if (data->is_nec_x)
+			data->necx_repeat = true;
+
 		ir_keydown(input_dev, scancode, 0);
 		ir_keydown(input_dev, scancode, 0);
 		data->state = STATE_INACTIVE;
 		data->state = STATE_INACTIVE;
 		return 0;
 		return 0;

+ 133 - 26
drivers/media/IR/ir-raw-event.c

@@ -12,9 +12,10 @@
  *  GNU General Public License for more details.
  *  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/sched.h>
+#include <linux/freezer.h>
 #include "ir-core-priv.h"
 #include "ir-core-priv.h"
 
 
 /* Define the max number of pulse/space transitions to buffer */
 /* Define the max number of pulse/space transitions to buffer */
@@ -24,7 +25,7 @@
 static LIST_HEAD(ir_raw_client_list);
 static LIST_HEAD(ir_raw_client_list);
 
 
 /* Used to handle IR raw handler extensions */
 /* 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 LIST_HEAD(ir_raw_handler_list);
 static u64 available_protocols;
 static u64 available_protocols;
 
 
@@ -33,20 +34,30 @@ static u64 available_protocols;
 static struct work_struct wq_load;
 static struct work_struct wq_load;
 #endif
 #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_event ev;
 	struct ir_raw_handler *handler;
 	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)
 	if (!ir->raw)
 		return -EINVAL;
 		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))
 	if (kfifo_in(&ir->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev))
 		return -ENOMEM;
 		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);
 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
  * ir_raw_event_handle() - schedules the decoding of stored ir data
  * @input_dev:	the struct input_dev device descriptor
  * @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)
 	if (!ir->raw)
 		return;
 		return;
 
 
-	schedule_work(&ir->raw->rx_work);
+	wake_up_process(ir->raw->thread);
 }
 }
 EXPORT_SYMBOL_GPL(ir_raw_event_handle);
 EXPORT_SYMBOL_GPL(ir_raw_event_handle);
 
 
@@ -147,9 +245,9 @@ u64
 ir_raw_get_allowed_protocols()
 ir_raw_get_allowed_protocols()
 {
 {
 	u64 protocols;
 	u64 protocols;
-	spin_lock(&ir_raw_handler_lock);
+	mutex_lock(&ir_raw_handler_lock);
 	protocols = available_protocols;
 	protocols = available_protocols;
-	spin_unlock(&ir_raw_handler_lock);
+	mutex_unlock(&ir_raw_handler_lock);
 	return protocols;
 	return protocols;
 }
 }
 
 
@@ -167,7 +265,7 @@ int ir_raw_event_register(struct input_dev *input_dev)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	ir->raw->input_dev = input_dev;
 	ir->raw->input_dev = input_dev;
-	INIT_WORK(&ir->raw->rx_work, ir_raw_event_work);
+
 	ir->raw->enabled_protocols = ~0;
 	ir->raw->enabled_protocols = ~0;
 	rc = kfifo_alloc(&ir->raw->kfifo, sizeof(s64) * MAX_IR_EVENT_SIZE,
 	rc = kfifo_alloc(&ir->raw->kfifo, sizeof(s64) * MAX_IR_EVENT_SIZE,
 			 GFP_KERNEL);
 			 GFP_KERNEL);
@@ -177,12 +275,21 @@ int ir_raw_event_register(struct input_dev *input_dev)
 		return rc;
 		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_add_tail(&ir->raw->list, &ir_raw_client_list);
 	list_for_each_entry(handler, &ir_raw_handler_list, list)
 	list_for_each_entry(handler, &ir_raw_handler_list, list)
 		if (handler->raw_register)
 		if (handler->raw_register)
 			handler->raw_register(ir->raw->input_dev);
 			handler->raw_register(ir->raw->input_dev);
-	spin_unlock(&ir_raw_handler_lock);
+	mutex_unlock(&ir_raw_handler_lock);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -195,14 +302,14 @@ void ir_raw_event_unregister(struct input_dev *input_dev)
 	if (!ir->raw)
 	if (!ir->raw)
 		return;
 		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_del(&ir->raw->list);
 	list_for_each_entry(handler, &ir_raw_handler_list, list)
 	list_for_each_entry(handler, &ir_raw_handler_list, list)
 		if (handler->raw_unregister)
 		if (handler->raw_unregister)
 			handler->raw_unregister(ir->raw->input_dev);
 			handler->raw_unregister(ir->raw->input_dev);
-	spin_unlock(&ir_raw_handler_lock);
+	mutex_unlock(&ir_raw_handler_lock);
 
 
 	kfifo_free(&ir->raw->kfifo);
 	kfifo_free(&ir->raw->kfifo);
 	kfree(ir->raw);
 	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;
 	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);
 	list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list);
 	if (ir_raw_handler->raw_register)
 	if (ir_raw_handler->raw_register)
 		list_for_each_entry(raw, &ir_raw_client_list, list)
 		list_for_each_entry(raw, &ir_raw_client_list, list)
 			ir_raw_handler->raw_register(raw->input_dev);
 			ir_raw_handler->raw_register(raw->input_dev);
 	available_protocols |= ir_raw_handler->protocols;
 	available_protocols |= ir_raw_handler->protocols;
-	spin_unlock(&ir_raw_handler_lock);
+	mutex_unlock(&ir_raw_handler_lock);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -233,13 +340,13 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler)
 {
 {
 	struct ir_raw_event_ctrl *raw;
 	struct ir_raw_event_ctrl *raw;
 
 
-	spin_lock(&ir_raw_handler_lock);
+	mutex_lock(&ir_raw_handler_lock);
 	list_del(&ir_raw_handler->list);
 	list_del(&ir_raw_handler->list);
 	if (ir_raw_handler->raw_unregister)
 	if (ir_raw_handler->raw_unregister)
 		list_for_each_entry(raw, &ir_raw_client_list, list)
 		list_for_each_entry(raw, &ir_raw_client_list, list)
 			ir_raw_handler->raw_unregister(raw->input_dev);
 			ir_raw_handler->raw_unregister(raw->input_dev);
 	available_protocols &= ~ir_raw_handler->protocols;
 	available_protocols &= ~ir_raw_handler->protocols;
-	spin_unlock(&ir_raw_handler_lock);
+	mutex_unlock(&ir_raw_handler_lock);
 }
 }
 EXPORT_SYMBOL(ir_raw_handler_unregister);
 EXPORT_SYMBOL(ir_raw_handler_unregister);
 
 

+ 2 - 0
drivers/media/IR/ir-sysfs.c

@@ -325,6 +325,7 @@ static int __init ir_core_init(void)
 
 
 	/* Initialize/load the decoders/keymap code that will be used */
 	/* Initialize/load the decoders/keymap code that will be used */
 	ir_raw_init();
 	ir_raw_init();
+	ir_rcmap_init();
 
 
 	return 0;
 	return 0;
 }
 }
@@ -332,6 +333,7 @@ static int __init ir_core_init(void)
 static void __exit ir_core_exit(void)
 static void __exit ir_core_exit(void)
 {
 {
 	class_unregister(&ir_input_class);
 	class_unregister(&ir_input_class);
+	ir_rcmap_cleanup();
 }
 }
 
 
 module_init(ir_core_init);
 module_init(ir_core_init);

+ 1 - 1
drivers/media/IR/keymaps/Makefile

@@ -19,7 +19,6 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
 			rc-dm1105-nec.o \
 			rc-dm1105-nec.o \
 			rc-dntv-live-dvb-t.o \
 			rc-dntv-live-dvb-t.o \
 			rc-dntv-live-dvbt-pro.o \
 			rc-dntv-live-dvbt-pro.o \
-			rc-empty.o \
 			rc-em-terratec.o \
 			rc-em-terratec.o \
 			rc-encore-enltv2.o \
 			rc-encore-enltv2.o \
 			rc-encore-enltv.o \
 			rc-encore-enltv.o \
@@ -59,6 +58,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
 			rc-purpletv.o \
 			rc-purpletv.o \
 			rc-pv951.o \
 			rc-pv951.o \
 			rc-rc5-hauppauge-new.o \
 			rc-rc5-hauppauge-new.o \
+			rc-rc5-streamzap.o \
 			rc-rc5-tv.o \
 			rc-rc5-tv.o \
 			rc-rc6-mce.o \
 			rc-rc6-mce.o \
 			rc-real-audio-220-32-keys.o \
 			rc-real-audio-220-32-keys.o \

+ 0 - 44
drivers/media/IR/keymaps/rc-empty.c

@@ -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>");

+ 81 - 0
drivers/media/IR/keymaps/rc-rc5-streamzap.c

@@ -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>");

+ 2 - 0
drivers/media/IR/keymaps/rc-rc6-mce.c

@@ -74,6 +74,8 @@ static struct ir_scancode rc6_mce[] = {
 	{ 0x800f045a, KEY_SUBTITLE }, /* Caption/Teletext */
 	{ 0x800f045a, KEY_SUBTITLE }, /* Caption/Teletext */
 	{ 0x800f044d, KEY_TITLE },
 	{ 0x800f044d, KEY_TITLE },
 
 
+       { 0x800f044e, KEY_PRINT }, /* Print - HP OEM version of remote */
+
 	{ 0x800f040c, KEY_POWER },
 	{ 0x800f040c, KEY_POWER },
 	{ 0x800f040d, KEY_PROG1 }, /* Windows MCE button */
 	{ 0x800f040d, KEY_PROG1 }, /* Windows MCE button */
 
 

+ 3 - 18
drivers/media/IR/mceusb.c

@@ -228,7 +228,6 @@ static struct usb_device_id std_tx_mask_list[] = {
 /* data structure for each usb transceiver */
 /* data structure for each usb transceiver */
 struct mceusb_dev {
 struct mceusb_dev {
 	/* ir-core bits */
 	/* ir-core bits */
-	struct ir_input_dev *irdev;
 	struct ir_dev_props *props;
 	struct ir_dev_props *props;
 	struct ir_raw_event rawir;
 	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;
 	struct mceusb_dev *ir;
 	int len;
 	int len;
@@ -477,7 +476,7 @@ static void mce_request_packet(struct mceusb_dev *ir,
 		/* outbound data */
 		/* outbound data */
 		usb_fill_int_urb(async_urb, ir->usbdev,
 		usb_fill_int_urb(async_urb, ir->usbdev,
 			usb_sndintpipe(ir->usbdev, ep->bEndpointAddress),
 			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);
 			ir, ep->bInterval);
 		memcpy(async_buf, data, size);
 		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) {
 	if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
 		ir->send_flags = SEND_FLAG_COMPLETE;
 		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);
 			buf_len);
 	}
 	}
 
 
@@ -861,7 +860,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
 {
 {
 	struct input_dev *idev;
 	struct input_dev *idev;
 	struct ir_dev_props *props;
 	struct ir_dev_props *props;
-	struct ir_input_dev *irdev;
 	struct device *dev = ir->dev;
 	struct device *dev = ir->dev;
 	int ret = -ENODEV;
 	int ret = -ENODEV;
 
 
@@ -878,12 +876,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
 		goto props_alloc_failed;
 		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 "
 	snprintf(ir->name, sizeof(ir->name), "Media Center Ed. eHome "
 		 "Infrared Remote Transceiver (%04x:%04x)",
 		 "Infrared Remote Transceiver (%04x:%04x)",
 		 le16_to_cpu(ir->usbdev->descriptor.idVendor),
 		 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;
 	props->tx_ir = mceusb_tx_ir;
 
 
 	ir->props = props;
 	ir->props = props;
-	ir->irdev = irdev;
-
-	input_set_drvdata(idev, irdev);
 
 
 	ret = ir_input_register(idev, RC_MAP_RC6_MCE, props, DRIVER_NAME);
 	ret = ir_input_register(idev, RC_MAP_RC6_MCE, props, DRIVER_NAME);
 	if (ret < 0) {
 	if (ret < 0) {
@@ -915,8 +904,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
 	return idev;
 	return idev;
 
 
 irdev_failed:
 irdev_failed:
-	kfree(irdev);
-ir_dev_alloc_failed:
 	kfree(props);
 	kfree(props);
 props_alloc_failed:
 props_alloc_failed:
 	input_free_device(idev);
 	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 = NULL;
 	struct usb_endpoint_descriptor *ep_in = NULL;
 	struct usb_endpoint_descriptor *ep_in = NULL;
 	struct usb_endpoint_descriptor *ep_out = NULL;
 	struct usb_endpoint_descriptor *ep_out = NULL;
-	struct usb_host_config *config;
 	struct mceusb_dev *ir = NULL;
 	struct mceusb_dev *ir = NULL;
 	int pipe, maxp, i;
 	int pipe, maxp, i;
 	char buf[63], name[128] = "";
 	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__);
 	dev_dbg(&intf->dev, ": %s called\n", __func__);
 
 
-	config = dev->actconfig;
 	idesc  = intf->cur_altsetting;
 	idesc  = intf->cur_altsetting;
 
 
 	is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0;
 	is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0;

+ 23 - 0
drivers/media/IR/rc-map.c

@@ -82,3 +82,26 @@ void ir_unregister_map(struct rc_keymap *map)
 }
 }
 EXPORT_SYMBOL_GPL(ir_unregister_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);
+}

+ 741 - 0
drivers/media/IR/streamzap.c

@@ -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");

+ 1 - 1
drivers/media/common/tuners/Kconfig

@@ -34,7 +34,7 @@ config MEDIA_TUNER
 menuconfig MEDIA_TUNER_CUSTOMISE
 menuconfig MEDIA_TUNER_CUSTOMISE
 	bool "Customize analog and hybrid tuner modules to build"
 	bool "Customize analog and hybrid tuner modules to build"
 	depends on MEDIA_TUNER
 	depends on MEDIA_TUNER
-	default n
+	default y if EMBEDDED
 	help
 	help
 	  This allows the user to deselect tuner drivers unnecessary
 	  This allows the user to deselect tuner drivers unnecessary
 	  for their hardware from the build. Use this option with care
 	  for their hardware from the build. Use this option with care

+ 9 - 1
drivers/media/dvb/bt8xx/dst.c

@@ -1763,7 +1763,15 @@ static struct dvb_frontend_ops dst_dvbt_ops = {
 		.frequency_min = 137000000,
 		.frequency_min = 137000000,
 		.frequency_max = 858000000,
 		.frequency_max = 858000000,
 		.frequency_stepsize = 166667,
 		.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,
 	.release = dst_release,

+ 1 - 1
drivers/media/dvb/frontends/Kconfig

@@ -1,7 +1,7 @@
 config DVB_FE_CUSTOMISE
 config DVB_FE_CUSTOMISE
 	bool "Customise the frontend modules to build"
 	bool "Customise the frontend modules to build"
 	depends on DVB_CORE
 	depends on DVB_CORE
-	default N
+	default y if EMBEDDED
 	help
 	help
 	  This allows the user to select/deselect frontend drivers for their
 	  This allows the user to select/deselect frontend drivers for their
 	  hardware from the build.
 	  hardware from the build.

+ 4 - 2
drivers/media/dvb/siano/smscoreapi.c

@@ -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);
 	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();
 		schedule();
+		spin_lock_irqsave(&coredev->bufferslock, flags);
+	}
 
 
 	finish_wait(&coredev->buffer_mng_waitq, &wait);
 	finish_wait(&coredev->buffer_mng_waitq, &wait);
 
 

+ 13 - 3
drivers/media/video/Kconfig

@@ -83,7 +83,7 @@ config VIDEO_FIXED_MINOR_RANGES
 
 
 config VIDEO_HELPER_CHIPS_AUTO
 config VIDEO_HELPER_CHIPS_AUTO
 	bool "Autoselect pertinent encoders/decoders and other helper chips"
 	bool "Autoselect pertinent encoders/decoders and other helper chips"
-	default y
+	default y if !EMBEDDED
 	---help---
 	---help---
 	  Most video cards may require additional modules to encode or
 	  Most video cards may require additional modules to encode or
 	  decode audio/video standards. This option will autoselect
 	  decode audio/video standards. This option will autoselect
@@ -792,10 +792,11 @@ config SOC_CAMERA_MT9M001
 	  and colour models.
 	  and colour models.
 
 
 config SOC_CAMERA_MT9M111
 config SOC_CAMERA_MT9M111
-	tristate "mt9m111 and mt9m112 support"
+	tristate "mt9m111, mt9m112 and mt9m131 support"
 	depends on SOC_CAMERA && I2C
 	depends on SOC_CAMERA && I2C
 	help
 	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
 config SOC_CAMERA_MT9T031
 	tristate "mt9t031 support"
 	tristate "mt9t031 support"
@@ -1016,4 +1017,13 @@ config VIDEO_MEM2MEM_TESTDEV
 	  This is a virtual test device for the memory-to-memory driver
 	  This is a virtual test device for the memory-to-memory driver
 	  framework.
 	  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
 endif # V4L_MEM2MEM_DRIVERS

+ 2 - 1
drivers/media/video/Makefile

@@ -11,7 +11,7 @@ stkwebcam-objs	:=	stk-webcam.o stk-sensor.o
 omap2cam-objs	:=	omap24xxcam.o omap24xxcam-dma.o
 omap2cam-objs	:=	omap24xxcam.o omap24xxcam-dma.o
 
 
 videodev-objs	:=	v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.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
 # V4L2 core modules
 
 
@@ -163,6 +163,7 @@ obj-$(CONFIG_VIDEO_MX3)			+= mx3_camera.o
 obj-$(CONFIG_VIDEO_PXA27x)		+= pxa_camera.o
 obj-$(CONFIG_VIDEO_PXA27x)		+= pxa_camera.o
 obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2)	+= sh_mobile_csi2.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_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) 	+= s5p-fimc/
 
 
 obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
 obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
 
 

+ 68 - 39
drivers/media/video/cs53l32a.c

@@ -26,10 +26,10 @@
 #include <linux/ioctl.h>
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
 #include <linux/i2c.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/videodev2.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-i2c-drv.h>
 #include <media/v4l2-i2c-drv.h>
 
 
 MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
 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");
 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)
 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;
 	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;
 		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)
 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)
 static int cs53l32a_log_status(struct v4l2_subdev *sd)
 {
 {
+	struct cs53l32a_state *state = to_state(sd);
 	u8 v = cs53l32a_read(sd, 0x01);
 	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;
 	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 = {
 static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
 	.log_status = cs53l32a_log_status,
 	.log_status = cs53l32a_log_status,
 	.g_chip_ident = cs53l32a_g_chip_ident,
 	.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 = {
 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,
 static int cs53l32a_probe(struct i2c_client *client,
 			  const struct i2c_device_id *id)
 			  const struct i2c_device_id *id)
 {
 {
+	struct cs53l32a_state *state;
 	struct v4l2_subdev *sd;
 	struct v4l2_subdev *sd;
 	int i;
 	int i;
 
 
@@ -164,9 +176,10 @@ static int cs53l32a_probe(struct i2c_client *client,
 	v4l_info(client, "chip found @ 0x%x (%s)\n",
 	v4l_info(client, "chip found @ 0x%x (%s)\n",
 			client->addr << 1, client->adapter->name);
 			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;
 		return -ENOMEM;
+	sd = &state->sd;
 	v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops);
 	v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops);
 
 
 	for (i = 1; i <= 7; i++) {
 	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_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 */
 	/* 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 */
 	/* 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)
 static int cs53l32a_remove(struct i2c_client *client)
 {
 {
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct cs53l32a_state *state = to_state(sd);
 
 
 	v4l2_device_unregister_subdev(sd);
 	v4l2_device_unregister_subdev(sd);
-	kfree(sd);
+	v4l2_ctrl_handler_free(&state->hdl);
+	kfree(state);
 	return 0;
 	return 0;
 }
 }
 
 

+ 635 - 112
drivers/media/video/cx2341x.c

@@ -38,6 +38,145 @@ static int debug;
 module_param(debug, int, 0644);
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Debug level (0-1)");
 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! */
 /* Must be sorted from low to high control ID! */
 const u32 cx2341x_mpeg_ctrls[] = {
 const u32 cx2341x_mpeg_ctrls[] = {
 	V4L2_CID_MPEG_CLASS,
 	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_top = 255,
 	.video_chroma_median_filter_bottom = 0,
 	.video_chroma_median_filter_bottom = 0,
 };
 };
-
-
 /* Map the control ID to the correct field in the cx2341x_mpeg_params
 /* Map the control ID to the correct field in the cx2341x_mpeg_params
    struct. Return -EINVAL if the ID is unknown, else return 0. */
    struct. Return -EINVAL if the ID is unknown, else return 0. */
 static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params,
 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;
 	const char *name;
 
 
-	qctrl->flags = 0;
 	switch (qctrl->id) {
 	switch (qctrl->id) {
 	/* MPEG controls */
 	/* MPEG controls */
 	case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
 	case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
-		name = "Spatial Filter Mode";
-		break;
 	case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
 	case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
-		name = "Spatial Filter";
-		break;
 	case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
 	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:
 	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:
 	case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
-		name = "Temporal Filter Mode";
-		break;
 	case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
 	case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
-		name = "Temporal Filter";
-		break;
 	case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
 	case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
-		name = "Median Filter Type";
-		break;
 	case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
 	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:
 	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:
 	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:
 	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:
 	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:
 	default:
 		return v4l2_ctrl_query_fill(qctrl, min, max, step, def);
 		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,
 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
 		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) {
 	switch (id) {
 	case V4L2_CID_MPEG_STREAM_TYPE:
 	case V4L2_CID_MPEG_STREAM_TYPE:
 		return (p->capabilities & CX2341X_CAP_HAS_TS) ?
 		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:
 	case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
 		return NULL;
 		return NULL;
 	case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
 	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:
 	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:
 	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:
 	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:
 	case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
-		return cx2341x_video_median_filter_type_menu;
+		return cx2341x_get_menu(id);
 	default:
 	default:
 		return v4l2_ctrl_get_menu(id);
 		return v4l2_ctrl_get_menu(id);
 	}
 	}
 }
 }
 EXPORT_SYMBOL(cx2341x_ctrl_get_menu);
 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)
 static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
 {
 {
 	params->audio_properties =
 	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);
 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);

+ 1 - 1
drivers/media/video/cx23885/Kconfig

@@ -5,7 +5,7 @@ config VIDEO_CX23885
 	select VIDEO_BTCX
 	select VIDEO_BTCX
 	select VIDEO_TUNER
 	select VIDEO_TUNER
 	select VIDEO_TVEEPROM
 	select VIDEO_TVEEPROM
-	select VIDEO_IR
+	select IR_CORE
 	select VIDEOBUF_DVB
 	select VIDEOBUF_DVB
 	select VIDEOBUF_DMA_SG
 	select VIDEOBUF_DMA_SG
 	select VIDEO_CX25840
 	select VIDEO_CX25840

+ 3 - 2
drivers/media/video/cx23885/Makefile

@@ -1,7 +1,8 @@
 cx23885-objs	:= cx23885-cards.o cx23885-video.o cx23885-vbi.o \
 cx23885-objs	:= cx23885-cards.o cx23885-video.o cx23885-vbi.o \
 		    cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.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
 obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
 
 

+ 35 - 0
drivers/media/video/cx23885/cx23885-av.c

@@ -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);
+}

+ 27 - 0
drivers/media/video/cx23885/cx23885-av.h

@@ -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

+ 107 - 7
drivers/media/video/cx23885/cx23885-cards.c

@@ -30,6 +30,16 @@
 #include "netup-init.h"
 #include "netup-init.h"
 #include "cx23888-ir.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                                                  */
 /* board config info                                                  */
 
 
@@ -626,6 +636,9 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
 	case 79101:
 	case 79101:
 		/* WinTV-HVR1250 (PCIe, Retail, IR, half height,
 		/* WinTV-HVR1250 (PCIe, Retail, IR, half height,
 			ATSC and Basic analog */
 			ATSC and Basic analog */
+	case 79501:
+		/* WinTV-HVR1250 (PCIe, No IR, half height,
+			ATSC [at least] and Basic analog) */
 	case 79561:
 	case 79561:
 		/* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
 		/* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
 			ATSC and Basic analog */
 			ATSC and Basic analog */
@@ -959,9 +972,37 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
 
 
 int cx23885_ir_init(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;
 	int ret = 0;
 	switch (dev->board) {
 	switch (dev->board) {
-	case CX23885_BOARD_HAUPPAUGE_HVR1250:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
 	case CX23885_BOARD_HAUPPAUGE_HVR1800:
 	case CX23885_BOARD_HAUPPAUGE_HVR1800:
@@ -979,7 +1020,41 @@ int cx23885_ir_init(struct cx23885_dev *dev)
 		if (ret)
 		if (ret)
 			break;
 			break;
 		dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
 		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;
 		break;
 	case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
 	case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
 		request_module("ir-kbd-i2c");
 		request_module("ir-kbd-i2c");
@@ -994,11 +1069,16 @@ void cx23885_ir_fini(struct cx23885_dev *dev)
 	switch (dev->board) {
 	switch (dev->board) {
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 	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);
 		cx23888_ir_remove(dev);
 		dev->sd_ir = NULL;
 		dev->sd_ir = NULL;
 		break;
 		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) {
 	switch (dev->board) {
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 	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;
 		break;
 	}
 	}
 }
 }
@@ -1028,6 +1113,13 @@ void cx23885_card_setup(struct cx23885_dev *dev)
 
 
 	switch (dev->board) {
 	switch (dev->board) {
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
 	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_HVR1500:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
 	case CX23885_BOARD_HAUPPAUGE_HVR1400:
 	case CX23885_BOARD_HAUPPAUGE_HVR1400:
@@ -1136,6 +1228,11 @@ void cx23885_card_setup(struct cx23885_dev *dev)
 	 * loaded, ensure this happens.
 	 * loaded, ensure this happens.
 	 */
 	 */
 	switch (dev->board) {
 	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_HVR1800:
 	case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
 	case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
 	case CX23885_BOARD_HAUPPAUGE_HVR1700:
 	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->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
 				&dev->i2c_bus[2].i2c_adap,
 				&dev->i2c_bus[2].i2c_adap,
 				"cx25840", "cx25840", 0x88 >> 1, NULL);
 				"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;
 		break;
 	}
 	}
 
 

+ 110 - 14
drivers/media/video/cx23885/cx23885-core.c

@@ -34,6 +34,7 @@
 #include "cimax2.h"
 #include "cimax2.h"
 #include "cx23888-ir.h"
 #include "cx23888-ir.h"
 #include "cx23885-ir.h"
 #include "cx23885-ir.h"
+#include "cx23885-av.h"
 #include "cx23885-input.h"
 #include "cx23885-input.h"
 
 
 MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
 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 int cx23885_risc_decode(u32 risc)
 {
 {
 	static char *instr[16] = {
 	static char *instr[16] = {
@@ -548,7 +626,7 @@ static void cx23885_shutdown(struct cx23885_dev *dev)
 	cx_write(UART_CTL, 0);
 	cx_write(UART_CTL, 0);
 
 
 	/* Disable Interrupts */
 	/* Disable Interrupts */
-	cx_write(PCI_INT_MSK, 0);
+	cx23885_irq_disable_all(dev);
 	cx_write(VID_A_INT_MSK, 0);
 	cx_write(VID_A_INT_MSK, 0);
 	cx_write(VID_B_INT_MSK, 0);
 	cx_write(VID_B_INT_MSK, 0);
 	cx_write(VID_C_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;
 	int i;
 
 
+	spin_lock_init(&dev->pci_irqmask_lock);
+
 	mutex_init(&dev->lock);
 	mutex_init(&dev->lock);
 	mutex_init(&dev->gpio_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_bus  = dev->pci->bus->number;
 	dev->pci_slot = PCI_SLOT(dev->pci->devfn);
 	dev->pci_slot = PCI_SLOT(dev->pci->devfn);
-	dev->pci_irqmask = 0x001f00;
+	cx23885_irq_add(dev, 0x001f00);
 	if (cx23885_boards[dev->board].cimax > 0)
 	if (cx23885_boards[dev->board].cimax > 0)
-		dev->pci_irqmask |= 0x01800000; /* for CiMaxes */
+		cx23885_irq_add(dev, 0x01800000); /* for CiMaxes */
 
 
 	/* External Master 1 Bus */
 	/* External Master 1 Bus */
 	dev->i2c_bus[0].nr = 0;
 	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__,
 	dprintk(1, "%s() DEV_CNTRL2               0x%08X\n", __func__,
 		cx_read(DEV_CNTRL2));
 		cx_read(DEV_CNTRL2));
 	dprintk(1, "%s() PCI_INT_MSK              0x%08X\n", __func__,
 	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__,
 	dprintk(1, "%s() AUD_INT_INT_MSK          0x%08X\n", __func__,
 		cx_read(AUDIO_INT_INT_MSK));
 		cx_read(AUDIO_INT_INT_MSK));
 	dprintk(1, "%s() AUD_INT_DMA_CTL          0x%08X\n", __func__,
 	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__);
 		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_ts_int_msk,  port->ts_int_msk_val);
 		cx_set(port->reg_dma_ctl, port->dma_ctl_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;
 		break;
 	default:
 	default:
 		BUG();
 		BUG();
@@ -1650,10 +1731,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
 	u32 ts1_status, ts1_mask;
 	u32 ts1_status, ts1_mask;
 	u32 ts2_status, ts2_mask;
 	u32 ts2_status, ts2_mask;
 	int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
 	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_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_status = cx_read(VID_A_INT_STAT);
 	vida_mask = cx_read(VID_A_INT_MSK);
 	vida_mask = cx_read(VID_A_INT_MSK);
 	ts1_status = cx_read(VID_B_INT_STAT);
 	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_VID_C   | PCI_MSK_VID_B   | PCI_MSK_VID_A   |
 			  PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT |
 			  PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT |
 			  PCI_MSK_GPIO0   | PCI_MSK_GPIO1   |
 			  PCI_MSK_GPIO0   | PCI_MSK_GPIO1   |
-			  PCI_MSK_IR)) {
+			  PCI_MSK_AV_CORE | PCI_MSK_IR)) {
 
 
 		if (pci_status & PCI_MSK_RISC_RD)
 		if (pci_status & PCI_MSK_RISC_RD)
 			dprintk(7, " (PCI_MSK_RISC_RD   0x%08x)\n",
 			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",
 			dprintk(7, " (PCI_MSK_GPIO1     0x%08x)\n",
 				PCI_MSK_GPIO1);
 				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)
 		if (pci_status & PCI_MSK_IR)
 			dprintk(7, " (PCI_MSK_IR        0x%08x)\n",
 			dprintk(7, " (PCI_MSK_IR        0x%08x)\n",
 				PCI_MSK_IR);
 				PCI_MSK_IR);
@@ -1765,12 +1850,22 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
 		handled += cx23885_video_irq(dev, vida_status);
 		handled += cx23885_video_irq(dev, vida_status);
 
 
 	if (pci_status & PCI_MSK_IR) {
 	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++;
 			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)
 	if (handled)
 		cx_write(PCI_INT_STAT, pci_status);
 		cx_write(PCI_INT_STAT, pci_status);
 out:
 out:
@@ -1788,11 +1883,11 @@ static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
 	dev = to_cx23885(sd->v4l2_dev);
 	dev = to_cx23885(sd->v4l2_dev);
 
 
 	switch (notification) {
 	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)
 		if (sd == dev->sd_ir)
 			cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg);
 			cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg);
 		break;
 		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)
 		if (sd == dev->sd_ir)
 			cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg);
 			cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg);
 		break;
 		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)
 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_rx_work, cx23885_ir_rx_work_handler);
 	INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler);
 	INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler);
 	dev->v4l2_dev.notify = cx23885_v4l2_dev_notify;
 	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) {
 	switch (dev->board) {
 	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
 	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
-		cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */
+		cx23885_irq_add_enable(dev, 0x01800000); /* for NetUP */
 		break;
 		break;
 	}
 	}
 
 

+ 13 - 14
drivers/media/video/cx23885/cx23885-i2c.c

@@ -99,7 +99,7 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
 		if (!i2c_wait_done(i2c_adap))
 		if (!i2c_wait_done(i2c_adap))
 			return -EIO;
 			return -EIO;
 		if (!i2c_slave_did_ack(i2c_adap))
 		if (!i2c_slave_did_ack(i2c_adap))
-			return -EIO;
+			return -ENXIO;
 
 
 		dprintk(1, "%s() returns 0\n", __func__);
 		dprintk(1, "%s() returns 0\n", __func__);
 		return 0;
 		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_wdata, wdata);
 	cx_write(bus->reg_ctrl, ctrl);
 	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;
 		goto eio;
+	if (!i2c_slave_did_ack(i2c_adap)) {
+		retval = -ENXIO;
+		goto err;
+	}
 	if (i2c_debug) {
 	if (i2c_debug) {
 		printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
 		printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
 		if (!(ctrl & I2C_NOSTOP))
 		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_wdata, wdata);
 		cx_write(bus->reg_ctrl, ctrl);
 		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;
 			goto eio;
 		if (i2c_debug) {
 		if (i2c_debug) {
 			dprintk(1, " %02x", msg->buf[cnt]);
 			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))
 		if (!i2c_wait_done(i2c_adap))
 			return -EIO;
 			return -EIO;
 		if (!i2c_slave_did_ack(i2c_adap))
 		if (!i2c_slave_did_ack(i2c_adap))
-			return -EIO;
+			return -ENXIO;
 
 
 
 
 		dprintk(1, "%s() returns 0\n", __func__);
 		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_addr, msg->addr << 25);
 		cx_write(bus->reg_ctrl, ctrl);
 		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;
 			goto eio;
+		if (cnt == 0 && !i2c_slave_did_ack(i2c_adap)) {
+			retval = -ENXIO;
+			goto err;
+		}
 		msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
 		msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
 		if (i2c_debug) {
 		if (i2c_debug) {
 			dprintk(1, " %02x", msg->buf[cnt]);
 			dprintk(1, " %02x", msg->buf[cnt]);

+ 50 - 22
drivers/media/video/cx23885/cx23885-input.c

@@ -44,40 +44,26 @@
 
 
 #define MODULE_NAME "cx23885"
 #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,
 static void cx23885_input_process_measurements(struct cx23885_dev *dev,
 					       bool overrun)
 					       bool overrun)
 {
 {
 	struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir;
 	struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir;
-	struct ir_raw_event kernel_ir_event;
 
 
-	u32 sd_ir_data[64];
 	ssize_t num;
 	ssize_t num;
 	int count, i;
 	int count, i;
 	bool handle = false;
 	bool handle = false;
+	struct ir_raw_event ir_core_event[64];
 
 
 	do {
 	do {
 		num = 0;
 		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++) {
 		for (i = 0; i < count; i++) {
-			convert_measurement(sd_ir_data[i], &kernel_ir_event);
 			ir_raw_event_store(kernel_ir->inp_dev,
 			ir_raw_event_store(kernel_ir->inp_dev,
-					   &kernel_ir_event);
+					   &ir_core_event[i]);
 			handle = true;
 			handle = true;
 		}
 		}
 	} while (num != 0);
 	} while (num != 0);
@@ -99,8 +85,10 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
 	switch (dev->board) {
 	switch (dev->board) {
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 	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
 		 * using the CX2388x integrated IR controller should be similar
 		 */
 		 */
 		break;
 		break;
@@ -148,6 +136,7 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
 	switch (dev->board) {
 	switch (dev->board) {
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
+	case CX23885_BOARD_HAUPPAUGE_HVR1250:
 		/*
 		/*
 		 * The IR controller on this board only returns pulse widths.
 		 * The IR controller on this board only returns pulse widths.
 		 * Any other mode setting will fail to set up the device.
 		 * 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;
 		 * mark is received as low logic level;
 		 * falling edges are detected as rising edges; etc.
 		 * 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;
 		break;
 	}
 	}
 	v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
 	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) {
 	switch (dev->board) {
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 	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;
 		driver_type = RC_DRIVER_IR_RAW;
 		allowed_protos = IR_TYPE_ALL;
 		allowed_protos = IR_TYPE_ALL;
 		/* The grey Hauppauge RC-5 remote */
 		/* The grey Hauppauge RC-5 remote */
 		rc_map = RC_MAP_RC5_HAUPPAUGE_NEW;
 		rc_map = RC_MAP_RC5_HAUPPAUGE_NEW;
 		break;
 		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:
 	default:
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}

+ 20 - 4
drivers/media/video/cx23885/cx23885-ir.c

@@ -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)
 void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
 {
 {
 	struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
 	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);
 		set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications);
 	if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN)
 	if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN)
 		set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications);
 		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)
 void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
 {
 {
 	struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
 	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)
 	if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ)
 		set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications);
 		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);
 }
 }

+ 1 - 0
drivers/media/video/cx23885/cx23885-reg.h

@@ -213,6 +213,7 @@ Channel manager Data Structure entry = 20 DWORD
 #define DEV_CNTRL2	0x00040000
 #define DEV_CNTRL2	0x00040000
 
 
 #define PCI_MSK_IR        (1 << 28)
 #define PCI_MSK_IR        (1 << 28)
+#define PCI_MSK_AV_CORE   (1 << 27)
 #define PCI_MSK_GPIO1     (1 << 24)
 #define PCI_MSK_GPIO1     (1 << 24)
 #define PCI_MSK_GPIO0     (1 << 23)
 #define PCI_MSK_GPIO0     (1 << 23)
 #define PCI_MSK_APB_DMA   (1 << 12)
 #define PCI_MSK_APB_DMA   (1 << 12)

+ 1 - 1
drivers/media/video/cx23885/cx23885-vbi.c

@@ -74,7 +74,7 @@ static int cx23885_start_vbi_dma(struct cx23885_dev    *dev,
 	q->count = 1;
 	q->count = 1;
 
 
 	/* enable irqs */
 	/* 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);
 	cx_set(VID_A_INT_MSK, 0x000022);
 
 
 	/* start dma */
 	/* start dma */

+ 20 - 3
drivers/media/video/cx23885/cx23885-video.c

@@ -441,7 +441,7 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev,
 	q->count = 1;
 	q->count = 1;
 
 
 	/* enable irq */
 	/* 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);
 	cx_set(VID_A_INT_MSK, 0x000011);
 
 
 	/* start dma */
 	/* start dma */
@@ -1205,6 +1205,21 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
 	return 0;
 	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,
 static int vidioc_queryctrl(struct file *file, void *priv,
 				struct v4l2_queryctrl *qctrl)
 				struct v4l2_queryctrl *qctrl)
 {
 {
@@ -1410,6 +1425,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
 	.vidioc_enum_input    = vidioc_enum_input,
 	.vidioc_enum_input    = vidioc_enum_input,
 	.vidioc_g_input       = vidioc_g_input,
 	.vidioc_g_input       = vidioc_g_input,
 	.vidioc_s_input       = vidioc_s_input,
 	.vidioc_s_input       = vidioc_s_input,
+	.vidioc_log_status    = vidioc_log_status,
 	.vidioc_queryctrl     = vidioc_queryctrl,
 	.vidioc_queryctrl     = vidioc_queryctrl,
 	.vidioc_g_ctrl        = vidioc_g_ctrl,
 	.vidioc_g_ctrl        = vidioc_g_ctrl,
 	.vidioc_s_ctrl        = vidioc_s_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)
 void cx23885_video_unregister(struct cx23885_dev *dev)
 {
 {
 	dprintk(1, "%s()\n", __func__);
 	dprintk(1, "%s()\n", __func__);
-	cx_clear(PCI_INT_MSK, 1);
+	cx23885_irq_remove(dev, 0x01);
 
 
 	if (dev->video_dev) {
 	if (dev->video_dev) {
 		if (video_is_registered(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);
 		VID_A_DMA_CTL, 0x11, 0x00);
 
 
 	/* Don't enable VBI yet */
 	/* Don't enable VBI yet */
-	cx_set(PCI_INT_MSK, 1);
+
+	cx23885_irq_add_enable(dev, 0x01);
 
 
 	if (TUNER_ABSENT != dev->tuner_type) {
 	if (TUNER_ABSENT != dev->tuner_type) {
 		struct v4l2_subdev *sd = NULL;
 		struct v4l2_subdev *sd = NULL;

+ 8 - 1
drivers/media/video/cx23885/cx23885.h

@@ -325,6 +325,7 @@ struct cx23885_dev {
 	u32                        __iomem *lmmio;
 	u32                        __iomem *lmmio;
 	u8                         __iomem *bmmio;
 	u8                         __iomem *bmmio;
 	int                        pci_irqmask;
 	int                        pci_irqmask;
+	spinlock_t		   pci_irqmask_lock; /* protects mask reg too */
 	int                        hwrevision;
 	int                        hwrevision;
 
 
 	/* This valud is board specific and is used to configure the
 	/* This valud is board specific and is used to configure the
@@ -365,6 +366,7 @@ struct cx23885_dev {
 	unsigned char              radio_addr;
 	unsigned char              radio_addr;
 	unsigned int               has_radio;
 	unsigned int               has_radio;
 	struct v4l2_subdev 	   *sd_cx25840;
 	struct v4l2_subdev 	   *sd_cx25840;
+	struct work_struct	   cx25840_work;
 
 
 	/* Infrared */
 	/* Infrared */
 	struct v4l2_subdev         *sd_ir;
 	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...) \
 #define call_all(dev, o, f, args...) \
 	v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
 	v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
 
 
-#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...) \
 #define call_hw(dev, grpid, o, f, args...) \
 	v4l2_device_call_all(&dev->v4l2_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,
 extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask,
 	int asoutput);
 	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                                             */
 /* cx23885-cards.c                                             */

+ 88 - 54
drivers/media/video/cx23885/cx23888-ir.c

@@ -26,6 +26,7 @@
 
 
 #include <media/v4l2-device.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/ir-core.h>
 
 
 #include "cx23885.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_CPL	0x00001000
 #define CNTRL_LBM	0x00002000
 #define CNTRL_LBM	0x00002000
 #define CNTRL_R		0x00004000
 #define CNTRL_R		0x00004000
+/* CX23888 specific control flag */
+#define CNTRL_IVO	0x00008000
 
 
 #define CX23888_IR_TXCLK_REG	0x170004
 #define CX23888_IR_TXCLK_REG	0x170004
 #define TXCLK_TCD	0x0000FFFF
 #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_VIDCLK_FREQ	108000000 /* 108 MHz, BT.656 */
 #define CX23888_IR_REFCLK_FREQ	(CX23888_VIDCLK_FREQ / 2)
 #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 cx23888_ir_state {
 	struct v4l2_subdev sd;
 	struct v4l2_subdev sd;
@@ -423,6 +436,13 @@ static inline void control_tx_polarity_invert(struct cx23885_dev *dev,
 			   invert ? CNTRL_CPL : 0);
 			   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
  * 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;
 	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);
 	pulse_clocks = ns_to_pulse_clocks(ns);
 	*divider = pulse_clocks_to_clock_divider(pulse_clocks);
 	*divider = pulse_clocks_to_clock_divider(pulse_clocks);
 	cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider);
 	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;
 	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);
 	pulse_clocks = ns_to_pulse_clocks(ns);
 	*divider = pulse_clocks_to_clock_divider(pulse_clocks);
 	*divider = pulse_clocks_to_clock_divider(pulse_clocks);
 	cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider);
 	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 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG);
 	u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_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;
 	u32 events, v;
 	int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
 	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;
 			for (j = 0;
 			     (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
 			     (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
 				v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG);
 				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)
 			if (i == 0)
 				break;
 				break;
-			j = i * sizeof(u32);
+			j = i * sizeof(union cx23888_ir_fifo_rec);
 			k = kfifo_in_locked(&state->rx_kfifo,
 			k = kfifo_in_locked(&state->rx_kfifo,
 				      (unsigned char *) rx_data, j,
 				      (unsigned char *) rx_data, j,
 				      &state->rx_kfifo_lock);
 				      &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);
 	u16 divider = (u16) atomic_read(&state->rxclk_divider);
 
 
 	unsigned int i, n;
 	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) {
 	if (n == 0) {
 		*num = 0;
 		*num = 0;
 		return 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 = 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");
 			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",
 		v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns  %s\n",
 			 v, u ? "mark" : "space");
 			 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->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 */
 	/* Before we tweak the hardware, we have to disable the receiver */
 	irqenable_rx(dev, 0);
 	irqenable_rx(dev, 0);
@@ -762,12 +787,15 @@ static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
 					    &p->carrier_range_upper);
 					    &p->carrier_range_upper);
 		o->carrier_range_lower = p->carrier_range_lower;
 		o->carrier_range_lower = p->carrier_range_lower;
 		o->carrier_range_upper = p->carrier_range_upper;
 		o->carrier_range_upper = p->carrier_range_upper;
+
+		p->max_pulse_width =
+			(u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider);
 	} else {
 	} else {
 		p->max_pulse_width =
 		p->max_pulse_width =
 			    rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width,
 			    rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width,
 						       &rxclk_divider);
 						       &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);
 	atomic_set(&state->rxclk_divider, rxclk_divider);
 
 
 	p->noise_filter_min_width =
 	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);
 	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->interrupt_enable = p->interrupt_enable;
 	o->enable = p->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->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 */
 	/* Before we tweak the hardware, we have to disable the transmitter */
 	irqenable_tx(dev, 0);
 	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);
 		p->duty_cycle = cduty_tx_s_duty_cycle(dev, p->duty_cycle);
 		o->duty_cycle = p->duty_cycle;
 		o->duty_cycle = p->duty_cycle;
+
+		p->max_pulse_width =
+			(u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider);
 	} else {
 	} else {
 		p->max_pulse_width =
 		p->max_pulse_width =
 			    txclk_tx_s_max_pulse_width(dev, p->max_pulse_width,
 			    txclk_tx_s_max_pulse_width(dev, p->max_pulse_width,
 						       &txclk_divider);
 						       &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);
 	atomic_set(&state->txclk_divider, txclk_divider);
 
 
 	p->resolution = clock_divider_to_resolution(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 */
 	/* FIXME - make this dependent on resolution for better performance */
 	control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY);
 	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->interrupt_enable = p->interrupt_enable;
 	o->enable = p->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,
 			  "-%1d/+%1d, %u to %u Hz\n", i, j,
 			  clock_divider_to_freq(rxclk, 16 + j),
 			  clock_divider_to_freq(rxclk, 16 + j),
 			  clock_divider_to_freq(rxclk, 16 - i));
 			  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",
 	v4l2_info(sd, "\tLow pass filter:                   %s\n",
 		  filtr ? "enabled" : "disabled");
 		  filtr ? "enabled" : "disabled");
 	if (filtr)
 	if (filtr)
@@ -1025,19 +1058,20 @@ static int cx23888_ir_log_status(struct v4l2_subdev *sd)
 		  cntrl & CNTRL_TFE ? "enabled" : "disabled");
 		  cntrl & CNTRL_TFE ? "enabled" : "disabled");
 	v4l2_info(sd, "\tFIFO interrupt watermark:          %s\n",
 	v4l2_info(sd, "\tFIFO interrupt watermark:          %s\n",
 		  cntrl & CNTRL_TIC ? "not empty" : "half full or less");
 		  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) {
 	if (cntrl & CNTRL_MOD) {
 		v4l2_info(sd, "\tCarrier (16 clocks):               %u Hz\n",
 		v4l2_info(sd, "\tCarrier (16 clocks):               %u Hz\n",
 			  clock_divider_to_carrier_freq(txclk));
 			  clock_divider_to_carrier_freq(txclk));
 		v4l2_info(sd, "\tCarrier duty cycle:                %2u/16\n",
 		v4l2_info(sd, "\tCarrier duty cycle:                %2u/16\n",
 			  cduty + 1);
 			  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",
 	v4l2_info(sd, "\tBusy:                              %s\n",
 		  stats & STATS_TBY ? "yes" : "no");
 		  stats & STATS_TBY ? "yes" : "no");
 	v4l2_info(sd, "\tFIFO service requested:            %s\n",
 	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,
 	.g_register = cx23888_ir_g_register,
 	.s_register = cx23888_ir_s_register,
 	.s_register = cx23888_ir_s_register,
 #endif
 #endif
+	.interrupt_service_routine = cx23888_ir_irq_handler,
 };
 };
 
 
 static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = {
 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_read = cx23888_ir_rx_read,
 	.rx_g_parameters = cx23888_ir_rx_g_parameters,
 	.rx_g_parameters = cx23888_ir_rx_g_parameters,
 	.rx_s_parameters = cx23888_ir_rx_s_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 = {
 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,
 	.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
 
 
 	.enable = false,
 	.enable = false,
@@ -1146,11 +1179,11 @@ static const struct v4l2_subdev_ir_parameters default_rx_params = {
 	.noise_filter_min_width = 333333, /* ns */
 	.noise_filter_min_width = 333333, /* ns */
 	.carrier_range_lower = 35000,
 	.carrier_range_lower = 35000,
 	.carrier_range_upper = 37000,
 	.carrier_range_upper = 37000,
-	.invert = false,
+	.invert_level = false,
 };
 };
 
 
 static const struct v4l2_subdev_ir_parameters default_tx_params = {
 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,
 	.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
 
 
 	.enable = false,
 	.enable = false,
@@ -1160,7 +1193,8 @@ static const struct v4l2_subdev_ir_parameters default_tx_params = {
 	.modulation = true,
 	.modulation = true,
 	.carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
 	.carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
 	.duty_cycle = 25,      /* 25 %   - 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)
 int cx23888_ir_probe(struct cx23885_dev *dev)

+ 1 - 1
drivers/media/video/cx25840/Makefile

@@ -1,5 +1,5 @@
 cx25840-objs    := cx25840-core.o cx25840-audio.o cx25840-firmware.o \
 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
 obj-$(CONFIG_VIDEO_CX25840) += cx25840.o
 
 

+ 17 - 127
drivers/media/video/cx25840/cx25840-audio.c

@@ -474,33 +474,10 @@ void cx25840_audio_set_path(struct i2c_client *client)
 		cx25840_and_or(client, 0x803, ~0x10, 0x10);
 		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)
 static void set_volume(struct i2c_client *client, int volume)
 {
 {
-	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
 	int vol;
 	int vol;
 
 
-	if (state->unmute_volume >= 0) {
-		state->unmute_volume = volume;
-		return;
-	}
-
 	/* Convert the volume to msp3400 values (0-127) */
 	/* Convert the volume to msp3400 values (0-127) */
 	vol = volume >> 9;
 	vol = volume >> 9;
 
 
@@ -517,52 +494,6 @@ static void set_volume(struct i2c_client *client, int volume)
 	cx25840_write(client, 0x8d4, 228 - (vol * 2));
 	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)
 static void set_balance(struct i2c_client *client, int balance)
 {
 {
 	int bal = balance >> 8;
 	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)
 int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
 {
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	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;
 	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);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 
 	switch (ctrl->id) {
 	switch (ctrl->id) {
 	case V4L2_CID_AUDIO_VOLUME:
 	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;
 		break;
 	case V4L2_CID_AUDIO_BASS:
 	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;
 		break;
 	case V4L2_CID_AUDIO_TREBLE:
 	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;
 		break;
 	case V4L2_CID_AUDIO_BALANCE:
 	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;
 		break;
 	default:
 	default:
 		return -EINVAL;
 		return -EINVAL;
@@ -650,28 +562,6 @@ int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 	return 0;
 	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,
+};

+ 398 - 142
drivers/media/video/cx25840/cx25840-core.c

@@ -15,6 +15,9 @@
  *
  *
  * CX23885 support by Steven Toth <stoth@linuxtv.org>.
  * 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
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * 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_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford");
 MODULE_LICENSE("GPL");
 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;
 static int cx25840_debug;
 
 
 module_param_named(debug,cx25840_debug, int, 0644);
 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 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 0;
 
 
-	return buffer[0];
+	return rx_buf[0];
 }
 }
 
 
 u32 cx25840_read4(struct i2c_client * client, u16 addr)
 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 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,
 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);
 			     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,
 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)
 static void init_dll1(struct i2c_client *client)
 {
 {
 	/* This is the Hauppauge sequence used to
 	/* This is the Hauppauge sequence used to
@@ -420,6 +625,13 @@ static void cx23885_initialize(struct i2c_client *client)
 
 
 	/* start microcontroller */
 	/* start microcontroller */
 	cx25840_and_or(client, 0x803, ~0x10, 0x10);
 	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);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 
 	switch (ctrl->id) {
 	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:
 	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;
 		break;
 
 
 	case V4L2_CID_CONTRAST:
 	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;
 		break;
 
 
 	case V4L2_CID_SATURATION:
 	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;
 		break;
 
 
 	case V4L2_CID_HUE:
 	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;
 		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:
 	default:
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
@@ -1163,8 +1302,6 @@ static void log_audio_status(struct i2c_client *client)
 	default: p = "not defined";
 	default: p = "not defined";
 	}
 	}
 	v4l_info(client, "Detected audio standard:   %s\n", p);
 	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",
 	v4l_info(client, "Audio microcontroller:     %s\n",
 		    (download_ctl & 0x10) ?
 		    (download_ctl & 0x10) ?
 				((mute_ctl & 0x2) ? "detecting" : "running") : "stopped");
 				((mute_ctl & 0x2) ? "detecting" : "running") : "stopped");
@@ -1381,40 +1518,6 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
 	return 0;
 	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)
 static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
 {
 {
 	struct cx25840_state *state = to_state(sd);
 	struct cx25840_state *state = to_state(sd);
@@ -1576,24 +1679,134 @@ static int cx25840_log_status(struct v4l2_subdev *sd)
 	log_video_status(client);
 	log_video_status(client);
 	if (!is_cx2583x(state))
 	if (!is_cx2583x(state))
 		log_audio_status(client);
 		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;
 	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 = {
 static const struct v4l2_subdev_core_ops cx25840_core_ops = {
 	.log_status = cx25840_log_status,
 	.log_status = cx25840_log_status,
+	.s_config = cx25840_s_config,
 	.g_chip_ident = cx25840_g_chip_ident,
 	.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,
 	.s_std = cx25840_s_std,
 	.reset = cx25840_reset,
 	.reset = cx25840_reset,
 	.load_fw = cx25840_load_fw,
 	.load_fw = cx25840_load_fw,
+	.s_io_pin_config = common_s_io_pin_config,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.g_register = cx25840_g_register,
 	.g_register = cx25840_g_register,
 	.s_register = cx25840_s_register,
 	.s_register = cx25840_s_register,
 #endif
 #endif
+	.interrupt_service_routine = cx25840_irq_handler,
 };
 };
 
 
 static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
 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,
 	.audio = &cx25840_audio_ops,
 	.video = &cx25840_video_ops,
 	.video = &cx25840_video_ops,
 	.vbi = &cx25840_vbi_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 cx25840_state *state;
 	struct v4l2_subdev *sd;
 	struct v4l2_subdev *sd;
+	int default_volume;
 	u32 id = V4L2_IDENT_NONE;
 	u32 id = V4L2_IDENT_NONE;
 	u16 device_id;
 	u16 device_id;
 
 
@@ -1718,6 +1933,7 @@ static int cx25840_probe(struct i2c_client *client,
 
 
 	sd = &state->sd;
 	sd = &state->sd;
 	v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
 	v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
+
 	switch (id) {
 	switch (id) {
 	case V4L2_IDENT_CX23885_AV:
 	case V4L2_IDENT_CX23885_AV:
 		v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n",
 		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->audclk_freq = 48000;
 	state->pvr150_workaround = 0;
 	state->pvr150_workaround = 0;
 	state->audmode = V4L2_TUNER_MODE_LANG1;
 	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->vbi_line_offset = 8;
 	state->id = id;
 	state->id = id;
 	state->rev = device_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;
 	return 0;
 }
 }
 
 
 static int cx25840_remove(struct i2c_client *client)
 static int cx25840_remove(struct i2c_client *client)
 {
 {
 	struct v4l2_subdev *sd = i2c_get_clientdata(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);
 	v4l2_device_unregister_subdev(sd);
-	kfree(to_state(sd));
+	v4l2_ctrl_handler_free(&state->hdl);
+	kfree(state);
 	return 0;
 	return 0;
 }
 }
 
 

+ 41 - 11
drivers/media/video/cx25840/cx25840-core.h

@@ -24,19 +24,20 @@
 #include <linux/videodev2.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
 #include <linux/i2c.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 cx25840_state {
 	struct i2c_client *c;
 	struct i2c_client *c;
 	struct v4l2_subdev sd;
 	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler hdl;
+	struct {
+		/* volume cluster */
+		struct v4l2_ctrl *volume;
+		struct v4l2_ctrl *mute;
+	};
 	int pvr150_workaround;
 	int pvr150_workaround;
 	int radio;
 	int radio;
 	v4l2_std_id std;
 	v4l2_std_id std;
@@ -44,14 +45,13 @@ struct cx25840_state {
 	enum cx25840_audio_input aud_input;
 	enum cx25840_audio_input aud_input;
 	u32 audclk_freq;
 	u32 audclk_freq;
 	int audmode;
 	int audmode;
-	int unmute_volume; /* -1 if not muted */
-	int default_volume;
 	int vbi_line_offset;
 	int vbi_line_offset;
 	u32 id;
 	u32 id;
 	u32 rev;
 	u32 rev;
 	int is_initialized;
 	int is_initialized;
 	wait_queue_head_t fw_wait;    /* wake up when the fw load is finished */
 	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 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)
 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);
 	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)
 static inline bool is_cx2583x(struct cx25840_state *state)
 {
 {
 	return state->id == V4L2_IDENT_CX25836 ||
 	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;
 	       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 							   */
 /* cx25850-core.c 							   */
 int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
 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);
 u8 cx25840_read(struct i2c_client *client, u16 addr);
 u32 cx25840_read4(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_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);
 void cx25840_std_setup(struct i2c_client *client);
 
 
 /* ----------------------------------------------------------------------- */
 /* ----------------------------------------------------------------------- */
@@ -94,8 +116,8 @@ int cx25840_loadfw(struct i2c_client *client);
 /* cx25850-audio.c                                                         */
 /* cx25850-audio.c                                                         */
 void cx25840_audio_set_path(struct i2c_client *client);
 void cx25840_audio_set_path(struct i2c_client *client);
 int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
 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                                                           */
 /* 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_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);
 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
 #endif

+ 1279 - 0
drivers/media/video/cx25840/cx25840-ir.c

@@ -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;
+}

+ 16 - 5
drivers/media/video/gspca/gspca.c

@@ -55,7 +55,7 @@ MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
 MODULE_DESCRIPTION("GSPCA USB Camera Driver");
 MODULE_DESCRIPTION("GSPCA USB Camera Driver");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 
 
-#define DRIVER_VERSION_NUMBER	KERNEL_VERSION(2, 9, 0)
+#define DRIVER_VERSION_NUMBER	KERNEL_VERSION(2, 10, 0)
 
 
 #ifdef GSPCA_DEBUG
 #ifdef GSPCA_DEBUG
 int gspca_debug = D_ERR | D_PROBE;
 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;
 		frame->v4l2_buf.sequence = ++gspca_dev->sequence;
 		gspca_dev->image = frame->data;
 		gspca_dev->image = frame->data;
 		gspca_dev->image_len = 0;
 		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 */
 	/* append the packet to the frame buffer */
@@ -454,6 +459,12 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
 				gspca_dev->frsz);
 				gspca_dev->frsz);
 			packet_type = DISCARD_PACKET;
 			packet_type = DISCARD_PACKET;
 		} else {
 		} 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,
 			memcpy(gspca_dev->image + gspca_dev->image_len,
 				data, len);
 				data, len);
 			gspca_dev->image_len += len;
 			gspca_dev->image_len += len;

+ 5 - 5
drivers/media/video/gspca/sonixj.c

@@ -66,7 +66,11 @@ struct sd {
 #define BRIDGE_SN9C110 2
 #define BRIDGE_SN9C110 2
 #define BRIDGE_SN9C120 3
 #define BRIDGE_SN9C120 3
 	u8 sensor;			/* Type of image sensor chip */
 	u8 sensor;			/* Type of image sensor chip */
-enum {
+	u8 i2c_addr;
+
+	u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+enum sensors {
 	SENSOR_ADCM1700,
 	SENSOR_ADCM1700,
 	SENSOR_GC0307,
 	SENSOR_GC0307,
 	SENSOR_HV7131R,
 	SENSOR_HV7131R,
@@ -81,10 +85,6 @@ enum {
 	SENSOR_PO2030N,
 	SENSOR_PO2030N,
 	SENSOR_SOI768,
 	SENSOR_SOI768,
 	SENSOR_SP80708,
 	SENSOR_SP80708,
-} sensors;
-	u8 i2c_addr;
-
-	u8 jpeg_hdr[JPEG_HDR_SZ];
 };
 };
 
 
 /* V4L2 controls supported by the driver */
 /* V4L2 controls supported by the driver */

+ 74 - 273
drivers/media/video/gspca/sq930x.c

@@ -23,7 +23,6 @@
 #define MODULE_NAME "sq930x"
 #define MODULE_NAME "sq930x"
 
 
 #include "gspca.h"
 #include "gspca.h"
-#include "jpeg.h"
 
 
 MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
 MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
 		"Gerard Klaver <gerard at gkall dot hobby dot nl\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_DESCRIPTION("GSPCA/SQ930x USB Camera Driver");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 
 
-#define BULK_TRANSFER_LEN 5128
-
 /* Structure to hold all of our device specific stuff */
 /* Structure to hold all of our device specific stuff */
 struct sd {
 struct sd {
 	struct gspca_dev gspca_dev;	/* !! must be the first item */
 	struct gspca_dev gspca_dev;	/* !! must be the first item */
@@ -40,28 +37,20 @@ struct sd {
 	u16 expo;
 	u16 expo;
 	u8 gain;
 	u8 gain;
 
 
-	u8 quality;		/* webcam quality 0..3 */
-#define QUALITY_DEF 1
-
-	u8 gpio[2];
-
-	u8 eof_len;
 	u8 do_ctrl;
 	u8 do_ctrl;
-
+	u8 gpio[2];
 	u8 sensor;
 	u8 sensor;
-enum {
+	u8 type;
+#define Generic 0
+#define Creative_live_motion 1
+};
+enum sensors {
 	SENSOR_ICX098BQ,
 	SENSOR_ICX098BQ,
 	SENSOR_LZ24BP,
 	SENSOR_LZ24BP,
 	SENSOR_MI0360,
 	SENSOR_MI0360,
-	SENSOR_MT9V111,
+	SENSOR_MT9V111,		/* = MI360SOC */
 	SENSOR_OV7660,
 	SENSOR_OV7660,
 	SENSOR_OV9630,
 	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);
 static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val);
@@ -78,7 +67,7 @@ static const struct ctrl sd_ctrls[] = {
 		.minimum = 0x0001,
 		.minimum = 0x0001,
 		.maximum = 0x0fff,
 		.maximum = 0x0fff,
 		.step = 1,
 		.step = 1,
-#define EXPO_DEF 0x027d
+#define EXPO_DEF 0x0356
 		.default_value = EXPO_DEF,
 		.default_value = EXPO_DEF,
 	    },
 	    },
 	    .set = sd_setexpo,
 	    .set = sd_setexpo,
@@ -92,7 +81,7 @@ static const struct ctrl sd_ctrls[] = {
 		.minimum = 0x01,
 		.minimum = 0x01,
 		.maximum = 0xff,
 		.maximum = 0xff,
 		.step = 1,
 		.step = 1,
-#define GAIN_DEF 0x61
+#define GAIN_DEF 0x8d
 		.default_value = GAIN_DEF,
 		.default_value = GAIN_DEF,
 	    },
 	    },
 	    .set = sd_setgain,
 	    .set = sd_setgain,
@@ -101,30 +90,18 @@ static const struct ctrl sd_ctrls[] = {
 };
 };
 
 
 static struct v4l2_pix_format vga_mode[] = {
 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,
 		.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,
 		.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 */
 /* sq930x registers */
 #define SQ930_CTRL_UCBUS_IO	0x0001
 #define SQ930_CTRL_UCBUS_IO	0x0001
 #define SQ930_CTRL_I2C_IO	0x0002
 #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 */
 	{0x01, 0x0001},		/* select IFP/SOC registers */
 	{0x06, 0x300c},		/* operating mode control */
 	{0x06, 0x300c},		/* operating mode control */
 	{0x08, 0xcc00},		/* output format control (RGB) */
 	{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[] = {
 static const struct i2c_write_cmd mt9v111_init_1[] = {
 	{0x03, 0x01e5},		/* window height */
 	{0x03, 0x01e5},		/* window height */
@@ -330,7 +307,8 @@ static const struct i2c_write_cmd mt9v111_init_3[] = {
 	{0x62, 0x0405},
 	{0x62, 0x0405},
 };
 };
 static const struct i2c_write_cmd mt9v111_init_4[] = {
 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[] = {
 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}
 	{0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03}
 };
 };
 
 
+/* start parameters indexed by [sensor][mode] */
 static const struct cap_s {
 static const struct cap_s {
 	u8	cc_sizeid;
 	u8	cc_sizeid;
 	u8	cc_bytes[32];
 	u8	cc_bytes[32];
-} capconfig[4][3] = {
+} capconfig[4][2] = {
 	[SENSOR_ICX098BQ] = {
 	[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, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
 		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
 		   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] = {
 	[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, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
 		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
 		   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] = {
 	[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,
 		  {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
-/*fixme				       03		       e3 */
 		   0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
 		   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] = {
 	[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,
 		   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++] = 0x35;	/* reg = global gain */
 		buf[i++] = 0x00;	/* val H */
 		buf[i++] = 0x00;	/* val H */
 		buf[i++] = sensor->i2c_dum;
 		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;
 		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->nmodes = ARRAY_SIZE(vga_mode);
 
 
 	cam->bulk = 1;
 	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->gain = GAIN_DEF;
 	sd->expo = EXPO_DEF;
 	sd->expo = EXPO_DEF;
 
 
@@ -945,13 +900,10 @@ static int sd_init(struct gspca_dev *gspca_dev)
 	if (sd->sensor == SENSOR_MI0360) {
 	if (sd->sensor == SENSOR_MI0360) {
 
 
 		/* no sensor probe for icam tracer */
 		/* 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;
 			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);
 			cmos_probe(gspca_dev);
-		}
 	}
 	}
 
 
 	PDEBUG(D_PROBE, "Sensor %s", sensor_tb[sd->sensor].name);
 	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;
 	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 */
 /* send the start/stop commands to the webcam */
 static void send_start(struct gspca_dev *gspca_dev)
 static void send_start(struct gspca_dev *gspca_dev)
 {
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 	struct sd *sd = (struct sd *) gspca_dev;
 	const struct cap_s *cap;
 	const struct cap_s *cap;
-	int mode, quality;
+	int mode;
 
 
 	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
 	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
 	cap = &capconfig[sd->sensor][mode];
 	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);
 			cap->cc_bytes, 32);
-};
+}
+
 static void send_stop(struct gspca_dev *gspca_dev)
 static void send_stop(struct gspca_dev *gspca_dev)
 {
 {
 	reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0);
 	reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0);
-};
+}
 
 
 /* function called at start time before URB creation */
 /* function called at start time before URB creation */
 static int sd_isoc_init(struct gspca_dev *gspca_dev)
 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 */
 	gspca_dev->cam.bulk_nurbs = 1;	/* there must be one URB only */
 	sd->do_ctrl = 0;
 	sd->do_ctrl = 0;
+	gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height + 8;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1022,11 +948,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
 	struct sd *sd = (struct sd *) gspca_dev;
 	struct sd *sd = (struct sd *) gspca_dev;
 	int mode;
 	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);
 	bridge_init(sd);
 	global_init(sd, 0);
 	global_init(sd, 0);
 	msleep(100);
 	msleep(100);
@@ -1071,7 +992,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
 				ARRAY_SIZE(lz24bp_start_2),
 				ARRAY_SIZE(lz24bp_start_2),
 				6);
 				6);
 		mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
 		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);
 		msleep(10);
 		break;
 		break;
 	case SENSOR_MI0360:
 	case SENSOR_MI0360:
@@ -1095,7 +1016,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
 		/* 1st start */
 		/* 1st start */
 		send_start(gspca_dev);
 		send_start(gspca_dev);
 		msleep(60);
 		msleep(60);
-		reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000);
+		send_stop(gspca_dev);
 
 
 		i2c_write(sd,
 		i2c_write(sd,
 			mi0360_start_4, ARRAY_SIZE(mi0360_start_4));
 			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));
 				ARRAY_SIZE(mt9v111_init_2));
 		ucbus_write(gspca_dev, mt9v111_start_1,
 		ucbus_write(gspca_dev, mt9v111_start_1,
 				ARRAY_SIZE(mt9v111_start_1),
 				ARRAY_SIZE(mt9v111_start_1),
-				8);
+				5);
 		i2c_write(sd, mt9v111_init_3,
 		i2c_write(sd, mt9v111_init_3,
 				ARRAY_SIZE(mt9v111_init_3));
 				ARRAY_SIZE(mt9v111_init_3));
 		i2c_write(sd, mt9v111_init_4,
 		i2c_write(sd, mt9v111_init_4,
@@ -1125,8 +1046,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
 out:
 out:
 	msleep(1000);
 	msleep(1000);
 
 
-	sd->eof_len = 0;	/* init packet scan */
-
 	if (sd->sensor == SENSOR_MT9V111)
 	if (sd->sensor == SENSOR_MT9V111)
 		gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED);
 		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);
 	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,
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
 			u8 *data,		/* isoc packet */
 			u8 *data,		/* isoc packet */
 			int len)		/* iso packet length */
 			int len)		/* iso packet length */
 {
 {
 	struct sd *sd = (struct sd *) gspca_dev;
 	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)
 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;
 	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 */
 /* sub-driver description */
 static const struct sd_desc sd_desc = {
 static const struct sd_desc sd_desc = {
 	.name   = MODULE_NAME,
 	.name   = MODULE_NAME,
@@ -1342,8 +1145,6 @@ static const struct sd_desc sd_desc = {
 	.stopN  = sd_stopN,
 	.stopN  = sd_stopN,
 	.pkt_scan = sd_pkt_scan,
 	.pkt_scan = sd_pkt_scan,
 	.dq_callback = sd_dq_callback,
 	.dq_callback = sd_dq_callback,
-	.get_jcomp = sd_get_jcomp,
-	.set_jcomp = sd_set_jcomp,
 };
 };
 
 
 /* Table of supported USB devices */
 /* Table of supported USB devices */

+ 2 - 2
drivers/media/video/gspca/t613.c

@@ -55,12 +55,12 @@ struct sd {
 	u8 effect;
 	u8 effect;
 
 
 	u8 sensor;
 	u8 sensor;
-enum {
+};
+enum sensors {
 	SENSOR_OM6802,
 	SENSOR_OM6802,
 	SENSOR_OTHER,
 	SENSOR_OTHER,
 	SENSOR_TAS5130A,
 	SENSOR_TAS5130A,
 	SENSOR_LT168G,		/* must verify if this is the actual model */
 	SENSOR_LT168G,		/* must verify if this is the actual model */
-} sensors;
 };
 };
 
 
 /* V4L2 controls supported by the driver */
 /* V4L2 controls supported by the driver */

+ 301 - 59
drivers/media/video/gspca/vc032x.c

@@ -39,6 +39,10 @@ struct sd {
 	u8 vflip;
 	u8 vflip;
 	u8 lightfreq;
 	u8 lightfreq;
 	s8 sharpness;
 	s8 sharpness;
+	u16 exposure;
+	u8 gain;
+	u8 autogain;
+	u8 backlight;
 
 
 	u8 image_offset;
 	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_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
 static int sd_setsharpness(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_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[] = {
 static const struct ctrl sd_ctrls[] = {
 #define BRIGHTNESS_IDX 0
 #define BRIGHTNESS_IDX 0
@@ -185,6 +197,66 @@ static const struct ctrl sd_ctrls[] = {
 	 .set = sd_setsharpness,
 	 .set = sd_setsharpness,
 	 .get = sd_getsharpness,
 	 .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 */
 /* table of the disabled controls */
@@ -192,33 +264,51 @@ static u32 ctrl_dis[] = {
 /* SENSOR_HV7131R 0 */
 /* SENSOR_HV7131R 0 */
 	(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
 	(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
 		| (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_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 */
 /* SENSOR_MI0360 1 */
 	(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
 	(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
 		| (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_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 */
 /* SENSOR_MI1310_SOC 2 */
 	(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
 	(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 */
 /* SENSOR_MI1320 3 */
 	(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
 	(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 */
 /* SENSOR_MI1320_SOC 4 */
 	(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
 	(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 */
 /* SENSOR_OV7660 5 */
 	(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
 	(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 */
 /* SENSOR_OV7670 6 */
 	(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
 	(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 */
 /* SENSOR_PO1200 7 */
 	(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
 	(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 */
 /* SENSOR_PO3130NC 8 */
 	(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
 	(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
 		| (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_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 */
 /* SENSOR_POxxxx 9 */
 	(1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX),
 	(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, 0x1e, 0xc6, 0xaa},
 	{0x00, 0x00, 0x40, 0xdd},
 	{0x00, 0x00, 0x40, 0xdd},
 	{0x00, 0x1d, 0x05, 0xaa},
 	{0x00, 0x1d, 0x05, 0xaa},
-
+	{}
+};
+static const u8 poxxxx_gamma[][4] = {
 	{0x00, 0xd6, 0x22, 0xaa},	/* gamma 0 */
 	{0x00, 0xd6, 0x22, 0xaa},	/* gamma 0 */
 	{0x00, 0x73, 0x00, 0xaa},
 	{0x00, 0x73, 0x00, 0xaa},
 	{0x00, 0x74, 0x0a, 0xaa},
 	{0x00, 0x74, 0x0a, 0xaa},
@@ -2867,19 +2959,9 @@ static const u8 poxxxx_init_common[][4] = {
 	{0x00, 0x7c, 0xba, 0xaa},
 	{0x00, 0x7c, 0xba, 0xaa},
 	{0x00, 0x7d, 0xd4, 0xaa},
 	{0x00, 0x7d, 0xd4, 0xaa},
 	{0x00, 0x7e, 0xea, 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, 0xb8, 0x28, 0xaa},
 	{0x00, 0xb9, 0x1e, 0xaa},
 	{0x00, 0xb9, 0x1e, 0xaa},
 	{0x00, 0xb6, 0x14, 0xaa},
 	{0x00, 0xb6, 0x14, 0xaa},
@@ -2959,9 +3041,6 @@ static const u8 poxxxx_init_end_1[][4] = {
 	{0x00, 0xb3, 0x08, 0xaa},
 	{0x00, 0xb3, 0x08, 0xaa},
 	{0x00, 0xb4, 0x0b, 0xaa},
 	{0x00, 0xb4, 0x0b, 0xaa},
 	{0x00, 0xb5, 0x0d, 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] = {
 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 */
 /* this function is called at probe time */
 static int sd_config(struct gspca_dev *gspca_dev,
 static int sd_config(struct gspca_dev *gspca_dev,
 			const struct usb_device_id *id)
 			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 sd *sd = (struct sd *) gspca_dev;
 	struct cam *cam;
 	struct cam *cam;
@@ -3327,14 +3433,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
 		128,		/* POxxxx 9 */
 		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);
 		sensor = vc032x_probe_sensor(gspca_dev);
+	else
+		sensor = sd->sensor;
+
 	switch (sensor) {
 	switch (sensor) {
 	case -1:
 	case -1:
 		PDEBUG(D_PROBE, "Unknown sensor...");
 		PDEBUG(D_PROBE, "Unknown sensor...");
@@ -3373,6 +3476,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
 	}
 	}
 	sd->sensor = sensor;
 	sd->sensor = sensor;
 
 
+	cam = &gspca_dev->cam;
 	if (sd->bridge == BRIDGE_VC0321) {
 	if (sd->bridge == BRIDGE_VC0321) {
 		cam->cam_mode = vc0321_mode;
 		cam->cam_mode = vc0321_mode;
 		cam->nmodes = ARRAY_SIZE(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];
 	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];
 	gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
 
 
 	if (sd->sensor == SENSOR_OV7670)
 	if (sd->sensor == SENSOR_OV7670)
 		sd->flags |= FL_HFLIP | FL_VFLIP;
 		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) {
 	if (sd->bridge == BRIDGE_VC0321) {
 		reg_r(gspca_dev, 0x8a, 0, 3);
 		reg_r(gspca_dev, 0x8a, 0, 3);
 		reg_w(gspca_dev, 0x87, 0x00, 0x0f0f);
 		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) {
 			if (gspca_dev->usb_buf[0] != 0) {
 				reg_w(gspca_dev, 0xa0, 0x26, 0xb300);
 				reg_w(gspca_dev, 0xa0, 0x26, 0xb300);
 				reg_w(gspca_dev, 0xa0, 0x04, 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;
 	return gspca_dev->usb_err;
@@ -3551,6 +3638,82 @@ static void setsharpness(struct gspca_dev *gspca_dev)
 		break;
 		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)
 static int sd_start(struct gspca_dev *gspca_dev)
 {
 {
@@ -3662,6 +3825,16 @@ static int sd_start(struct gspca_dev *gspca_dev)
 	default:
 	default:
 /*	case SENSOR_POxxxx: */
 /*	case SENSOR_POxxxx: */
 		usb_exchange(gspca_dev, poxxxx_init_common);
 		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)
 		if (mode)
 			init = poxxxx_initQVGA;
 			init = poxxxx_initQVGA;
 		else
 		else
@@ -3693,7 +3866,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
 			break;
 			break;
 		}
 		}
 		msleep(100);
 		msleep(100);
-		setsharpness(gspca_dev);
 		sethvflip(gspca_dev);
 		sethvflip(gspca_dev);
 		setlightfreq(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);
 		reg_w(gspca_dev, 0xa0, 0x0000, 0xbfff);
 		break;
 		break;
 	case SENSOR_POxxxx:
 	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);
 		usb_exchange(gspca_dev, poxxxx_init_end_2);
+		setwb(gspca_dev);
+		msleep(80);		/* led on */
+		reg_w(gspca_dev, 0x89, 0xffff, 0xfdff);
 		break;
 		break;
 	}
 	}
 	return gspca_dev->usb_err;
 	return gspca_dev->usb_err;
@@ -3911,6 +4079,80 @@ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
 	return 0;
 	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,
 static int sd_querymenu(struct gspca_dev *gspca_dev,
 			struct v4l2_querymenu *menu)
 			struct v4l2_querymenu *menu)
 {
 {

File diff suppressed because it is too large
+ 498 - 240
drivers/media/video/gspca/zc3xx.c


+ 32 - 244
drivers/media/video/ivtv/ivtv-controls.c

@@ -17,163 +17,14 @@
     along with this program; if not, write to the Free Software
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     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-driver.h"
-#include "ivtv-cards.h"
 #include "ivtv-ioctl.h"
 #include "ivtv-ioctl.h"
-#include "ivtv-routing.h"
-#include "ivtv-i2c.h"
-#include "ivtv-mailbox.h"
 #include "ivtv-controls.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. */
 	/* First try to allocate sliced VBI buffers if needed. */
 	if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) {
 	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;
 	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,
+};

+ 1 - 5
drivers/media/video/ivtv/ivtv-controls.h

@@ -21,10 +21,6 @@
 #ifndef IVTV_CONTROLS_H
 #ifndef IVTV_CONTROLS_H
 #define 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
 #endif

+ 14 - 12
drivers/media/video/ivtv/ivtv-driver.c

@@ -53,6 +53,7 @@
 #include "ivtv-cards.h"
 #include "ivtv-cards.h"
 #include "ivtv-vbi.h"
 #include "ivtv-vbi.h"
 #include "ivtv-routing.h"
 #include "ivtv-routing.h"
+#include "ivtv-controls.h"
 #include "ivtv-gpio.h"
 #include "ivtv-gpio.h"
 
 
 #include <media/tveeprom.h>
 #include <media/tveeprom.h>
@@ -734,9 +735,8 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv)
 	itv->open_id = 1;
 	itv->open_id = 1;
 
 
 	/* Initial settings */
 	/* 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->eos_waitq);
 	init_waitqueue_head(&itv->event_waitq);
 	init_waitqueue_head(&itv->event_waitq);
 	init_waitqueue_head(&itv->vsync_waitq);
 	init_waitqueue_head(&itv->vsync_waitq);
@@ -1006,6 +1006,13 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
 		retval = -ENOMEM;
 		retval = -ENOMEM;
 		goto err;
 		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);
 	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_w = itv->yuv_info.osd_full_w;
 	itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h;
 	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_MPG] = 0x08000;
 	itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200;
 	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_DEBUG_INFO("Getting firmware version..\n");
 	ivtv_firmware_versions(itv);
 	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);
 		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.tuner = 0;
 	vf.type = V4L2_TUNER_ANALOG_TV;
 	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 */
 	/* For cards with video out, this call needs interrupts enabled */
 	ivtv_s_std(NULL, &fh, &itv->tuner_std);
 	ivtv_s_std(NULL, &fh, &itv->tuner_std);
 
 
+	/* Setup initial controls */
+	cx2341x_handler_setup(&itv->cxhdl);
 	return 0;
 	return 0;
 }
 }
 
 

+ 3 - 1
drivers/media/video/ivtv/ivtv-driver.h

@@ -62,6 +62,7 @@
 #include <linux/dvb/audio.h>
 #include <linux/dvb/audio.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-fh.h>
 #include <media/v4l2-fh.h>
 #include <media/tuner.h>
 #include <media/tuner.h>
@@ -631,6 +632,8 @@ struct ivtv {
 	struct ivtv_options options; 	/* user options */
 	struct ivtv_options options; 	/* user options */
 
 
 	struct v4l2_device v4l2_dev;
 	struct v4l2_device v4l2_dev;
+	struct cx2341x_handler cxhdl;
+	struct v4l2_ctrl_handler hdl_gpio;
 	struct v4l2_subdev sd_gpio;	/* GPIO sub-device */
 	struct v4l2_subdev sd_gpio;	/* GPIO sub-device */
 	u16 instance;
 	u16 instance;
 
 
@@ -648,7 +651,6 @@ struct ivtv {
 	v4l2_std_id std_out;            /* current TV output standard */
 	v4l2_std_id std_out;            /* current TV output standard */
 	u8 audio_stereo_mode;           /* decoder setting how to handle stereo MPEG audio */
 	u8 audio_stereo_mode;           /* decoder setting how to handle stereo MPEG audio */
 	u8 audio_bilingual_mode;        /* decoder setting how to handle bilingual MPEG audio */
 	u8 audio_bilingual_mode;        /* decoder setting how to handle bilingual MPEG audio */
-	struct cx2341x_mpeg_params params;              /* current encoder parameters */
 
 
 
 
 	/* Locking */
 	/* Locking */

+ 8 - 15
drivers/media/video/ivtv/ivtv-fileops.c

@@ -150,12 +150,10 @@ void ivtv_release_stream(struct ivtv_stream *s)
 static void ivtv_dualwatch(struct ivtv *itv)
 static void ivtv_dualwatch(struct ivtv *itv)
 {
 {
 	struct v4l2_tuner vt;
 	struct v4l2_tuner vt;
-	u32 new_bitmap;
 	u32 new_stereo_mode;
 	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));
 	memset(&vt, 0, sizeof(vt));
 	ivtv_call_all(itv, tuner, g_tuner, &vt);
 	ivtv_call_all(itv, tuner, g_tuner, &vt);
 	if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
 	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)
 	if (new_stereo_mode == itv->dualwatch_stereo_mode)
 		return;
 		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)
 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) {
 		if (atomic_read(&itv->capturing) > 0) {
 			/* Undo video mute */
 			/* Undo video mute */
 			ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
 			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. */
 		/* Done! Unmute and continue. */
 		ivtv_unmute(itv);
 		ivtv_unmute(itv);

+ 3 - 3
drivers/media/video/ivtv/ivtv-firmware.c

@@ -248,9 +248,9 @@ void ivtv_init_mpeg_decoder(struct ivtv *itv)
 	volatile u8 __iomem *mem_offset;
 	volatile u8 __iomem *mem_offset;
 
 
 	data[0] = 0;
 	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. */
 							   bitmap. see docs. */
 	if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) {
 	if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) {
 		IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n");
 		IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n");

+ 36 - 41
drivers/media/video/ivtv/ivtv-gpio.c

@@ -24,6 +24,7 @@
 #include "ivtv-gpio.h"
 #include "ivtv-gpio.h"
 #include "tuner-xc2028.h"
 #include "tuner-xc2028.h"
 #include <media/tuner.h>
 #include <media/tuner.h>
+#include <media/v4l2-ctrls.h>
 
 
 /*
 /*
  * GPIO assignment of Yuan MPG600/MPG160
  * 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);
 	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)
 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;
 	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);
 	struct ivtv *itv = sd_to_ivtv(sd);
 	u16 mask, data;
 	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)
 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",
 	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_DIR), read_reg(IVTV_REG_GPIO_OUT),
 			read_reg(IVTV_REG_GPIO_IN));
 			read_reg(IVTV_REG_GPIO_IN));
+	v4l2_ctrl_handler_log_status(&itv->hdl_gpio, sd->name);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -327,11 +307,19 @@ static int subdev_s_video_routing(struct v4l2_subdev *sd,
 	return 0;
 	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 = {
 static const struct v4l2_subdev_core_ops subdev_core_ops = {
 	.log_status = subdev_log_status,
 	.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 = {
 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);
 	v4l2_subdev_init(&itv->sd_gpio, &subdev_ops);
 	snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->v4l2_dev.name);
 	snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->v4l2_dev.name);
 	itv->sd_gpio.grp_id = IVTV_HW_GPIO;
 	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);
 	return v4l2_device_register_subdev(&itv->v4l2_dev, &itv->sd_gpio);
 }
 }

+ 7 - 0
drivers/media/video/ivtv/ivtv-i2c.c

@@ -63,6 +63,7 @@
 #include "ivtv-cards.h"
 #include "ivtv-cards.h"
 #include "ivtv-gpio.h"
 #include "ivtv-gpio.h"
 #include "ivtv-i2c.h"
 #include "ivtv-i2c.h"
+#include <media/cx25840.h>
 
 
 /* i2c implementation for cx23415/6 chip, ivtv project.
 /* i2c implementation for cx23415/6 chip, ivtv project.
  * Author: Kevin Thayer (nufan_wfk at yahoo.com)
  * 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) {
 	if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) {
 		sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
 		sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
 				adap, mod, type, 0, I2C_ADDRS(hw_addrs[idx]));
 				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 {
 	} else {
 		sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
 		sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
 				adap, mod, type, hw_addrs[idx], NULL);
 				adap, mod, type, hw_addrs[idx], NULL);

+ 13 - 18
drivers/media/video/ivtv/ivtv-ioctl.c

@@ -162,7 +162,7 @@ int ivtv_set_speed(struct ivtv *itv, int speed)
 	data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;
 	data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;
 	data[1] = (speed < 0);
 	data[1] = (speed < 0);
 	data[2] = speed < 0 ? 3 : 7;
 	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[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;
 	data[5] = 0;
 	data[5] = 0;
 	data[6] = 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 ivtv *itv = id->itv;
 	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
 	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->colorspace = V4L2_COLORSPACE_SMPTE170M;
 	pixfmt->field = V4L2_FIELD_INTERLACED;
 	pixfmt->field = V4L2_FIELD_INTERLACED;
 	pixfmt->priv = 0;
 	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_open_id *id = fh;
 	struct ivtv *itv = id->itv;
 	struct ivtv *itv = id->itv;
-	struct cx2341x_mpeg_params *p = &itv->params;
 	struct v4l2_mbus_framefmt mbus_fmt;
 	struct v4l2_mbus_framefmt mbus_fmt;
 	int ret = ivtv_try_fmt_vid_cap(file, fh, fmt);
 	int ret = ivtv_try_fmt_vid_cap(file, fh, fmt);
 	int w = fmt->fmt.pix.width;
 	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)
 	if (ret)
 		return ret;
 		return ret;
 
 
-	if (p->width == w && p->height == h)
+	if (itv->cxhdl.width == w && itv->cxhdl.height == h)
 		return 0;
 		return 0;
 
 
 	if (atomic_read(&itv->capturing) > 0)
 	if (atomic_read(&itv->capturing) > 0)
 		return -EBUSY;
 		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;
 		fmt->fmt.pix.width /= 2;
 	mbus_fmt.width = fmt->fmt.pix.width;
 	mbus_fmt.width = fmt->fmt.pix.width;
 	mbus_fmt.height = h;
 	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->std = *std;
 	itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
 	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.count = itv->is_50hz ? 18 : 12;
 	itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
 	itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
 	itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
 	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);
 		ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
 		itv->main_rect.left = itv->main_rect.top = 0;
 		itv->main_rect.left = itv->main_rect.top = 0;
 		itv->main_rect.width = 720;
 		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,
 		ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
 			720, itv->main_rect.height, 0, 0);
 			720, itv->main_rect.height, 0, 0);
 		yi->main_rect = itv->main_rect;
 		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",
 	IVTV_INFO("Tuner:  %s\n",
 		test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
 		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);
 	IVTV_INFO("Status flags:    0x%08lx\n", itv->i_flags);
 	for (i = 0; i < IVTV_MAX_STREAMS; i++) {
 	for (i = 0; i < IVTV_MAX_STREAMS; i++) {
 		struct ivtv_stream *s = &itv->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,
 	.vidioc_s_register 		    = ivtv_s_register,
 #endif
 #endif
 	.vidioc_default 		    = ivtv_default,
 	.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_subscribe_event 	    = ivtv_subscribe_event,
 	.vidioc_unsubscribe_event 	    = v4l2_event_unsubscribe,
 	.vidioc_unsubscribe_event 	    = v4l2_event_unsubscribe,
 };
 };

+ 15 - 9
drivers/media/video/ivtv/ivtv-streams.c

@@ -210,6 +210,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
 
 
 	s->vdev->num = num;
 	s->vdev->num = num;
 	s->vdev->v4l2_dev = &itv->v4l2_dev;
 	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->fops = ivtv_stream_info[type].fops;
 	s->vdev->release = video_device_release;
 	s->vdev->release = video_device_release;
 	s->vdev->tvnorms = V4L2_STD_ALL;
 	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];
 	u32 data[CX2341X_MBOX_MAX_DATA];
 	struct ivtv *itv = s->itv;
 	struct ivtv *itv = s->itv;
-	struct cx2341x_mpeg_params *p = &itv->params;
 	int captype = 0, subtype = 0;
 	int captype = 0, subtype = 0;
 	int enable_passthrough = 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->mpg_data_received = itv->vbi_data_inserted = 0;
 		itv->dualwatch_jiffies = jiffies;
 		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;
 		itv->search_pack_header = 0;
 		break;
 		break;
 
 
@@ -560,12 +560,12 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
 				itv->pgm_info_offset, itv->pgm_info_num);
 				itv->pgm_info_offset, itv->pgm_info_num);
 
 
 		/* Setup API for Stream */
 		/* Setup API for Stream */
-		cx2341x_update(itv, ivtv_api_func, NULL, p);
+		cx2341x_handler_setup(&itv->cxhdl);
 
 
 		/* mute if capturing radio */
 		/* mute if capturing radio */
 		if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags))
 		if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags))
 			ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
 			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 */
 	/* 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);
 		clear_bit(IVTV_F_I_EOS, &itv->i_flags);
 
 
+		cx2341x_handler_set_busy(&itv->cxhdl, 1);
+
 		/* Initialize Digitizer for Capture */
 		/* Initialize Digitizer for Capture */
 		/* Avoid tinny audio problem - ensure audio clocks are going */
 		/* Avoid tinny audio problem - ensure audio clocks are going */
 		v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1);
 		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];
 	u32 data[CX2341X_MBOX_MAX_DATA];
 	struct ivtv *itv = s->itv;
 	struct ivtv *itv = s->itv;
-	struct cx2341x_mpeg_params *p = &itv->params;
 	int datatype;
 	int datatype;
 	u16 width;
 	u16 width;
 	u16 height;
 	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");
 	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. */
 	/* 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);
 	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;
 		break;
 	}
 	}
 	if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype,
 	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");
 		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;
 		return 0;
 	}
 	}
 
 
+	cx2341x_handler_set_busy(&itv->cxhdl, 0);
+
 	/* Set the following Interrupt mask bits for capture */
 	/* Set the following Interrupt mask bits for capture */
 	ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
 	ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
 	del_timer(&itv->dma_timer);
 	del_timer(&itv->dma_timer);
@@ -967,7 +970,8 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable)
 
 
 		/* Setup capture if not already done */
 		/* Setup capture if not already done */
 		if (atomic_read(&itv->capturing) == 0) {
 		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 */
 		/* 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_PASSTHROUGH, &dec_stream->s_flags);
 	clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
 	clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
 	itv->output_mode = OUT_NONE;
 	itv->output_mode = OUT_NONE;
+	if (atomic_read(&itv->capturing) == 0)
+		cx2341x_handler_set_busy(&itv->cxhdl, 0);
 
 
 	return 0;
 	return 0;
 }
 }

+ 88 - 160
drivers/media/video/msp3400-driver.c

@@ -283,51 +283,6 @@ void msp_set_scart(struct i2c_client *client, int in, int out)
 		msp_write_dem(client, 0x40, state->i2s_mode);
 		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)
 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) {
 	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;
 		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:
 	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;
 		break;
 
 
 	case V4L2_CID_AUDIO_TREBLE:
 	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;
 		break;
 
 
 	case V4L2_CID_AUDIO_LOUDNESS:
 	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;
 		break;
 
 
 	case V4L2_CID_AUDIO_BALANCE:
 	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;
 		break;
 
 
 	default:
 	default:
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
-	msp_set_audio(client);
 	return 0;
 	return 0;
 }
 }
 
 
+void msp_update_volume(struct msp_state *state)
+{
+	v4l2_ctrl_s_ctrl(state->volume, v4l2_ctrl_g_ctrl(state->volume));
+}
+
 /* --- v4l2 ioctls --- */
 /* --- v4l2 ioctls --- */
 static int msp_s_radio(struct v4l2_subdev *sd)
 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_mode(client, MSP_MODE_FM_RADIO);
 		msp3400c_set_carrier(client, MSP_CARRIER(10.7),
 		msp3400c_set_carrier(client, MSP_CARRIER(10.7),
 				MSP_CARRIER(10.7));
 				MSP_CARRIER(10.7));
-		msp_set_audio(client);
+		msp_update_volume(state);
 		break;
 		break;
 	case OPMODE_AUTODETECT:
 	case OPMODE_AUTODETECT:
 	case OPMODE_AUTOSELECT:
 	case OPMODE_AUTOSELECT:
@@ -592,33 +522,6 @@ static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)
 	return 0;
 	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)
 static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
 {
 {
 	struct msp_state *state = to_state(sd);
 	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 msp_state *state = to_state(sd);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	const char *p;
 	const char *p;
+	char prefix[V4L2_SUBDEV_NAME_SIZE + 20];
 
 
 	if (state->opmode == OPMODE_AUTOSELECT)
 	if (state->opmode == OPMODE_AUTOSELECT)
 		msp_detect_stereo(client);
 		msp_detect_stereo(client);
 	v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
 	v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
 			client->name, state->rev1, state->rev2);
 			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) {
 	switch (state->mode) {
 		case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
 		case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
 		case MSP_MODE_FM_RADIO: p = "FM Radio"; 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 = {
 static const struct v4l2_subdev_core_ops msp_core_ops = {
 	.log_status = msp_log_status,
 	.log_status = msp_log_status,
 	.g_chip_ident = msp_g_chip_ident,
 	.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,
 	.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 msp_state *state;
 	struct v4l2_subdev *sd;
 	struct v4l2_subdev *sd;
+	struct v4l2_ctrl_handler *hdl;
 	int (*thread_func)(void *data) = NULL;
 	int (*thread_func)(void *data) = NULL;
 	int msp_hard;
 	int msp_hard;
 	int msp_family;
 	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->v4l2_std = V4L2_STD_NTSC;
 	state->audmode = V4L2_TUNER_MODE_STEREO;
 	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->input = -1;
-	state->muted = 0;
 	state->i2s_mode = 0;
 	state->i2s_mode = 0;
 	init_waitqueue_head(&state->wq);
 	init_waitqueue_head(&state->wq);
 	/* These are the reset input/output positions */
 	/* 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;
 		return -ENODEV;
 	}
 	}
 
 
-	msp_set_audio(client);
-
 	msp_family = ((state->rev1 >> 4) & 0x0f) + 3;
 	msp_family = ((state->rev1 >> 4) & 0x0f) + 3;
 	msp_product = (state->rev2 >> 8) & 0xff;
 	msp_product = (state->rev2 >> 8) & 0xff;
 	msp_prod_hi = msp_product / 10;
 	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;
 			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 :-) */
 	/* hello world :-) */
 	v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n",
 	v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n",
 			msp_family, msp_product,
 			msp_family, msp_product,
@@ -903,6 +830,7 @@ static int msp_remove(struct i2c_client *client)
 	}
 	}
 	msp_reset(client);
 	msp_reset(client);
 
 
+	v4l2_ctrl_handler_free(&state->hdl);
 	kfree(state);
 	kfree(state);
 	return 0;
 	return 0;
 }
 }

+ 14 - 4
drivers/media/video/msp3400-driver.h

@@ -6,6 +6,7 @@
 
 
 #include <media/msp3400.h>
 #include <media/msp3400.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
 
 
 /* ---------------------------------------------------------------------- */
 /* ---------------------------------------------------------------------- */
 
 
@@ -51,6 +52,7 @@ extern int msp_stereo_thresh;
 
 
 struct msp_state {
 struct msp_state {
 	struct v4l2_subdev sd;
 	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler hdl;
 	int rev1, rev2;
 	int rev1, rev2;
 	int ident;
 	int ident;
 	u8 has_nicam;
 	u8 has_nicam;
@@ -87,9 +89,12 @@ struct msp_state {
 	int audmode;
 	int audmode;
 	int rxsubchans;
 	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;
 	int scan_in_progress;
 
 
 	/* thread */
 	/* thread */
@@ -104,6 +109,11 @@ static inline struct msp_state *to_state(struct v4l2_subdev *sd)
 	return container_of(sd, struct msp_state, 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 */
 /* msp3400-driver.c */
 int msp_write_dem(struct i2c_client *client, int addr, int val);
 int msp_write_dem(struct i2c_client *client, int addr, int val);
 int msp_write_dsp(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_read_dsp(struct i2c_client *client, int addr);
 int msp_reset(struct i2c_client *client);
 int msp_reset(struct i2c_client *client);
 void msp_set_scart(struct i2c_client *client, int in, int out);
 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);
 int msp_sleep(struct msp_state *state, int timeout);
 
 
 /* msp3400-kthreads.c */
 /* msp3400-kthreads.c */

+ 8 - 8
drivers/media/video/msp3400-kthreads.c

@@ -496,13 +496,13 @@ restart:
 			v4l_dbg(1, msp_debug, client,
 			v4l_dbg(1, msp_debug, client,
 				"thread: no carrier scan\n");
 				"thread: no carrier scan\n");
 			state->scan_in_progress = 0;
 			state->scan_in_progress = 0;
-			msp_set_audio(client);
+			msp_update_volume(state);
 			continue;
 			continue;
 		}
 		}
 
 
 		/* mute audio */
 		/* mute audio */
 		state->scan_in_progress = 1;
 		state->scan_in_progress = 1;
-		msp_set_audio(client);
+		msp_update_volume(state);
 
 
 		msp3400c_set_mode(client, MSP_MODE_AM_DETECT);
 		msp3400c_set_mode(client, MSP_MODE_AM_DETECT);
 		val1 = val2 = 0;
 		val1 = val2 = 0;
@@ -634,7 +634,7 @@ no_second:
 		/* unmute */
 		/* unmute */
 		state->scan_in_progress = 0;
 		state->scan_in_progress = 0;
 		msp3400c_set_audmode(client);
 		msp3400c_set_audmode(client);
-		msp_set_audio(client);
+		msp_update_volume(state);
 
 
 		if (msp_debug)
 		if (msp_debug)
 			msp3400c_print_mode(client);
 			msp3400c_print_mode(client);
@@ -679,13 +679,13 @@ restart:
 			v4l_dbg(1, msp_debug, client,
 			v4l_dbg(1, msp_debug, client,
 				"thread: no carrier scan\n");
 				"thread: no carrier scan\n");
 			state->scan_in_progress = 0;
 			state->scan_in_progress = 0;
-			msp_set_audio(client);
+			msp_update_volume(state);
 			continue;
 			continue;
 		}
 		}
 
 
 		/* mute audio */
 		/* mute audio */
 		state->scan_in_progress = 1;
 		state->scan_in_progress = 1;
-		msp_set_audio(client);
+		msp_update_volume(state);
 
 
 		/* start autodetect. Note: autodetect is not supported for
 		/* start autodetect. Note: autodetect is not supported for
 		   NTSC-M and radio, hence we force the standard in those
 		   NTSC-M and radio, hence we force the standard in those
@@ -797,7 +797,7 @@ restart:
 		/* unmute */
 		/* unmute */
 		msp3400c_set_audmode(client);
 		msp3400c_set_audmode(client);
 		state->scan_in_progress = 0;
 		state->scan_in_progress = 0;
-		msp_set_audio(client);
+		msp_update_volume(state);
 
 
 		/* monitor tv audio mode, the first time don't wait
 		/* monitor tv audio mode, the first time don't wait
 		   so long to get a quick stereo/bilingual result */
 		   so long to get a quick stereo/bilingual result */
@@ -974,7 +974,7 @@ restart:
 			v4l_dbg(1, msp_debug, client,
 			v4l_dbg(1, msp_debug, client,
 				"thread: no carrier scan\n");
 				"thread: no carrier scan\n");
 			state->scan_in_progress = 0;
 			state->scan_in_progress = 0;
-			msp_set_audio(client);
+			msp_update_volume(state);
 			continue;
 			continue;
 		}
 		}
 
 
@@ -1020,7 +1020,7 @@ unmute:
 		}
 		}
 
 
 		/* unmute: dispatch sound to scart output, set scart volume */
 		/* unmute: dispatch sound to scart output, set scart volume */
-		msp_set_audio(client);
+		msp_update_volume(state);
 
 
 		/* restore ACB */
 		/* restore ACB */
 		if (msp_write_dsp(client, 0x13, state->acb))
 		if (msp_write_dsp(client, 0x13, state->acb))

+ 23 - 17
drivers/media/video/mt9m111.c

@@ -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>
  * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
  *
  *
@@ -19,11 +19,14 @@
 #include <media/soc_camera.h>
 #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()
  * 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_CHIP_VERSION		0x000
 #define MT9M111_ROW_START		0x001
 #define MT9M111_ROW_START		0x001
 #define MT9M111_COLUMN_START		0x002
 #define MT9M111_COLUMN_START		0x002
@@ -72,8 +75,9 @@
 #define MT9M111_CTXT_CTRL_LED_FLASH_EN	(1 << 2)
 #define MT9M111_CTXT_CTRL_LED_FLASH_EN	(1 << 2)
 #define MT9M111_CTXT_CTRL_VBLANK_SEL_B	(1 << 1)
 #define MT9M111_CTXT_CTRL_VBLANK_SEL_B	(1 << 1)
 #define MT9M111_CTXT_CTRL_HBLANK_SEL_B	(1 << 0)
 #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_OPER_MODE_CTRL		0x106
 #define MT9M111_OUTPUT_FORMAT_CTRL	0x108
 #define MT9M111_OUTPUT_FORMAT_CTRL	0x108
@@ -109,8 +113,9 @@
 #define MT9M111_OUTFMT_SWAP_YCbCr_C_Y	(1 << 1)
 #define MT9M111_OUTFMT_SWAP_YCbCr_C_Y	(1 << 1)
 #define MT9M111_OUTFMT_SWAP_RGB_EVEN	(1 << 1)
 #define MT9M111_OUTFMT_SWAP_RGB_EVEN	(1 << 1)
 #define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr	(1 << 0)
 #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)
 #define reg_read(reg) mt9m111_reg_read(client, MT9M111_##reg)
@@ -160,7 +165,8 @@ enum mt9m111_context {
 
 
 struct mt9m111 {
 struct mt9m111 {
 	struct v4l2_subdev subdev;
 	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;
 	enum mt9m111_context context;
 	struct v4l2_rect rect;
 	struct v4l2_rect rect;
 	const struct mt9m111_datafmt *fmt;
 	const struct mt9m111_datafmt *fmt;
@@ -934,7 +940,7 @@ static int mt9m111_init(struct i2c_client *client)
 	if (!ret)
 	if (!ret)
 		ret = mt9m111_set_autoexposure(client, mt9m111->autoexposure);
 		ret = mt9m111_set_autoexposure(client, mt9m111->autoexposure);
 	if (ret)
 	if (ret)
-		dev_err(&client->dev, "mt9m11x init failed: %d\n", ret);
+		dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
 	return 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_even_odd = 1;
 	mt9m111->swap_rgb_red_blue = 1;
 	mt9m111->swap_rgb_red_blue = 1;
 
 
-	ret = mt9m111_init(client);
-	if (ret)
-		goto ei2c;
-
 	data = reg_read(CHIP_VERSION);
 	data = reg_read(CHIP_VERSION);
 
 
 	switch (data) {
 	switch (data) {
-	case 0x143a: /* MT9M111 */
+	case 0x143a: /* MT9M111 or MT9M131 */
 		mt9m111->model = V4L2_IDENT_MT9M111;
 		mt9m111->model = V4L2_IDENT_MT9M111;
+		dev_info(&client->dev,
+			"Detected a MT9M111/MT9M131 chip ID %x\n", data);
 		break;
 		break;
 	case 0x148c: /* MT9M112 */
 	case 0x148c: /* MT9M112 */
 		mt9m111->model = V4L2_IDENT_MT9M112;
 		mt9m111->model = V4L2_IDENT_MT9M112;
+		dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
 		break;
 		break;
 	default:
 	default:
 		ret = -ENODEV;
 		ret = -ENODEV;
 		dev_err(&client->dev,
 		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;
 		goto ei2c;
 	}
 	}
 
 
-	dev_info(&client->dev, "Detected a MT9M11x chip ID %x\n", data);
+	ret = mt9m111_init(client);
 
 
 ei2c:
 ei2c:
 	return ret;
 	return ret;
@@ -1034,13 +1040,13 @@ static int mt9m111_probe(struct i2c_client *client,
 	int ret;
 	int ret;
 
 
 	if (!icd) {
 	if (!icd) {
-		dev_err(&client->dev, "MT9M11x: missing soc-camera data!\n");
+		dev_err(&client->dev, "mt9m111: soc-camera data missing!\n");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
 	icl = to_soc_camera_link(icd);
 	icl = to_soc_camera_link(icd);
 	if (!icl) {
 	if (!icl) {
-		dev_err(&client->dev, "MT9M11x driver needs platform data\n");
+		dev_err(&client->dev, "mt9m111: driver needs platform data\n");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
@@ -1114,6 +1120,6 @@ static void __exit mt9m111_mod_exit(void)
 module_init(mt9m111_mod_init);
 module_init(mt9m111_mod_init);
 module_exit(mt9m111_mod_exit);
 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_AUTHOR("Robert Jarzmik");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 3 - 1
drivers/media/video/mx2_camera.c

@@ -785,6 +785,8 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
+	if (common_flags & SOCAM_PCLK_SAMPLE_RISING)
+		csicr1 |= CSICR1_REDGE;
 	if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
 	if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
 		csicr1 |= CSICR1_INV_PCLK;
 		csicr1 |= CSICR1_INV_PCLK;
 	if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH)
 	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,
 	buf = list_entry(pcdev->capture.next,
 			struct mx2_buffer, vb.queue);
 			struct mx2_buffer, vb.queue);
 
 
-	buf->bufnum = bufnum;
+	buf->bufnum = !bufnum;
 
 
 	list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
 	list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
 
 

+ 2 - 12
drivers/media/video/pvrusb2/pvrusb2-debugifc.c

@@ -94,8 +94,6 @@ static int debugifc_parse_unsigned_number(const char *buf,unsigned int count,
 					  u32 *num_ptr)
 					  u32 *num_ptr)
 {
 {
 	u32 result = 0;
 	u32 result = 0;
-	u32 val;
-	int ch;
 	int radix = 10;
 	int radix = 10;
 	if ((count >= 2) && (buf[0] == '0') &&
 	if ((count >= 2) && (buf[0] == '0') &&
 	    ((buf[1] == 'x') || (buf[1] == 'X'))) {
 	    ((buf[1] == 'x') || (buf[1] == 'X'))) {
@@ -107,17 +105,9 @@ static int debugifc_parse_unsigned_number(const char *buf,unsigned int count,
 	}
 	}
 
 
 	while (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;
 			return -EINVAL;
-		}
-		if (val >= radix) return -EINVAL;
 		result *= radix;
 		result *= radix;
 		result += val;
 		result += val;
 	}
 	}

+ 3 - 0
drivers/media/video/s5p-fimc/Makefile

@@ -0,0 +1,3 @@
+
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) := s5p-fimc.o
+s5p-fimc-y := fimc-core.o fimc-reg.o

+ 1586 - 0
drivers/media/video/s5p-fimc/fimc-core.c

@@ -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");

+ 471 - 0
drivers/media/video/s5p-fimc/fimc-core.h

@@ -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_ */

+ 527 - 0
drivers/media/video/s5p-fimc/fimc-reg.c

@@ -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));
+	}
+}

+ 293 - 0
drivers/media/video/s5p-fimc/regs-fimc.h

@@ -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_ */

+ 83 - 100
drivers/media/video/saa7115.c

@@ -45,6 +45,7 @@
 #include <linux/i2c.h>
 #include <linux/i2c.h>
 #include <linux/videodev2.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-i2c-drv.h>
 #include <media/v4l2-i2c-drv.h>
 #include <media/saa7115.h>
 #include <media/saa7115.h>
@@ -65,16 +66,19 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
 
 
 struct saa711x_state {
 struct saa711x_state {
 	struct v4l2_subdev sd;
 	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;
 	v4l2_std_id std;
 	int input;
 	int input;
 	int output;
 	int output;
 	int enable;
 	int enable;
 	int radio;
 	int radio;
-	int bright;
-	int contrast;
-	int hue;
-	int sat;
-	int chroma_agc;
 	int width;
 	int width;
 	int height;
 	int height;
 	u32 ident;
 	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);
 	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)
 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;
 	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);
 	struct saa711x_state *state = to_state(sd);
-	u8 val;
 
 
 	switch (ctrl->id) {
 	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:
 	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;
 		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;
 	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);
 	struct saa711x_state *state = to_state(sd);
 
 
 	switch (ctrl->id) {
 	switch (ctrl->id) {
 	case V4L2_CID_BRIGHTNESS:
 	case V4L2_CID_BRIGHTNESS:
-		ctrl->value = state->bright;
+		saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val);
 		break;
 		break;
+
 	case V4L2_CID_CONTRAST:
 	case V4L2_CID_CONTRAST:
-		ctrl->value = state->contrast;
+		saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val);
 		break;
 		break;
+
 	case V4L2_CID_SATURATION:
 	case V4L2_CID_SATURATION:
-		ctrl->value = state->sat;
+		saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val);
 		break;
 		break;
+
 	case V4L2_CID_HUE:
 	case V4L2_CID_HUE:
-		ctrl->value = state->hue;
+		saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val);
 		break;
 		break;
+
 	case V4L2_CID_CHROMA_AGC:
 	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;
 		break;
+
 	default:
 	default:
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
@@ -1223,25 +1189,6 @@ static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
 	return 0;
 	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)
 static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
 {
 {
 	struct saa711x_state *state = to_state(sd);
 	struct saa711x_state *state = to_state(sd);
@@ -1518,17 +1465,27 @@ static int saa711x_log_status(struct v4l2_subdev *sd)
 		break;
 		break;
 	}
 	}
 	v4l2_info(sd, "Width, Height:   %d, %d\n", state->width, state->height);
 	v4l2_info(sd, "Width, Height:   %d, %d\n", state->width, state->height);
+	v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
 	return 0;
 	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 = {
 static const struct v4l2_subdev_core_ops saa711x_core_ops = {
 	.log_status = saa711x_log_status,
 	.log_status = saa711x_log_status,
 	.g_chip_ident = saa711x_g_chip_ident,
 	.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,
 	.s_std = saa711x_s_std,
 	.reset = saa711x_reset,
 	.reset = saa711x_reset,
 	.s_gpio = saa711x_s_gpio,
 	.s_gpio = saa711x_s_gpio,
@@ -1579,8 +1536,9 @@ static int saa711x_probe(struct i2c_client *client,
 {
 {
 	struct saa711x_state *state;
 	struct saa711x_state *state;
 	struct v4l2_subdev *sd;
 	struct v4l2_subdev *sd;
-	int	i;
-	char	name[17];
+	struct v4l2_ctrl_handler *hdl;
+	int i;
+	char name[17];
 	char chip_id;
 	char chip_id;
 	int autodetect = !id || id->driver_data == 1;
 	int autodetect = !id || id->driver_data == 1;
 
 
@@ -1619,15 +1577,38 @@ static int saa711x_probe(struct i2c_client *client,
 		return -ENOMEM;
 		return -ENOMEM;
 	sd = &state->sd;
 	sd = &state->sd;
 	v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
 	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->input = -1;
 	state->output = SAA7115_IPORT_ON;
 	state->output = SAA7115_IPORT_ON;
 	state->enable = 1;
 	state->enable = 1;
 	state->radio = 0;
 	state->radio = 0;
-	state->bright = 128;
-	state->contrast = 64;
-	state->hue = 0;
-	state->sat = 64;
-	state->chroma_agc = 1;
 	switch (chip_id) {
 	switch (chip_id) {
 	case '1':
 	case '1':
 		state->ident = V4L2_IDENT_SAA7111;
 		state->ident = V4L2_IDENT_SAA7111;
@@ -1675,6 +1656,7 @@ static int saa711x_probe(struct i2c_client *client,
 	if (state->ident > V4L2_IDENT_SAA7111A)
 	if (state->ident > V4L2_IDENT_SAA7111A)
 		saa711x_writeregs(sd, saa7115_init_misc);
 		saa711x_writeregs(sd, saa7115_init_misc);
 	saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
 	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",
 	v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
 		saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
 		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);
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 
 
 	v4l2_device_unregister_subdev(sd);
 	v4l2_device_unregister_subdev(sd);
+	v4l2_ctrl_handler_free(sd->ctrl_handler);
 	kfree(to_state(sd));
 	kfree(to_state(sd));
 	return 0;
 	return 0;
 }
 }

+ 81 - 242
drivers/media/video/saa717x.c

@@ -38,6 +38,7 @@
 #include <linux/videodev2.h>
 #include <linux/videodev2.h>
 #include <linux/i2c.h>
 #include <linux/i2c.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-i2c-drv.h>
 #include <media/v4l2-i2c-drv.h>
 
 
 MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver");
 MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver");
@@ -55,14 +56,11 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
 
 
 struct saa717x_state {
 struct saa717x_state {
 	struct v4l2_subdev sd;
 	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler hdl;
 	v4l2_std_id std;
 	v4l2_std_id std;
 	int input;
 	int input;
 	int enable;
 	int enable;
 	int radio;
 	int radio;
-	int bright;
-	int contrast;
-	int hue;
-	int sat;
 	int playback;
 	int playback;
 	int audio;
 	int audio;
 	int tuner_audio_mode;
 	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);
 	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 */
 /* 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]);
 	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 */
 /* write regs to set audio volume, bass and treble */
 static int set_audio_regs(struct v4l2_subdev *sd,
 static int set_audio_regs(struct v4l2_subdev *sd,
 		struct saa717x_state *decoder)
 		struct saa717x_state *decoder)
@@ -829,9 +809,9 @@ static int set_audio_regs(struct v4l2_subdev *sd,
 
 
 	saa717x_write(sd, 0x480, val);
 	saa717x_write(sd, 0x480, val);
 
 
-	/* bass and treble; go to another function */
 	/* set bass and treble */
 	/* 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);
 	saa717x_write(sd, 0x488, val);
 	return 0;
 	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);
 	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);
 	struct saa717x_state *state = to_state(sd);
 
 
 	switch (ctrl->id) {
 	switch (ctrl->id) {
 	case V4L2_CID_BRIGHTNESS:
 	case V4L2_CID_BRIGHTNESS:
-		ctrl->value = state->bright;
-		break;
+		saa717x_write(sd, 0x10a, ctrl->val);
+		return 0;
 
 
 	case V4L2_CID_CONTRAST:
 	case V4L2_CID_CONTRAST:
-		ctrl->value = state->contrast;
-		break;
+		saa717x_write(sd, 0x10b, ctrl->val);
+		return 0;
 
 
 	case V4L2_CID_SATURATION:
 	case V4L2_CID_SATURATION:
-		ctrl->value = state->sat;
-		break;
+		saa717x_write(sd, 0x10c, ctrl->val);
+		return 0;
 
 
 	case V4L2_CID_HUE:
 	case V4L2_CID_HUE:
-		ctrl->value = state->hue;
-		break;
+		saa717x_write(sd, 0x10d, ctrl->val);
+		return 0;
 
 
 	case V4L2_CID_AUDIO_MUTE:
 	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = state->audio_main_mute;
+		state->audio_main_mute = ctrl->val;
 		break;
 		break;
 
 
 	case V4L2_CID_AUDIO_VOLUME:
 	case V4L2_CID_AUDIO_VOLUME:
-		ctrl->value = state->audio_main_volume;
+		state->audio_main_volume = ctrl->val;
 		break;
 		break;
 
 
 	case V4L2_CID_AUDIO_BALANCE:
 	case V4L2_CID_AUDIO_BALANCE:
-		ctrl->value = state->audio_main_balance;
+		state->audio_main_balance = ctrl->val;
 		break;
 		break;
 
 
 	case V4L2_CID_AUDIO_TREBLE:
 	case V4L2_CID_AUDIO_TREBLE:
-		ctrl->value = state->audio_main_treble;
+		state->audio_main_treble = ctrl->val;
 		break;
 		break;
 
 
 	case V4L2_CID_AUDIO_BASS:
 	case V4L2_CID_AUDIO_BASS:
-		ctrl->value = state->audio_main_bass;
+		state->audio_main_bass = ctrl->val;
 		break;
 		break;
 
 
 	default:
 	default:
-		return -EINVAL;
+		return 0;
 	}
 	}
-
+	set_audio_regs(sd, state);
 	return 0;
 	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,
 static int saa717x_s_video_routing(struct v4l2_subdev *sd,
 				   u32 input, u32 output, u32 config)
 				   u32 input, u32 output, u32 config)
 {
 {
@@ -1158,18 +975,6 @@ static int saa717x_s_video_routing(struct v4l2_subdev *sd,
 	return 0;
 	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
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
 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;
 	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 = {
 static const struct v4l2_subdev_core_ops saa717x_core_ops = {
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.g_register = saa717x_g_register,
 	.g_register = saa717x_g_register,
 	.s_register = saa717x_s_register,
 	.s_register = saa717x_s_register,
 #endif
 #endif
-	.queryctrl = saa717x_queryctrl,
-	.g_ctrl = saa717x_g_ctrl,
-	.s_ctrl = saa717x_s_ctrl,
 	.s_std = saa717x_s_std,
 	.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 = {
 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)
 			 const struct i2c_device_id *did)
 {
 {
 	struct saa717x_state *decoder;
 	struct saa717x_state *decoder;
+	struct v4l2_ctrl_handler *hdl;
 	struct v4l2_subdev *sd;
 	struct v4l2_subdev *sd;
 	u8 id = 0;
 	u8 id = 0;
 	char *p = "";
 	char *p = "";
@@ -1467,16 +1290,41 @@ static int saa717x_probe(struct i2c_client *client,
 		p = "saa7171";
 		p = "saa7171";
 	v4l2_info(sd, "%s found @ 0x%x (%s)\n", p,
 	v4l2_info(sd, "%s found @ 0x%x (%s)\n", p,
 			client->addr << 1, client->adapter->name);
 			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->std = V4L2_STD_NTSC;
 	decoder->input = -1;
 	decoder->input = -1;
 	decoder->enable = 1;
 	decoder->enable = 1;
 
 
-	/* tune these parameters */
-	decoder->bright = 0x80;
-	decoder->contrast = 0x44;
-	decoder->sat = 0x40;
-	decoder->hue = 0x00;
-
 	/* FIXME!! */
 	/* FIXME!! */
 	decoder->playback = 0;	/* initially capture mode used */
 	decoder->playback = 0;	/* initially capture mode used */
 	decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */
 	decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */
@@ -1487,23 +1335,13 @@ static int saa717x_probe(struct i2c_client *client,
 	/* set volume, bass and treble */
 	/* set volume, bass and treble */
 	decoder->audio_main_vol_l = 6;
 	decoder->audio_main_vol_l = 6;
 	decoder->audio_main_vol_r = 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");
 	v4l2_dbg(1, debug, sd, "writing init values\n");
 
 
 	/* FIXME!! */
 	/* FIXME!! */
 	saa717x_write_regs(sd, reg_init_initialize);
 	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);
 	set_current_state(TASK_INTERRUPTIBLE);
 	schedule_timeout(2*HZ);
 	schedule_timeout(2*HZ);
@@ -1515,6 +1353,7 @@ static int saa717x_remove(struct i2c_client *client)
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 
 
 	v4l2_device_unregister_subdev(sd);
 	v4l2_device_unregister_subdev(sd);
+	v4l2_ctrl_handler_free(sd->ctrl_handler);
 	kfree(to_state(sd));
 	kfree(to_state(sd));
 	return 0;
 	return 0;
 }
 }

+ 6 - 3
drivers/media/video/soc_camera.c

@@ -779,9 +779,12 @@ static int soc_camera_s_crop(struct file *file, void *fh,
 	ret = ici->ops->get_crop(icd, &current_crop);
 	ret = ici->ops->get_crop(icd, &current_crop);
 
 
 	/* Prohibit window size change with initialised buffers */
 	/* 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,
 		dev_err(&icd->dev,
 			"S_CROP denied: queue initialised and sizes differ\n");
 			"S_CROP denied: queue initialised and sizes differ\n");
 		ret = -EBUSY;
 		ret = -EBUSY;

+ 5 - 5
drivers/media/video/tvp7002.c

@@ -179,7 +179,7 @@ static const struct i2c_reg_value tvp7002_init_default[] = {
 /* Register parameters for 480P */
 /* Register parameters for 480P */
 static const struct i2c_reg_value tvp7002_parms_480P[] = {
 static const struct i2c_reg_value tvp7002_parms_480P[] = {
 	{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE },
 	{ 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_CRTL, 0x02, TVP7002_WRITE },
 	{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
 	{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
 	{ TVP7002_AVID_START_PIXEL_LSBS, 0x91, 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 */
 /* Register parameters for 1080I60 */
 static const struct i2c_reg_value tvp7002_parms_1080I60[] = {
 static const struct i2c_reg_value tvp7002_parms_1080I60[] = {
 	{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
 	{ 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_CRTL, 0x98, TVP7002_WRITE },
 	{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
 	{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
 	{ TVP7002_AVID_START_PIXEL_LSBS, 0x06, 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 */
 /* Register parameters for 1080P60 */
 static const struct i2c_reg_value tvp7002_parms_1080P60[] = {
 static const struct i2c_reg_value tvp7002_parms_1080P60[] = {
 	{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
 	{ 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_CRTL, 0xE0, TVP7002_WRITE },
 	{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
 	{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
 	{ TVP7002_AVID_START_PIXEL_LSBS, 0x06, 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 */
 /* Register parameters for 720P60 */
 static const struct i2c_reg_value tvp7002_parms_720P60[] = {
 static const struct i2c_reg_value tvp7002_parms_720P60[] = {
 	{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE },
 	{ 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_CRTL, 0xa0, TVP7002_WRITE },
 	{ TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
 	{ TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
 	{ TVP7002_AVID_START_PIXEL_LSBS, 0x47, 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 */
 /* Register parameters for 720P50 */
 static const struct i2c_reg_value tvp7002_parms_720P50[] = {
 static const struct i2c_reg_value tvp7002_parms_720P50[] = {
 	{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE },
 	{ 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_CRTL, 0x98, TVP7002_WRITE },
 	{ TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
 	{ TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
 	{ TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
 	{ TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },

+ 4 - 8
drivers/media/video/usbvideo/usbvideo.c

@@ -282,19 +282,15 @@ static void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame,
 	};
 	};
 	unsigned short digit;
 	unsigned short digit;
 	int ix, iy;
 	int ix, iy;
+	int value;
 
 
 	if ((uvd == NULL) || (frame == NULL))
 	if ((uvd == NULL) || (frame == NULL))
 		return;
 		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;
 		return;
-	digit = digits[ch];
+	digit = digits[value];
 
 
 	for (iy=0; iy < 5; iy++) {
 	for (iy=0; iy < 5; iy++) {
 		for (ix=0; ix < 3; ix++) {
 		for (ix=0; ix < 3; ix++) {

+ 9 - 0
drivers/media/video/uvc/uvc_driver.c

@@ -2145,6 +2145,15 @@ static struct usb_device_id uvc_ids[] = {
 	  .bInterfaceSubClass	= 1,
 	  .bInterfaceSubClass	= 1,
 	  .bInterfaceProtocol	= 0,
 	  .bInterfaceProtocol	= 0,
 	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
 	  .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 */
 	/* Lenovo Thinkpad SL400/SL500 */
 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
 				| USB_DEVICE_ID_MATCH_INT_INFO,
 				| USB_DEVICE_ID_MATCH_INT_INFO,

+ 9 - 4
drivers/media/video/uvc/uvc_queue.c

@@ -78,12 +78,14 @@
  *
  *
  */
  */
 
 
-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);
 	mutex_init(&queue->mutex);
 	spin_lock_init(&queue->irqlock);
 	spin_lock_init(&queue->irqlock);
 	INIT_LIST_HEAD(&queue->mainqueue);
 	INIT_LIST_HEAD(&queue->mainqueue);
 	INIT_LIST_HEAD(&queue->irqqueue);
 	INIT_LIST_HEAD(&queue->irqqueue);
+	queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0;
 	queue->type = type;
 	queue->type = type;
 }
 }
 
 
@@ -435,8 +437,10 @@ int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
 		uvc_queue_cancel(queue, 0);
 		uvc_queue_cancel(queue, 0);
 		INIT_LIST_HEAD(&queue->mainqueue);
 		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->buffer[i].state = UVC_BUF_STATE_IDLE;
+		}
 
 
 		queue->flags &= ~UVC_QUEUE_STREAMING;
 		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;
 	struct uvc_buffer *nextbuf;
 	unsigned long flags;
 	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->state = UVC_BUF_STATE_QUEUED;
 		buf->buf.bytesused = 0;
 		buf->buf.bytesused = 0;
 		return buf;
 		return buf;
@@ -497,6 +501,7 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
 
 
 	spin_lock_irqsave(&queue->irqlock, flags);
 	spin_lock_irqsave(&queue->irqlock, flags);
 	list_del(&buf->queue);
 	list_del(&buf->queue);
+	buf->error = 0;
 	buf->state = UVC_BUF_STATE_DONE;
 	buf->state = UVC_BUF_STATE_DONE;
 	if (!list_empty(&queue->irqqueue))
 	if (!list_empty(&queue->irqqueue))
 		nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
 		nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,

+ 11 - 8
drivers/media/video/uvc/uvc_video.c

@@ -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) {
 		if (urb->iso_frame_desc[i].status < 0) {
 			uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
 			uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
 				"lost (%d).\n", urb->iso_frame_desc[i].status);
 				"lost (%d).\n", urb->iso_frame_desc[i].status);
+			/* Mark the buffer as faulty. */
+			if (buf != NULL)
+				buf->error = 1;
 			continue;
 			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,
 		uvc_video_decode_end(stream, buf, mem,
 			urb->iso_frame_desc[i].actual_length);
 			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);
 			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);
 	atomic_set(&stream->active, 0);
 
 
 	/* Initialize the video buffers queue. */
 	/* 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
 	/* Alternate setting 0 should be the default, yet the XBox Live Vision
 	 * Cam (and possibly other devices) crash or otherwise misbehave if
 	 * 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;
 		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);
 	ret = uvc_queue_enable(&stream->queue, 1);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;

+ 3 - 2
drivers/media/video/uvc/uvcvideo.h

@@ -379,11 +379,12 @@ struct uvc_buffer {
 	struct list_head queue;
 	struct list_head queue;
 	wait_queue_head_t wait;
 	wait_queue_head_t wait;
 	enum uvc_buffer_state state;
 	enum uvc_buffer_state state;
+	unsigned int error;
 };
 };
 
 
 #define UVC_QUEUE_STREAMING		(1 << 0)
 #define UVC_QUEUE_STREAMING		(1 << 0)
 #define UVC_QUEUE_DISCONNECTED		(1 << 1)
 #define UVC_QUEUE_DISCONNECTED		(1 << 1)
-#define UVC_QUEUE_DROP_INCOMPLETE	(1 << 2)
+#define UVC_QUEUE_DROP_CORRUPTED	(1 << 2)
 
 
 struct uvc_video_queue {
 struct uvc_video_queue {
 	enum v4l2_buf_type type;
 	enum v4l2_buf_type type;
@@ -562,7 +563,7 @@ extern struct uvc_driver uvc_driver;
 
 
 /* Video buffers queue management. */
 /* Video buffers queue management. */
 extern void uvc_queue_init(struct uvc_video_queue *queue,
 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,
 extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
 		unsigned int nbuffers, unsigned int buflength);
 		unsigned int nbuffers, unsigned int buflength);
 extern int uvc_free_buffers(struct uvc_video_queue *queue);
 extern int uvc_free_buffers(struct uvc_video_queue *queue);

+ 5 - 474
drivers/media/video/v4l2-common.c

@@ -62,6 +62,7 @@
 #define __OLD_VIDIOC_ /* To allow fixing old calls*/
 #define __OLD_VIDIOC_ /* To allow fixing old calls*/
 #include <media/v4l2-common.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-chip-ident.h>
 
 
 #include <linux/videodev2.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);
 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 */
 /* Fill in a struct v4l2_queryctrl */
 int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def)
 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)
 	if (name == NULL)
 		return -EINVAL;
 		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->minimum = min;
 	qctrl->maximum = max;
 	qctrl->maximum = max;
 	qctrl->step = step;
 	qctrl->step = step;

+ 1851 - 0
drivers/media/video/v4l2-ctrls.c

@@ -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);

+ 6 - 2
drivers/media/video/v4l2-dev.c

@@ -428,8 +428,12 @@ static int __video_register_device(struct video_device *vdev, int type, int nr,
 
 
 	vdev->vfl_type = type;
 	vdev->vfl_type = type;
 	vdev->cdev = NULL;
 	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. */
 	/* Part 2: find a free minor, device node number and device index. */
 #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
 #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES

+ 7 - 0
drivers/media/video/v4l2-device.c

@@ -26,6 +26,7 @@
 #endif
 #endif
 #include <linux/videodev2.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
 
 
 int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
 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,
 int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
 						struct v4l2_subdev *sd)
 						struct v4l2_subdev *sd)
 {
 {
+	int err;
+
 	/* Check for valid input */
 	/* Check for valid input */
 	if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
 	if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
 		return -EINVAL;
 		return -EINVAL;
@@ -122,6 +125,10 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
 	WARN_ON(sd->v4l2_dev != NULL);
 	WARN_ON(sd->v4l2_dev != NULL);
 	if (!try_module_get(sd->owner))
 	if (!try_module_get(sd->owner))
 		return -ENODEV;
 		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;
 	sd->v4l2_dev = v4l2_dev;
 	spin_lock(&v4l2_dev->lock);
 	spin_lock(&v4l2_dev->lock);
 	list_add_tail(&sd->list, &v4l2_dev->subdevs);
 	list_add_tail(&sd->list, &v4l2_dev->subdevs);

+ 33 - 13
drivers/media/video/v4l2-ioctl.c

@@ -26,6 +26,7 @@
 #endif
 #endif
 #include <media/v4l2-common.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-fh.h>
 #include <media/v4l2-fh.h>
 #include <media/v4l2-event.h>
 #include <media/v4l2-event.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-chip-ident.h>
@@ -1259,9 +1260,12 @@ static long __video_do_ioctl(struct file *file,
 	{
 	{
 		struct v4l2_queryctrl *p = arg;
 		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;
 			break;
-		ret = ops->vidioc_queryctrl(file, fh, p);
 		if (!ret)
 		if (!ret)
 			dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, "
 			dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, "
 					"step=%d, default=%d, flags=0x%08x\n",
 					"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;
 		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);
 			ret = ops->vidioc_g_ctrl(file, fh, p);
 		else if (ops->vidioc_g_ext_ctrls) {
 		else if (ops->vidioc_g_ext_ctrls) {
 			struct v4l2_ext_controls 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_controls ctrls;
 		struct v4l2_ext_control ctrl;
 		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;
 			break;
 
 
 		dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
 		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) {
 		if (ops->vidioc_s_ctrl) {
 			ret = ops->vidioc_s_ctrl(file, fh, p);
 			ret = ops->vidioc_s_ctrl(file, fh, p);
 			break;
 			break;
@@ -1332,10 +1343,12 @@ static long __video_do_ioctl(struct file *file,
 		struct v4l2_ext_controls *p = arg;
 		struct v4l2_ext_controls *p = arg;
 
 
 		p->error_idx = p->count;
 		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);
 			ret = ops->vidioc_g_ext_ctrls(file, fh, p);
+		else
+			break;
 		v4l_print_ext_ctrls(cmd, vfd, p, !ret);
 		v4l_print_ext_ctrls(cmd, vfd, p, !ret);
 		break;
 		break;
 	}
 	}
@@ -1344,10 +1357,12 @@ static long __video_do_ioctl(struct file *file,
 		struct v4l2_ext_controls *p = arg;
 		struct v4l2_ext_controls *p = arg;
 
 
 		p->error_idx = p->count;
 		p->error_idx = p->count;
-		if (!ops->vidioc_s_ext_ctrls)
+		if (!vfd->ctrl_handler && !ops->vidioc_s_ext_ctrls)
 			break;
 			break;
 		v4l_print_ext_ctrls(cmd, vfd, p, 1);
 		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);
 			ret = ops->vidioc_s_ext_ctrls(file, fh, p);
 		break;
 		break;
 	}
 	}
@@ -1356,10 +1371,12 @@ static long __video_do_ioctl(struct file *file,
 		struct v4l2_ext_controls *p = arg;
 		struct v4l2_ext_controls *p = arg;
 
 
 		p->error_idx = p->count;
 		p->error_idx = p->count;
-		if (!ops->vidioc_try_ext_ctrls)
+		if (!vfd->ctrl_handler && !ops->vidioc_try_ext_ctrls)
 			break;
 			break;
 		v4l_print_ext_ctrls(cmd, vfd, p, 1);
 		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);
 			ret = ops->vidioc_try_ext_ctrls(file, fh, p);
 		break;
 		break;
 	}
 	}
@@ -1367,9 +1384,12 @@ static long __video_do_ioctl(struct file *file,
 	{
 	{
 		struct v4l2_querymenu *p = arg;
 		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;
 			break;
-		ret = ops->vidioc_querymenu(file, fh, p);
 		if (!ret)
 		if (!ret)
 			dbgarg(cmd, "id=0x%x, index=%d, name=%s\n",
 			dbgarg(cmd, "id=0x%x, index=%d, name=%s\n",
 				p->id, p->index, p->name);
 				p->id, p->index, p->name);

+ 62 - 117
drivers/media/video/wm8739.c

@@ -27,11 +27,11 @@
 #include <linux/ioctl.h>
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
 #include <linux/i2c.h>
 #include <linux/i2c.h>
-#include <linux/i2c-id.h>
 #include <linux/videodev2.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-i2c-drv.h>
 #include <media/v4l2-i2c-drv.h>
+#include <media/v4l2-ctrls.h>
 
 
 MODULE_DESCRIPTION("wm8739 driver");
 MODULE_DESCRIPTION("wm8739 driver");
 MODULE_AUTHOR("T. Adachi, Hans Verkuil");
 MODULE_AUTHOR("T. Adachi, Hans Verkuil");
@@ -54,12 +54,14 @@ enum {
 
 
 struct wm8739_state {
 struct wm8739_state {
 	struct v4l2_subdev sd;
 	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;
 	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)
 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);
 	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)
 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;
 	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);
 	struct wm8739_state *state = to_state(sd);
 	unsigned int work_l, work_r;
 	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) {
 	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_MUTE:
-		state->muted = ctrl->value;
-		break;
-
 	case V4L2_CID_AUDIO_VOLUME:
 	case V4L2_CID_AUDIO_VOLUME:
-		state->volume = ctrl->value;
-		break;
-
-	case V4L2_CID_AUDIO_BALANCE:
-		state->balance = ctrl->value;
 		break;
 		break;
 
 
 	default:
 	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) ) */
 	/* 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. */
 	/* 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;
 	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)
 static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq)
 {
 {
 	struct wm8739_state *state = to_state(sd);
 	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;
 	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)
 static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
 {
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	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);
 	struct wm8739_state *state = to_state(sd);
 
 
 	v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq);
 	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;
 	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 = {
 static const struct v4l2_subdev_core_ops wm8739_core_ops = {
 	.log_status = wm8739_log_status,
 	.log_status = wm8739_log_status,
 	.g_chip_ident = wm8739_g_chip_ident,
 	.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 = {
 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",
 	v4l_info(client, "chip found @ 0x%x (%s)\n",
 			client->addr << 1, client->adapter->name);
 			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)
 	if (state == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
 	sd = &state->sd;
 	sd = &state->sd;
 	v4l2_i2c_subdev_init(sd, client, &wm8739_ops);
 	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;
 	state->clock_freq = 48000;
 
 
 	/* Initialize wm8739 */
 	/* Initialize wm8739 */
@@ -318,15 +261,17 @@ static int wm8739_probe(struct i2c_client *client,
 	/* activate */
 	/* activate */
 	wm8739_write(sd, R9, 0x001);
 	wm8739_write(sd, R9, 0x001);
 	/* set volume/mute */
 	/* set volume/mute */
-	wm8739_set_audio(sd);
+	v4l2_ctrl_handler_setup(&state->hdl);
 	return 0;
 	return 0;
 }
 }
 
 
 static int wm8739_remove(struct i2c_client *client)
 static int wm8739_remove(struct i2c_client *client)
 {
 {
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct wm8739_state *state = to_state(sd);
 
 
 	v4l2_device_unregister_subdev(sd);
 	v4l2_device_unregister_subdev(sd);
+	v4l2_ctrl_handler_free(&state->hdl);
 	kfree(to_state(sd));
 	kfree(to_state(sd));
 	return 0;
 	return 0;
 }
 }

+ 50 - 29
drivers/media/video/wm8775.c

@@ -35,6 +35,7 @@
 #include <linux/videodev2.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-i2c-drv.h>
 #include <media/v4l2-i2c-drv.h>
 
 
 MODULE_DESCRIPTION("wm8775 driver");
 MODULE_DESCRIPTION("wm8775 driver");
@@ -53,8 +54,9 @@ enum {
 
 
 struct wm8775_state {
 struct wm8775_state {
 	struct v4l2_subdev sd;
 	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler hdl;
+	struct v4l2_ctrl *mute;
 	u8 input;		/* Last selected input (0-0xf) */
 	u8 input;		/* Last selected input (0-0xf) */
-	u8 muted;
 };
 };
 
 
 static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
 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);
 	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)
 static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
 {
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -95,7 +102,7 @@ static int wm8775_s_routing(struct v4l2_subdev *sd,
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 	state->input = input;
 	state->input = input;
-	if (state->muted)
+	if (!v4l2_ctrl_g_ctrl(state->mute))
 		return 0;
 		return 0;
 	wm8775_write(sd, R21, 0x0c0);
 	wm8775_write(sd, R21, 0x0c0);
 	wm8775_write(sd, R14, 0x1d4);
 	wm8775_write(sd, R14, 0x1d4);
@@ -104,29 +111,21 @@ static int wm8775_s_routing(struct v4l2_subdev *sd,
 	return 0;
 	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);
 	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)
 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);
 	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;
 	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 = {
 static const struct v4l2_subdev_core_ops wm8775_core_ops = {
 	.log_status = wm8775_log_status,
 	.log_status = wm8775_log_status,
 	.g_chip_ident = wm8775_g_chip_ident,
 	.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 = {
 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",
 	v4l_info(client, "chip found @ 0x%02x (%s)\n",
 			client->addr << 1, client->adapter->name);
 			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)
 	if (state == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
 	sd = &state->sd;
 	sd = &state->sd;
 	v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
 	v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
 	state->input = 2;
 	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 */
 	/* Initialize wm8775 */
 
 
@@ -248,9 +267,11 @@ static int wm8775_probe(struct i2c_client *client,
 static int wm8775_remove(struct i2c_client *client)
 static int wm8775_remove(struct i2c_client *client)
 {
 {
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct wm8775_state *state = to_state(sd);
 
 
 	v4l2_device_unregister_subdev(sd);
 	v4l2_device_unregister_subdev(sd);
-	kfree(to_state(sd));
+	v4l2_ctrl_handler_free(&state->hdl);
+	kfree(state);
 	return 0;
 	return 0;
 }
 }
 
 

+ 8 - 21
drivers/staging/lirc/Kconfig

@@ -3,6 +3,7 @@
 #
 #
 menuconfig LIRC_STAGING
 menuconfig LIRC_STAGING
 	bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
 	bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
+	depends on LIRC
 	help
 	help
 	  Say Y here, and all supported Linux Infrared Remote Control IR and
 	  Say Y here, and all supported Linux Infrared Remote Control IR and
 	  RF receiver and transmitter drivers will be displayed. When paired
 	  RF receiver and transmitter drivers will be displayed. When paired
@@ -13,21 +14,13 @@ if LIRC_STAGING
 
 
 config LIRC_BT829
 config LIRC_BT829
         tristate "BT829 based hardware"
         tristate "BT829 based hardware"
-	depends on LIRC_STAGING
+	depends on LIRC_STAGING && PCI
 	help
 	help
 	  Driver for the IR interface on BT829-based hardware
 	  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
 config LIRC_I2C
 	tristate "I2C Based IR Receivers"
 	tristate "I2C Based IR Receivers"
-	depends on LIRC_STAGING
+	depends on LIRC_STAGING && I2C
 	help
 	help
 	  Driver for I2C-based IR receivers, such as those commonly
 	  Driver for I2C-based IR receivers, such as those commonly
 	  found onboard Hauppauge PVR-150/250/350 video capture cards
 	  found onboard Hauppauge PVR-150/250/350 video capture cards
@@ -40,7 +33,7 @@ config LIRC_IGORPLUGUSB
 
 
 config LIRC_IMON
 config LIRC_IMON
 	tristate "Legacy SoundGraph iMON Receiver and Display"
 	tristate "Legacy SoundGraph iMON Receiver and Display"
-	depends on LIRC_STAGING
+	depends on LIRC_STAGING && USB
 	help
 	help
 	  Driver for the original SoundGraph iMON IR Receiver and Display
 	  Driver for the original SoundGraph iMON IR Receiver and Display
 
 
@@ -48,7 +41,7 @@ config LIRC_IMON
 
 
 config LIRC_IT87
 config LIRC_IT87
 	tristate "ITE IT87XX CIR Port Receiver"
 	tristate "ITE IT87XX CIR Port Receiver"
-	depends on LIRC_STAGING
+	depends on LIRC_STAGING && PNP
 	help
 	help
 	  Driver for the ITE IT87xx IR Receiver
 	  Driver for the ITE IT87xx IR Receiver
 
 
@@ -60,13 +53,13 @@ config LIRC_ITE8709
 
 
 config LIRC_PARALLEL
 config LIRC_PARALLEL
 	tristate "Homebrew Parallel Port Receiver"
 	tristate "Homebrew Parallel Port Receiver"
-	depends on LIRC_STAGING && !SMP
+	depends on LIRC_STAGING && PARPORT && !SMP
 	help
 	help
 	  Driver for Homebrew Parallel Port Receivers
 	  Driver for Homebrew Parallel Port Receivers
 
 
 config LIRC_SASEM
 config LIRC_SASEM
 	tristate "Sasem USB IR Remote"
 	tristate "Sasem USB IR Remote"
-	depends on LIRC_STAGING
+	depends on LIRC_STAGING && USB
 	help
 	help
 	  Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
 	  Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
 
 
@@ -89,12 +82,6 @@ config LIRC_SIR
 	help
 	help
 	  Driver for the SIR IrDA port
 	  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
 config LIRC_TTUSBIR
 	tristate "Technotrend USB IR Receiver"
 	tristate "Technotrend USB IR Receiver"
 	depends on LIRC_STAGING && USB
 	depends on LIRC_STAGING && USB
@@ -103,7 +90,7 @@ config LIRC_TTUSBIR
 
 
 config LIRC_ZILOG
 config LIRC_ZILOG
 	tristate "Zilog/Hauppauge IR Transmitter"
 	tristate "Zilog/Hauppauge IR Transmitter"
-	depends on LIRC_STAGING
+	depends on LIRC_STAGING && I2C
 	help
 	help
 	  Driver for the Zilog/Hauppauge IR Transmitter, found on
 	  Driver for the Zilog/Hauppauge IR Transmitter, found on
 	  PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards
 	  PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards

+ 0 - 2
drivers/staging/lirc/Makefile

@@ -4,7 +4,6 @@
 # Each configuration option enables a list of files.
 # Each configuration option enables a list of files.
 
 
 obj-$(CONFIG_LIRC_BT829)	+= lirc_bt829.o
 obj-$(CONFIG_LIRC_BT829)	+= lirc_bt829.o
-obj-$(CONFIG_LIRC_ENE0100)	+= lirc_ene0100.o
 obj-$(CONFIG_LIRC_I2C)		+= lirc_i2c.o
 obj-$(CONFIG_LIRC_I2C)		+= lirc_i2c.o
 obj-$(CONFIG_LIRC_IGORPLUGUSB)	+= lirc_igorplugusb.o
 obj-$(CONFIG_LIRC_IGORPLUGUSB)	+= lirc_igorplugusb.o
 obj-$(CONFIG_LIRC_IMON)		+= lirc_imon.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_SASEM)	+= lirc_sasem.o
 obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
 obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
 obj-$(CONFIG_LIRC_SIR)		+= lirc_sir.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_TTUSBIR)	+= lirc_ttusbir.o
 obj-$(CONFIG_LIRC_ZILOG)	+= lirc_zilog.o
 obj-$(CONFIG_LIRC_ZILOG)	+= lirc_zilog.o

+ 0 - 646
drivers/staging/lirc/lirc_ene0100.c

@@ -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);

+ 6 - 3
drivers/staging/lirc/lirc_it87.c

@@ -109,6 +109,7 @@ static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
 
 
 static DEFINE_SPINLOCK(hardware_lock);
 static DEFINE_SPINLOCK(hardware_lock);
 static DEFINE_SPINLOCK(dev_lock);
 static DEFINE_SPINLOCK(dev_lock);
+static bool device_open;
 
 
 static int rx_buf[RBUF_LEN];
 static int rx_buf[RBUF_LEN];
 unsigned int rx_tail, rx_head;
 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)
 static int lirc_open(struct inode *inode, struct file *file)
 {
 {
 	spin_lock(&dev_lock);
 	spin_lock(&dev_lock);
-	if (module_refcount(THIS_MODULE)) {
+	if (device_open) {
 		spin_unlock(&dev_lock);
 		spin_unlock(&dev_lock);
 		return -EBUSY;
 		return -EBUSY;
 	}
 	}
+	device_open = true;
 	spin_unlock(&dev_lock);
 	spin_unlock(&dev_lock);
 	return 0;
 	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)
 static int lirc_close(struct inode *inode, struct file *file)
 {
 {
+	spin_lock(&dev_lock);
+	device_open = false;
+	spin_unlock(&dev_lock);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -363,7 +368,6 @@ static struct lirc_driver driver = {
 };
 };
 
 
 
 
-#ifdef MODULE
 static int init_chrdev(void)
 static int init_chrdev(void)
 {
 {
 	driver.minor = lirc_register_driver(&driver);
 	driver.minor = lirc_register_driver(&driver);
@@ -380,7 +384,6 @@ static void drop_chrdev(void)
 {
 {
 	lirc_unregister_driver(driver.minor);
 	lirc_unregister_driver(driver.minor);
 }
 }
-#endif
 
 
 
 
 /* SECTION: Hardware */
 /* SECTION: Hardware */

+ 2 - 2
drivers/staging/lirc/lirc_parallel.c

@@ -240,7 +240,7 @@ static void irq_handler(void *blah)
 	unsigned int level, newlevel;
 	unsigned int level, newlevel;
 	unsigned int timeout;
 	unsigned int timeout;
 
 
-	if (!module_refcount(THIS_MODULE))
+	if (!is_open)
 		return;
 		return;
 
 
 	if (!is_claimed)
 	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)
 static int lirc_open(struct inode *node, struct file *filep)
 {
 {
-	if (module_refcount(THIS_MODULE) || !lirc_claim())
+	if (is_open || !lirc_claim())
 		return -EBUSY;
 		return -EBUSY;
 
 
 	parport_enable_irq(pport);
 	parport_enable_irq(pport);

+ 0 - 821
drivers/staging/lirc/lirc_streamzap.c

@@ -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");

+ 1 - 0
include/linux/videodev2.h

@@ -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_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_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_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_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_RGB24   v4l2_fourcc('R', 'G', 'B', '3') /* 24  RGB-8-8-8     */
 #define V4L2_PIX_FMT_BGR32   v4l2_fourcc('B', 'G', 'R', '4') /* 32  BGR-8-8-8-8   */
 #define V4L2_PIX_FMT_BGR32   v4l2_fourcc('B', 'G', 'R', '4') /* 32  BGR-8-8-8-8   */

Some files were not shown because too many files changed in this diff