summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Documentation/DocBook/media/v4l/compat.xml3
-rw-r--r--Documentation/DocBook/media/v4l/controls.xml5
-rw-r--r--Documentation/DocBook/media/v4l/io.xml27
-rw-r--r--Documentation/DocBook/media/v4l/v4l2.xml2
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-create-bufs.xml139
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml88
-rw-r--r--arch/arm/mach-pxa/pcm990-baseboard.c4
-rw-r--r--arch/arm/mach-shmobile/board-ap4evb.c2
-rw-r--r--arch/arm/mach-shmobile/board-mackerel.c7
-rw-r--r--arch/sh/boards/mach-ap325rxa/setup.c10
-rw-r--r--arch/sh/boards/mach-migor/setup.c4
-rw-r--r--drivers/dma/ipu/ipu_idmac.c65
-rw-r--r--drivers/media/dvb/ddbridge/Makefile2
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile1
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h2
-rw-r--r--drivers/media/dvb/dvb-usb/it913x.c105
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-demod.c614
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-demod.h55
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf.c228
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf.h2
-rw-r--r--drivers/media/dvb/ngene/Makefile2
-rw-r--r--drivers/media/radio/radio-tea5764.c4
-rw-r--r--drivers/media/video/Kconfig9
-rw-r--r--drivers/media/video/Makefile2
-rw-r--r--drivers/media/video/atmel-isi.c142
-rw-r--r--drivers/media/video/cx18/cx18-driver.c2
-rw-r--r--drivers/media/video/cx25821/Kconfig (renamed from drivers/staging/cx25821/Kconfig)0
-rw-r--r--drivers/media/video/cx25821/Makefile (renamed from drivers/staging/cx25821/Makefile)0
-rw-r--r--drivers/media/video/cx25821/cx25821-alsa.c (renamed from drivers/staging/cx25821/cx25821-alsa.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-audio-upstream.c (renamed from drivers/staging/cx25821/cx25821-audio-upstream.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-audio-upstream.h (renamed from drivers/staging/cx25821/cx25821-audio-upstream.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-audio.h (renamed from drivers/staging/cx25821/cx25821-audio.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-biffuncs.h (renamed from drivers/staging/cx25821/cx25821-biffuncs.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-cards.c (renamed from drivers/staging/cx25821/cx25821-cards.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-core.c (renamed from drivers/staging/cx25821/cx25821-core.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-gpio.c (renamed from drivers/staging/cx25821/cx25821-gpio.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-i2c.c (renamed from drivers/staging/cx25821/cx25821-i2c.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-defines.h (renamed from drivers/staging/cx25821/cx25821-medusa-defines.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-reg.h (renamed from drivers/staging/cx25821/cx25821-medusa-reg.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-video.c (renamed from drivers/staging/cx25821/cx25821-medusa-video.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-video.h (renamed from drivers/staging/cx25821/cx25821-medusa-video.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-reg.h (renamed from drivers/staging/cx25821/cx25821-reg.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-sram.h (renamed from drivers/staging/cx25821/cx25821-sram.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream-ch2.c (renamed from drivers/staging/cx25821/cx25821-video-upstream-ch2.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream-ch2.h (renamed from drivers/staging/cx25821/cx25821-video-upstream-ch2.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream.c (renamed from drivers/staging/cx25821/cx25821-video-upstream.c)0
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream.h (renamed from drivers/staging/cx25821/cx25821-video-upstream.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821-video.c (renamed from drivers/staging/cx25821/cx25821-video.c)2
-rw-r--r--drivers/media/video/cx25821/cx25821-video.h (renamed from drivers/staging/cx25821/cx25821-video.h)0
-rw-r--r--drivers/media/video/cx25821/cx25821.h (renamed from drivers/staging/cx25821/cx25821.h)3
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c2
-rw-r--r--drivers/media/video/imx074.c54
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c2
-rw-r--r--drivers/media/video/marvell-ccic/mcam-core.c3
-rw-r--r--drivers/media/video/mem2mem_testdev.c7
-rw-r--r--drivers/media/video/mt9m001.c328
-rw-r--r--drivers/media/video/mt9m111.c260
-rw-r--r--drivers/media/video/mt9t031.c347
-rw-r--r--drivers/media/video/mt9t112.c269
-rw-r--r--drivers/media/video/mt9v022.c447
-rw-r--r--drivers/media/video/mx1_camera.c71
-rw-r--r--drivers/media/video/mx2_camera.c78
-rw-r--r--drivers/media/video/mx3_camera.c359
-rw-r--r--drivers/media/video/omap/omap_vout.c10
-rw-r--r--drivers/media/video/omap1_camera.c62
-rw-r--r--drivers/media/video/omap3isp/isp.c3
-rw-r--r--drivers/media/video/omap3isp/ispccdc.c86
-rw-r--r--drivers/media/video/omap3isp/ispccp2.c125
-rw-r--r--drivers/media/video/omap3isp/ispcsi2.c91
-rw-r--r--drivers/media/video/omap3isp/isph3a_aewb.c2
-rw-r--r--drivers/media/video/omap3isp/isph3a_af.c2
-rw-r--r--drivers/media/video/omap3isp/isphist.c2
-rw-r--r--drivers/media/video/omap3isp/isppreview.c419
-rw-r--r--drivers/media/video/omap3isp/isppreview.h9
-rw-r--r--drivers/media/video/omap3isp/ispreg.h3
-rw-r--r--drivers/media/video/omap3isp/ispresizer.c104
-rw-r--r--drivers/media/video/omap3isp/ispstat.c52
-rw-r--r--drivers/media/video/omap3isp/ispstat.h2
-rw-r--r--drivers/media/video/omap3isp/ispvideo.c11
-rw-r--r--drivers/media/video/omap3isp/ispvideo.h1
-rw-r--r--drivers/media/video/ov2640.c178
-rw-r--r--drivers/media/video/ov5642.c288
-rw-r--r--drivers/media/video/ov6650.c504
-rw-r--r--drivers/media/video/ov772x.c198
-rw-r--r--drivers/media/video/ov9640.c186
-rw-r--r--drivers/media/video/ov9640.h4
-rw-r--r--drivers/media/video/ov9740.c151
-rw-r--r--drivers/media/video/pwc/pwc-if.c6
-rw-r--r--drivers/media/video/pxa_camera.c140
-rw-r--r--drivers/media/video/rj54n1cb0c.c223
-rw-r--r--drivers/media/video/s5k6aa.c1680
-rw-r--r--drivers/media/video/s5p-fimc/fimc-capture.c6
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.c6
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c4
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_dec.c7
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_enc.c5
-rw-r--r--drivers/media/video/s5p-tv/mixer_video.c4
-rw-r--r--drivers/media/video/saa7134/saa7134.h12
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c491
-rw-r--r--drivers/media/video/sh_mobile_csi2.c132
-rw-r--r--drivers/media/video/soc_camera.c273
-rw-r--r--drivers/media/video/soc_camera_platform.c45
-rw-r--r--drivers/media/video/soc_mediabus.c33
-rw-r--r--drivers/media/video/tw9910.c268
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c76
-rw-r--r--drivers/media/video/v4l2-ctrls.c1
-rw-r--r--drivers/media/video/v4l2-device.c36
-rw-r--r--drivers/media/video/v4l2-ioctl.c36
-rw-r--r--drivers/media/video/videobuf2-core.c391
-rw-r--r--drivers/media/video/vivi.c6
-rw-r--r--drivers/staging/Kconfig16
-rw-r--r--drivers/staging/Makefile8
-rw-r--r--drivers/staging/cx25821/README6
-rw-r--r--drivers/staging/media/Kconfig37
-rw-r--r--drivers/staging/media/Makefile7
-rw-r--r--drivers/staging/media/as102/Kconfig7
-rw-r--r--drivers/staging/media/as102/Makefile6
-rw-r--r--drivers/staging/media/as102/as102_drv.c351
-rw-r--r--drivers/staging/media/as102/as102_drv.h141
-rw-r--r--drivers/staging/media/as102/as102_fe.c603
-rw-r--r--drivers/staging/media/as102/as102_fw.c251
-rw-r--r--drivers/staging/media/as102/as102_fw.h42
-rw-r--r--drivers/staging/media/as102/as102_usb_drv.c478
-rw-r--r--drivers/staging/media/as102/as102_usb_drv.h59
-rw-r--r--drivers/staging/media/as102/as10x_cmd.c452
-rw-r--r--drivers/staging/media/as102/as10x_cmd.h540
-rw-r--r--drivers/staging/media/as102/as10x_cmd_cfg.c215
-rw-r--r--drivers/staging/media/as102/as10x_cmd_stream.c223
-rw-r--r--drivers/staging/media/as102/as10x_handle.h58
-rw-r--r--drivers/staging/media/as102/as10x_types.h198
-rw-r--r--drivers/staging/media/cxd2099/Kconfig (renamed from drivers/staging/cxd2099/Kconfig)0
-rw-r--r--drivers/staging/media/cxd2099/Makefile (renamed from drivers/staging/cxd2099/Makefile)0
-rw-r--r--drivers/staging/media/cxd2099/TODO (renamed from drivers/staging/cxd2099/TODO)0
-rw-r--r--drivers/staging/media/cxd2099/cxd2099.c (renamed from drivers/staging/cxd2099/cxd2099.c)0
-rw-r--r--drivers/staging/media/cxd2099/cxd2099.h (renamed from drivers/staging/cxd2099/cxd2099.h)0
-rw-r--r--drivers/staging/media/dt3155v4l/Kconfig (renamed from drivers/staging/dt3155v4l/Kconfig)0
-rw-r--r--drivers/staging/media/dt3155v4l/Makefile (renamed from drivers/staging/dt3155v4l/Makefile)0
-rw-r--r--drivers/staging/media/dt3155v4l/dt3155v4l.c (renamed from drivers/staging/dt3155v4l/dt3155v4l.c)0
-rw-r--r--drivers/staging/media/dt3155v4l/dt3155v4l.h (renamed from drivers/staging/dt3155v4l/dt3155v4l.h)0
-rw-r--r--drivers/staging/media/easycap/Kconfig (renamed from drivers/staging/easycap/Kconfig)0
-rw-r--r--drivers/staging/media/easycap/Makefile (renamed from drivers/staging/easycap/Makefile)0
-rw-r--r--drivers/staging/media/easycap/README (renamed from drivers/staging/easycap/README)0
-rw-r--r--drivers/staging/media/easycap/easycap.h (renamed from drivers/staging/easycap/easycap.h)0
-rw-r--r--drivers/staging/media/easycap/easycap_ioctl.c (renamed from drivers/staging/easycap/easycap_ioctl.c)0
-rw-r--r--drivers/staging/media/easycap/easycap_low.c (renamed from drivers/staging/easycap/easycap_low.c)0
-rw-r--r--drivers/staging/media/easycap/easycap_main.c (renamed from drivers/staging/easycap/easycap_main.c)0
-rw-r--r--drivers/staging/media/easycap/easycap_settings.c (renamed from drivers/staging/easycap/easycap_settings.c)0
-rw-r--r--drivers/staging/media/easycap/easycap_sound.c (renamed from drivers/staging/easycap/easycap_sound.c)0
-rw-r--r--drivers/staging/media/easycap/easycap_testcard.c (renamed from drivers/staging/easycap/easycap_testcard.c)0
-rw-r--r--drivers/staging/media/go7007/Kconfig (renamed from drivers/staging/go7007/Kconfig)0
-rw-r--r--drivers/staging/media/go7007/Makefile (renamed from drivers/staging/go7007/Makefile)0
-rw-r--r--drivers/staging/media/go7007/README (renamed from drivers/staging/go7007/README)0
-rw-r--r--drivers/staging/media/go7007/go7007-driver.c (renamed from drivers/staging/go7007/go7007-driver.c)0
-rw-r--r--drivers/staging/media/go7007/go7007-fw.c (renamed from drivers/staging/go7007/go7007-fw.c)0
-rw-r--r--drivers/staging/media/go7007/go7007-i2c.c (renamed from drivers/staging/go7007/go7007-i2c.c)0
-rw-r--r--drivers/staging/media/go7007/go7007-priv.h (renamed from drivers/staging/go7007/go7007-priv.h)0
-rw-r--r--drivers/staging/media/go7007/go7007-usb.c (renamed from drivers/staging/go7007/go7007-usb.c)0
-rw-r--r--drivers/staging/media/go7007/go7007-v4l2.c (renamed from drivers/staging/go7007/go7007-v4l2.c)0
-rw-r--r--drivers/staging/media/go7007/go7007.h (renamed from drivers/staging/go7007/go7007.h)0
-rw-r--r--drivers/staging/media/go7007/go7007.txt (renamed from drivers/staging/go7007/go7007.txt)0
-rw-r--r--drivers/staging/media/go7007/s2250-board.c (renamed from drivers/staging/go7007/s2250-board.c)0
-rw-r--r--drivers/staging/media/go7007/s2250-loader.c (renamed from drivers/staging/go7007/s2250-loader.c)0
-rw-r--r--drivers/staging/media/go7007/s2250-loader.h (renamed from drivers/staging/go7007/s2250-loader.h)0
-rw-r--r--drivers/staging/media/go7007/saa7134-go7007.c (renamed from drivers/staging/go7007/saa7134-go7007.c)0
-rw-r--r--drivers/staging/media/go7007/snd-go7007.c (renamed from drivers/staging/go7007/snd-go7007.c)0
-rw-r--r--drivers/staging/media/go7007/wis-i2c.h (renamed from drivers/staging/go7007/wis-i2c.h)0
-rw-r--r--drivers/staging/media/go7007/wis-ov7640.c (renamed from drivers/staging/go7007/wis-ov7640.c)0
-rw-r--r--drivers/staging/media/go7007/wis-saa7113.c (renamed from drivers/staging/go7007/wis-saa7113.c)0
-rw-r--r--drivers/staging/media/go7007/wis-saa7115.c (renamed from drivers/staging/go7007/wis-saa7115.c)0
-rw-r--r--drivers/staging/media/go7007/wis-sony-tuner.c (renamed from drivers/staging/go7007/wis-sony-tuner.c)0
-rw-r--r--drivers/staging/media/go7007/wis-tw2804.c (renamed from drivers/staging/go7007/wis-tw2804.c)0
-rw-r--r--drivers/staging/media/go7007/wis-tw9903.c (renamed from drivers/staging/go7007/wis-tw9903.c)0
-rw-r--r--drivers/staging/media/go7007/wis-uda1342.c (renamed from drivers/staging/go7007/wis-uda1342.c)0
-rw-r--r--drivers/staging/media/lirc/Kconfig (renamed from drivers/staging/lirc/Kconfig)0
-rw-r--r--drivers/staging/media/lirc/Makefile (renamed from drivers/staging/lirc/Makefile)0
-rw-r--r--drivers/staging/media/lirc/TODO (renamed from drivers/staging/lirc/TODO)0
-rw-r--r--drivers/staging/media/lirc/TODO.lirc_zilog (renamed from drivers/staging/lirc/TODO.lirc_zilog)0
-rw-r--r--drivers/staging/media/lirc/lirc_bt829.c (renamed from drivers/staging/lirc/lirc_bt829.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_ene0100.h (renamed from drivers/staging/lirc/lirc_ene0100.h)0
-rw-r--r--drivers/staging/media/lirc/lirc_igorplugusb.c (renamed from drivers/staging/lirc/lirc_igorplugusb.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_imon.c (renamed from drivers/staging/lirc/lirc_imon.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_parallel.c (renamed from drivers/staging/lirc/lirc_parallel.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_parallel.h (renamed from drivers/staging/lirc/lirc_parallel.h)0
-rw-r--r--drivers/staging/media/lirc/lirc_sasem.c (renamed from drivers/staging/lirc/lirc_sasem.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_serial.c (renamed from drivers/staging/lirc/lirc_serial.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_sir.c (renamed from drivers/staging/lirc/lirc_sir.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_ttusbir.c (renamed from drivers/staging/lirc/lirc_ttusbir.c)0
-rw-r--r--drivers/staging/media/lirc/lirc_zilog.c (renamed from drivers/staging/lirc/lirc_zilog.c)0
-rw-r--r--drivers/staging/media/solo6x10/Kconfig (renamed from drivers/staging/solo6x10/Kconfig)0
-rw-r--r--drivers/staging/media/solo6x10/Makefile (renamed from drivers/staging/solo6x10/Makefile)0
-rw-r--r--drivers/staging/media/solo6x10/TODO (renamed from drivers/staging/solo6x10/TODO)0
-rw-r--r--drivers/staging/media/solo6x10/core.c (renamed from drivers/staging/solo6x10/core.c)0
-rw-r--r--drivers/staging/media/solo6x10/disp.c (renamed from drivers/staging/solo6x10/disp.c)0
-rw-r--r--drivers/staging/media/solo6x10/enc.c (renamed from drivers/staging/solo6x10/enc.c)0
-rw-r--r--drivers/staging/media/solo6x10/g723.c (renamed from drivers/staging/solo6x10/g723.c)0
-rw-r--r--drivers/staging/media/solo6x10/gpio.c (renamed from drivers/staging/solo6x10/gpio.c)0
-rw-r--r--drivers/staging/media/solo6x10/i2c.c (renamed from drivers/staging/solo6x10/i2c.c)0
-rw-r--r--drivers/staging/media/solo6x10/jpeg.h (renamed from drivers/staging/solo6x10/jpeg.h)0
-rw-r--r--drivers/staging/media/solo6x10/offsets.h (renamed from drivers/staging/solo6x10/offsets.h)0
-rw-r--r--drivers/staging/media/solo6x10/osd-font.h (renamed from drivers/staging/solo6x10/osd-font.h)0
-rw-r--r--drivers/staging/media/solo6x10/p2m.c (renamed from drivers/staging/solo6x10/p2m.c)0
-rw-r--r--drivers/staging/media/solo6x10/registers.h (renamed from drivers/staging/solo6x10/registers.h)0
-rw-r--r--drivers/staging/media/solo6x10/solo6x10.h (renamed from drivers/staging/solo6x10/solo6x10.h)0
-rw-r--r--drivers/staging/media/solo6x10/tw28.c (renamed from drivers/staging/solo6x10/tw28.c)0
-rw-r--r--drivers/staging/media/solo6x10/tw28.h (renamed from drivers/staging/solo6x10/tw28.h)0
-rw-r--r--drivers/staging/media/solo6x10/v4l2-enc.c (renamed from drivers/staging/solo6x10/v4l2-enc.c)0
-rw-r--r--drivers/staging/media/solo6x10/v4l2.c (renamed from drivers/staging/solo6x10/v4l2.c)0
-rw-r--r--include/linux/videodev2.h27
-rw-r--r--include/media/ov772x.h26
-rw-r--r--include/media/s5k6aa.h51
-rw-r--r--include/media/soc_camera.h104
-rw-r--r--include/media/soc_camera_platform.h4
-rw-r--r--include/media/soc_mediabus.h2
-rw-r--r--include/media/v4l2-ioctl.h2
-rw-r--r--include/media/v4l2-subdev.h5
-rw-r--r--include/media/videobuf2-core.h50
216 files changed, 10316 insertions, 3785 deletions
diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml
index 91410b6e7e08..b68698f96e7f 100644
--- a/Documentation/DocBook/media/v4l/compat.xml
+++ b/Documentation/DocBook/media/v4l/compat.xml
@@ -2486,6 +2486,9 @@ ioctls.</para>
         <listitem>
 	  <para>Flash API. <xref linkend="flash-controls" /></para>
         </listitem>
+        <listitem>
+	  <para>&VIDIOC-CREATE-BUFS; and &VIDIOC-PREPARE-BUF; ioctls.</para>
+        </listitem>
       </itemizedlist>
     </section>
 
diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml
index 23fdf79f8cf3..3bc5ee8b2c74 100644
--- a/Documentation/DocBook/media/v4l/controls.xml
+++ b/Documentation/DocBook/media/v4l/controls.xml
@@ -232,8 +232,9 @@ control is deprecated. New drivers and applications should use the
 	    <entry>Enables a power line frequency filter to avoid
 flicker. Possible values for <constant>enum v4l2_power_line_frequency</constant> are:
 <constant>V4L2_CID_POWER_LINE_FREQUENCY_DISABLED</constant> (0),
-<constant>V4L2_CID_POWER_LINE_FREQUENCY_50HZ</constant> (1) and
-<constant>V4L2_CID_POWER_LINE_FREQUENCY_60HZ</constant> (2).</entry>
+<constant>V4L2_CID_POWER_LINE_FREQUENCY_50HZ</constant> (1),
+<constant>V4L2_CID_POWER_LINE_FREQUENCY_60HZ</constant> (2) and
+<constant>V4L2_CID_POWER_LINE_FREQUENCY_AUTO</constant> (3).</entry>
 	  </row>
 	  <row>
 	    <entry><constant>V4L2_CID_HUE_AUTO</constant></entry>
diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml
index c57d1ec6291c..3f47df1aa54a 100644
--- a/Documentation/DocBook/media/v4l/io.xml
+++ b/Documentation/DocBook/media/v4l/io.xml
@@ -927,6 +927,33 @@ ioctl is called.</entry>
 Applications set or clear this flag before calling the
 <constant>VIDIOC_QBUF</constant> ioctl.</entry>
 	  </row>
+	  <row>
+	    <entry><constant>V4L2_BUF_FLAG_PREPARED</constant></entry>
+	    <entry>0x0400</entry>
+	    <entry>The buffer has been prepared for I/O and can be queued by the
+application. Drivers set or clear this flag when the
+<link linkend="vidioc-querybuf">VIDIOC_QUERYBUF</link>, <link
+	  linkend="vidioc-qbuf">VIDIOC_PREPARE_BUF</link>, <link
+	  linkend="vidioc-qbuf">VIDIOC_QBUF</link> or <link
+	  linkend="vidioc-qbuf">VIDIOC_DQBUF</link> ioctl is called.</entry>
+	  </row>
+	  <row>
+	    <entry><constant>V4L2_BUF_FLAG_NO_CACHE_INVALIDATE</constant></entry>
+	    <entry>0x0400</entry>
+	    <entry>Caches do not have to be invalidated for this buffer.
+Typically applications shall use this flag if the data captured in the buffer
+is not going to be touched by the CPU, instead the buffer will, probably, be
+passed on to a DMA-capable hardware unit for further processing or output.
+</entry>
+	  </row>
+	  <row>
+	    <entry><constant>V4L2_BUF_FLAG_NO_CACHE_CLEAN</constant></entry>
+	    <entry>0x0800</entry>
+	    <entry>Caches do not have to be cleaned for this buffer.
+Typically applications shall use this flag for output buffers if the data
+in this buffer has not been created by the CPU but by some DMA-capable unit,
+in which case caches have not been used.</entry>
+	  </row>
 	</tbody>
       </tgroup>
     </table>
diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml
index 40132c277647..2ab365c10fb9 100644
--- a/Documentation/DocBook/media/v4l/v4l2.xml
+++ b/Documentation/DocBook/media/v4l/v4l2.xml
@@ -469,6 +469,7 @@ and discussions on the V4L mailing list.</revremark>
     &sub-close;
     &sub-ioctl;
     <!-- All ioctls go here. -->
+    &sub-create-bufs;
     &sub-cropcap;
     &sub-dbg-g-chip-ident;
     &sub-dbg-g-register;
@@ -511,6 +512,7 @@ and discussions on the V4L mailing list.</revremark>
     &sub-queryctrl;
     &sub-query-dv-preset;
     &sub-querystd;
+    &sub-prepare-buf;
     &sub-reqbufs;
     &sub-s-hw-freq-seek;
     &sub-streamon;
diff --git a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml
new file mode 100644
index 000000000000..73ae8a6cd004
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml
@@ -0,0 +1,139 @@
+<refentry id="vidioc-create-bufs">
+  <refmeta>
+    <refentrytitle>ioctl VIDIOC_CREATE_BUFS</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>VIDIOC_CREATE_BUFS</refname>
+    <refpurpose>Create buffers for Memory Mapped or User Pointer I/O</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+	<funcdef>int <function>ioctl</function></funcdef>
+	<paramdef>int <parameter>fd</parameter></paramdef>
+	<paramdef>int <parameter>request</parameter></paramdef>
+	<paramdef>struct v4l2_create_buffers *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+	<term><parameter>fd</parameter></term>
+	<listitem>
+	  <para>&fd;</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>request</parameter></term>
+	<listitem>
+	  <para>VIDIOC_CREATE_BUFS</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>argp</parameter></term>
+	<listitem>
+	  <para></para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>This ioctl is used to create buffers for <link linkend="mmap">memory
+mapped</link> or <link linkend="userp">user pointer</link>
+I/O. It can be used as an alternative or in addition to the
+<constant>VIDIOC_REQBUFS</constant> ioctl, when a tighter control over buffers
+is required. This ioctl can be called multiple times to create buffers of
+different sizes.</para>
+
+    <para>To allocate device buffers applications initialize relevant fields of
+the <structname>v4l2_create_buffers</structname> structure. They set the
+<structfield>type</structfield> field in the
+<structname>v4l2_format</structname> structure, embedded in this
+structure, to the respective stream or buffer type.
+<structfield>count</structfield> must be set to the number of required buffers.
+<structfield>memory</structfield> specifies the required I/O method. The
+<structfield>format</structfield> field shall typically be filled in using
+either the <constant>VIDIOC_TRY_FMT</constant> or
+<constant>VIDIOC_G_FMT</constant> ioctl(). Additionally, applications can adjust
+<structfield>sizeimage</structfield> fields to fit their specific needs. The
+<structfield>reserved</structfield> array must be zeroed.</para>
+
+    <para>When the ioctl is called with a pointer to this structure the driver
+will attempt to allocate up to the requested number of buffers and store the
+actual number allocated and the starting index in the
+<structfield>count</structfield> and the <structfield>index</structfield> fields
+respectively. On return <structfield>count</structfield> can be smaller than
+the number requested. The driver may also increase buffer sizes if required,
+however, it will not update <structfield>sizeimage</structfield> field values.
+The user has to use <constant>VIDIOC_QUERYBUF</constant> to retrieve that
+information.</para>
+
+    <table pgwide="1" frame="none" id="v4l2-create-buffers">
+      <title>struct <structname>v4l2_create_buffers</structname></title>
+      <tgroup cols="3">
+	&cs-str;
+	<tbody valign="top">
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>index</structfield></entry>
+	    <entry>The starting buffer index, returned by the driver.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>count</structfield></entry>
+	    <entry>The number of buffers requested or granted.</entry>
+	  </row>
+	  <row>
+	    <entry>&v4l2-memory;</entry>
+	    <entry><structfield>memory</structfield></entry>
+	    <entry>Applications set this field to
+<constant>V4L2_MEMORY_MMAP</constant> or
+<constant>V4L2_MEMORY_USERPTR</constant>.</entry>
+	  </row>
+	  <row>
+	    <entry>&v4l2-format;</entry>
+	    <entry><structfield>format</structfield></entry>
+	    <entry>Filled in by the application, preserved by the driver.</entry>
+	  </row>
+	  <row>
+	    <entry>__u32</entry>
+	    <entry><structfield>reserved</structfield>[8]</entry>
+	    <entry>A place holder for future extensions.</entry>
+	  </row>
+	</tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+
+    <variablelist>
+      <varlistentry>
+	<term><errorcode>ENOMEM</errorcode></term>
+	<listitem>
+	  <para>No memory to allocate buffers for <link linkend="mmap">memory
+mapped</link> I/O.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><errorcode>EINVAL</errorcode></term>
+	<listitem>
+	  <para>The buffer type (<structfield>type</structfield> field) or the
+requested I/O method (<structfield>memory</structfield>) is not
+supported.</para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml b/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml
new file mode 100644
index 000000000000..7bde698760e4
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml
@@ -0,0 +1,88 @@
+<refentry id="vidioc-prepare-buf">
+  <refmeta>
+    <refentrytitle>ioctl VIDIOC_PREPARE_BUF</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>VIDIOC_PREPARE_BUF</refname>
+    <refpurpose>Prepare a buffer for I/O</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+	<funcdef>int <function>ioctl</function></funcdef>
+	<paramdef>int <parameter>fd</parameter></paramdef>
+	<paramdef>int <parameter>request</parameter></paramdef>
+	<paramdef>struct v4l2_buffer *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+	<term><parameter>fd</parameter></term>
+	<listitem>
+	  <para>&fd;</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>request</parameter></term>
+	<listitem>
+	  <para>VIDIOC_PREPARE_BUF</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><parameter>argp</parameter></term>
+	<listitem>
+	  <para></para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>Applications can optionally call the
+<constant>VIDIOC_PREPARE_BUF</constant> ioctl to pass ownership of the buffer
+to the driver before actually enqueuing it, using the
+<constant>VIDIOC_QBUF</constant> ioctl, and to prepare it for future I/O.
+Such preparations may include cache invalidation or cleaning. Performing them
+in advance saves time during the actual I/O. In case such cache operations are
+not required, the application can use one of
+<constant>V4L2_BUF_FLAG_NO_CACHE_INVALIDATE</constant> and
+<constant>V4L2_BUF_FLAG_NO_CACHE_CLEAN</constant> flags to skip the respective
+step.</para>
+
+    <para>The <structname>v4l2_buffer</structname> structure is
+specified in <xref linkend="buffer" />.</para>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+
+    <variablelist>
+      <varlistentry>
+	<term><errorcode>EBUSY</errorcode></term>
+	<listitem>
+	  <para>File I/O is in progress.</para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term><errorcode>EINVAL</errorcode></term>
+	<listitem>
+	  <para>The buffer <structfield>type</structfield> is not
+supported, or the <structfield>index</structfield> is out of bounds,
+or no buffers have been allocated yet, or the
+<structfield>userptr</structfield> or
+<structfield>length</structfield> are invalid.</para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+</refentry>
diff --git a/arch/arm/mach-pxa/pcm990-baseboard.c b/arch/arm/mach-pxa/pcm990-baseboard.c
index 9a9c539f6c01..6d38c6548b3d 100644
--- a/arch/arm/mach-pxa/pcm990-baseboard.c
+++ b/arch/arm/mach-pxa/pcm990-baseboard.c
@@ -394,9 +394,9 @@ static int pcm990_camera_set_bus_param(struct soc_camera_link *link,
 	}
 
 	if (flags & SOCAM_DATAWIDTH_8)
-		gpio_set_value(gpio_bus_switch, 1);
+		gpio_set_value_cansleep(gpio_bus_switch, 1);
 	else
-		gpio_set_value(gpio_bus_switch, 0);
+		gpio_set_value_cansleep(gpio_bus_switch, 0);
 
 	return 0;
 }
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index 5b7edadf4647..f9f66c20c9f1 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -933,7 +933,7 @@ static struct platform_device ap4evb_camera = {
 static struct sh_csi2_client_config csi2_clients[] = {
 	{
 		.phy		= SH_CSI2_PHY_MAIN,
-		.lanes		= 3,
+		.lanes		= 0,		/* default: 2 lanes */
 		.channel	= 0,
 		.pdev		= &ap4evb_camera,
 	},
diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c
index 3689ad2e9156..682042306ea2 100644
--- a/arch/arm/mach-shmobile/board-mackerel.c
+++ b/arch/arm/mach-shmobile/board-mackerel.c
@@ -1223,9 +1223,10 @@ static struct soc_camera_platform_info camera_info = {
 		.width = 640,
 		.height = 480,
 	},
-	.bus_param = SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH |
-	SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_MASTER | SOCAM_DATAWIDTH_8 |
-	SOCAM_DATA_ACTIVE_HIGH,
+	.mbus_param = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+	V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+	V4L2_MBUS_DATA_ACTIVE_HIGH,
+	.mbus_type = V4L2_MBUS_PARALLEL,
 	.set_capture = camera_set_capture,
 };
 
diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c
index d36265758911..7030f4c8cf11 100644
--- a/arch/sh/boards/mach-ap325rxa/setup.c
+++ b/arch/sh/boards/mach-ap325rxa/setup.c
@@ -345,9 +345,10 @@ static struct soc_camera_platform_info camera_info = {
 		.width = 640,
 		.height = 480,
 	},
-	.bus_param = SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH |
-	SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_MASTER | SOCAM_DATAWIDTH_8 |
-	SOCAM_DATA_ACTIVE_HIGH,
+	.mbus_param = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+	V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+	V4L2_MBUS_DATA_ACTIVE_HIGH,
+	.mbus_type = V4L2_MBUS_PARALLEL,
 	.set_capture = camera_set_capture,
 };
 
@@ -501,8 +502,7 @@ static struct i2c_board_info ap325rxa_i2c_camera[] = {
 };
 
 static struct ov772x_camera_info ov7725_info = {
-	.flags		= OV772X_FLAG_VFLIP | OV772X_FLAG_HFLIP | \
-			  OV772X_FLAG_8BIT,
+	.flags		= OV772X_FLAG_VFLIP | OV772X_FLAG_HFLIP,
 	.edgectrl	= OV772X_AUTO_EDGECTRL(0xf, 0),
 };
 
diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c
index 2d4c9c8c6664..e4c81195929c 100644
--- a/arch/sh/boards/mach-migor/setup.c
+++ b/arch/sh/boards/mach-migor/setup.c
@@ -448,9 +448,7 @@ static struct i2c_board_info migor_i2c_camera[] = {
 	},
 };
 
-static struct ov772x_camera_info ov7725_info = {
-	.flags		= OV772X_FLAG_8BIT,
-};
+static struct ov772x_camera_info ov7725_info;
 
 static struct soc_camera_link ov7725_link = {
 	.power		= ov7725_power,
diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c
index 6815905a772f..ddc2a1331822 100644
--- a/drivers/dma/ipu/ipu_idmac.c
+++ b/drivers/dma/ipu/ipu_idmac.c
@@ -1307,6 +1307,7 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
 	    ipu_submit_buffer(ichan, descnew, sgnew, ichan->active_buffer) < 0) {
 		callback = descnew->txd.callback;
 		callback_param = descnew->txd.callback_param;
+		list_del_init(&descnew->list);
 		spin_unlock(&ichan->lock);
 		if (callback)
 			callback(callback_param);
@@ -1428,39 +1429,58 @@ static int __idmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 {
 	struct idmac_channel *ichan = to_idmac_chan(chan);
 	struct idmac *idmac = to_idmac(chan->device);
+	struct ipu *ipu = to_ipu(idmac);
+	struct list_head *list, *tmp;
 	unsigned long flags;
 	int i;
 
-	/* Only supports DMA_TERMINATE_ALL */
-	if (cmd != DMA_TERMINATE_ALL)
-		return -ENXIO;
+	switch (cmd) {
+	case DMA_PAUSE:
+		spin_lock_irqsave(&ipu->lock, flags);
+		ipu_ic_disable_task(ipu, chan->chan_id);
 
-	ipu_disable_channel(idmac, ichan,
-			    ichan->status >= IPU_CHANNEL_ENABLED);
+		/* Return all descriptors into "prepared" state */
+		list_for_each_safe(list, tmp, &ichan->queue)
+			list_del_init(list);
 
-	tasklet_disable(&to_ipu(idmac)->tasklet);
+		ichan->sg[0] = NULL;
+		ichan->sg[1] = NULL;
 
-	/* ichan->queue is modified in ISR, have to spinlock */
-	spin_lock_irqsave(&ichan->lock, flags);
-	list_splice_init(&ichan->queue, &ichan->free_list);
+		spin_unlock_irqrestore(&ipu->lock, flags);
 
-	if (ichan->desc)
-		for (i = 0; i < ichan->n_tx_desc; i++) {
-			struct idmac_tx_desc *desc = ichan->desc + i;
-			if (list_empty(&desc->list))
-				/* Descriptor was prepared, but not submitted */
-				list_add(&desc->list, &ichan->free_list);
+		ichan->status = IPU_CHANNEL_INITIALIZED;
+		break;
+	case DMA_TERMINATE_ALL:
+		ipu_disable_channel(idmac, ichan,
+				    ichan->status >= IPU_CHANNEL_ENABLED);
 
-			async_tx_clear_ack(&desc->txd);
-		}
+		tasklet_disable(&ipu->tasklet);
 
-	ichan->sg[0] = NULL;
-	ichan->sg[1] = NULL;
-	spin_unlock_irqrestore(&ichan->lock, flags);
+		/* ichan->queue is modified in ISR, have to spinlock */
+		spin_lock_irqsave(&ichan->lock, flags);
+		list_splice_init(&ichan->queue, &ichan->free_list);
 
-	tasklet_enable(&to_ipu(idmac)->tasklet);
+		if (ichan->desc)
+			for (i = 0; i < ichan->n_tx_desc; i++) {
+				struct idmac_tx_desc *desc = ichan->desc + i;
+				if (list_empty(&desc->list))
+					/* Descriptor was prepared, but not submitted */
+					list_add(&desc->list, &ichan->free_list);
 
-	ichan->status = IPU_CHANNEL_INITIALIZED;
+				async_tx_clear_ack(&desc->txd);
+			}
+
+		ichan->sg[0] = NULL;
+		ichan->sg[1] = NULL;
+		spin_unlock_irqrestore(&ichan->lock, flags);
+
+		tasklet_enable(&ipu->tasklet);
+
+		ichan->status = IPU_CHANNEL_INITIALIZED;
+		break;
+	default:
+		return -ENOSYS;
+	}
 
 	return 0;
 }
@@ -1663,7 +1683,6 @@ static void __exit ipu_idmac_exit(struct ipu *ipu)
 		struct idmac_channel *ichan = ipu->channel + i;
 
 		idmac_control(&ichan->dma_chan, DMA_TERMINATE_ALL, 0);
-		idmac_prep_slave_sg(&ichan->dma_chan, NULL, 0, DMA_NONE, 0);
 	}
 
 	dma_async_device_unregister(&idmac->dma);
diff --git a/drivers/media/dvb/ddbridge/Makefile b/drivers/media/dvb/ddbridge/Makefile
index cf7214edf65f..38019bafb862 100644
--- a/drivers/media/dvb/ddbridge/Makefile
+++ b/drivers/media/dvb/ddbridge/Makefile
@@ -11,4 +11,4 @@ ccflags-y += -Idrivers/media/dvb/frontends/
 ccflags-y += -Idrivers/media/common/tuners/
 
 # For the staging CI driver cxd2099
-ccflags-y += -Idrivers/staging/cxd2099/
+ccflags-y += -Idrivers/staging/media/cxd2099/
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index 7d0710bb1978..26c8b9e57050 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -102,6 +102,7 @@ obj-$(CONFIG_DVB_USB_IT913X) += dvb-usb-it913x.o
 
 dvb-usb-mxl111sf-objs = mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o mxl111sf-gpio.o
 obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o
+obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o
 obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
 
 ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 2ad33ba92ba2..2d08c9b5128a 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -37,6 +37,7 @@
 #define USB_VID_HAUPPAUGE			0x2040
 #define USB_VID_HYPER_PALTEK			0x1025
 #define USB_VID_INTEL				0x8086
+#define USB_VID_ITETECH				0x048d
 #define USB_VID_KWORLD				0xeb2a
 #define USB_VID_KWORLD_2			0x1b80
 #define USB_VID_KYE				0x0458
@@ -126,6 +127,7 @@
 #define USB_PID_GRANDTEC_DVBT_USB_COLD			0x0fa0
 #define USB_PID_GRANDTEC_DVBT_USB_WARM			0x0fa1
 #define USB_PID_INTEL_CE9500				0x9500
+#define USB_PID_ITETECH_IT9135				0x9135
 #define USB_PID_KWORLD_399U				0xe399
 #define USB_PID_KWORLD_399U_2				0xe400
 #define USB_PID_KWORLD_395U				0xe396
diff --git a/drivers/media/dvb/dvb-usb/it913x.c b/drivers/media/dvb/dvb-usb/it913x.c
index f027a2c1c3e8..c46226187143 100644
--- a/drivers/media/dvb/dvb-usb/it913x.c
+++ b/drivers/media/dvb/dvb-usb/it913x.c
@@ -60,6 +60,17 @@ struct it913x_state {
 	u8 id;
 };
 
+struct ite_config {
+	u8 chip_ver;
+	u16 chip_type;
+	u32 firmware;
+	u8 tuner_id_0;
+	u8 tuner_id_1;
+	u8 dual_mode;
+};
+
+struct ite_config it913x_config;
+
 static int it913x_bulk_write(struct usb_device *dev,
 				u8 *snd, int len, u8 pipe)
 {
@@ -191,18 +202,23 @@ static int it913x_read_reg(struct usb_device *udev, u32 reg)
 static u32 it913x_query(struct usb_device *udev, u8 pro)
 {
 	int ret;
-	u32 res = 0;
 	u8 data[4];
 	ret = it913x_io(udev, READ_LONG, pro, CMD_DEMOD_READ,
-		0x1222, 0, &data[0], 1);
-	if (data[0] == 0x1) {
-		ret = it913x_io(udev, READ_SHORT, pro,
+		0x1222, 0, &data[0], 3);
+
+	it913x_config.chip_ver = data[0];
+	it913x_config.chip_type = (u16)(data[2] << 8) + data[1];
+
+	info("Chip Version=%02x Chip Type=%04x", it913x_config.chip_ver,
+		it913x_config.chip_type);
+
+	ret |= it913x_io(udev, READ_SHORT, pro,
 			CMD_QUERYINFO, 0, 0x1, &data[0], 4);
-		res = (data[0] << 24) + (data[1] << 16) +
+
+	it913x_config.firmware = (data[0] << 24) + (data[1] << 16) +
 			(data[2] << 8) + data[3];
-	}
 
-	return (ret < 0) ? 0 : res;
+	return (ret < 0) ? 0 : it913x_config.firmware;
 }
 
 static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
@@ -336,26 +352,35 @@ static int it913x_identify_state(struct usb_device *udev,
 		int *cold)
 {
 	int ret = 0, firm_no;
-	u8 reg, adap, ep, tun0, tun1;
+	u8 reg, remote;
 
 	firm_no = it913x_return_status(udev);
 
-	ep = it913x_read_reg(udev, 0x49ac);
-	adap = it913x_read_reg(udev, 0x49c5);
-	tun0 = it913x_read_reg(udev, 0x49d0);
-	info("No. Adapters=%x Endpoints=%x Tuner Type=%x", adap, ep, tun0);
+	/* checnk for dual mode */
+	it913x_config.dual_mode =  it913x_read_reg(udev, 0x49c5);
+
+	/* TODO different remotes */
+	remote = it913x_read_reg(udev, 0x49ac); /* Remote */
+	if (remote == 0)
+		props->rc.core.rc_codes = NULL;
+
+	/* TODO at the moment tuner_id is always assigned to 0x38 */
+	it913x_config.tuner_id_0 = it913x_read_reg(udev, 0x49d0);
+
+	info("Dual mode=%x Remote=%x Tuner Type=%x", it913x_config.dual_mode
+		, remote, it913x_config.tuner_id_0);
 
 	if (firm_no > 0) {
 		*cold = 0;
 		return 0;
 	}
 
-	if (adap > 2) {
-		tun1 = it913x_read_reg(udev, 0x49e0);
+	if (it913x_config.dual_mode) {
+		it913x_config.tuner_id_1 = it913x_read_reg(udev, 0x49e0);
 		ret = it913x_wr_reg(udev, DEV_0, GPIOH1_EN, 0x1);
 		ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_ON, 0x1);
 		ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x1);
-		msleep(50); /* Delay noticed reset cycle ? */
+		msleep(50);
 		ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x0);
 		msleep(50);
 		reg = it913x_read_reg(udev, GPIOH1_O);
@@ -366,14 +391,19 @@ static int it913x_identify_state(struct usb_device *udev,
 				ret = it913x_wr_reg(udev, DEV_0,
 					GPIOH1_O, 0x0);
 		}
+		props->num_adapters = 2;
 	} else
 		props->num_adapters = 1;
 
 	reg = it913x_read_reg(udev, IO_MUX_POWER_CLK);
 
-	ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, CHIP2_I2C_ADDR);
-
-	ret |= it913x_wr_reg(udev, DEV_0,  CLK_O_EN, 0x1);
+	if (it913x_config.dual_mode) {
+		ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, CHIP2_I2C_ADDR);
+		ret |= it913x_wr_reg(udev, DEV_0,  CLK_O_EN, 0x1);
+	} else {
+		ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, 0x0);
+		ret |= it913x_wr_reg(udev, DEV_0,  CLK_O_EN, 0x0);
+	}
 
 	*cold = 1;
 
@@ -403,13 +433,11 @@ static int it913x_download_firmware(struct usb_device *udev,
 					const struct firmware *fw)
 {
 	int ret = 0, i;
-	u8 packet_size, dlen, tun1;
+	u8 packet_size, dlen;
 	u8 *fw_data;
 
 	packet_size = 0x29;
 
-	tun1 = it913x_read_reg(udev, 0x49e0);
-
 	ret = it913x_wr_reg(udev, DEV_0,  I2C_CLK, I2C_CLK_100);
 
 	info("FRM Starting Firmware Download");
@@ -444,11 +472,12 @@ static int it913x_download_firmware(struct usb_device *udev,
 	ret |= it913x_wr_reg(udev, DEV_0,  I2C_CLK, I2C_CLK_400);
 
 	/* Tuner function */
-	ret |= it913x_wr_reg(udev, DEV_0_DMOD , 0xec4c, 0xa0);
+	if (it913x_config.dual_mode)
+		ret |= it913x_wr_reg(udev, DEV_0_DMOD , 0xec4c, 0xa0);
 
 	ret |= it913x_wr_reg(udev, DEV_0,  PADODPU, 0x0);
 	ret |= it913x_wr_reg(udev, DEV_0,  AGC_O_D, 0x0);
-	if (tun1 > 0) {
+	if (it913x_config.dual_mode) {
 		ret |= it913x_wr_reg(udev, DEV_1,  PADODPU, 0x0);
 		ret |= it913x_wr_reg(udev, DEV_1,  AGC_O_D, 0x0);
 	}
@@ -475,9 +504,28 @@ static int it913x_frontend_attach(struct dvb_usb_adapter *adap)
 	u8 adf = it913x_read_reg(udev, IO_MUX_POWER_CLK);
 	u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5);
 	u16 ep_size = adap->props.fe[0].stream.u.bulk.buffersize;
+	u8 tuner_id, tuner_type;
+
+	if (adap->id == 0)
+		tuner_id = it913x_config.tuner_id_0;
+	else
+		tuner_id = it913x_config.tuner_id_1;
+
+	/* TODO we always use IT9137 possible references here*/
+	/* Documentation suggests don't care */
+	switch (tuner_id) {
+	case 0x51:
+	case 0x52:
+	case 0x60:
+	case 0x61:
+	case 0x62:
+	default:
+	case 0x38:
+		tuner_type = IT9137;
+	}
 
 	adap->fe_adap[0].fe = dvb_attach(it913x_fe_attach,
-		&adap->dev->i2c_adap, adap_addr, adf, IT9137);
+		&adap->dev->i2c_adap, adap_addr, adf, tuner_type);
 
 	if (adap->id == 0 && adap->fe_adap[0].fe) {
 		ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2_SW_RST, 0x1);
@@ -533,6 +581,7 @@ static int it913x_probe(struct usb_interface *intf,
 
 static struct usb_device_id it913x_table[] = {
 	{ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09) },
+	{ USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135) },
 	{}		/* Terminating entry */
 };
 
@@ -608,12 +657,14 @@ static struct dvb_usb_device_properties it913x_properties = {
 		.rc_codes	= RC_MAP_KWORLD_315U,
 	},
 	.i2c_algo         = &it913x_i2c_algo,
-	.num_device_descs = 1,
+	.num_device_descs = 2,
 	.devices = {
 		{   "Kworld UB499-2T T09(IT9137)",
 			{ &it913x_table[0], NULL },
 			},
-
+		{   "ITE 9135 Generic",
+			{ &it913x_table[1], NULL },
+			},
 	}
 };
 
@@ -647,5 +698,5 @@ module_exit(it913x_module_exit);
 
 MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
 MODULE_DESCRIPTION("it913x USB 2 Driver");
-MODULE_VERSION("1.06");
+MODULE_VERSION("1.07");
 MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-demod.c b/drivers/media/dvb/dvb-usb/mxl111sf-demod.c
new file mode 100644
index 000000000000..d1f58371c711
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-demod.c
@@ -0,0 +1,614 @@
+/*
+ *  mxl111sf-demod.c - driver for the MaxLinear MXL111SF DVB-T demodulator
+ *
+ *  Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mxl111sf-demod.h"
+#include "mxl111sf-reg.h"
+
+/* debug */
+static int mxl111sf_demod_debug;
+module_param_named(debug, mxl111sf_demod_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+#define mxl_dbg(fmt, arg...) \
+	if (mxl111sf_demod_debug) \
+		mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+/* ------------------------------------------------------------------------ */
+
+struct mxl111sf_demod_state {
+	struct mxl111sf_state *mxl_state;
+
+	struct mxl111sf_demod_config *cfg;
+
+	struct dvb_frontend fe;
+};
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_demod_read_reg(struct mxl111sf_demod_state *state,
+				   u8 addr, u8 *data)
+{
+	return (state->cfg->read_reg) ?
+		state->cfg->read_reg(state->mxl_state, addr, data) :
+		-EINVAL;
+}
+
+static int mxl111sf_demod_write_reg(struct mxl111sf_demod_state *state,
+				    u8 addr, u8 data)
+{
+	return (state->cfg->write_reg) ?
+		state->cfg->write_reg(state->mxl_state, addr, data) :
+		-EINVAL;
+}
+
+static
+int mxl111sf_demod_program_regs(struct mxl111sf_demod_state *state,
+				struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
+{
+	return (state->cfg->program_regs) ?
+		state->cfg->program_regs(state->mxl_state, ctrl_reg_info) :
+		-EINVAL;
+}
+
+/* ------------------------------------------------------------------------ */
+/* TPS */
+
+static
+int mxl1x1sf_demod_get_tps_code_rate(struct mxl111sf_demod_state *state,
+				     fe_code_rate_t *code_rate)
+{
+	u8 val;
+	int ret = mxl111sf_demod_read_reg(state, V6_CODE_RATE_TPS_REG, &val);
+	/* bit<2:0> - 000:1/2, 001:2/3, 010:3/4, 011:5/6, 100:7/8 */
+	if (mxl_fail(ret))
+		goto fail;
+
+	switch (val & V6_CODE_RATE_TPS_MASK) {
+	case 0:
+		*code_rate = FEC_1_2;
+		break;
+	case 1:
+		*code_rate = FEC_2_3;
+		break;
+	case 2:
+		*code_rate = FEC_3_4;
+		break;
+	case 3:
+		*code_rate = FEC_5_6;
+		break;
+	case 4:
+		*code_rate = FEC_7_8;
+		break;
+	}
+fail:
+	return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_constellation(struct mxl111sf_demod_state *state,
+					 fe_modulation_t *constellation)
+{
+	u8 val;
+	int ret = mxl111sf_demod_read_reg(state, V6_MODORDER_TPS_REG, &val);
+	/* Constellation, 00 : QPSK, 01 : 16QAM, 10:64QAM */
+	if (mxl_fail(ret))
+		goto fail;
+
+	switch ((val & V6_PARAM_CONSTELLATION_MASK) >> 4) {
+	case 0:
+		*constellation = QPSK;
+		break;
+	case 1:
+		*constellation = QAM_16;
+		break;
+	case 2:
+		*constellation = QAM_64;
+		break;
+	}
+fail:
+	return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_guard_fft_mode(struct mxl111sf_demod_state *state,
+					  fe_transmit_mode_t *fft_mode)
+{
+	u8 val;
+	int ret = mxl111sf_demod_read_reg(state, V6_MODE_TPS_REG, &val);
+	/* FFT Mode, 00:2K, 01:8K, 10:4K */
+	if (mxl_fail(ret))
+		goto fail;
+
+	switch ((val & V6_PARAM_FFT_MODE_MASK) >> 2) {
+	case 0:
+		*fft_mode = TRANSMISSION_MODE_2K;
+		break;
+	case 1:
+		*fft_mode = TRANSMISSION_MODE_8K;
+		break;
+	case 2:
+		*fft_mode = TRANSMISSION_MODE_4K;
+		break;
+	}
+fail:
+	return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_guard_interval(struct mxl111sf_demod_state *state,
+					  fe_guard_interval_t *guard)
+{
+	u8 val;
+	int ret = mxl111sf_demod_read_reg(state, V6_CP_TPS_REG, &val);
+	/* 00:1/32, 01:1/16, 10:1/8, 11:1/4 */
+	if (mxl_fail(ret))
+		goto fail;
+
+	switch ((val & V6_PARAM_GI_MASK) >> 4) {
+	case 0:
+		*guard = GUARD_INTERVAL_1_32;
+		break;
+	case 1:
+		*guard = GUARD_INTERVAL_1_16;
+		break;
+	case 2:
+		*guard = GUARD_INTERVAL_1_8;
+		break;
+	case 3:
+		*guard = GUARD_INTERVAL_1_4;
+		break;
+	}
+fail:
+	return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_hierarchy(struct mxl111sf_demod_state *state,
+				     fe_hierarchy_t *hierarchy)
+{
+	u8 val;
+	int ret = mxl111sf_demod_read_reg(state, V6_TPS_HIERACHY_REG, &val);
+	/* bit<6:4> - 000:Non hierarchy, 001:1, 010:2, 011:4 */
+	if (mxl_fail(ret))
+		goto fail;
+
+	switch ((val & V6_TPS_HIERARCHY_INFO_MASK) >> 6) {
+	case 0:
+		*hierarchy = HIERARCHY_NONE;
+		break;
+	case 1:
+		*hierarchy = HIERARCHY_1;
+		break;
+	case 2:
+		*hierarchy = HIERARCHY_2;
+		break;
+	case 3:
+		*hierarchy = HIERARCHY_4;
+		break;
+	}
+fail:
+	return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+/* LOCKS */
+
+static
+int mxl1x1sf_demod_get_sync_lock_status(struct mxl111sf_demod_state *state,
+					int *sync_lock)
+{
+	u8 val = 0;
+	int ret = mxl111sf_demod_read_reg(state, V6_SYNC_LOCK_REG, &val);
+	if (mxl_fail(ret))
+		goto fail;
+	*sync_lock = (val & SYNC_LOCK_MASK) >> 4;
+fail:
+	return ret;
+}
+
+static
+int mxl1x1sf_demod_get_rs_lock_status(struct mxl111sf_demod_state *state,
+				      int *rs_lock)
+{
+	u8 val = 0;
+	int ret = mxl111sf_demod_read_reg(state, V6_RS_LOCK_DET_REG, &val);
+	if (mxl_fail(ret))
+		goto fail;
+	*rs_lock = (val & RS_LOCK_DET_MASK) >> 3;
+fail:
+	return ret;
+}
+
+static
+int mxl1x1sf_demod_get_tps_lock_status(struct mxl111sf_demod_state *state,
+				       int *tps_lock)
+{
+	u8 val = 0;
+	int ret = mxl111sf_demod_read_reg(state, V6_TPS_LOCK_REG, &val);
+	if (mxl_fail(ret))
+		goto fail;
+	*tps_lock = (val & V6_PARAM_TPS_LOCK_MASK) >> 6;
+fail:
+	return ret;
+}
+
+static
+int mxl1x1sf_demod_get_fec_lock_status(struct mxl111sf_demod_state *state,
+				       int *fec_lock)
+{
+	u8 val = 0;
+	int ret = mxl111sf_demod_read_reg(state, V6_IRQ_STATUS_REG, &val);
+	if (mxl_fail(ret))
+		goto fail;
+	*fec_lock = (val & IRQ_MASK_FEC_LOCK) >> 4;
+fail:
+	return ret;
+}
+
+#if 0
+static
+int mxl1x1sf_demod_get_cp_lock_status(struct mxl111sf_demod_state *state,
+				      int *cp_lock)
+{
+	u8 val = 0;
+	int ret = mxl111sf_demod_read_reg(state, V6_CP_LOCK_DET_REG, &val);
+	if (mxl_fail(ret))
+		goto fail;
+	*cp_lock = (val & V6_CP_LOCK_DET_MASK) >> 2;
+fail:
+	return ret;
+}
+#endif
+
+static int mxl1x1sf_demod_reset_irq_status(struct mxl111sf_demod_state *state)
+{
+	return mxl111sf_demod_write_reg(state, 0x0e, 0xff);
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_demod_set_frontend(struct dvb_frontend *fe,
+				       struct dvb_frontend_parameters *param)
+{
+	struct mxl111sf_demod_state *state = fe->demodulator_priv;
+	int ret = 0;
+
+	struct mxl111sf_reg_ctrl_info phy_pll_patch[] = {
+		{0x00, 0xff, 0x01}, /* change page to 1 */
+		{0x40, 0xff, 0x05},
+		{0x40, 0xff, 0x01},
+		{0x41, 0xff, 0xca},
+		{0x41, 0xff, 0xc0},
+		{0x00, 0xff, 0x00}, /* change page to 0 */
+		{0,    0,    0}
+	};
+
+	mxl_dbg("()");
+
+	if (fe->ops.tuner_ops.set_params) {
+		ret = fe->ops.tuner_ops.set_params(fe, param);
+		if (mxl_fail(ret))
+			goto fail;
+		msleep(50);
+	}
+	ret = mxl111sf_demod_program_regs(state, phy_pll_patch);
+	mxl_fail(ret);
+	msleep(50);
+	ret = mxl1x1sf_demod_reset_irq_status(state);
+	mxl_fail(ret);
+	msleep(100);
+fail:
+	return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+#if 0
+/* resets TS Packet error count */
+/* After setting 7th bit of V5_PER_COUNT_RESET_REG, it should be reset to 0. */
+static
+int mxl1x1sf_demod_reset_packet_error_count(struct mxl111sf_demod_state *state)
+{
+	struct mxl111sf_reg_ctrl_info reset_per_count[] = {
+		{0x20, 0x01, 0x01},
+		{0x20, 0x01, 0x00},
+		{0,    0,    0}
+	};
+	return mxl111sf_demod_program_regs(state, reset_per_count);
+}
+#endif
+
+/* returns TS Packet error count */
+/* PER Count = FEC_PER_COUNT * (2 ** (FEC_PER_SCALE * 4)) */
+static int mxl111sf_demod_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	struct mxl111sf_demod_state *state = fe->demodulator_priv;
+	u32 fec_per_count, fec_per_scale;
+	u8 val;
+	int ret;
+
+	*ucblocks = 0;
+
+	/* FEC_PER_COUNT Register */
+	ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_COUNT_REG, &val);
+	if (mxl_fail(ret))
+		goto fail;
+
+	fec_per_count = val;
+
+	/* FEC_PER_SCALE Register */
+	ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_SCALE_REG, &val);
+	if (mxl_fail(ret))
+		goto fail;
+
+	val &= V6_FEC_PER_SCALE_MASK;
+	val *= 4;
+
+	fec_per_scale = 1 << val;
+
+	fec_per_count *= fec_per_scale;
+
+	*ucblocks = fec_per_count;
+fail:
+	return ret;
+}
+
+#ifdef MXL111SF_DEMOD_ENABLE_CALCULATIONS
+/* FIXME: leaving this enabled breaks the build on some architectures,
+ * and we shouldn't have any floating point math in the kernel, anyway.
+ *
+ * These macros need to be re-written, but it's harmless to simply
+ * return zero for now. */
+#define CALCULATE_BER(avg_errors, count) \
+	((u32)(avg_errors * 4)/(count*64*188*8))
+#define CALCULATE_SNR(data) \
+	((u32)((10 * (u32)data / 64) - 2.5))
+#else
+#define CALCULATE_BER(avg_errors, count) 0
+#define CALCULATE_SNR(data) 0
+#endif
+
+static int mxl111sf_demod_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	struct mxl111sf_demod_state *state = fe->demodulator_priv;
+	u8 val1, val2, val3;
+	int ret;
+
+	*ber = 0;
+
+	ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_LSB_REG, &val1);
+	if (mxl_fail(ret))
+		goto fail;
+	ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_MSB_REG, &val2);
+	if (mxl_fail(ret))
+		goto fail;
+	ret = mxl111sf_demod_read_reg(state, V6_N_ACCUMULATE_REG, &val3);
+	if (mxl_fail(ret))
+		goto fail;
+
+	*ber = CALCULATE_BER((val1 | (val2 << 8)), val3);
+fail:
+	return ret;
+}
+
+static int mxl111sf_demod_calc_snr(struct mxl111sf_demod_state *state,
+				   u16 *snr)
+{
+	u8 val1, val2;
+	int ret;
+
+	*snr = 0;
+
+	ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_LSB_REG, &val1);
+	if (mxl_fail(ret))
+		goto fail;
+	ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_MSB_REG, &val2);
+	if (mxl_fail(ret))
+		goto fail;
+
+	*snr = CALCULATE_SNR(val1 | ((val2 & 0x03) << 8));
+fail:
+	return ret;
+}
+
+static int mxl111sf_demod_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct mxl111sf_demod_state *state = fe->demodulator_priv;
+
+	int ret = mxl111sf_demod_calc_snr(state, snr);
+	if (mxl_fail(ret))
+		goto fail;
+
+	*snr /= 10; /* 0.1 dB */
+fail:
+	return ret;
+}
+
+static int mxl111sf_demod_read_status(struct dvb_frontend *fe,
+				      fe_status_t *status)
+{
+	struct mxl111sf_demod_state *state = fe->demodulator_priv;
+	int ret, locked, cr_lock, sync_lock, fec_lock;
+
+	*status = 0;
+
+	ret = mxl1x1sf_demod_get_rs_lock_status(state, &locked);
+	if (mxl_fail(ret))
+		goto fail;
+	ret = mxl1x1sf_demod_get_tps_lock_status(state, &cr_lock);
+	if (mxl_fail(ret))
+		goto fail;
+	ret = mxl1x1sf_demod_get_sync_lock_status(state, &sync_lock);
+	if (mxl_fail(ret))
+		goto fail;
+	ret = mxl1x1sf_demod_get_fec_lock_status(state, &fec_lock);
+	if (mxl_fail(ret))
+		goto fail;
+
+	if (locked)
+		*status |= FE_HAS_SIGNAL;
+	if (cr_lock)
+		*status |= FE_HAS_CARRIER;
+	if (sync_lock)
+		*status |= FE_HAS_SYNC;
+	if (fec_lock) /* false positives? */
+		*status |= FE_HAS_VITERBI;
+
+	if ((locked) && (cr_lock) && (sync_lock))
+		*status |= FE_HAS_LOCK;
+fail:
+	return ret;
+}
+
+static int mxl111sf_demod_read_signal_strength(struct dvb_frontend *fe,
+					       u16 *signal_strength)
+{
+	struct mxl111sf_demod_state *state = fe->demodulator_priv;
+	fe_modulation_t constellation;
+	u16 snr;
+
+	mxl111sf_demod_calc_snr(state, &snr);
+	mxl1x1sf_demod_get_tps_constellation(state, &constellation);
+
+	switch (constellation) {
+	case QPSK:
+		*signal_strength = (snr >= 1300) ?
+			min(65535, snr * 44) : snr * 38;
+		break;
+	case QAM_16:
+		*signal_strength = (snr >= 1500) ?
+			min(65535, snr * 38) : snr * 33;
+		break;
+	case QAM_64:
+		*signal_strength = (snr >= 2000) ?
+			min(65535, snr * 29) : snr * 25;
+		break;
+	default:
+		*signal_strength = 0;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mxl111sf_demod_get_frontend(struct dvb_frontend *fe,
+				       struct dvb_frontend_parameters *p)
+{
+	struct mxl111sf_demod_state *state = fe->demodulator_priv;
+
+	mxl_dbg("()");
+#if 0
+	p->inversion = /* FIXME */ ? INVERSION_ON : INVERSION_OFF;
+#endif
+	if (fe->ops.tuner_ops.get_bandwidth)
+		fe->ops.tuner_ops.get_bandwidth(fe, &p->u.ofdm.bandwidth);
+	if (fe->ops.tuner_ops.get_frequency)
+		fe->ops.tuner_ops.get_frequency(fe, &p->frequency);
+	mxl1x1sf_demod_get_tps_code_rate(state, &p->u.ofdm.code_rate_HP);
+	mxl1x1sf_demod_get_tps_code_rate(state, &p->u.ofdm.code_rate_LP);
+	mxl1x1sf_demod_get_tps_constellation(state, &p->u.ofdm.constellation);
+	mxl1x1sf_demod_get_tps_guard_fft_mode(state,
+					      &p->u.ofdm.transmission_mode);
+	mxl1x1sf_demod_get_tps_guard_interval(state,
+					      &p->u.ofdm.guard_interval);
+	mxl1x1sf_demod_get_tps_hierarchy(state,
+					 &p->u.ofdm.hierarchy_information);
+
+	return 0;
+}
+
+static
+int mxl111sf_demod_get_tune_settings(struct dvb_frontend *fe,
+				     struct dvb_frontend_tune_settings *tune)
+{
+	tune->min_delay_ms = 1000;
+	return 0;
+}
+
+static void mxl111sf_demod_release(struct dvb_frontend *fe)
+{
+	struct mxl111sf_demod_state *state = fe->demodulator_priv;
+	mxl_dbg("()");
+	kfree(state);
+	fe->demodulator_priv = NULL;
+}
+
+static struct dvb_frontend_ops mxl111sf_demod_ops = {
+
+	.info = {
+		.name               = "MaxLinear MxL111SF DVB-T demodulator",
+		.type               = FE_OFDM,
+		.frequency_min      = 177000000,
+		.frequency_max      = 858000000,
+		.frequency_stepsize = 166666,
+		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+			FE_CAN_QAM_AUTO |
+			FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+			FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
+	},
+	.release              = mxl111sf_demod_release,
+#if 0
+	.init                 = mxl111sf_init,
+	.i2c_gate_ctrl        = mxl111sf_i2c_gate_ctrl,
+#endif
+	.set_frontend         = mxl111sf_demod_set_frontend,
+	.get_frontend         = mxl111sf_demod_get_frontend,
+	.get_tune_settings    = mxl111sf_demod_get_tune_settings,
+	.read_status          = mxl111sf_demod_read_status,
+	.read_signal_strength = mxl111sf_demod_read_signal_strength,
+	.read_ber             = mxl111sf_demod_read_ber,
+	.read_snr             = mxl111sf_demod_read_snr,
+	.read_ucblocks        = mxl111sf_demod_read_ucblocks,
+};
+
+struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
+					   struct mxl111sf_demod_config *cfg)
+{
+	struct mxl111sf_demod_state *state = NULL;
+
+	mxl_dbg("()");
+
+	state = kzalloc(sizeof(struct mxl111sf_demod_state), GFP_KERNEL);
+	if (state == NULL)
+		return NULL;
+
+	state->mxl_state = mxl_state;
+	state->cfg = cfg;
+
+	memcpy(&state->fe.ops, &mxl111sf_demod_ops,
+	       sizeof(struct dvb_frontend_ops));
+
+	state->fe.demodulator_priv = state;
+	return &state->fe;
+}
+EXPORT_SYMBOL_GPL(mxl111sf_demod_attach);
+
+MODULE_DESCRIPTION("MaxLinear MxL111SF DVB-T demodulator driver");
+MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-demod.h b/drivers/media/dvb/dvb-usb/mxl111sf-demod.h
new file mode 100644
index 000000000000..432706ae5274
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-demod.h
@@ -0,0 +1,55 @@
+/*
+ *  mxl111sf-demod.h - driver for the MaxLinear MXL111SF DVB-T demodulator
+ *
+ *  Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MXL111SF_DEMOD_H__
+#define __MXL111SF_DEMOD_H__
+
+#include "dvb_frontend.h"
+#include "mxl111sf.h"
+
+struct mxl111sf_demod_config {
+	int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data);
+	int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data);
+	int (*program_regs)(struct mxl111sf_state *state,
+			    struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
+};
+
+#if defined(CONFIG_DVB_USB_MXL111SF) || \
+	(defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE))
+extern
+struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
+					   struct mxl111sf_demod_config *cfg);
+#else
+static inline
+struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
+					   struct mxl111sf_demod_config *cfg)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_USB_MXL111SF */
+
+#endif /* __MXL111SF_DEMOD_H__ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c
index 546ba5915a5b..b5c98da5d9e2 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf.c
+++ b/drivers/media/dvb/dvb-usb/mxl111sf.c
@@ -17,6 +17,7 @@
 #include "mxl111sf-i2c.h"
 #include "mxl111sf-gpio.h"
 
+#include "mxl111sf-demod.h"
 #include "mxl111sf-tuner.h"
 
 #include "lgdt3305.h"
@@ -362,6 +363,22 @@ static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
 	return ret;
 }
 
+static int mxl111sf_ep4_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+	struct dvb_usb_device *d = adap->dev;
+	struct mxl111sf_state *state = d->priv;
+	int ret = 0;
+
+	deb_info("%s(%d)\n", __func__, onoff);
+
+	if (onoff) {
+		ret = mxl111sf_enable_usb_output(state);
+		mxl_fail(ret);
+	}
+
+	return ret;
+}
+
 /* ------------------------------------------------------------------------ */
 
 static struct lgdt3305_config hauppauge_lgdt3305_config = {
@@ -438,6 +455,70 @@ fail:
 	return ret;
 }
 
+static struct mxl111sf_demod_config mxl_demod_config = {
+	.read_reg        = mxl111sf_read_reg,
+	.write_reg       = mxl111sf_write_reg,
+	.program_regs    = mxl111sf_ctrl_program_regs,
+};
+
+static int mxl111sf_attach_demod(struct dvb_usb_adapter *adap)
+{
+	struct dvb_usb_device *d = adap->dev;
+	struct mxl111sf_state *state = d->priv;
+	int fe_id = adap->num_frontends_initialized;
+	struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv;
+	int ret;
+
+	deb_adv("%s()\n", __func__);
+
+	/* save a pointer to the dvb_usb_device in device state */
+	state->d = d;
+	adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 1 : 2;
+	state->alt_mode = adap_state->alt_mode;
+
+	if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
+		err("set interface failed");
+
+	state->gpio_mode = MXL111SF_GPIO_MOD_DVBT;
+	adap_state->gpio_mode = state->gpio_mode;
+	adap_state->device_mode = MXL_SOC_MODE;
+	adap_state->ep6_clockphase = 1;
+
+	ret = mxl1x1sf_soft_reset(state);
+	if (mxl_fail(ret))
+		goto fail;
+	ret = mxl111sf_init_tuner_demod(state);
+	if (mxl_fail(ret))
+		goto fail;
+
+	ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+	if (mxl_fail(ret))
+		goto fail;
+
+	ret = mxl111sf_enable_usb_output(state);
+	if (mxl_fail(ret))
+		goto fail;
+	ret = mxl1x1sf_top_master_ctrl(state, 1);
+	if (mxl_fail(ret))
+		goto fail;
+
+	/* dont care if this fails */
+	mxl111sf_init_port_expander(state);
+
+	adap->fe_adap[fe_id].fe = dvb_attach(mxl111sf_demod_attach, state,
+			      &mxl_demod_config);
+	if (adap->fe_adap[fe_id].fe) {
+		adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init;
+		adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init;
+		adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep;
+		adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep;
+		return 0;
+	}
+	ret = -EIO;
+fail:
+	return ret;
+}
+
 static inline int mxl111sf_set_ant_path(struct mxl111sf_state *state,
 					int antpath)
 {
@@ -567,7 +648,8 @@ struct i2c_algorithm mxl111sf_i2c_algo = {
 #endif
 };
 
-/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties;
+static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties;
 static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties;
 static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties;
 
@@ -580,9 +662,15 @@ static int mxl111sf_probe(struct usb_interface *intf,
 
 	if (((dvb_usb_mxl111sf_isoc) &&
 	     (0 == dvb_usb_device_init(intf,
+				       &mxl111sf_dvbt_isoc_properties,
+				       THIS_MODULE, &d, adapter_nr) ||
+	      0 == dvb_usb_device_init(intf,
 				       &mxl111sf_atsc_isoc_properties,
 				       THIS_MODULE, &d, adapter_nr))) ||
 	    0 == dvb_usb_device_init(intf,
+				     &mxl111sf_dvbt_bulk_properties,
+				     THIS_MODULE, &d, adapter_nr) ||
+	    0 == dvb_usb_device_init(intf,
 				     &mxl111sf_atsc_bulk_properties,
 				     THIS_MODULE, &d, adapter_nr) || 0) {
 
@@ -669,6 +757,36 @@ static struct usb_device_id mxl111sf_table[] = {
 MODULE_DEVICE_TABLE(usb, mxl111sf_table);
 
 
+#define MXL111SF_EP4_BULK_STREAMING_CONFIG		\
+	.streaming_ctrl = mxl111sf_ep4_streaming_ctrl,	\
+	.stream = {					\
+		.type = USB_BULK,			\
+		.count = 5,				\
+		.endpoint = 0x04,			\
+		.u = {					\
+			.bulk = {			\
+				.buffersize = 8192,	\
+			}				\
+		}					\
+	}
+
+/* FIXME: works for v6 but not v8 silicon */
+#define MXL111SF_EP4_ISOC_STREAMING_CONFIG		\
+	.streaming_ctrl = mxl111sf_ep4_streaming_ctrl,	\
+	.stream = {					\
+		.type = USB_ISOC,			\
+		.count = 5,				\
+		.endpoint = 0x04,			\
+		.u = {					\
+			.isoc = {			\
+				.framesperurb = 96,	\
+				/* FIXME: v6 SILICON: */	\
+				.framesize = 564,	\
+				.interval = 1,		\
+			}				\
+		}					\
+	}
+
 #define MXL111SF_EP6_BULK_STREAMING_CONFIG		\
 	.streaming_ctrl = mxl111sf_ep6_streaming_ctrl,	\
 	.stream = {					\
@@ -712,7 +830,7 @@ MODULE_DEVICE_TABLE(usb, mxl111sf_table);
 	.generic_bulk_ctrl_endpoint_response = MXL_EP1_REG_READ, \
 	.size_of_priv     = sizeof(struct mxl111sf_state)
 
-static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = {
+static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties = {
 	MXL111SF_DEFAULT_DEVICE_PROPERTIES,
 
 	.num_adapters = 1,
@@ -723,10 +841,106 @@ static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = {
 		.fe = {{
 			.size_of_priv     = sizeof(struct mxl111sf_adap_state),
 
+			.frontend_attach  = mxl111sf_attach_demod,
+			.tuner_attach     = mxl111sf_attach_tuner,
+
+			MXL111SF_EP4_BULK_STREAMING_CONFIG,
+		} },
+		},
+	},
+	.num_device_descs = 4,
+	.devices = {
+		{   "Hauppauge 126xxx DVBT (bulk)",
+			{ NULL },
+			{ &mxl111sf_table[4], &mxl111sf_table[8],
+			  NULL },
+		},
+		{   "Hauppauge 117xxx DVBT (bulk)",
+			{ NULL },
+			{ &mxl111sf_table[15], &mxl111sf_table[18],
+			  NULL },
+		},
+		{   "Hauppauge 138xxx DVBT (bulk)",
+			{ NULL },
+			{ &mxl111sf_table[20], &mxl111sf_table[22],
+			  &mxl111sf_table[24], &mxl111sf_table[26],
+			  NULL },
+		},
+		{   "Hauppauge 126xxx (tp-bulk)",
+			{ NULL },
+			{ &mxl111sf_table[28], &mxl111sf_table[30],
+			  NULL },
+		},
+	}
+};
+
+static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties = {
+	MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+	.num_adapters = 1,
+	.adapter = {
+		{
+		.fe_ioctl_override = mxl111sf_fe_ioctl_override,
+		.num_frontends = 1,
+		.fe = {{
+			.size_of_priv     = sizeof(struct mxl111sf_adap_state),
+
+			.frontend_attach  = mxl111sf_attach_demod,
+			.tuner_attach     = mxl111sf_attach_tuner,
+
+			MXL111SF_EP4_ISOC_STREAMING_CONFIG,
+		} },
+		},
+	},
+	.num_device_descs = 4,
+	.devices = {
+		{   "Hauppauge 126xxx DVBT (isoc)",
+			{ NULL },
+			{ &mxl111sf_table[4], &mxl111sf_table[8],
+			  NULL },
+		},
+		{   "Hauppauge 117xxx DVBT (isoc)",
+			{ NULL },
+			{ &mxl111sf_table[15], &mxl111sf_table[18],
+			  NULL },
+		},
+		{   "Hauppauge 138xxx DVBT (isoc)",
+			{ NULL },
+			{ &mxl111sf_table[20], &mxl111sf_table[22],
+			  &mxl111sf_table[24], &mxl111sf_table[26],
+			  NULL },
+		},
+		{   "Hauppauge 126xxx (tp-isoc)",
+			{ NULL },
+			{ &mxl111sf_table[28], &mxl111sf_table[30],
+			  NULL },
+		},
+	}
+};
+
+static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = {
+	MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+	.num_adapters = 1,
+	.adapter = {
+		{
+		.fe_ioctl_override = mxl111sf_fe_ioctl_override,
+		.num_frontends = 2,
+		.fe = {{
+			.size_of_priv     = sizeof(struct mxl111sf_adap_state),
+
 			.frontend_attach  = mxl111sf_lgdt3305_frontend_attach,
 			.tuner_attach     = mxl111sf_attach_tuner,
 
 			MXL111SF_EP6_BULK_STREAMING_CONFIG,
+		},
+		{
+			.size_of_priv     = sizeof(struct mxl111sf_adap_state),
+
+			.frontend_attach  = mxl111sf_attach_demod,
+			.tuner_attach     = mxl111sf_attach_tuner,
+
+			MXL111SF_EP4_BULK_STREAMING_CONFIG,
 		}},
 		},
 	},
@@ -776,7 +990,7 @@ static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = {
 	.adapter = {
 		{
 		.fe_ioctl_override = mxl111sf_fe_ioctl_override,
-		.num_frontends = 1,
+		.num_frontends = 2,
 		.fe = {{
 			.size_of_priv     = sizeof(struct mxl111sf_adap_state),
 
@@ -784,6 +998,14 @@ static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = {
 			.tuner_attach     = mxl111sf_attach_tuner,
 
 			MXL111SF_EP6_ISOC_STREAMING_CONFIG,
+		},
+		{
+			.size_of_priv     = sizeof(struct mxl111sf_adap_state),
+
+			.frontend_attach  = mxl111sf_attach_demod,
+			.tuner_attach     = mxl111sf_attach_tuner,
+
+			MXL111SF_EP4_ISOC_STREAMING_CONFIG,
 		}},
 		},
 	},
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.h b/drivers/media/dvb/dvb-usb/mxl111sf.h
index 5a2c7bb386cd..364d89f826bd 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf.h
+++ b/drivers/media/dvb/dvb-usb/mxl111sf.h
@@ -133,7 +133,7 @@ extern int dvb_usb_mxl111sf_debug;
 /* The following allows the mxl_fail() macro defined below to work
  * in externel modules, such as mxl111sf-tuner.ko, even though
  * dvb_usb_mxl111sf_debug is not defined within those modules */
-#ifdef __MXL111SF_TUNER_H__
+#if (defined(__MXL111SF_TUNER_H__)) || (defined(__MXL111SF_DEMOD_H__))
 #define MXL_ADV_DEBUG_ENABLED MXL_ADV_DBG
 #else
 #define MXL_ADV_DEBUG_ENABLED dvb_usb_mxl111sf_debug
diff --git a/drivers/media/dvb/ngene/Makefile b/drivers/media/dvb/ngene/Makefile
index 89873615e683..13ebeffb705f 100644
--- a/drivers/media/dvb/ngene/Makefile
+++ b/drivers/media/dvb/ngene/Makefile
@@ -11,4 +11,4 @@ ccflags-y += -Idrivers/media/dvb/frontends/
 ccflags-y += -Idrivers/media/common/tuners/
 
 # For the staging CI driver cxd2099
-ccflags-y += -Idrivers/staging/cxd2099/
+ccflags-y += -Idrivers/staging/media/cxd2099/
diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c
index 95ddcc4845d3..db20904d01f0 100644
--- a/drivers/media/radio/radio-tea5764.c
+++ b/drivers/media/radio/radio-tea5764.c
@@ -128,8 +128,10 @@ struct tea5764_write_regs {
 	u16 rdsbbl;				/* PAUSEDET & RDSBBL */
 } __attribute__ ((packed));
 
-#ifndef RADIO_TEA5764_XTAL
+#ifdef CONFIG_RADIO_TEA5764_XTAL
 #define RADIO_TEA5764_XTAL 1
+#else
+#define RADIO_TEA5764_XTAL 0
 #endif
 
 static int radio_nr = -1;
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index d285c8c92819..b303a3f8a9f8 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -517,6 +517,13 @@ config VIDEO_NOON010PC30
 
 source "drivers/media/video/m5mols/Kconfig"
 
+config VIDEO_S5K6AA
+	tristate "Samsung S5K6AAFX sensor support"
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	---help---
+	  This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
+	  camera sensor with an embedded SoC image signal processor.
+
 comment "Flash devices"
 
 config VIDEO_ADP1653
@@ -736,6 +743,8 @@ source "drivers/media/video/cx88/Kconfig"
 
 source "drivers/media/video/cx23885/Kconfig"
 
+source "drivers/media/video/cx25821/Kconfig"
+
 source "drivers/media/video/au0828/Kconfig"
 
 source "drivers/media/video/ivtv/Kconfig"
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 11fff97e7196..117f9c4b4cb9 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
 obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
 obj-$(CONFIG_VIDEO_NOON010PC30)	+= noon010pc30.o
 obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
+obj-$(CONFIG_VIDEO_S5K6AA)	+= s5k6aa.o
 obj-$(CONFIG_VIDEO_ADP1653)	+= adp1653.o
 
 obj-$(CONFIG_SOC_CAMERA_IMX074)		+= imx074.o
@@ -104,6 +105,7 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/
 obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
 obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/
 obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
+obj-$(CONFIG_VIDEO_CX25821) += cx25821/
 obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
 obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
 obj-$(CONFIG_VIDEO_CPIA2) += cpia2/
diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c
index 774715d2f84f..8c775c59e120 100644
--- a/drivers/media/video/atmel-isi.c
+++ b/drivers/media/video/atmel-isi.c
@@ -94,6 +94,7 @@ struct atmel_isi {
 	unsigned int			irq;
 
 	struct isi_platform_data	*pdata;
+	u16				width_flags;	/* max 12 bits */
 
 	struct list_head		video_buffer_list;
 	struct frame_buffer		*active;
@@ -248,9 +249,9 @@ static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset)
 /* ------------------------------------------------------------------
 	Videobuf operations
    ------------------------------------------------------------------*/
-static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
-				unsigned int *nplanes, unsigned int sizes[],
-				void *alloc_ctxs[])
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+				unsigned int *nbuffers, unsigned int *nplanes,
+				unsigned int sizes[], void *alloc_ctxs[])
 {
 	struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -647,50 +648,42 @@ static bool isi_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
 		 fmt->packing == SOC_MBUS_PACKING_EXTEND16);
 }
 
-static unsigned long make_bus_param(struct atmel_isi *isi)
-{
-	unsigned long flags;
-	/*
-	 * Platform specified synchronization and pixel clock polarities are
-	 * only a recommendation and are only used during probing. Atmel ISI
-	 * camera interface only works in master mode, i.e., uses HSYNC and
-	 * VSYNC signals from the sensor
-	 */
-	flags = SOCAM_MASTER |
-		SOCAM_HSYNC_ACTIVE_HIGH |
-		SOCAM_HSYNC_ACTIVE_LOW |
-		SOCAM_VSYNC_ACTIVE_HIGH |
-		SOCAM_VSYNC_ACTIVE_LOW |
-		SOCAM_PCLK_SAMPLE_RISING |
-		SOCAM_PCLK_SAMPLE_FALLING |
-		SOCAM_DATA_ACTIVE_HIGH;
-
-	if (isi->pdata->data_width_flags & ISI_DATAWIDTH_10)
-		flags |= SOCAM_DATAWIDTH_10;
-
-	if (isi->pdata->data_width_flags & ISI_DATAWIDTH_8)
-		flags |= SOCAM_DATAWIDTH_8;
-
-	if (flags & SOCAM_DATAWIDTH_MASK)
-		return flags;
-
-	return 0;
-}
+#define ISI_BUS_PARAM (V4L2_MBUS_MASTER |	\
+		V4L2_MBUS_HSYNC_ACTIVE_HIGH |	\
+		V4L2_MBUS_HSYNC_ACTIVE_LOW |	\
+		V4L2_MBUS_VSYNC_ACTIVE_HIGH |	\
+		V4L2_MBUS_VSYNC_ACTIVE_LOW |	\
+		V4L2_MBUS_PCLK_SAMPLE_RISING |	\
+		V4L2_MBUS_PCLK_SAMPLE_FALLING |	\
+		V4L2_MBUS_DATA_ACTIVE_HIGH)
 
 static int isi_camera_try_bus_param(struct soc_camera_device *icd,
 				    unsigned char buswidth)
 {
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct atmel_isi *isi = ici->priv;
-	unsigned long camera_flags;
+	struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+	unsigned long common_flags;
 	int ret;
 
-	camera_flags = icd->ops->query_bus_param(icd);
-	ret = soc_camera_bus_param_compatible(camera_flags,
-					make_bus_param(isi));
-	if (!ret)
-		return -EINVAL;
-	return 0;
+	ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+	if (!ret) {
+		common_flags = soc_mbus_config_compatible(&cfg,
+							  ISI_BUS_PARAM);
+		if (!common_flags) {
+			dev_warn(icd->parent,
+				 "Flags incompatible: camera 0x%x, host 0x%x\n",
+				 cfg.flags, ISI_BUS_PARAM);
+			return -EINVAL;
+		}
+	} else if (ret != -ENOIOCTLCMD) {
+		return ret;
+	}
+
+	if ((1 << (buswidth - 1)) & isi->width_flags)
+		return 0;
+	return -EINVAL;
 }
 
 
@@ -812,59 +805,71 @@ static int isi_camera_querycap(struct soc_camera_host *ici,
 
 static int isi_camera_set_bus_param(struct soc_camera_device *icd, u32 pixfmt)
 {
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct atmel_isi *isi = ici->priv;
-	unsigned long bus_flags, camera_flags, common_flags;
+	struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+	unsigned long common_flags;
 	int ret;
 	u32 cfg1 = 0;
 
-	camera_flags = icd->ops->query_bus_param(icd);
-
-	bus_flags = make_bus_param(isi);
-	common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
-	dev_dbg(icd->parent, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n",
-		camera_flags, bus_flags, common_flags);
-	if (!common_flags)
-		return -EINVAL;
+	ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+	if (!ret) {
+		common_flags = soc_mbus_config_compatible(&cfg,
+							  ISI_BUS_PARAM);
+		if (!common_flags) {
+			dev_warn(icd->parent,
+				 "Flags incompatible: camera 0x%x, host 0x%x\n",
+				 cfg.flags, ISI_BUS_PARAM);
+			return -EINVAL;
+		}
+	} else if (ret != -ENOIOCTLCMD) {
+		return ret;
+	} else {
+		common_flags = ISI_BUS_PARAM;
+	}
+	dev_dbg(icd->parent, "Flags cam: 0x%x host: 0x%x common: 0x%lx\n",
+		cfg.flags, ISI_BUS_PARAM, common_flags);
 
 	/* Make choises, based on platform preferences */
-	if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
-	    (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+	if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+	    (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
 		if (isi->pdata->hsync_act_low)
-			common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+			common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
 		else
-			common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+			common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
 	}
 
-	if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
-	    (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+	if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+	    (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
 		if (isi->pdata->vsync_act_low)
-			common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+			common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
 		else
-			common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+			common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
 	}
 
-	if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
-	    (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+	if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+	    (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
 		if (isi->pdata->pclk_act_falling)
-			common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+			common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
 		else
-			common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+			common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
 	}
 
-	ret = icd->ops->set_bus_param(icd, common_flags);
-	if (ret < 0) {
-		dev_dbg(icd->parent, "Camera set_bus_param(%lx) returned %d\n",
+	cfg.flags = common_flags;
+	ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+	if (ret < 0 && ret != -ENOIOCTLCMD) {
+		dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
 			common_flags, ret);
 		return ret;
 	}
 
 	/* set bus param for ISI */
-	if (common_flags & SOCAM_HSYNC_ACTIVE_LOW)
+	if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
 		cfg1 |= ISI_CFG1_HSYNC_POL_ACTIVE_LOW;
-	if (common_flags & SOCAM_VSYNC_ACTIVE_LOW)
+	if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
 		cfg1 |= ISI_CFG1_VSYNC_POL_ACTIVE_LOW;
-	if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
+	if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
 		cfg1 |= ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING;
 
 	if (isi->pdata->has_emb_sync)
@@ -983,6 +988,11 @@ static int __devinit atmel_isi_probe(struct platform_device *pdev)
 		goto err_ioremap;
 	}
 
+	if (pdata->data_width_flags & ISI_DATAWIDTH_8)
+		isi->width_flags = 1 << 7;
+	if (pdata->data_width_flags & ISI_DATAWIDTH_10)
+		isi->width_flags |= 1 << 9;
+
 	isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
 
 	irq = platform_get_irq(pdev, 0);
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index 9e2f870f4258..c6ff32a6137c 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -1085,6 +1085,8 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
 		setup.addr = ADDR_UNSET;
 		setup.type = cx->options.tuner;
 		setup.mode_mask = T_ANALOG_TV;  /* matches TV tuners */
+		if (cx->options.radio > 0)
+			setup.mode_mask |= T_RADIO;
 		setup.tuner_callback = (setup.type == TUNER_XC2028) ?
 			cx18_reset_tuner_gpio : NULL;
 		cx18_call_all(cx, tuner, s_type_addr, &setup);
diff --git a/drivers/staging/cx25821/Kconfig b/drivers/media/video/cx25821/Kconfig
index 5f6b54213713..5f6b54213713 100644
--- a/drivers/staging/cx25821/Kconfig
+++ b/drivers/media/video/cx25821/Kconfig
diff --git a/drivers/staging/cx25821/Makefile b/drivers/media/video/cx25821/Makefile
index aedde18c68f9..aedde18c68f9 100644
--- a/drivers/staging/cx25821/Makefile
+++ b/drivers/media/video/cx25821/Makefile
diff --git a/drivers/staging/cx25821/cx25821-alsa.c b/drivers/media/video/cx25821/cx25821-alsa.c
index 09e99de5fd21..09e99de5fd21 100644
--- a/drivers/staging/cx25821/cx25821-alsa.c
+++ b/drivers/media/video/cx25821/cx25821-alsa.c
diff --git a/drivers/staging/cx25821/cx25821-audio-upstream.c b/drivers/media/video/cx25821/cx25821-audio-upstream.c
index c20d6dece154..c20d6dece154 100644
--- a/drivers/staging/cx25821/cx25821-audio-upstream.c
+++ b/drivers/media/video/cx25821/cx25821-audio-upstream.c
diff --git a/drivers/staging/cx25821/cx25821-audio-upstream.h b/drivers/media/video/cx25821/cx25821-audio-upstream.h
index af2ae7c5815a..af2ae7c5815a 100644
--- a/drivers/staging/cx25821/cx25821-audio-upstream.h
+++ b/drivers/media/video/cx25821/cx25821-audio-upstream.h
diff --git a/drivers/staging/cx25821/cx25821-audio.h b/drivers/media/video/cx25821/cx25821-audio.h
index 8eb55b7b88cb..8eb55b7b88cb 100644
--- a/drivers/staging/cx25821/cx25821-audio.h
+++ b/drivers/media/video/cx25821/cx25821-audio.h
diff --git a/drivers/staging/cx25821/cx25821-biffuncs.h b/drivers/media/video/cx25821/cx25821-biffuncs.h
index 9326a7c729ec..9326a7c729ec 100644
--- a/drivers/staging/cx25821/cx25821-biffuncs.h
+++ b/drivers/media/video/cx25821/cx25821-biffuncs.h
diff --git a/drivers/staging/cx25821/cx25821-cards.c b/drivers/media/video/cx25821/cx25821-cards.c
index 6ace60313b49..6ace60313b49 100644
--- a/drivers/staging/cx25821/cx25821-cards.c
+++ b/drivers/media/video/cx25821/cx25821-cards.c
diff --git a/drivers/staging/cx25821/cx25821-core.c b/drivers/media/video/cx25821/cx25821-core.c
index a7fa38f9594e..a7fa38f9594e 100644
--- a/drivers/staging/cx25821/cx25821-core.c
+++ b/drivers/media/video/cx25821/cx25821-core.c
diff --git a/drivers/staging/cx25821/cx25821-gpio.c b/drivers/media/video/cx25821/cx25821-gpio.c
index 29e43b03c85e..29e43b03c85e 100644
--- a/drivers/staging/cx25821/cx25821-gpio.c
+++ b/drivers/media/video/cx25821/cx25821-gpio.c
diff --git a/drivers/staging/cx25821/cx25821-i2c.c b/drivers/media/video/cx25821/cx25821-i2c.c
index 4d3d0ce40785..4d3d0ce40785 100644
--- a/drivers/staging/cx25821/cx25821-i2c.c
+++ b/drivers/media/video/cx25821/cx25821-i2c.c
diff --git a/drivers/staging/cx25821/cx25821-medusa-defines.h b/drivers/media/video/cx25821/cx25821-medusa-defines.h
index 60d197f57556..60d197f57556 100644
--- a/drivers/staging/cx25821/cx25821-medusa-defines.h
+++ b/drivers/media/video/cx25821/cx25821-medusa-defines.h
diff --git a/drivers/staging/cx25821/cx25821-medusa-reg.h b/drivers/media/video/cx25821/cx25821-medusa-reg.h
index 1c1c228352d1..1c1c228352d1 100644
--- a/drivers/staging/cx25821/cx25821-medusa-reg.h
+++ b/drivers/media/video/cx25821/cx25821-medusa-reg.h
diff --git a/drivers/staging/cx25821/cx25821-medusa-video.c b/drivers/media/video/cx25821/cx25821-medusa-video.c
index fc780d0908dc..fc780d0908dc 100644
--- a/drivers/staging/cx25821/cx25821-medusa-video.c
+++ b/drivers/media/video/cx25821/cx25821-medusa-video.c
diff --git a/drivers/staging/cx25821/cx25821-medusa-video.h b/drivers/media/video/cx25821/cx25821-medusa-video.h
index 6175e0961855..6175e0961855 100644
--- a/drivers/staging/cx25821/cx25821-medusa-video.h
+++ b/drivers/media/video/cx25821/cx25821-medusa-video.h
diff --git a/drivers/staging/cx25821/cx25821-reg.h b/drivers/media/video/cx25821/cx25821-reg.h
index a3fc25a4dc0b..a3fc25a4dc0b 100644
--- a/drivers/staging/cx25821/cx25821-reg.h
+++ b/drivers/media/video/cx25821/cx25821-reg.h
diff --git a/drivers/staging/cx25821/cx25821-sram.h b/drivers/media/video/cx25821/cx25821-sram.h
index 5f05d153bc4d..5f05d153bc4d 100644
--- a/drivers/staging/cx25821/cx25821-sram.h
+++ b/drivers/media/video/cx25821/cx25821-sram.h
diff --git a/drivers/staging/cx25821/cx25821-video-upstream-ch2.c b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c
index 2a724ddfa53f..2a724ddfa53f 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream-ch2.c
+++ b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c
diff --git a/drivers/staging/cx25821/cx25821-video-upstream-ch2.h b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.h
index d42dab59b663..d42dab59b663 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream-ch2.h
+++ b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.h
diff --git a/drivers/staging/cx25821/cx25821-video-upstream.c b/drivers/media/video/cx25821/cx25821-video-upstream.c
index c0b80068f468..c0b80068f468 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream.c
+++ b/drivers/media/video/cx25821/cx25821-video-upstream.c
diff --git a/drivers/staging/cx25821/cx25821-video-upstream.h b/drivers/media/video/cx25821/cx25821-video-upstream.h
index 268ec8aa6a61..268ec8aa6a61 100644
--- a/drivers/staging/cx25821/cx25821-video-upstream.h
+++ b/drivers/media/video/cx25821/cx25821-video-upstream.h
diff --git a/drivers/staging/cx25821/cx25821-video.c b/drivers/media/video/cx25821/cx25821-video.c
index 084fc0899e13..4d6907cda75b 100644
--- a/drivers/staging/cx25821/cx25821-video.c
+++ b/drivers/media/video/cx25821/cx25821-video.c
@@ -1312,7 +1312,7 @@ int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i)
 			return err;
 	}
 
-	if (i > 2) {
+	if (i >= CX25821_NR_INPUT) {
 		dprintk(1, "%s(): -EINVAL\n", __func__);
 		return -EINVAL;
 	}
diff --git a/drivers/staging/cx25821/cx25821-video.h b/drivers/media/video/cx25821/cx25821-video.h
index d0d9538ca5b3..d0d9538ca5b3 100644
--- a/drivers/staging/cx25821/cx25821-video.h
+++ b/drivers/media/video/cx25821/cx25821-video.h
diff --git a/drivers/staging/cx25821/cx25821.h b/drivers/media/video/cx25821/cx25821.h
index db2615b2bac3..2d2d00932823 100644
--- a/drivers/staging/cx25821/cx25821.h
+++ b/drivers/media/video/cx25821/cx25821.h
@@ -98,6 +98,7 @@
 #define CX25821_BOARD_CONEXANT_ATHENA10 1
 #define MAX_VID_CHANNEL_NUM     12
 #define VID_CHANNEL_NUM 8
+#define CX25821_NR_INPUT 2
 
 struct cx25821_fmt {
 	char *name;
@@ -196,7 +197,7 @@ struct cx25821_board {
 	unsigned char radio_addr;
 
 	u32 clk_freq;
-	struct cx25821_input input[2];
+	struct cx25821_input input[CX25821_NR_INPUT];
 };
 
 struct cx25821_subid {
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 4240f0b720fa..9b747c266afa 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -1923,6 +1923,8 @@ struct usb_device_id em28xx_id_table[] = {
 			.driver_info = EM2860_BOARD_TERRATEC_AV350 },
 	{ USB_DEVICE(0x0ccd, 0x0096),
 			.driver_info = EM2860_BOARD_TERRATEC_GRABBY },
+	{ USB_DEVICE(0x0ccd, 0x10AF),
+			.driver_info = EM2860_BOARD_TERRATEC_GRABBY },
 	{ USB_DEVICE(0x0fd9, 0x0033),
 			.driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE},
 	{ USB_DEVICE(0x185b, 0x2870),
diff --git a/drivers/media/video/imx074.c b/drivers/media/video/imx074.c
index 0382ea752e6f..8775e262bb6e 100644
--- a/drivers/media/video/imx074.c
+++ b/drivers/media/video/imx074.c
@@ -12,11 +12,11 @@
 
 #include <linux/delay.h>
 #include <linux/i2c.h>
+#include <linux/v4l2-mediabus.h>
 #include <linux/slab.h>
 #include <linux/videodev2.h>
 
 #include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
 
@@ -267,6 +267,17 @@ static int imx074_g_chip_ident(struct v4l2_subdev *sd,
 	return 0;
 }
 
+static int imx074_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	cfg->type = V4L2_MBUS_CSI2;
+	cfg->flags = V4L2_MBUS_CSI2_2_LANE |
+		V4L2_MBUS_CSI2_CHANNEL_0 |
+		V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+	return 0;
+}
+
 static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
 	.s_stream	= imx074_s_stream,
 	.s_mbus_fmt	= imx074_s_fmt,
@@ -275,6 +286,7 @@ static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
 	.enum_mbus_fmt	= imx074_enum_fmt,
 	.g_crop		= imx074_g_crop,
 	.cropcap	= imx074_cropcap,
+	.g_mbus_config	= imx074_g_mbus_config,
 };
 
 static struct v4l2_subdev_core_ops imx074_subdev_core_ops = {
@@ -286,28 +298,7 @@ static struct v4l2_subdev_ops imx074_subdev_ops = {
 	.video	= &imx074_subdev_video_ops,
 };
 
-/*
- * We have to provide soc-camera operations, but we don't have anything to say
- * there. The MIPI CSI2 driver will provide .query_bus_param and .set_bus_param
- */
-static unsigned long imx074_query_bus_param(struct soc_camera_device *icd)
-{
-	return 0;
-}
-
-static int imx074_set_bus_param(struct soc_camera_device *icd,
-				 unsigned long flags)
-{
-	return -EINVAL;
-}
-
-static struct soc_camera_ops imx074_ops = {
-	.query_bus_param	= imx074_query_bus_param,
-	.set_bus_param		= imx074_set_bus_param,
-};
-
-static int imx074_video_probe(struct soc_camera_device *icd,
-			      struct i2c_client *client)
+static int imx074_video_probe(struct i2c_client *client)
 {
 	int ret;
 	u16 id;
@@ -417,17 +408,10 @@ static int imx074_probe(struct i2c_client *client,
 			const struct i2c_device_id *did)
 {
 	struct imx074 *priv;
-	struct soc_camera_device *icd = client->dev.platform_data;
 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
-	struct soc_camera_link *icl;
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 	int ret;
 
-	if (!icd) {
-		dev_err(&client->dev, "IMX074: missing soc-camera data!\n");
-		return -EINVAL;
-	}
-
-	icl = to_soc_camera_link(icd);
 	if (!icl) {
 		dev_err(&client->dev, "IMX074: missing platform data!\n");
 		return -EINVAL;
@@ -445,12 +429,10 @@ static int imx074_probe(struct i2c_client *client,
 
 	v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops);
 
-	icd->ops	= &imx074_ops;
 	priv->fmt	= &imx074_colour_fmts[0];
 
-	ret = imx074_video_probe(icd, client);
+	ret = imx074_video_probe(client);
 	if (ret < 0) {
-		icd->ops = NULL;
 		kfree(priv);
 		return ret;
 	}
@@ -461,10 +443,8 @@ static int imx074_probe(struct i2c_client *client,
 static int imx074_remove(struct i2c_client *client)
 {
 	struct imx074 *priv = to_imx074(client);
-	struct soc_camera_device *icd = client->dev.platform_data;
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 
-	icd->ops = NULL;
 	if (icl->free_bus)
 		icl->free_bus(icl);
 	kfree(priv);
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 0fb75524484d..41108a9a195e 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -1180,6 +1180,8 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
 		setup.addr = ADDR_UNSET;
 		setup.type = itv->options.tuner;
 		setup.mode_mask = T_ANALOG_TV;  /* matches TV tuners */
+		if (itv->options.radio > 0)
+			setup.mode_mask |= T_RADIO;
 		setup.tuner_callback = (setup.type == TUNER_XC2028) ?
 			ivtv_reset_tuner_gpio : NULL;
 		ivtv_call_all(itv, tuner, s_type_addr, &setup);
diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/video/marvell-ccic/mcam-core.c
index 1141b976dff4..80ec64d2d6d8 100644
--- a/drivers/media/video/marvell-ccic/mcam-core.c
+++ b/drivers/media/video/marvell-ccic/mcam-core.c
@@ -883,7 +883,8 @@ static int mcam_read_setup(struct mcam_camera *cam)
  * Videobuf2 interface code.
  */
 
-static int mcam_vb_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
+static int mcam_vb_queue_setup(struct vb2_queue *vq,
+		const struct v4l2_format *fmt, unsigned int *nbufs,
 		unsigned int *num_planes, unsigned int sizes[],
 		void *alloc_ctxs[])
 {
diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c
index 9594b52f8605..12897e8a3314 100644
--- a/drivers/media/video/mem2mem_testdev.c
+++ b/drivers/media/video/mem2mem_testdev.c
@@ -738,9 +738,10 @@ static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
  * Queue operations
  */
 
-static int m2mtest_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
-				unsigned int *nplanes, unsigned int sizes[],
-				void *alloc_ctxs[])
+static int m2mtest_queue_setup(struct vb2_queue *vq,
+				const struct v4l2_format *fmt,
+				unsigned int *nbuffers, unsigned int *nplanes,
+				unsigned int sizes[], void *alloc_ctxs[])
 {
 	struct m2mtest_ctx *ctx = vb2_get_drv_priv(vq);
 	struct m2mtest_q_data *q_data;
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
index 4da9cca939c1..63ae5c61c9bf 100644
--- a/drivers/media/video/mt9m001.c
+++ b/drivers/media/video/mt9m001.c
@@ -13,9 +13,11 @@
 #include <linux/i2c.h>
 #include <linux/log2.h>
 
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/soc_camera.h>
+#include <media/v4l2-ctrls.h>
 
 /*
  * mt9m001 i2c address 0x5d
@@ -84,15 +86,19 @@ static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = {
 
 struct mt9m001 {
 	struct v4l2_subdev subdev;
+	struct v4l2_ctrl_handler hdl;
+	struct {
+		/* exposure/auto-exposure cluster */
+		struct v4l2_ctrl *autoexposure;
+		struct v4l2_ctrl *exposure;
+	};
 	struct v4l2_rect rect;	/* Sensor window */
 	const struct mt9m001_datafmt *fmt;
 	const struct mt9m001_datafmt *fmts;
 	int num_fmts;
 	int model;	/* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
-	unsigned int gain;
-	unsigned int exposure;
+	unsigned int total_h;
 	unsigned short y_skip_top;	/* Lines to skip at the top */
-	unsigned char autoexposure;
 };
 
 static struct mt9m001 *to_mt9m001(const struct i2c_client *client)
@@ -165,54 +171,13 @@ static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable)
 	return 0;
 }
 
-static int mt9m001_set_bus_param(struct soc_camera_device *icd,
-				 unsigned long flags)
-{
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-	unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK;
-
-	/* Only one width bit may be set */
-	if (!is_power_of_2(width_flag))
-		return -EINVAL;
-
-	if (icl->set_bus_param)
-		return icl->set_bus_param(icl, width_flag);
-
-	/*
-	 * Without board specific bus width settings we only support the
-	 * sensors native bus width
-	 */
-	if (width_flag == SOCAM_DATAWIDTH_10)
-		return 0;
-
-	return -EINVAL;
-}
-
-static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd)
-{
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-	/* MT9M001 has all capture_format parameters fixed */
-	unsigned long flags = SOCAM_PCLK_SAMPLE_FALLING |
-		SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
-		SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER;
-
-	if (icl->query_bus_param)
-		flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
-	else
-		flags |= SOCAM_DATAWIDTH_10;
-
-	return soc_camera_apply_sensor_flags(icl, flags);
-}
-
 static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
 	struct v4l2_rect rect = a->c;
-	struct soc_camera_device *icd = client->dev.platform_data;
 	int ret;
 	const u16 hblank = 9, vblank = 25;
-	unsigned int total_h;
 
 	if (mt9m001->fmts == mt9m001_colour_fmts)
 		/*
@@ -231,7 +196,7 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 	soc_camera_limit_side(&rect.top, &rect.height,
 		     MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT);
 
-	total_h = rect.height + mt9m001->y_skip_top + vblank;
+	mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank;
 
 	/* Blanking and start values - default... */
 	ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank);
@@ -240,7 +205,7 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 
 	/*
 	 * The caller provides a supported format, as verified per
-	 * call to icd->try_fmt()
+	 * call to .try_mbus_fmt()
 	 */
 	if (!ret)
 		ret = reg_write(client, MT9M001_COLUMN_START, rect.left);
@@ -251,17 +216,8 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 	if (!ret)
 		ret = reg_write(client, MT9M001_WINDOW_HEIGHT,
 				rect.height + mt9m001->y_skip_top - 1);
-	if (!ret && mt9m001->autoexposure) {
-		ret = reg_write(client, MT9M001_SHUTTER_WIDTH, total_h);
-		if (!ret) {
-			const struct v4l2_queryctrl *qctrl =
-				soc_camera_find_qctrl(icd->ops,
-						      V4L2_CID_EXPOSURE);
-			mt9m001->exposure = (524 + (total_h - 1) *
-				 (qctrl->maximum - qctrl->minimum)) /
-				1048 + qctrl->minimum;
-		}
-	}
+	if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO)
+		ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h);
 
 	if (!ret)
 		mt9m001->rect = rect;
@@ -421,107 +377,48 @@ static int mt9m001_s_register(struct v4l2_subdev *sd,
 }
 #endif
 
-static const struct v4l2_queryctrl mt9m001_controls[] = {
-	{
-		.id		= V4L2_CID_VFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Vertically",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	}, {
-		.id		= V4L2_CID_GAIN,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Gain",
-		.minimum	= 0,
-		.maximum	= 127,
-		.step		= 1,
-		.default_value	= 64,
-		.flags		= V4L2_CTRL_FLAG_SLIDER,
-	}, {
-		.id		= V4L2_CID_EXPOSURE,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Exposure",
-		.minimum	= 1,
-		.maximum	= 255,
-		.step		= 1,
-		.default_value	= 255,
-		.flags		= V4L2_CTRL_FLAG_SLIDER,
-	}, {
-		.id		= V4L2_CID_EXPOSURE_AUTO,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Automatic Exposure",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 1,
-	}
-};
-
-static struct soc_camera_ops mt9m001_ops = {
-	.set_bus_param		= mt9m001_set_bus_param,
-	.query_bus_param	= mt9m001_query_bus_param,
-	.controls		= mt9m001_controls,
-	.num_controls		= ARRAY_SIZE(mt9m001_controls),
-};
-
-static int mt9m001_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct mt9m001 *mt9m001 = to_mt9m001(client);
-	int data;
+	struct mt9m001 *mt9m001 = container_of(ctrl->handler,
+					       struct mt9m001, hdl);
+	s32 min, max;
 
 	switch (ctrl->id) {
-	case V4L2_CID_VFLIP:
-		data = reg_read(client, MT9M001_READ_OPTIONS2);
-		if (data < 0)
-			return -EIO;
-		ctrl->value = !!(data & 0x8000);
-		break;
 	case V4L2_CID_EXPOSURE_AUTO:
-		ctrl->value = mt9m001->autoexposure;
-		break;
-	case V4L2_CID_GAIN:
-		ctrl->value = mt9m001->gain;
-		break;
-	case V4L2_CID_EXPOSURE:
-		ctrl->value = mt9m001->exposure;
+		min = mt9m001->exposure->minimum;
+		max = mt9m001->exposure->maximum;
+		mt9m001->exposure->val =
+			(524 + (mt9m001->total_h - 1) * (max - min)) / 1048 + min;
 		break;
 	}
 	return 0;
 }
 
-static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+	struct mt9m001 *mt9m001 = container_of(ctrl->handler,
+					       struct mt9m001, hdl);
+	struct v4l2_subdev *sd = &mt9m001->subdev;
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct mt9m001 *mt9m001 = to_mt9m001(client);
-	struct soc_camera_device *icd = client->dev.platform_data;
-	const struct v4l2_queryctrl *qctrl;
+	struct v4l2_ctrl *exp = mt9m001->exposure;
 	int data;
 
-	qctrl = soc_camera_find_qctrl(&mt9m001_ops, ctrl->id);
-
-	if (!qctrl)
-		return -EINVAL;
-
 	switch (ctrl->id) {
 	case V4L2_CID_VFLIP:
-		if (ctrl->value)
+		if (ctrl->val)
 			data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000);
 		else
 			data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000);
 		if (data < 0)
 			return -EIO;
-		break;
+		return 0;
+
 	case V4L2_CID_GAIN:
-		if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
-			return -EINVAL;
 		/* See Datasheet Table 7, Gain settings. */
-		if (ctrl->value <= qctrl->default_value) {
+		if (ctrl->val <= ctrl->default_value) {
 			/* Pack it into 0..1 step 0.125, register values 0..8 */
-			unsigned long range = qctrl->default_value - qctrl->minimum;
-			data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;
+			unsigned long range = ctrl->default_value - ctrl->minimum;
+			data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
 
 			dev_dbg(&client->dev, "Setting gain %d\n", data);
 			data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
@@ -530,8 +427,8 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 		} else {
 			/* Pack it into 1.125..15 variable step, register values 9..67 */
 			/* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
-			unsigned long range = qctrl->maximum - qctrl->default_value - 1;
-			unsigned long gain = ((ctrl->value - qctrl->default_value - 1) *
+			unsigned long range = ctrl->maximum - ctrl->default_value - 1;
+			unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
 					       111 + range / 2) / range + 9;
 
 			if (gain <= 32)
@@ -547,66 +444,44 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 			if (data < 0)
 				return -EIO;
 		}
+		return 0;
 
-		/* Success */
-		mt9m001->gain = ctrl->value;
-		break;
-	case V4L2_CID_EXPOSURE:
-		/* mt9m001 has maximum == default */
-		if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
-			return -EINVAL;
-		else {
-			unsigned long range = qctrl->maximum - qctrl->minimum;
-			unsigned long shutter = ((ctrl->value - qctrl->minimum) * 1048 +
+	case V4L2_CID_EXPOSURE_AUTO:
+		if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
+			unsigned long range = exp->maximum - exp->minimum;
+			unsigned long shutter = ((exp->val - exp->minimum) * 1048 +
 						 range / 2) / range + 1;
 
 			dev_dbg(&client->dev,
 				"Setting shutter width from %d to %lu\n",
-				reg_read(client, MT9M001_SHUTTER_WIDTH),
-				shutter);
+				reg_read(client, MT9M001_SHUTTER_WIDTH), shutter);
 			if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0)
 				return -EIO;
-			mt9m001->exposure = ctrl->value;
-			mt9m001->autoexposure = 0;
-		}
-		break;
-	case V4L2_CID_EXPOSURE_AUTO:
-		if (ctrl->value) {
+		} else {
 			const u16 vblank = 25;
-			unsigned int total_h = mt9m001->rect.height +
+
+			mt9m001->total_h = mt9m001->rect.height +
 				mt9m001->y_skip_top + vblank;
-			if (reg_write(client, MT9M001_SHUTTER_WIDTH,
-				      total_h) < 0)
+			if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0)
 				return -EIO;
-			qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
-			mt9m001->exposure = (524 + (total_h - 1) *
-				 (qctrl->maximum - qctrl->minimum)) /
-				1048 + qctrl->minimum;
-			mt9m001->autoexposure = 1;
-		} else
-			mt9m001->autoexposure = 0;
-		break;
+		}
+		return 0;
 	}
-	return 0;
+	return -EINVAL;
 }
 
 /*
  * Interface active, can use i2c. If it fails, it can indeed mean, that
  * this wasn't our capture interface, so, we wait for the right one
  */
-static int mt9m001_video_probe(struct soc_camera_device *icd,
+static int mt9m001_video_probe(struct soc_camera_link *icl,
 			       struct i2c_client *client)
 {
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
 	s32 data;
 	unsigned long flags;
 	int ret;
 
-	/* We must have a parent by now. And it cannot be a wrong one. */
-	BUG_ON(!icd->parent ||
-	       to_soc_camera_host(icd->parent)->nr != icd->iface);
-
 	/* Enable the chip */
 	data = reg_write(client, MT9M001_CHIP_ENABLE, 1);
 	dev_dbg(&client->dev, "write: %d\n", data);
@@ -661,18 +536,11 @@ static int mt9m001_video_probe(struct soc_camera_device *icd,
 		dev_err(&client->dev, "Failed to initialise the camera\n");
 
 	/* mt9m001_init() has reset the chip, returning registers to defaults */
-	mt9m001->gain = 64;
-	mt9m001->exposure = 255;
-
-	return ret;
+	return v4l2_ctrl_handler_setup(&mt9m001->hdl);
 }
 
-static void mt9m001_video_remove(struct soc_camera_device *icd)
+static void mt9m001_video_remove(struct soc_camera_link *icl)
 {
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-
-	dev_dbg(icd->pdev, "Video removed: %p, %p\n",
-		icd->parent, icd->vdev);
 	if (icl->free_bus)
 		icl->free_bus(icl);
 }
@@ -687,9 +555,12 @@ static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
 	return 0;
 }
 
+static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = {
+	.g_volatile_ctrl = mt9m001_g_volatile_ctrl,
+	.s_ctrl = mt9m001_s_ctrl,
+};
+
 static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
-	.g_ctrl		= mt9m001_g_ctrl,
-	.s_ctrl		= mt9m001_s_ctrl,
 	.g_chip_ident	= mt9m001_g_chip_ident,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.g_register	= mt9m001_g_register,
@@ -710,6 +581,40 @@ static int mt9m001_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
 	return 0;
 }
 
+static int mt9m001_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+	/* MT9M001 has all capture_format parameters fixed */
+	cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING |
+		V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+		V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER;
+	cfg->type = V4L2_MBUS_PARALLEL;
+	cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+	return 0;
+}
+
+static int mt9m001_s_mbus_config(struct v4l2_subdev *sd,
+				const struct v4l2_mbus_config *cfg)
+{
+	const struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+	struct mt9m001 *mt9m001 = to_mt9m001(client);
+	unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample;
+
+	if (icl->set_bus_param)
+		return icl->set_bus_param(icl, 1 << (bps - 1));
+
+	/*
+	 * Without board specific bus width settings we only support the
+	 * sensors native bus width
+	 */
+	return bps == 10 ? 0 : -EINVAL;
+}
+
 static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
 	.s_stream	= mt9m001_s_stream,
 	.s_mbus_fmt	= mt9m001_s_fmt,
@@ -719,6 +624,8 @@ static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
 	.g_crop		= mt9m001_g_crop,
 	.cropcap	= mt9m001_cropcap,
 	.enum_mbus_fmt	= mt9m001_enum_fmt,
+	.g_mbus_config	= mt9m001_g_mbus_config,
+	.s_mbus_config	= mt9m001_s_mbus_config,
 };
 
 static struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = {
@@ -735,17 +642,10 @@ static int mt9m001_probe(struct i2c_client *client,
 			 const struct i2c_device_id *did)
 {
 	struct mt9m001 *mt9m001;
-	struct soc_camera_device *icd = client->dev.platform_data;
 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
-	struct soc_camera_link *icl;
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 	int ret;
 
-	if (!icd) {
-		dev_err(&client->dev, "MT9M001: missing soc-camera data!\n");
-		return -EINVAL;
-	}
-
-	icl = to_soc_camera_link(icd);
 	if (!icl) {
 		dev_err(&client->dev, "MT9M001 driver needs platform data\n");
 		return -EINVAL;
@@ -762,25 +662,40 @@ static int mt9m001_probe(struct i2c_client *client,
 		return -ENOMEM;
 
 	v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops);
+	v4l2_ctrl_handler_init(&mt9m001->hdl, 4);
+	v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+			V4L2_CID_GAIN, 0, 127, 1, 64);
+	mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+			V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+	/*
+	 * Simulated autoexposure. If enabled, we calculate shutter width
+	 * ourselves in the driver based on vertical blanking and frame width
+	 */
+	mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl,
+			&mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+			V4L2_EXPOSURE_AUTO);
+	mt9m001->subdev.ctrl_handler = &mt9m001->hdl;
+	if (mt9m001->hdl.error) {
+		int err = mt9m001->hdl.error;
 
-	/* Second stage probe - when a capture adapter is there */
-	icd->ops		= &mt9m001_ops;
+		kfree(mt9m001);
+		return err;
+	}
+	v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure,
+					V4L2_EXPOSURE_MANUAL, true);
 
+	/* Second stage probe - when a capture adapter is there */
 	mt9m001->y_skip_top	= 0;
 	mt9m001->rect.left	= MT9M001_COLUMN_SKIP;
 	mt9m001->rect.top	= MT9M001_ROW_SKIP;
 	mt9m001->rect.width	= MT9M001_MAX_WIDTH;
 	mt9m001->rect.height	= MT9M001_MAX_HEIGHT;
 
-	/*
-	 * Simulated autoexposure. If enabled, we calculate shutter width
-	 * ourselves in the driver based on vertical blanking and frame width
-	 */
-	mt9m001->autoexposure = 1;
-
-	ret = mt9m001_video_probe(icd, client);
+	ret = mt9m001_video_probe(icl, client);
 	if (ret) {
-		icd->ops = NULL;
+		v4l2_ctrl_handler_free(&mt9m001->hdl);
 		kfree(mt9m001);
 	}
 
@@ -790,10 +705,11 @@ static int mt9m001_probe(struct i2c_client *client,
 static int mt9m001_remove(struct i2c_client *client)
 {
 	struct mt9m001 *mt9m001 = to_mt9m001(client);
-	struct soc_camera_device *icd = client->dev.platform_data;
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 
-	icd->ops = NULL;
-	mt9m001_video_remove(icd);
+	v4l2_device_unregister_subdev(&mt9m001->subdev);
+	v4l2_ctrl_handler_free(&mt9m001->hdl);
+	mt9m001_video_remove(icl);
 	kfree(mt9m001);
 
 	return 0;
diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
index 07af26e6bebd..f023cc092c2b 100644
--- a/drivers/media/video/mt9m111.c
+++ b/drivers/media/video/mt9m111.c
@@ -13,10 +13,12 @@
 #include <linux/log2.h>
 #include <linux/gpio.h>
 #include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
 
+#include <media/soc_camera.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/soc_camera.h>
 
 /*
  * MT9M111, MT9M112 and MT9M131:
@@ -177,6 +179,8 @@ enum mt9m111_context {
 
 struct mt9m111 {
 	struct v4l2_subdev subdev;
+	struct v4l2_ctrl_handler hdl;
+	struct v4l2_ctrl *gain;
 	int model;	/* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code
 			 * from v4l2-chip-ident.h */
 	enum mt9m111_context context;
@@ -185,13 +189,8 @@ struct mt9m111 {
 	int power_count;
 	const struct mt9m111_datafmt *fmt;
 	int lastpage;	/* PageMap cache value */
-	unsigned int gain;
-	unsigned char autoexposure;
 	unsigned char datawidth;
 	unsigned int powered:1;
-	unsigned int hflip:1;
-	unsigned int vflip:1;
-	unsigned int autowhitebalance:1;
 };
 
 static struct mt9m111 *to_mt9m111(const struct i2c_client *client)
@@ -363,21 +362,6 @@ static int mt9m111_reset(struct mt9m111 *mt9m111)
 	return ret;
 }
 
-static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd)
-{
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-	unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING |
-		SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
-		SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
-
-	return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f)
-{
-	return 0;
-}
-
 static int mt9m111_make_rect(struct mt9m111 *mt9m111,
 			     struct v4l2_rect *rect)
 {
@@ -660,50 +644,6 @@ static int mt9m111_s_register(struct v4l2_subdev *sd,
 }
 #endif
 
-static const struct v4l2_queryctrl mt9m111_controls[] = {
-	{
-		.id		= V4L2_CID_VFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Verticaly",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	}, {
-		.id		= V4L2_CID_HFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Horizontaly",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	}, {	/* gain = 1/32*val (=>gain=1 if val==32) */
-		.id		= V4L2_CID_GAIN,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Gain",
-		.minimum	= 0,
-		.maximum	= 63 * 2 * 2,
-		.step		= 1,
-		.default_value	= 32,
-		.flags		= V4L2_CTRL_FLAG_SLIDER,
-	}, {
-		.id		= V4L2_CID_EXPOSURE_AUTO,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Auto Exposure",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 1,
-	}
-};
-
-static struct soc_camera_ops mt9m111_ops = {
-	.query_bus_param	= mt9m111_query_bus_param,
-	.set_bus_param		= mt9m111_set_bus_param,
-	.controls		= mt9m111_controls,
-	.num_controls		= ARRAY_SIZE(mt9m111_controls),
-};
-
 static int mt9m111_set_flip(struct mt9m111 *mt9m111, int flip, int mask)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
@@ -744,7 +684,6 @@ static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain)
 	if (gain > 63 * 2 * 2)
 		return -EINVAL;
 
-	mt9m111->gain = gain;
 	if ((gain >= 64 * 2) && (gain < 63 * 2 * 2))
 		val = (1 << 10) | (1 << 9) | (gain / 4);
 	else if ((gain >= 64) && (gain < 64 * 2))
@@ -758,118 +697,47 @@ static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain)
 static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
-	int ret;
 
 	if (on)
-		ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
-	else
-		ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
-
-	if (!ret)
-		mt9m111->autoexposure = on;
-
-	return ret;
+		return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
+	return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
 }
 
 static int mt9m111_set_autowhitebalance(struct mt9m111 *mt9m111, int on)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
-	int ret;
 
 	if (on)
-		ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
-	else
-		ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
-
-	if (!ret)
-		mt9m111->autowhitebalance = on;
-
-	return ret;
-}
-
-static int mt9m111_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
-	int data;
-
-	switch (ctrl->id) {
-	case V4L2_CID_VFLIP:
-		if (mt9m111->context == HIGHPOWER)
-			data = reg_read(READ_MODE_B);
-		else
-			data = reg_read(READ_MODE_A);
-
-		if (data < 0)
-			return -EIO;
-		ctrl->value = !!(data & MT9M111_RMB_MIRROR_ROWS);
-		break;
-	case V4L2_CID_HFLIP:
-		if (mt9m111->context == HIGHPOWER)
-			data = reg_read(READ_MODE_B);
-		else
-			data = reg_read(READ_MODE_A);
-
-		if (data < 0)
-			return -EIO;
-		ctrl->value = !!(data & MT9M111_RMB_MIRROR_COLS);
-		break;
-	case V4L2_CID_GAIN:
-		data = mt9m111_get_global_gain(mt9m111);
-		if (data < 0)
-			return data;
-		ctrl->value = data;
-		break;
-	case V4L2_CID_EXPOSURE_AUTO:
-		ctrl->value = mt9m111->autoexposure;
-		break;
-	case V4L2_CID_AUTO_WHITE_BALANCE:
-		ctrl->value = mt9m111->autowhitebalance;
-		break;
-	}
-	return 0;
+		return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
+	return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
 }
 
-static int mt9m111_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
-	const struct v4l2_queryctrl *qctrl;
-	int ret;
-
-	qctrl = soc_camera_find_qctrl(&mt9m111_ops, ctrl->id);
-	if (!qctrl)
-		return -EINVAL;
+	struct mt9m111 *mt9m111 = container_of(ctrl->handler,
+					       struct mt9m111, hdl);
 
 	switch (ctrl->id) {
 	case V4L2_CID_VFLIP:
-		mt9m111->vflip = ctrl->value;
-		ret = mt9m111_set_flip(mt9m111, ctrl->value,
+		return mt9m111_set_flip(mt9m111, ctrl->val,
 					MT9M111_RMB_MIRROR_ROWS);
-		break;
 	case V4L2_CID_HFLIP:
-		mt9m111->hflip = ctrl->value;
-		ret = mt9m111_set_flip(mt9m111, ctrl->value,
+		return mt9m111_set_flip(mt9m111, ctrl->val,
 					MT9M111_RMB_MIRROR_COLS);
-		break;
 	case V4L2_CID_GAIN:
-		ret = mt9m111_set_global_gain(mt9m111, ctrl->value);
-		break;
+		return mt9m111_set_global_gain(mt9m111, ctrl->val);
 	case V4L2_CID_EXPOSURE_AUTO:
-		ret =  mt9m111_set_autoexposure(mt9m111, ctrl->value);
-		break;
+		return mt9m111_set_autoexposure(mt9m111, ctrl->val);
 	case V4L2_CID_AUTO_WHITE_BALANCE:
-		ret =  mt9m111_set_autowhitebalance(mt9m111, ctrl->value);
-		break;
-	default:
-		ret = -EINVAL;
+		return mt9m111_set_autowhitebalance(mt9m111, ctrl->val);
 	}
 
-	return ret;
+	return -EINVAL;
 }
 
 static int mt9m111_suspend(struct mt9m111 *mt9m111)
 {
-	mt9m111->gain = mt9m111_get_global_gain(mt9m111);
+	v4l2_ctrl_s_ctrl(mt9m111->gain, mt9m111_get_global_gain(mt9m111));
 
 	return 0;
 }
@@ -879,11 +747,7 @@ static void mt9m111_restore_state(struct mt9m111 *mt9m111)
 	mt9m111_set_context(mt9m111, mt9m111->context);
 	mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code);
 	mt9m111_setup_rect(mt9m111, &mt9m111->rect);
-	mt9m111_set_flip(mt9m111, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS);
-	mt9m111_set_flip(mt9m111, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
-	mt9m111_set_global_gain(mt9m111, mt9m111->gain);
-	mt9m111_set_autoexposure(mt9m111, mt9m111->autoexposure);
-	mt9m111_set_autowhitebalance(mt9m111, mt9m111->autowhitebalance);
+	v4l2_ctrl_handler_setup(&mt9m111->hdl);
 }
 
 static int mt9m111_resume(struct mt9m111 *mt9m111)
@@ -911,8 +775,6 @@ static int mt9m111_init(struct mt9m111 *mt9m111)
 		ret = mt9m111_reset(mt9m111);
 	if (!ret)
 		ret = mt9m111_set_context(mt9m111, mt9m111->context);
-	if (!ret)
-		ret = mt9m111_set_autoexposure(mt9m111, mt9m111->autoexposure);
 	if (ret)
 		dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
 	return ret;
@@ -922,22 +784,12 @@ static int mt9m111_init(struct mt9m111 *mt9m111)
  * Interface active, can use i2c. If it fails, it can indeed mean, that
  * this wasn't our capture interface, so, we wait for the right one
  */
-static int mt9m111_video_probe(struct soc_camera_device *icd,
-			       struct i2c_client *client)
+static int mt9m111_video_probe(struct i2c_client *client)
 {
 	struct mt9m111 *mt9m111 = to_mt9m111(client);
 	s32 data;
 	int ret;
 
-	/* We must have a parent by now. And it cannot be a wrong one. */
-	BUG_ON(!icd->parent ||
-	       to_soc_camera_host(icd->parent)->nr != icd->iface);
-
-	mt9m111->lastpage = -1;
-
-	mt9m111->autoexposure = 1;
-	mt9m111->autowhitebalance = 1;
-
 	data = reg_read(CHIP_VERSION);
 
 	switch (data) {
@@ -951,17 +803,16 @@ static int mt9m111_video_probe(struct soc_camera_device *icd,
 		dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
 		break;
 	default:
-		ret = -ENODEV;
 		dev_err(&client->dev,
 			"No MT9M111/MT9M112/MT9M131 chip detected register read %x\n",
 			data);
-		goto ei2c;
+		return -ENODEV;
 	}
 
 	ret = mt9m111_init(mt9m111);
-
-ei2c:
-	return ret;
+	if (ret)
+		return ret;
+	return v4l2_ctrl_handler_setup(&mt9m111->hdl);
 }
 
 static int mt9m111_s_power(struct v4l2_subdev *sd, int on)
@@ -998,9 +849,11 @@ out:
 	return ret;
 }
 
+static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = {
+	.s_ctrl = mt9m111_s_ctrl,
+};
+
 static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
-	.g_ctrl		= mt9m111_g_ctrl,
-	.s_ctrl		= mt9m111_s_ctrl,
 	.g_chip_ident	= mt9m111_g_chip_ident,
 	.s_power	= mt9m111_s_power,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -1019,6 +872,21 @@ static int mt9m111_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
 	return 0;
 }
 
+static int mt9m111_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+	cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+		V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+		V4L2_MBUS_DATA_ACTIVE_HIGH;
+	cfg->type = V4L2_MBUS_PARALLEL;
+	cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+	return 0;
+}
+
 static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
 	.s_mbus_fmt	= mt9m111_s_fmt,
 	.g_mbus_fmt	= mt9m111_g_fmt,
@@ -1027,6 +895,7 @@ static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
 	.g_crop		= mt9m111_g_crop,
 	.cropcap	= mt9m111_cropcap,
 	.enum_mbus_fmt	= mt9m111_enum_fmt,
+	.g_mbus_config	= mt9m111_g_mbus_config,
 };
 
 static struct v4l2_subdev_ops mt9m111_subdev_ops = {
@@ -1038,17 +907,10 @@ static int mt9m111_probe(struct i2c_client *client,
 			 const struct i2c_device_id *did)
 {
 	struct mt9m111 *mt9m111;
-	struct soc_camera_device *icd = client->dev.platform_data;
 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
-	struct soc_camera_link *icl;
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 	int ret;
 
-	if (!icd) {
-		dev_err(&client->dev, "mt9m111: soc-camera data missing!\n");
-		return -EINVAL;
-	}
-
-	icl = to_soc_camera_link(icd);
 	if (!icl) {
 		dev_err(&client->dev, "mt9m111: driver needs platform data\n");
 		return -EINVAL;
@@ -1065,19 +927,37 @@ static int mt9m111_probe(struct i2c_client *client,
 		return -ENOMEM;
 
 	v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops);
+	v4l2_ctrl_handler_init(&mt9m111->hdl, 5);
+	v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+			V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+	mt9m111->gain = v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+			V4L2_CID_GAIN, 0, 63 * 2 * 2, 1, 32);
+	v4l2_ctrl_new_std_menu(&mt9m111->hdl,
+			&mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+			V4L2_EXPOSURE_AUTO);
+	mt9m111->subdev.ctrl_handler = &mt9m111->hdl;
+	if (mt9m111->hdl.error) {
+		int err = mt9m111->hdl.error;
 
-	/* Second stage probe - when a capture adapter is there */
-	icd->ops		= &mt9m111_ops;
+		kfree(mt9m111);
+		return err;
+	}
 
+	/* Second stage probe - when a capture adapter is there */
 	mt9m111->rect.left	= MT9M111_MIN_DARK_COLS;
 	mt9m111->rect.top	= MT9M111_MIN_DARK_ROWS;
 	mt9m111->rect.width	= MT9M111_MAX_WIDTH;
 	mt9m111->rect.height	= MT9M111_MAX_HEIGHT;
 	mt9m111->fmt		= &mt9m111_colour_fmts[0];
+	mt9m111->lastpage	= -1;
 
-	ret = mt9m111_video_probe(icd, client);
+	ret = mt9m111_video_probe(client);
 	if (ret) {
-		icd->ops = NULL;
+		v4l2_ctrl_handler_free(&mt9m111->hdl);
 		kfree(mt9m111);
 	}
 
@@ -1087,9 +967,9 @@ static int mt9m111_probe(struct i2c_client *client,
 static int mt9m111_remove(struct i2c_client *client)
 {
 	struct mt9m111 *mt9m111 = to_mt9m111(client);
-	struct soc_camera_device *icd = client->dev.platform_data;
 
-	icd->ops = NULL;
+	v4l2_device_unregister_subdev(&mt9m111->subdev);
+	v4l2_ctrl_handler_free(&mt9m111->hdl);
 	kfree(mt9m111);
 
 	return 0;
diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c
index 30547cc3f89b..7ee84cc578b9 100644
--- a/drivers/media/video/mt9t031.c
+++ b/drivers/media/video/mt9t031.c
@@ -13,11 +13,20 @@
 #include <linux/log2.h>
 #include <linux/pm.h>
 #include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
 #include <linux/videodev2.h>
 
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-subdev.h>
+#include <media/v4l2-ctrls.h>
+
+/*
+ * ATTENTION: this driver still cannot be used outside of the soc-camera
+ * framework because of its PM implementation, using the video_device node.
+ * If hardware becomes available for testing, alternative PM approaches shall
+ * be considered and tested.
+ */
 
 /*
  * mt9t031 i2c address 0x5d
@@ -57,21 +66,20 @@
 #define MT9T031_COLUMN_SKIP		32
 #define MT9T031_ROW_SKIP		20
 
-#define MT9T031_BUS_PARAM	(SOCAM_PCLK_SAMPLE_RISING |	\
-	SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH |	\
-	SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH |	\
-	SOCAM_MASTER | SOCAM_DATAWIDTH_10)
-
 struct mt9t031 {
 	struct v4l2_subdev subdev;
+	struct v4l2_ctrl_handler hdl;
+	struct {
+		/* exposure/auto-exposure cluster */
+		struct v4l2_ctrl *autoexposure;
+		struct v4l2_ctrl *exposure;
+	};
 	struct v4l2_rect rect;	/* Sensor window */
 	int model;	/* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
 	u16 xskip;
 	u16 yskip;
-	unsigned int gain;
+	unsigned int total_h;
 	unsigned short y_skip_top;	/* Lines to skip at the top */
-	unsigned int exposure;
-	unsigned char autoexposure;
 };
 
 static struct mt9t031 *to_mt9t031(const struct i2c_client *client)
@@ -179,95 +187,6 @@ static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable)
 	return 0;
 }
 
-static int mt9t031_set_bus_param(struct soc_camera_device *icd,
-				 unsigned long flags)
-{
-	struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-
-	/* The caller should have queried our parameters, check anyway */
-	if (flags & ~MT9T031_BUS_PARAM)
-		return -EINVAL;
-
-	if (flags & SOCAM_PCLK_SAMPLE_FALLING)
-		reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
-	else
-		reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
-
-	return 0;
-}
-
-static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd)
-{
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-
-	return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM);
-}
-
-enum {
-	MT9T031_CTRL_VFLIP,
-	MT9T031_CTRL_HFLIP,
-	MT9T031_CTRL_GAIN,
-	MT9T031_CTRL_EXPOSURE,
-	MT9T031_CTRL_EXPOSURE_AUTO,
-};
-
-static const struct v4l2_queryctrl mt9t031_controls[] = {
-	[MT9T031_CTRL_VFLIP] = {
-		.id		= V4L2_CID_VFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Vertically",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	},
-	[MT9T031_CTRL_HFLIP] = {
-		.id		= V4L2_CID_HFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Horizontally",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	},
-	[MT9T031_CTRL_GAIN] = {
-		.id		= V4L2_CID_GAIN,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Gain",
-		.minimum	= 0,
-		.maximum	= 127,
-		.step		= 1,
-		.default_value	= 64,
-		.flags		= V4L2_CTRL_FLAG_SLIDER,
-	},
-	[MT9T031_CTRL_EXPOSURE] = {
-		.id		= V4L2_CID_EXPOSURE,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Exposure",
-		.minimum	= 1,
-		.maximum	= 255,
-		.step		= 1,
-		.default_value	= 255,
-		.flags		= V4L2_CTRL_FLAG_SLIDER,
-	},
-	[MT9T031_CTRL_EXPOSURE_AUTO] = {
-		.id		= V4L2_CID_EXPOSURE_AUTO,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Automatic Exposure",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 1,
-	}
-};
-
-static struct soc_camera_ops mt9t031_ops = {
-	.set_bus_param		= mt9t031_set_bus_param,
-	.query_bus_param	= mt9t031_query_bus_param,
-	.controls		= mt9t031_controls,
-	.num_controls		= ARRAY_SIZE(mt9t031_controls),
-};
-
 /* target must be _even_ */
 static u16 mt9t031_skip(s32 *source, s32 target, s32 max)
 {
@@ -353,7 +272,7 @@ static int mt9t031_set_params(struct i2c_client *client,
 
 	/*
 	 * The caller provides a supported format, as guaranteed by
-	 * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap()
+	 * .try_mbus_fmt(), soc_camera_s_crop() and soc_camera_cropcap()
 	 */
 	if (ret >= 0)
 		ret = reg_write(client, MT9T031_COLUMN_START, rect->left);
@@ -364,17 +283,10 @@ static int mt9t031_set_params(struct i2c_client *client,
 	if (ret >= 0)
 		ret = reg_write(client, MT9T031_WINDOW_HEIGHT,
 				rect->height + mt9t031->y_skip_top - 1);
-	if (ret >= 0 && mt9t031->autoexposure) {
-		unsigned int total_h = rect->height + mt9t031->y_skip_top + vblank;
-		ret = set_shutter(client, total_h);
-		if (ret >= 0) {
-			const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
-			const struct v4l2_queryctrl *qctrl =
-				&mt9t031_controls[MT9T031_CTRL_EXPOSURE];
-			mt9t031->exposure = (shutter_max / 2 + (total_h - 1) *
-				 (qctrl->maximum - qctrl->minimum)) /
-				shutter_max + qctrl->minimum;
-		}
+	if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) {
+		mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank;
+
+		ret = set_shutter(client, mt9t031->total_h);
 	}
 
 	/* Re-enable register update, commit all changes */
@@ -543,71 +455,57 @@ static int mt9t031_s_register(struct v4l2_subdev *sd,
 }
 #endif
 
-static int mt9t031_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct mt9t031 *mt9t031 = to_mt9t031(client);
-	int data;
+	struct mt9t031 *mt9t031 = container_of(ctrl->handler,
+					       struct mt9t031, hdl);
+	const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK;
+	s32 min, max;
 
 	switch (ctrl->id) {
-	case V4L2_CID_VFLIP:
-		data = reg_read(client, MT9T031_READ_MODE_2);
-		if (data < 0)
-			return -EIO;
-		ctrl->value = !!(data & 0x8000);
-		break;
-	case V4L2_CID_HFLIP:
-		data = reg_read(client, MT9T031_READ_MODE_2);
-		if (data < 0)
-			return -EIO;
-		ctrl->value = !!(data & 0x4000);
-		break;
 	case V4L2_CID_EXPOSURE_AUTO:
-		ctrl->value = mt9t031->autoexposure;
-		break;
-	case V4L2_CID_GAIN:
-		ctrl->value = mt9t031->gain;
-		break;
-	case V4L2_CID_EXPOSURE:
-		ctrl->value = mt9t031->exposure;
+		min = mt9t031->exposure->minimum;
+		max = mt9t031->exposure->maximum;
+		mt9t031->exposure->val =
+			(shutter_max / 2 + (mt9t031->total_h - 1) * (max - min))
+				/ shutter_max + min;
 		break;
 	}
 	return 0;
 }
 
-static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+	struct mt9t031 *mt9t031 = container_of(ctrl->handler,
+					       struct mt9t031, hdl);
+	struct v4l2_subdev *sd = &mt9t031->subdev;
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct mt9t031 *mt9t031 = to_mt9t031(client);
-	const struct v4l2_queryctrl *qctrl;
+	struct v4l2_ctrl *exp = mt9t031->exposure;
 	int data;
 
 	switch (ctrl->id) {
 	case V4L2_CID_VFLIP:
-		if (ctrl->value)
+		if (ctrl->val)
 			data = reg_set(client, MT9T031_READ_MODE_2, 0x8000);
 		else
 			data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000);
 		if (data < 0)
 			return -EIO;
-		break;
+		return 0;
 	case V4L2_CID_HFLIP:
-		if (ctrl->value)
+		if (ctrl->val)
 			data = reg_set(client, MT9T031_READ_MODE_2, 0x4000);
 		else
 			data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000);
 		if (data < 0)
 			return -EIO;
-		break;
+		return 0;
 	case V4L2_CID_GAIN:
-		qctrl = &mt9t031_controls[MT9T031_CTRL_GAIN];
-		if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
-			return -EINVAL;
 		/* See Datasheet Table 7, Gain settings. */
-		if (ctrl->value <= qctrl->default_value) {
+		if (ctrl->val <= ctrl->default_value) {
 			/* Pack it into 0..1 step 0.125, register values 0..8 */
-			unsigned long range = qctrl->default_value - qctrl->minimum;
-			data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;
+			unsigned long range = ctrl->default_value - ctrl->minimum;
+			data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
 
 			dev_dbg(&client->dev, "Setting gain %d\n", data);
 			data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
@@ -616,9 +514,9 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 		} else {
 			/* Pack it into 1.125..128 variable step, register values 9..0x7860 */
 			/* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
-			unsigned long range = qctrl->maximum - qctrl->default_value - 1;
+			unsigned long range = ctrl->maximum - ctrl->default_value - 1;
 			/* calculated gain: map 65..127 to 9..1024 step 0.125 */
-			unsigned long gain = ((ctrl->value - qctrl->default_value - 1) *
+			unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
 					       1015 + range / 2) / range + 9;
 
 			if (gain <= 32)		/* calculated gain 9..32 -> 9..32 */
@@ -635,19 +533,13 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 			if (data < 0)
 				return -EIO;
 		}
+		return 0;
 
-		/* Success */
-		mt9t031->gain = ctrl->value;
-		break;
-	case V4L2_CID_EXPOSURE:
-		qctrl = &mt9t031_controls[MT9T031_CTRL_EXPOSURE];
-		/* mt9t031 has maximum == default */
-		if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
-			return -EINVAL;
-		else {
-			const unsigned long range = qctrl->maximum - qctrl->minimum;
-			const u32 shutter = ((ctrl->value - qctrl->minimum) * 1048 +
-					     range / 2) / range + 1;
+	case V4L2_CID_EXPOSURE_AUTO:
+		if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
+			unsigned int range = exp->maximum - exp->minimum;
+			unsigned int shutter = ((exp->val - exp->minimum) * 1048 +
+						 range / 2) / range + 1;
 			u32 old;
 
 			get_shutter(client, &old);
@@ -655,27 +547,15 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 				old, shutter);
 			if (set_shutter(client, shutter) < 0)
 				return -EIO;
-			mt9t031->exposure = ctrl->value;
-			mt9t031->autoexposure = 0;
-		}
-		break;
-	case V4L2_CID_EXPOSURE_AUTO:
-		if (ctrl->value) {
+		} else {
 			const u16 vblank = MT9T031_VERTICAL_BLANK;
-			const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
-			unsigned int total_h = mt9t031->rect.height +
+			mt9t031->total_h = mt9t031->rect.height +
 				mt9t031->y_skip_top + vblank;
 
-			if (set_shutter(client, total_h) < 0)
+			if (set_shutter(client, mt9t031->total_h) < 0)
 				return -EIO;
-			qctrl = &mt9t031_controls[MT9T031_CTRL_EXPOSURE];
-			mt9t031->exposure = (shutter_max / 2 + (total_h - 1) *
-				 (qctrl->maximum - qctrl->minimum)) /
-				shutter_max + qctrl->minimum;
-			mt9t031->autoexposure = 1;
-		} else
-			mt9t031->autoexposure = 0;
-		break;
+		}
+		return 0;
 	default:
 		return -EINVAL;
 	}
@@ -700,8 +580,7 @@ static int mt9t031_runtime_suspend(struct device *dev)
 static int mt9t031_runtime_resume(struct device *dev)
 {
 	struct video_device *vdev = to_video_device(dev);
-	struct soc_camera_device *icd = dev_get_drvdata(vdev->parent);
-	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
 
@@ -734,6 +613,19 @@ static struct device_type mt9t031_dev_type = {
 	.pm	= &mt9t031_dev_pm_ops,
 };
 
+static int mt9t031_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct video_device *vdev = soc_camera_i2c_to_vdev(client);
+
+	if (on)
+		vdev->dev.type = &mt9t031_dev_type;
+	else
+		vdev->dev.type = NULL;
+
+	return 0;
+}
+
 /*
  * Interface active, can use i2c. If it fails, it can indeed mean, that
  * this wasn't our capture interface, so, we wait for the right one
@@ -741,7 +633,6 @@ static struct device_type mt9t031_dev_type = {
 static int mt9t031_video_probe(struct i2c_client *client)
 {
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
-	struct video_device *vdev = soc_camera_i2c_to_vdev(client);
 	s32 data;
 	int ret;
 
@@ -768,11 +659,7 @@ static int mt9t031_video_probe(struct i2c_client *client)
 	if (ret < 0)
 		dev_err(&client->dev, "Failed to initialise the camera\n");
 	else
-		vdev->dev.type = &mt9t031_dev_type;
-
-	/* mt9t031_idle() has reset the chip to default. */
-	mt9t031->exposure = 255;
-	mt9t031->gain = 64;
+		v4l2_ctrl_handler_setup(&mt9t031->hdl);
 
 	return ret;
 }
@@ -787,10 +674,14 @@ static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
 	return 0;
 }
 
+static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = {
+	.g_volatile_ctrl = mt9t031_g_volatile_ctrl,
+	.s_ctrl = mt9t031_s_ctrl,
+};
+
 static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = {
-	.g_ctrl		= mt9t031_g_ctrl,
-	.s_ctrl		= mt9t031_s_ctrl,
 	.g_chip_ident	= mt9t031_g_chip_ident,
+	.s_power	= mt9t031_s_power,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.g_register	= mt9t031_g_register,
 	.s_register	= mt9t031_s_register,
@@ -807,6 +698,34 @@ static int mt9t031_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
 	return 0;
 }
 
+static int mt9t031_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+	cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+		V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+		V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH;
+	cfg->type = V4L2_MBUS_PARALLEL;
+	cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+	return 0;
+}
+
+static int mt9t031_s_mbus_config(struct v4l2_subdev *sd,
+				const struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+	if (soc_camera_apply_board_flags(icl, cfg) &
+	    V4L2_MBUS_PCLK_SAMPLE_FALLING)
+		return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+	else
+		return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+}
+
 static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
 	.s_stream	= mt9t031_s_stream,
 	.s_mbus_fmt	= mt9t031_s_fmt,
@@ -816,6 +735,8 @@ static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
 	.g_crop		= mt9t031_g_crop,
 	.cropcap	= mt9t031_cropcap,
 	.enum_mbus_fmt	= mt9t031_enum_fmt,
+	.g_mbus_config	= mt9t031_g_mbus_config,
+	.s_mbus_config	= mt9t031_s_mbus_config,
 };
 
 static struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = {
@@ -832,18 +753,13 @@ static int mt9t031_probe(struct i2c_client *client,
 			 const struct i2c_device_id *did)
 {
 	struct mt9t031 *mt9t031;
-	struct soc_camera_device *icd = client->dev.platform_data;
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
 	int ret;
 
-	if (icd) {
-		struct soc_camera_link *icl = to_soc_camera_link(icd);
-		if (!icl) {
-			dev_err(&client->dev, "MT9T031 driver needs platform data\n");
-			return -EINVAL;
-		}
-
-		icd->ops = &mt9t031_ops;
+	if (!icl) {
+		dev_err(&client->dev, "MT9T031 driver needs platform data\n");
+		return -EINVAL;
 	}
 
 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
@@ -857,6 +773,33 @@ static int mt9t031_probe(struct i2c_client *client,
 		return -ENOMEM;
 
 	v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops);
+	v4l2_ctrl_handler_init(&mt9t031->hdl, 5);
+	v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+			V4L2_CID_GAIN, 0, 127, 1, 64);
+
+	/*
+	 * Simulated autoexposure. If enabled, we calculate shutter width
+	 * ourselves in the driver based on vertical blanking and frame width
+	 */
+	mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl,
+			&mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+			V4L2_EXPOSURE_AUTO);
+	mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+			V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+
+	mt9t031->subdev.ctrl_handler = &mt9t031->hdl;
+	if (mt9t031->hdl.error) {
+		int err = mt9t031->hdl.error;
+
+		kfree(mt9t031);
+		return err;
+	}
+	v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure,
+				V4L2_EXPOSURE_MANUAL, true);
 
 	mt9t031->y_skip_top	= 0;
 	mt9t031->rect.left	= MT9T031_COLUMN_SKIP;
@@ -864,12 +807,6 @@ static int mt9t031_probe(struct i2c_client *client,
 	mt9t031->rect.width	= MT9T031_MAX_WIDTH;
 	mt9t031->rect.height	= MT9T031_MAX_HEIGHT;
 
-	/*
-	 * Simulated autoexposure. If enabled, we calculate shutter width
-	 * ourselves in the driver based on vertical blanking and frame width
-	 */
-	mt9t031->autoexposure = 1;
-
 	mt9t031->xskip = 1;
 	mt9t031->yskip = 1;
 
@@ -880,8 +817,7 @@ static int mt9t031_probe(struct i2c_client *client,
 	mt9t031_disable(client);
 
 	if (ret) {
-		if (icd)
-			icd->ops = NULL;
+		v4l2_ctrl_handler_free(&mt9t031->hdl);
 		kfree(mt9t031);
 	}
 
@@ -891,10 +827,9 @@ static int mt9t031_probe(struct i2c_client *client,
 static int mt9t031_remove(struct i2c_client *client)
 {
 	struct mt9t031 *mt9t031 = to_mt9t031(client);
-	struct soc_camera_device *icd = client->dev.platform_data;
 
-	if (icd)
-		icd->ops = NULL;
+	v4l2_device_unregister_subdev(&mt9t031->subdev);
+	v4l2_ctrl_handler_free(&mt9t031->hdl);
 	kfree(mt9t031);
 
 	return 0;
diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c
index d2e0a50063a2..32114a3c0ca7 100644
--- a/drivers/media/video/mt9t112.c
+++ b/drivers/media/video/mt9t112.c
@@ -22,11 +22,11 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
 #include <linux/videodev2.h>
 
 #include <media/mt9t112.h>
 #include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-common.h>
 
@@ -34,11 +34,7 @@
 /* #define EXT_CLOCK 24000000 */
 
 /************************************************************************
-
-
 			macro
-
-
 ************************************************************************/
 /*
  * frame size
@@ -80,17 +76,8 @@
 #define VAR8(id, offset) _VAR(id, offset, 0x8000)
 
 /************************************************************************
-
-
 			struct
-
-
 ************************************************************************/
-struct mt9t112_frame_size {
-	u16 width;
-	u16 height;
-};
-
 struct mt9t112_format {
 	enum v4l2_mbus_pixelcode code;
 	enum v4l2_colorspace colorspace;
@@ -102,21 +89,17 @@ struct mt9t112_priv {
 	struct v4l2_subdev		 subdev;
 	struct mt9t112_camera_info	*info;
 	struct i2c_client		*client;
-	struct soc_camera_device	 icd;
-	struct mt9t112_frame_size	 frame;
+	struct v4l2_rect		 frame;
 	const struct mt9t112_format	*format;
 	int				 model;
 	u32				 flags;
 /* for flags */
-#define INIT_DONE  (1<<0)
+#define INIT_DONE	(1 << 0)
+#define PCLK_RISING	(1 << 1)
 };
 
 /************************************************************************
-
-
 			supported format
-
-
 ************************************************************************/
 
 static const struct mt9t112_format mt9t112_cfmts[] = {
@@ -154,11 +137,7 @@ static const struct mt9t112_format mt9t112_cfmts[] = {
 };
 
 /************************************************************************
-
-
 			general function
-
-
 ************************************************************************/
 static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client)
 {
@@ -326,50 +305,47 @@ static int mt9t112_clock_info(const struct i2c_client *client, u32 ext)
 	n = (n >> 8) & 0x003f;
 
 	enable = ((6000 > ext) || (54000 < ext)) ? "X" : "";
-	dev_info(&client->dev, "EXTCLK          : %10u K %s\n", ext, enable);
+	dev_dbg(&client->dev, "EXTCLK          : %10u K %s\n", ext, enable);
 
 	vco = 2 * m * ext / (n+1);
 	enable = ((384000 > vco) || (768000 < vco)) ? "X" : "";
-	dev_info(&client->dev, "VCO             : %10u K %s\n", vco, enable);
+	dev_dbg(&client->dev, "VCO             : %10u K %s\n", vco, enable);
 
 	clk = vco / (p1+1) / (p2+1);
 	enable = (96000 < clk) ? "X" : "";
-	dev_info(&client->dev, "PIXCLK          : %10u K %s\n", clk, enable);
+	dev_dbg(&client->dev, "PIXCLK          : %10u K %s\n", clk, enable);
 
 	clk = vco / (p3+1);
 	enable = (768000 < clk) ? "X" : "";
-	dev_info(&client->dev, "MIPICLK         : %10u K %s\n", clk, enable);
+	dev_dbg(&client->dev, "MIPICLK         : %10u K %s\n", clk, enable);
 
 	clk = vco / (p6+1);
 	enable = (96000 < clk) ? "X" : "";
-	dev_info(&client->dev, "MCU CLK         : %10u K %s\n", clk, enable);
+	dev_dbg(&client->dev, "MCU CLK         : %10u K %s\n", clk, enable);
 
 	clk = vco / (p5+1);
 	enable = (54000 < clk) ? "X" : "";
-	dev_info(&client->dev, "SOC CLK         : %10u K %s\n", clk, enable);
+	dev_dbg(&client->dev, "SOC CLK         : %10u K %s\n", clk, enable);
 
 	clk = vco / (p4+1);
 	enable = (70000 < clk) ? "X" : "";
-	dev_info(&client->dev, "Sensor CLK      : %10u K %s\n", clk, enable);
+	dev_dbg(&client->dev, "Sensor CLK      : %10u K %s\n", clk, enable);
 
 	clk = vco / (p7+1);
-	dev_info(&client->dev, "External sensor : %10u K\n", clk);
+	dev_dbg(&client->dev, "External sensor : %10u K\n", clk);
 
 	clk = ext / (n+1);
 	enable = ((2000 > clk) || (24000 < clk)) ? "X" : "";
-	dev_info(&client->dev, "PFD             : %10u K %s\n", clk, enable);
+	dev_dbg(&client->dev, "PFD             : %10u K %s\n", clk, enable);
 
 	return 0;
 }
 #endif
 
-static void mt9t112_frame_check(u32 *width, u32 *height)
+static void mt9t112_frame_check(u32 *width, u32 *height, u32 *left, u32 *top)
 {
-	if (*width > MAX_WIDTH)
-		*width = MAX_WIDTH;
-
-	if (*height > MAX_HEIGHT)
-		*height = MAX_HEIGHT;
+	soc_camera_limit_side(left, width, 0, 0, MAX_WIDTH);
+	soc_camera_limit_side(top, height, 0, 0, MAX_HEIGHT);
 }
 
 static int mt9t112_set_a_frame_size(const struct i2c_client *client,
@@ -758,48 +734,7 @@ static int mt9t112_init_camera(const struct i2c_client *client)
 }
 
 /************************************************************************
-
-
-			soc_camera_ops
-
-
-************************************************************************/
-static int mt9t112_set_bus_param(struct soc_camera_device *icd,
-				 unsigned long	flags)
-{
-	return 0;
-}
-
-static unsigned long mt9t112_query_bus_param(struct soc_camera_device *icd)
-{
-	struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-	struct mt9t112_priv *priv = to_mt9t112(client);
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-	unsigned long flags = SOCAM_MASTER | SOCAM_VSYNC_ACTIVE_HIGH |
-		SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH;
-
-	flags |= (priv->info->flags & MT9T112_FLAG_PCLK_RISING_EDGE) ?
-		SOCAM_PCLK_SAMPLE_RISING : SOCAM_PCLK_SAMPLE_FALLING;
-
-	if (priv->info->flags & MT9T112_FLAG_DATAWIDTH_8)
-		flags |= SOCAM_DATAWIDTH_8;
-	else
-		flags |= SOCAM_DATAWIDTH_10;
-
-	return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static struct soc_camera_ops mt9t112_ops = {
-	.set_bus_param		= mt9t112_set_bus_param,
-	.query_bus_param	= mt9t112_query_bus_param,
-};
-
-/************************************************************************
-
-
 			v4l2_subdev_core_ops
-
-
 ************************************************************************/
 static int mt9t112_g_chip_ident(struct v4l2_subdev *sd,
 				struct v4l2_dbg_chip_ident *id)
@@ -850,11 +785,7 @@ static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = {
 
 
 /************************************************************************
-
-
 			v4l2_subdev_video_ops
-
-
 ************************************************************************/
 static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
 {
@@ -877,8 +808,7 @@ static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
 	}
 
 	if (!(priv->flags & INIT_DONE)) {
-		u16 param = (MT9T112_FLAG_PCLK_RISING_EDGE &
-			     priv->info->flags) ? 0x0001 : 0x0000;
+		u16 param = PCLK_RISING & priv->flags ? 0x0001 : 0x0000;
 
 		ECHECKER(ret, mt9t112_init_camera(client));
 
@@ -910,19 +840,12 @@ static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
 	return ret;
 }
 
-static int mt9t112_set_params(struct i2c_client *client, u32 width, u32 height,
+static int mt9t112_set_params(struct mt9t112_priv *priv,
+			      const struct v4l2_rect *rect,
 			      enum v4l2_mbus_pixelcode code)
 {
-	struct mt9t112_priv *priv = to_mt9t112(client);
 	int i;
 
-	priv->format = NULL;
-
-	/*
-	 * frame size check
-	 */
-	mt9t112_frame_check(&width, &height);
-
 	/*
 	 * get color format
 	 */
@@ -933,8 +856,13 @@ static int mt9t112_set_params(struct i2c_client *client, u32 width, u32 height,
 	if (i == ARRAY_SIZE(mt9t112_cfmts))
 		return -EINVAL;
 
-	priv->frame.width  = (u16)width;
-	priv->frame.height = (u16)height;
+	priv->frame  = *rect;
+
+	/*
+	 * frame size check
+	 */
+	mt9t112_frame_check(&priv->frame.width, &priv->frame.height,
+			    &priv->frame.left, &priv->frame.top);
 
 	priv->format = mt9t112_cfmts + i;
 
@@ -945,9 +873,12 @@ static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
 {
 	a->bounds.left			= 0;
 	a->bounds.top			= 0;
-	a->bounds.width			= VGA_WIDTH;
-	a->bounds.height		= VGA_HEIGHT;
-	a->defrect			= a->bounds;
+	a->bounds.width			= MAX_WIDTH;
+	a->bounds.height		= MAX_HEIGHT;
+	a->defrect.left			= 0;
+	a->defrect.top			= 0;
+	a->defrect.width		= VGA_WIDTH;
+	a->defrect.height		= VGA_HEIGHT;
 	a->type				= V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	a->pixelaspect.numerator	= 1;
 	a->pixelaspect.denominator	= 1;
@@ -957,11 +888,11 @@ static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
 
 static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-	a->c.left	= 0;
-	a->c.top	= 0;
-	a->c.width	= VGA_WIDTH;
-	a->c.height	= VGA_HEIGHT;
-	a->type		= V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct mt9t112_priv *priv = to_mt9t112(client);
+
+	a->c	= priv->frame;
+	a->type	= V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
 	return 0;
 }
@@ -969,10 +900,10 @@ static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 static int mt9t112_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct mt9t112_priv *priv = to_mt9t112(client);
 	struct v4l2_rect *rect = &a->c;
 
-	return mt9t112_set_params(client, rect->width, rect->height,
-				 V4L2_MBUS_FMT_UYVY8_2X8);
+	return mt9t112_set_params(priv, rect, priv->format->code);
 }
 
 static int mt9t112_g_fmt(struct v4l2_subdev *sd,
@@ -981,16 +912,9 @@ static int mt9t112_g_fmt(struct v4l2_subdev *sd,
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct mt9t112_priv *priv = to_mt9t112(client);
 
-	if (!priv->format) {
-		int ret = mt9t112_set_params(client, VGA_WIDTH, VGA_HEIGHT,
-					     V4L2_MBUS_FMT_UYVY8_2X8);
-		if (ret < 0)
-			return ret;
-	}
-
 	mf->width	= priv->frame.width;
 	mf->height	= priv->frame.height;
-	/* TODO: set colorspace */
+	mf->colorspace	= priv->format->colorspace;
 	mf->code	= priv->format->code;
 	mf->field	= V4L2_FIELD_NONE;
 
@@ -1001,17 +925,42 @@ static int mt9t112_s_fmt(struct v4l2_subdev *sd,
 			 struct v4l2_mbus_framefmt *mf)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct mt9t112_priv *priv = to_mt9t112(client);
+	struct v4l2_rect rect = {
+		.width = mf->width,
+		.height = mf->height,
+		.left = priv->frame.left,
+		.top = priv->frame.top,
+	};
+	int ret;
+
+	ret = mt9t112_set_params(priv, &rect, mf->code);
+
+	if (!ret)
+		mf->colorspace = priv->format->colorspace;
 
-	/* TODO: set colorspace */
-	return mt9t112_set_params(client, mf->width, mf->height, mf->code);
+	return ret;
 }
 
 static int mt9t112_try_fmt(struct v4l2_subdev *sd,
 			   struct v4l2_mbus_framefmt *mf)
 {
-	mt9t112_frame_check(&mf->width, &mf->height);
+	unsigned int top, left;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++)
+		if (mt9t112_cfmts[i].code == mf->code)
+			break;
+
+	if (i == ARRAY_SIZE(mt9t112_cfmts)) {
+		mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+		mf->colorspace = V4L2_COLORSPACE_JPEG;
+	} else {
+		mf->colorspace	= mt9t112_cfmts[i].colorspace;
+	}
+
+	mt9t112_frame_check(&mf->width, &mf->height, &left, &top);
 
-	/* TODO: set colorspace */
 	mf->field = V4L2_FIELD_NONE;
 
 	return 0;
@@ -1024,6 +973,35 @@ static int mt9t112_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
 		return -EINVAL;
 
 	*code = mt9t112_cfmts[index].code;
+
+	return 0;
+}
+
+static int mt9t112_g_mbus_config(struct v4l2_subdev *sd,
+				 struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+	cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+		V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH |
+		V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
+	cfg->type = V4L2_MBUS_PARALLEL;
+	cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+	return 0;
+}
+
+static int mt9t112_s_mbus_config(struct v4l2_subdev *sd,
+				 const struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+	struct mt9t112_priv *priv = to_mt9t112(client);
+
+	if (soc_camera_apply_board_flags(icl, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING)
+		priv->flags |= PCLK_RISING;
+
 	return 0;
 }
 
@@ -1036,31 +1014,24 @@ static struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = {
 	.g_crop		= mt9t112_g_crop,
 	.s_crop		= mt9t112_s_crop,
 	.enum_mbus_fmt	= mt9t112_enum_fmt,
+	.g_mbus_config	= mt9t112_g_mbus_config,
+	.s_mbus_config	= mt9t112_s_mbus_config,
 };
 
 /************************************************************************
-
-
 			i2c driver
-
-
 ************************************************************************/
 static struct v4l2_subdev_ops mt9t112_subdev_ops = {
 	.core	= &mt9t112_subdev_core_ops,
 	.video	= &mt9t112_subdev_video_ops,
 };
 
-static int mt9t112_camera_probe(struct soc_camera_device *icd,
-				struct i2c_client *client)
+static int mt9t112_camera_probe(struct i2c_client *client)
 {
 	struct mt9t112_priv *priv = to_mt9t112(client);
 	const char          *devname;
 	int                  chipid;
 
-	/* We must have a parent by now. And it cannot be a wrong one. */
-	BUG_ON(!icd->parent ||
-	       to_soc_camera_host(icd->parent)->nr != icd->iface);
-
 	/*
 	 * check and show chip ID
 	 */
@@ -1088,20 +1059,21 @@ static int mt9t112_camera_probe(struct soc_camera_device *icd,
 static int mt9t112_probe(struct i2c_client *client,
 			 const struct i2c_device_id *did)
 {
-	struct mt9t112_priv        *priv;
-	struct soc_camera_device   *icd = client->dev.platform_data;
-	struct soc_camera_link     *icl;
-	int                         ret;
+	struct mt9t112_priv *priv;
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+	struct v4l2_rect rect = {
+		.width = VGA_WIDTH,
+		.height = VGA_HEIGHT,
+		.left = (MAX_WIDTH - VGA_WIDTH) / 2,
+		.top = (MAX_HEIGHT - VGA_HEIGHT) / 2,
+	};
+	int ret;
 
-	if (!icd) {
-		dev_err(&client->dev, "mt9t112: missing soc-camera data!\n");
+	if (!icl || !icl->priv) {
+		dev_err(&client->dev, "mt9t112: missing platform data!\n");
 		return -EINVAL;
 	}
 
-	icl = to_soc_camera_link(icd);
-	if (!icl || !icl->priv)
-		return -EINVAL;
-
 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
@@ -1110,13 +1082,12 @@ static int mt9t112_probe(struct i2c_client *client,
 
 	v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops);
 
-	icd->ops = &mt9t112_ops;
-
-	ret = mt9t112_camera_probe(icd, client);
-	if (ret) {
-		icd->ops = NULL;
+	ret = mt9t112_camera_probe(client);
+	if (ret)
 		kfree(priv);
-	}
+
+	/* Cannot fail: using the default supported pixel code */
+	mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8);
 
 	return ret;
 }
@@ -1124,9 +1095,7 @@ static int mt9t112_probe(struct i2c_client *client,
 static int mt9t112_remove(struct i2c_client *client)
 {
 	struct mt9t112_priv *priv = to_mt9t112(client);
-	struct soc_camera_device *icd = client->dev.platform_data;
 
-	icd->ops = NULL;
 	kfree(priv);
 	return 0;
 }
@@ -1147,11 +1116,7 @@ static struct i2c_driver mt9t112_i2c_driver = {
 };
 
 /************************************************************************
-
-
 			module function
-
-
 ************************************************************************/
 static int __init mt9t112_module_init(void)
 {
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
index 51b0fccbfe70..b6a29f7de82c 100644
--- a/drivers/media/video/mt9v022.c
+++ b/drivers/media/video/mt9v022.c
@@ -14,9 +14,11 @@
 #include <linux/delay.h>
 #include <linux/log2.h>
 
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
-#include <media/soc_camera.h>
+#include <media/v4l2-ctrls.h>
 
 /*
  * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c
@@ -100,6 +102,17 @@ static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = {
 
 struct mt9v022 {
 	struct v4l2_subdev subdev;
+	struct v4l2_ctrl_handler hdl;
+	struct {
+		/* exposure/auto-exposure cluster */
+		struct v4l2_ctrl *autoexposure;
+		struct v4l2_ctrl *exposure;
+	};
+	struct {
+		/* gain/auto-gain cluster */
+		struct v4l2_ctrl *autogain;
+		struct v4l2_ctrl *gain;
+	};
 	struct v4l2_rect rect;	/* Sensor window */
 	const struct mt9v022_datafmt *fmt;
 	const struct mt9v022_datafmt *fmts;
@@ -178,6 +191,8 @@ static int mt9v022_init(struct i2c_client *client)
 		ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1);
 	if (!ret)
 		ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0);
+	if (!ret)
+		return v4l2_ctrl_handler_setup(&mt9v022->hdl);
 
 	return ret;
 }
@@ -199,78 +214,6 @@ static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable)
 	return 0;
 }
 
-static int mt9v022_set_bus_param(struct soc_camera_device *icd,
-				 unsigned long flags)
-{
-	struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-	struct mt9v022 *mt9v022 = to_mt9v022(client);
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-	unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK;
-	int ret;
-	u16 pixclk = 0;
-
-	/* Only one width bit may be set */
-	if (!is_power_of_2(width_flag))
-		return -EINVAL;
-
-	if (icl->set_bus_param) {
-		ret = icl->set_bus_param(icl, width_flag);
-		if (ret)
-			return ret;
-	} else {
-		/*
-		 * Without board specific bus width settings we only support the
-		 * sensors native bus width
-		 */
-		if (width_flag != SOCAM_DATAWIDTH_10)
-			return -EINVAL;
-	}
-
-	flags = soc_camera_apply_sensor_flags(icl, flags);
-
-	if (flags & SOCAM_PCLK_SAMPLE_FALLING)
-		pixclk |= 0x10;
-
-	if (!(flags & SOCAM_HSYNC_ACTIVE_HIGH))
-		pixclk |= 0x1;
-
-	if (!(flags & SOCAM_VSYNC_ACTIVE_HIGH))
-		pixclk |= 0x2;
-
-	ret = reg_write(client, MT9V022_PIXCLK_FV_LV, pixclk);
-	if (ret < 0)
-		return ret;
-
-	if (!(flags & SOCAM_MASTER))
-		mt9v022->chip_control &= ~0x8;
-
-	ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
-	if (ret < 0)
-		return ret;
-
-	dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
-		pixclk, mt9v022->chip_control);
-
-	return 0;
-}
-
-static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd)
-{
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-	unsigned int flags = SOCAM_MASTER | SOCAM_SLAVE |
-		SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
-		SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW |
-		SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW |
-		SOCAM_DATA_ACTIVE_HIGH;
-
-	if (icl->query_bus_param)
-		flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
-	else
-		flags |= SOCAM_DATAWIDTH_10;
-
-	return soc_camera_apply_sensor_flags(icl, flags);
-}
-
 static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -389,7 +332,7 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd,
 
 	/*
 	 * The caller provides a supported format, as verified per call to
-	 * icd->try_fmt(), datawidth is from our supported format list
+	 * .try_mbus_fmt(), datawidth is from our supported format list
 	 */
 	switch (mf->code) {
 	case V4L2_MBUS_FMT_Y8_1X8:
@@ -502,236 +445,131 @@ static int mt9v022_s_register(struct v4l2_subdev *sd,
 }
 #endif
 
-static const struct v4l2_queryctrl mt9v022_controls[] = {
-	{
-		.id		= V4L2_CID_VFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Vertically",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	}, {
-		.id		= V4L2_CID_HFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Horizontally",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	}, {
-		.id		= V4L2_CID_GAIN,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Analog Gain",
-		.minimum	= 64,
-		.maximum	= 127,
-		.step		= 1,
-		.default_value	= 64,
-		.flags		= V4L2_CTRL_FLAG_SLIDER,
-	}, {
-		.id		= V4L2_CID_EXPOSURE,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Exposure",
-		.minimum	= 1,
-		.maximum	= 255,
-		.step		= 1,
-		.default_value	= 255,
-		.flags		= V4L2_CTRL_FLAG_SLIDER,
-	}, {
-		.id		= V4L2_CID_AUTOGAIN,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Automatic Gain",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 1,
-	}, {
-		.id		= V4L2_CID_EXPOSURE_AUTO,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Automatic Exposure",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 1,
-	}
-};
-
-static struct soc_camera_ops mt9v022_ops = {
-	.set_bus_param		= mt9v022_set_bus_param,
-	.query_bus_param	= mt9v022_query_bus_param,
-	.controls		= mt9v022_controls,
-	.num_controls		= ARRAY_SIZE(mt9v022_controls),
-};
-
-static int mt9v022_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 {
+	struct mt9v022 *mt9v022 = container_of(ctrl->handler,
+					       struct mt9v022, hdl);
+	struct v4l2_subdev *sd = &mt9v022->subdev;
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	const struct v4l2_queryctrl *qctrl;
+	struct v4l2_ctrl *gain = mt9v022->gain;
+	struct v4l2_ctrl *exp = mt9v022->exposure;
 	unsigned long range;
 	int data;
 
-	qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id);
-
 	switch (ctrl->id) {
-	case V4L2_CID_VFLIP:
-		data = reg_read(client, MT9V022_READ_MODE);
-		if (data < 0)
-			return -EIO;
-		ctrl->value = !!(data & 0x10);
-		break;
-	case V4L2_CID_HFLIP:
-		data = reg_read(client, MT9V022_READ_MODE);
-		if (data < 0)
-			return -EIO;
-		ctrl->value = !!(data & 0x20);
-		break;
-	case V4L2_CID_EXPOSURE_AUTO:
-		data = reg_read(client, MT9V022_AEC_AGC_ENABLE);
-		if (data < 0)
-			return -EIO;
-		ctrl->value = !!(data & 0x1);
-		break;
 	case V4L2_CID_AUTOGAIN:
-		data = reg_read(client, MT9V022_AEC_AGC_ENABLE);
-		if (data < 0)
-			return -EIO;
-		ctrl->value = !!(data & 0x2);
-		break;
-	case V4L2_CID_GAIN:
 		data = reg_read(client, MT9V022_ANALOG_GAIN);
 		if (data < 0)
 			return -EIO;
 
-		range = qctrl->maximum - qctrl->minimum;
-		ctrl->value = ((data - 16) * range + 24) / 48 + qctrl->minimum;
-
-		break;
-	case V4L2_CID_EXPOSURE:
+		range = gain->maximum - gain->minimum;
+		gain->val = ((data - 16) * range + 24) / 48 + gain->minimum;
+		return 0;
+	case V4L2_CID_EXPOSURE_AUTO:
 		data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH);
 		if (data < 0)
 			return -EIO;
 
-		range = qctrl->maximum - qctrl->minimum;
-		ctrl->value = ((data - 1) * range + 239) / 479 + qctrl->minimum;
-
-		break;
+		range = exp->maximum - exp->minimum;
+		exp->val = ((data - 1) * range + 239) / 479 + exp->minimum;
+		return 0;
 	}
-	return 0;
+	return -EINVAL;
 }
 
-static int mt9v022_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	int data;
+	struct mt9v022 *mt9v022 = container_of(ctrl->handler,
+					       struct mt9v022, hdl);
+	struct v4l2_subdev *sd = &mt9v022->subdev;
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	const struct v4l2_queryctrl *qctrl;
-
-	qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id);
-	if (!qctrl)
-		return -EINVAL;
+	int data;
 
 	switch (ctrl->id) {
 	case V4L2_CID_VFLIP:
-		if (ctrl->value)
+		if (ctrl->val)
 			data = reg_set(client, MT9V022_READ_MODE, 0x10);
 		else
 			data = reg_clear(client, MT9V022_READ_MODE, 0x10);
 		if (data < 0)
 			return -EIO;
-		break;
+		return 0;
 	case V4L2_CID_HFLIP:
-		if (ctrl->value)
+		if (ctrl->val)
 			data = reg_set(client, MT9V022_READ_MODE, 0x20);
 		else
 			data = reg_clear(client, MT9V022_READ_MODE, 0x20);
 		if (data < 0)
 			return -EIO;
-		break;
-	case V4L2_CID_GAIN:
-		/* mt9v022 has minimum == default */
-		if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
-			return -EINVAL;
-		else {
-			unsigned long range = qctrl->maximum - qctrl->minimum;
+		return 0;
+	case V4L2_CID_AUTOGAIN:
+		if (ctrl->val) {
+			if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
+				return -EIO;
+		} else {
+			struct v4l2_ctrl *gain = mt9v022->gain;
+			/* mt9v022 has minimum == default */
+			unsigned long range = gain->maximum - gain->minimum;
 			/* Valid values 16 to 64, 32 to 64 must be even. */
-			unsigned long gain = ((ctrl->value - qctrl->minimum) *
+			unsigned long gain_val = ((gain->val - gain->minimum) *
 					      48 + range / 2) / range + 16;
-			if (gain >= 32)
-				gain &= ~1;
+
+			if (gain_val >= 32)
+				gain_val &= ~1;
+
 			/*
 			 * The user wants to set gain manually, hope, she
 			 * knows, what she's doing... Switch AGC off.
 			 */
-
 			if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
 				return -EIO;
 
 			dev_dbg(&client->dev, "Setting gain from %d to %lu\n",
-				reg_read(client, MT9V022_ANALOG_GAIN), gain);
-			if (reg_write(client, MT9V022_ANALOG_GAIN, gain) < 0)
+				reg_read(client, MT9V022_ANALOG_GAIN), gain_val);
+			if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0)
 				return -EIO;
 		}
-		break;
-	case V4L2_CID_EXPOSURE:
-		/* mt9v022 has maximum == default */
-		if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
-			return -EINVAL;
-		else {
-			unsigned long range = qctrl->maximum - qctrl->minimum;
-			unsigned long shutter = ((ctrl->value - qctrl->minimum) *
-						 479 + range / 2) / range + 1;
+		return 0;
+	case V4L2_CID_EXPOSURE_AUTO:
+		if (ctrl->val == V4L2_EXPOSURE_AUTO) {
+			data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1);
+		} else {
+			struct v4l2_ctrl *exp = mt9v022->exposure;
+			unsigned long range = exp->maximum - exp->minimum;
+			unsigned long shutter = ((exp->val - exp->minimum) *
+					479 + range / 2) / range + 1;
+
 			/*
 			 * The user wants to set shutter width manually, hope,
 			 * she knows, what she's doing... Switch AEC off.
 			 */
-
-			if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1) < 0)
+			data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1);
+			if (data < 0)
 				return -EIO;
-
 			dev_dbg(&client->dev, "Shutter width from %d to %lu\n",
-				reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH),
-				shutter);
+					reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH),
+					shutter);
 			if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH,
-				      shutter) < 0)
+						shutter) < 0)
 				return -EIO;
 		}
-		break;
-	case V4L2_CID_AUTOGAIN:
-		if (ctrl->value)
-			data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2);
-		else
-			data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2);
-		if (data < 0)
-			return -EIO;
-		break;
-	case V4L2_CID_EXPOSURE_AUTO:
-		if (ctrl->value)
-			data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1);
-		else
-			data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1);
-		if (data < 0)
-			return -EIO;
-		break;
+		return 0;
 	}
-	return 0;
+	return -EINVAL;
 }
 
 /*
  * Interface active, can use i2c. If it fails, it can indeed mean, that
  * this wasn't our capture interface, so, we wait for the right one
  */
-static int mt9v022_video_probe(struct soc_camera_device *icd,
-			       struct i2c_client *client)
+static int mt9v022_video_probe(struct i2c_client *client)
 {
 	struct mt9v022 *mt9v022 = to_mt9v022(client);
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 	s32 data;
 	int ret;
 	unsigned long flags;
 
-	/* We must have a parent by now. And it cannot be a wrong one. */
-	BUG_ON(!icd->parent ||
-	       to_soc_camera_host(icd->parent)->nr != icd->iface);
-
 	/* Read out the chip version register */
 	data = reg_read(client, MT9V022_CHIP_VERSION);
 
@@ -805,16 +643,6 @@ ei2c:
 	return ret;
 }
 
-static void mt9v022_video_remove(struct soc_camera_device *icd)
-{
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-
-	dev_dbg(icd->pdev, "Video removed: %p, %p\n",
-		icd->parent, icd->vdev);
-	if (icl->free_bus)
-		icl->free_bus(icl);
-}
-
 static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -825,9 +653,12 @@ static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
 	return 0;
 }
 
+static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = {
+	.g_volatile_ctrl = mt9v022_g_volatile_ctrl,
+	.s_ctrl = mt9v022_s_ctrl,
+};
+
 static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = {
-	.g_ctrl		= mt9v022_g_ctrl,
-	.s_ctrl		= mt9v022_s_ctrl,
 	.g_chip_ident	= mt9v022_g_chip_ident,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.g_register	= mt9v022_g_register,
@@ -848,6 +679,72 @@ static int mt9v022_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
 	return 0;
 }
 
+static int mt9v022_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+	cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE |
+		V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+		V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+		V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+		V4L2_MBUS_DATA_ACTIVE_HIGH;
+	cfg->type = V4L2_MBUS_PARALLEL;
+	cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+	return 0;
+}
+
+static int mt9v022_s_mbus_config(struct v4l2_subdev *sd,
+				 const struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+	struct mt9v022 *mt9v022 = to_mt9v022(client);
+	unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+	unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample;
+	int ret;
+	u16 pixclk = 0;
+
+	if (icl->set_bus_param) {
+		ret = icl->set_bus_param(icl, 1 << (bps - 1));
+		if (ret)
+			return ret;
+	} else if (bps != 10) {
+		/*
+		 * Without board specific bus width settings we only support the
+		 * sensors native bus width
+		 */
+		return -EINVAL;
+	}
+
+	if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+		pixclk |= 0x10;
+
+	if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH))
+		pixclk |= 0x1;
+
+	if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH))
+		pixclk |= 0x2;
+
+	ret = reg_write(client, MT9V022_PIXCLK_FV_LV, pixclk);
+	if (ret < 0)
+		return ret;
+
+	if (!(flags & V4L2_MBUS_MASTER))
+		mt9v022->chip_control &= ~0x8;
+
+	ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
+	if (ret < 0)
+		return ret;
+
+	dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
+		pixclk, mt9v022->chip_control);
+
+	return 0;
+}
+
 static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
 	.s_stream	= mt9v022_s_stream,
 	.s_mbus_fmt	= mt9v022_s_fmt,
@@ -857,6 +754,8 @@ static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
 	.g_crop		= mt9v022_g_crop,
 	.cropcap	= mt9v022_cropcap,
 	.enum_mbus_fmt	= mt9v022_enum_fmt,
+	.g_mbus_config	= mt9v022_g_mbus_config,
+	.s_mbus_config	= mt9v022_s_mbus_config,
 };
 
 static struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = {
@@ -873,17 +772,10 @@ static int mt9v022_probe(struct i2c_client *client,
 			 const struct i2c_device_id *did)
 {
 	struct mt9v022 *mt9v022;
-	struct soc_camera_device *icd = client->dev.platform_data;
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
-	struct soc_camera_link *icl;
 	int ret;
 
-	if (!icd) {
-		dev_err(&client->dev, "MT9V022: missing soc-camera data!\n");
-		return -EINVAL;
-	}
-
-	icl = to_soc_camera_link(icd);
 	if (!icl) {
 		dev_err(&client->dev, "MT9V022 driver needs platform data\n");
 		return -EINVAL;
@@ -900,10 +792,39 @@ static int mt9v022_probe(struct i2c_client *client,
 		return -ENOMEM;
 
 	v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops);
+	v4l2_ctrl_handler_init(&mt9v022->hdl, 6);
+	v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+	mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+			V4L2_CID_GAIN, 0, 127, 1, 64);
+
+	/*
+	 * Simulated autoexposure. If enabled, we calculate shutter width
+	 * ourselves in the driver based on vertical blanking and frame width
+	 */
+	mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl,
+			&mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+			V4L2_EXPOSURE_AUTO);
+	mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+			V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+
+	mt9v022->subdev.ctrl_handler = &mt9v022->hdl;
+	if (mt9v022->hdl.error) {
+		int err = mt9v022->hdl.error;
+
+		kfree(mt9v022);
+		return err;
+	}
+	v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure,
+				V4L2_EXPOSURE_MANUAL, true);
+	v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true);
 
 	mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT;
 
-	icd->ops		= &mt9v022_ops;
 	/*
 	 * MT9V022 _really_ corrupts the first read out line.
 	 * TODO: verify on i.MX31
@@ -914,9 +835,9 @@ static int mt9v022_probe(struct i2c_client *client,
 	mt9v022->rect.width	= MT9V022_MAX_WIDTH;
 	mt9v022->rect.height	= MT9V022_MAX_HEIGHT;
 
-	ret = mt9v022_video_probe(icd, client);
+	ret = mt9v022_video_probe(client);
 	if (ret) {
-		icd->ops = NULL;
+		v4l2_ctrl_handler_free(&mt9v022->hdl);
 		kfree(mt9v022);
 	}
 
@@ -926,10 +847,12 @@ static int mt9v022_probe(struct i2c_client *client,
 static int mt9v022_remove(struct i2c_client *client)
 {
 	struct mt9v022 *mt9v022 = to_mt9v022(client);
-	struct soc_camera_device *icd = client->dev.platform_data;
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 
-	icd->ops = NULL;
-	mt9v022_video_remove(icd);
+	v4l2_device_unregister_subdev(&mt9v022->subdev);
+	if (icl->free_bus)
+		icl->free_bus(icl);
+	v4l2_ctrl_handler_free(&mt9v022->hdl);
 	kfree(mt9v022);
 
 	return 0;
diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c
index 087db12a3a67..18e94c7d2be8 100644
--- a/drivers/media/video/mx1_camera.c
+++ b/drivers/media/video/mx1_camera.c
@@ -78,11 +78,10 @@
 #define CSI_IRQ_MASK	(CSISR_SFF_OR_INT | CSISR_RFF_OR_INT | \
 			CSISR_STATFF_INT | CSISR_RXFF_INT | CSISR_SOF_INT)
 
-#define CSI_BUS_FLAGS	(SOCAM_MASTER | SOCAM_HSYNC_ACTIVE_HIGH | \
-			SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | \
-			SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \
-			SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_LOW | \
-			SOCAM_DATAWIDTH_8)
+#define CSI_BUS_FLAGS	(V4L2_MBUS_MASTER | V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+			V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+			V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+			V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_LOW)
 
 #define MAX_VIDEO_MEM 16	/* Video memory limit in megabytes */
 
@@ -490,59 +489,73 @@ static int mx1_camera_set_crop(struct soc_camera_device *icd,
 
 static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
 {
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx1_camera_dev *pcdev = ici->priv;
-	unsigned long camera_flags, common_flags;
+	struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+	unsigned long common_flags;
 	unsigned int csicr1;
 	int ret;
 
-	camera_flags = icd->ops->query_bus_param(icd);
-
 	/* MX1 supports only 8bit buswidth */
-	common_flags = soc_camera_bus_param_compatible(camera_flags,
-						       CSI_BUS_FLAGS);
-	if (!common_flags)
-		return -EINVAL;
+	ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+	if (!ret) {
+		common_flags = soc_mbus_config_compatible(&cfg, CSI_BUS_FLAGS);
+		if (!common_flags) {
+			dev_warn(icd->parent,
+				 "Flags incompatible: camera 0x%x, host 0x%x\n",
+				 cfg.flags, CSI_BUS_FLAGS);
+			return -EINVAL;
+		}
+	} else if (ret != -ENOIOCTLCMD) {
+		return ret;
+	} else {
+		common_flags = CSI_BUS_FLAGS;
+	}
 
 	/* Make choises, based on platform choice */
-	if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
-		(common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+	if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+		(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
 			if (!pcdev->pdata ||
 			     pcdev->pdata->flags & MX1_CAMERA_VSYNC_HIGH)
-				common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+				common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
 			else
-				common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+				common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
 	}
 
-	if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
-		(common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+	if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+		(common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
 			if (!pcdev->pdata ||
 			     pcdev->pdata->flags & MX1_CAMERA_PCLK_RISING)
-				common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+				common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
 			else
-				common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+				common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
 	}
 
-	if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) &&
-		(common_flags & SOCAM_DATA_ACTIVE_LOW)) {
+	if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
+		(common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
 			if (!pcdev->pdata ||
 			     pcdev->pdata->flags & MX1_CAMERA_DATA_HIGH)
-				common_flags &= ~SOCAM_DATA_ACTIVE_LOW;
+				common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
 			else
-				common_flags &= ~SOCAM_DATA_ACTIVE_HIGH;
+				common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
 	}
 
-	ret = icd->ops->set_bus_param(icd, common_flags);
-	if (ret < 0)
+	cfg.flags = common_flags;
+	ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+	if (ret < 0 && ret != -ENOIOCTLCMD) {
+		dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+			common_flags, ret);
 		return ret;
+	}
 
 	csicr1 = __raw_readl(pcdev->base + CSICR1);
 
-	if (common_flags & SOCAM_PCLK_SAMPLE_RISING)
+	if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
 		csicr1 |= CSICR1_REDGE;
-	if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH)
+	if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
 		csicr1 |= CSICR1_SOF_POL;
-	if (common_flags & SOCAM_DATA_ACTIVE_LOW)
+	if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
 		csicr1 |= CSICR1_DATA_POL;
 
 	__raw_writel(csicr1, pcdev->base + CSICR1);
diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
index ec2410c0c806..a803d9ea8fd6 100644
--- a/drivers/media/video/mx2_camera.c
+++ b/drivers/media/video/mx2_camera.c
@@ -686,16 +686,15 @@ static void mx2_camera_init_videobuf(struct videobuf_queue *q,
 			icd, &icd->video_lock);
 }
 
-#define MX2_BUS_FLAGS	(SOCAM_DATAWIDTH_8 | \
-			SOCAM_MASTER | \
-			SOCAM_VSYNC_ACTIVE_HIGH | \
-			SOCAM_VSYNC_ACTIVE_LOW | \
-			SOCAM_HSYNC_ACTIVE_HIGH | \
-			SOCAM_HSYNC_ACTIVE_LOW | \
-			SOCAM_PCLK_SAMPLE_RISING | \
-			SOCAM_PCLK_SAMPLE_FALLING | \
-			SOCAM_DATA_ACTIVE_HIGH | \
-			SOCAM_DATA_ACTIVE_LOW)
+#define MX2_BUS_FLAGS	(V4L2_MBUS_MASTER | \
+			V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+			V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+			V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+			V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+			V4L2_MBUS_PCLK_SAMPLE_RISING | \
+			V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+			V4L2_MBUS_DATA_ACTIVE_HIGH | \
+			V4L2_MBUS_DATA_ACTIVE_LOW)
 
 static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
 {
@@ -770,46 +769,59 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
 static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
 		__u32 pixfmt)
 {
-	struct soc_camera_host *ici =
-		to_soc_camera_host(icd->parent);
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx2_camera_dev *pcdev = ici->priv;
-	unsigned long camera_flags, common_flags;
-	int ret = 0;
+	struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+	unsigned long common_flags;
+	int ret;
 	int bytesperline;
 	u32 csicr1 = pcdev->csicr1;
 
-	camera_flags = icd->ops->query_bus_param(icd);
-
-	common_flags = soc_camera_bus_param_compatible(camera_flags,
-				MX2_BUS_FLAGS);
-	if (!common_flags)
-		return -EINVAL;
+	ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+	if (!ret) {
+		common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS);
+		if (!common_flags) {
+			dev_warn(icd->parent,
+				 "Flags incompatible: camera 0x%x, host 0x%x\n",
+				 cfg.flags, MX2_BUS_FLAGS);
+			return -EINVAL;
+		}
+	} else if (ret != -ENOIOCTLCMD) {
+		return ret;
+	} else {
+		common_flags = MX2_BUS_FLAGS;
+	}
 
-	if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
-	    (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+	if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+	    (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
 		if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH)
-			common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+			common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
 		else
-			common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+			common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
 	}
 
-	if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
-	    (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+	if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+	    (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
 		if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING)
-			common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+			common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
 		else
-			common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+			common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
 	}
 
-	ret = icd->ops->set_bus_param(icd, common_flags);
-	if (ret < 0)
+	cfg.flags = common_flags;
+	ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+	if (ret < 0 && ret != -ENOIOCTLCMD) {
+		dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+			common_flags, ret);
 		return ret;
+	}
 
-	if (common_flags & SOCAM_PCLK_SAMPLE_RISING)
+	if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
 		csicr1 |= CSICR1_REDGE;
-	if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH)
+	if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
 		csicr1 |= CSICR1_SOF_POL;
-	if (common_flags & SOCAM_HSYNC_ACTIVE_HIGH)
+	if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
 		csicr1 |= CSICR1_HSYNC_POL;
 	if (pcdev->platform_flags & MX2_CAMERA_SWAP16)
 		csicr1 |= CSICR1_SWAP16_EN;
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
index c8e958a07e91..f96f92f00f92 100644
--- a/drivers/media/video/mx3_camera.c
+++ b/drivers/media/video/mx3_camera.c
@@ -109,10 +109,12 @@ struct mx3_camera_dev {
 
 	unsigned long		platform_flags;
 	unsigned long		mclk;
+	u16			width_flags;	/* max 15 bits */
 
 	struct list_head	capture;
 	spinlock_t		lock;		/* Protects video buffer lists */
 	struct mx3_camera_buffer *active;
+	size_t			buf_total;
 	struct vb2_alloc_ctx	*alloc_ctx;
 	enum v4l2_field		field;
 	int			sequence;
@@ -190,79 +192,53 @@ static void mx3_cam_dma_done(void *arg)
  * Calculate the __buffer__ (not data) size and number of buffers.
  */
 static int mx3_videobuf_setup(struct vb2_queue *vq,
+			const struct v4l2_format *fmt,
 			unsigned int *count, unsigned int *num_planes,
 			unsigned int sizes[], void *alloc_ctxs[])
 {
 	struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
-	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
-						icd->current_fmt->host_fmt);
-
-	if (bytes_per_line < 0)
-		return bytes_per_line;
+	int bytes_per_line;
+	unsigned int height;
 
 	if (!mx3_cam->idmac_channel[0])
 		return -EINVAL;
 
-	*num_planes = 1;
-
-	mx3_cam->sequence = 0;
-	sizes[0] = bytes_per_line * icd->user_height;
-	alloc_ctxs[0] = mx3_cam->alloc_ctx;
-
-	if (!*count)
-		*count = 32;
-
-	if (sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
-		*count = MAX_VIDEO_MEM * 1024 * 1024 / sizes[0];
-
-	return 0;
-}
-
-static int mx3_videobuf_prepare(struct vb2_buffer *vb)
-{
-	struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-	struct mx3_camera_dev *mx3_cam = ici->priv;
-	struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
-	struct scatterlist *sg;
-	struct mx3_camera_buffer *buf;
-	size_t new_size;
-	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+	if (fmt) {
+		const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
+								fmt->fmt.pix.pixelformat);
+		if (!xlate)
+			return -EINVAL;
+		bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+							 xlate->host_fmt);
+		height = fmt->fmt.pix.height;
+	} else {
+		/* Called from VIDIOC_REQBUFS or in compatibility mode */
+		bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
 						icd->current_fmt->host_fmt);
-
+		height = icd->user_height;
+	}
 	if (bytes_per_line < 0)
 		return bytes_per_line;
 
-	buf = to_mx3_vb(vb);
-	sg = &buf->sg;
-
-	new_size = bytes_per_line * icd->user_height;
+	sizes[0] = bytes_per_line * height;
 
-	if (vb2_plane_size(vb, 0) < new_size) {
-		dev_err(icd->parent, "Buffer too small (%lu < %zu)\n",
-			vb2_plane_size(vb, 0), new_size);
-		return -ENOBUFS;
-	}
+	alloc_ctxs[0] = mx3_cam->alloc_ctx;
 
-	if (buf->state == CSI_BUF_NEEDS_INIT) {
-		sg_dma_address(sg)	= vb2_dma_contig_plane_dma_addr(vb, 0);
-		sg_dma_len(sg)		= new_size;
+	if (!vq->num_buffers)
+		mx3_cam->sequence = 0;
 
-		buf->txd = ichan->dma_chan.device->device_prep_slave_sg(
-			&ichan->dma_chan, sg, 1, DMA_FROM_DEVICE,
-			DMA_PREP_INTERRUPT);
-		if (!buf->txd)
-			return -EIO;
-
-		buf->txd->callback_param	= buf->txd;
-		buf->txd->callback		= mx3_cam_dma_done;
+	if (!*count)
+		*count = 2;
 
-		buf->state = CSI_BUF_PREPARED;
-	}
+	/* If *num_planes != 0, we have already verified *count. */
+	if (!*num_planes &&
+	    sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024)
+		*count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) /
+			sizes[0];
 
-	vb2_set_plane_payload(vb, 0, new_size);
+	*num_planes = 1;
 
 	return 0;
 }
@@ -286,28 +262,58 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	struct mx3_camera_buffer *buf = to_mx3_vb(vb);
-	struct dma_async_tx_descriptor *txd = buf->txd;
-	struct idmac_channel *ichan = to_idmac_chan(txd->chan);
+	struct scatterlist *sg = &buf->sg;
+	struct dma_async_tx_descriptor *txd;
+	struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
 	struct idmac_video_param *video = &ichan->params.video;
-	dma_cookie_t cookie;
-	u32 fourcc = icd->current_fmt->host_fmt->fourcc;
+	const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
+	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, host_fmt);
 	unsigned long flags;
+	dma_cookie_t cookie;
+	size_t new_size;
+
+	BUG_ON(bytes_per_line <= 0);
+
+	new_size = bytes_per_line * icd->user_height;
+
+	if (vb2_plane_size(vb, 0) < new_size) {
+		dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n",
+			vb->v4l2_buf.index, vb2_plane_size(vb, 0), new_size);
+		goto error;
+	}
+
+	if (buf->state == CSI_BUF_NEEDS_INIT) {
+		sg_dma_address(sg)	= vb2_dma_contig_plane_dma_addr(vb, 0);
+		sg_dma_len(sg)		= new_size;
+
+		txd = ichan->dma_chan.device->device_prep_slave_sg(
+			&ichan->dma_chan, sg, 1, DMA_FROM_DEVICE,
+			DMA_PREP_INTERRUPT);
+		if (!txd)
+			goto error;
+
+		txd->callback_param	= txd;
+		txd->callback		= mx3_cam_dma_done;
+
+		buf->state		= CSI_BUF_PREPARED;
+		buf->txd		= txd;
+	} else {
+		txd = buf->txd;
+	}
+
+	vb2_set_plane_payload(vb, 0, new_size);
 
 	/* This is the configuration of one sg-element */
-	video->out_pixel_fmt	= fourcc_to_ipu_pix(fourcc);
+	video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc);
 
 	if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) {
 		/*
-		 * If the IPU DMA channel is configured to transport
-		 * generic 8-bit data, we have to set up correctly the
-		 * geometry parameters upon the current pixel format.
-		 * So, since the DMA horizontal parameters are expressed
-		 * in bytes not pixels, convert these in the right unit.
+		 * If the IPU DMA channel is configured to transfer generic
+		 * 8-bit data, we have to set up the geometry parameters
+		 * correctly, according to the current pixel format. The DMA
+		 * horizontal parameters in this case are expressed in bytes,
+		 * not in pixels.
 		 */
-		int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
-						icd->current_fmt->host_fmt);
-		BUG_ON(bytes_per_line <= 0);
-
 		video->out_width	= bytes_per_line;
 		video->out_height	= icd->user_height;
 		video->out_stride	= bytes_per_line;
@@ -351,6 +357,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
 		mx3_cam->active = NULL;
 
 	spin_unlock_irqrestore(&mx3_cam->lock, flags);
+error:
 	vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
 }
 
@@ -384,17 +391,24 @@ static void mx3_videobuf_release(struct vb2_buffer *vb)
 	}
 
 	spin_unlock_irqrestore(&mx3_cam->lock, flags);
+
+	mx3_cam->buf_total -= vb2_plane_size(vb, 0);
 }
 
 static int mx3_videobuf_init(struct vb2_buffer *vb)
 {
+	struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct mx3_camera_dev *mx3_cam = ici->priv;
 	struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+
 	/* This is for locking debugging only */
 	INIT_LIST_HEAD(&buf->queue);
 	sg_init_table(&buf->sg, 1);
 
 	buf->state = CSI_BUF_NEEDS_INIT;
-	buf->txd = NULL;
+
+	mx3_cam->buf_total += vb2_plane_size(vb, 0);
 
 	return 0;
 }
@@ -405,13 +419,12 @@ static int mx3_stop_streaming(struct vb2_queue *q)
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
 	struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
-	struct dma_chan *chan;
 	struct mx3_camera_buffer *buf, *tmp;
 	unsigned long flags;
 
 	if (ichan) {
-		chan = &ichan->dma_chan;
-		chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+		struct dma_chan *chan = &ichan->dma_chan;
+		chan->device->device_control(chan, DMA_PAUSE, 0);
 	}
 
 	spin_lock_irqsave(&mx3_cam->lock, flags);
@@ -419,8 +432,8 @@ static int mx3_stop_streaming(struct vb2_queue *q)
 	mx3_cam->active = NULL;
 
 	list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) {
-		buf->state = CSI_BUF_NEEDS_INIT;
 		list_del_init(&buf->queue);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
 	}
 
 	spin_unlock_irqrestore(&mx3_cam->lock, flags);
@@ -430,7 +443,6 @@ static int mx3_stop_streaming(struct vb2_queue *q)
 
 static struct vb2_ops mx3_videobuf_ops = {
 	.queue_setup	= mx3_videobuf_setup,
-	.buf_prepare	= mx3_videobuf_prepare,
 	.buf_queue	= mx3_videobuf_queue,
 	.buf_cleanup	= mx3_videobuf_release,
 	.buf_init	= mx3_videobuf_init,
@@ -514,6 +526,7 @@ static int mx3_camera_add_device(struct soc_camera_device *icd)
 
 	mx3_camera_activate(mx3_cam, icd);
 
+	mx3_cam->buf_total = 0;
 	mx3_cam->icd = icd;
 
 	dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
@@ -548,58 +561,27 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam,
 			       unsigned char buswidth, unsigned long *flags)
 {
 	/*
+	 * If requested data width is supported by the platform, use it or any
+	 * possible lower value - i.MX31 is smart enough to shift bits
+	 */
+	if (buswidth > fls(mx3_cam->width_flags))
+		return -EINVAL;
+
+	/*
 	 * Platform specified synchronization and pixel clock polarities are
 	 * only a recommendation and are only used during probing. MX3x
 	 * camera interface only works in master mode, i.e., uses HSYNC and
 	 * VSYNC signals from the sensor
 	 */
-	*flags = SOCAM_MASTER |
-		SOCAM_HSYNC_ACTIVE_HIGH |
-		SOCAM_HSYNC_ACTIVE_LOW |
-		SOCAM_VSYNC_ACTIVE_HIGH |
-		SOCAM_VSYNC_ACTIVE_LOW |
-		SOCAM_PCLK_SAMPLE_RISING |
-		SOCAM_PCLK_SAMPLE_FALLING |
-		SOCAM_DATA_ACTIVE_HIGH |
-		SOCAM_DATA_ACTIVE_LOW;
-
-	/*
-	 * If requested data width is supported by the platform, use it or any
-	 * possible lower value - i.MX31 is smart enough to schift bits
-	 */
-	if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
-		*flags |= SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_10 |
-			SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4;
-	else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
-		*flags |= SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8 |
-			SOCAM_DATAWIDTH_4;
-	else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
-		*flags |= SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4;
-	else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
-		*flags |= SOCAM_DATAWIDTH_4;
-
-	switch (buswidth) {
-	case 15:
-		if (!(*flags & SOCAM_DATAWIDTH_15))
-			return -EINVAL;
-		break;
-	case 10:
-		if (!(*flags & SOCAM_DATAWIDTH_10))
-			return -EINVAL;
-		break;
-	case 8:
-		if (!(*flags & SOCAM_DATAWIDTH_8))
-			return -EINVAL;
-		break;
-	case 4:
-		if (!(*flags & SOCAM_DATAWIDTH_4))
-			return -EINVAL;
-		break;
-	default:
-		dev_warn(mx3_cam->soc_host.v4l2_dev.dev,
-			 "Unsupported bus width %d\n", buswidth);
-		return -EINVAL;
-	}
+	*flags = V4L2_MBUS_MASTER |
+		V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+		V4L2_MBUS_HSYNC_ACTIVE_LOW |
+		V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+		V4L2_MBUS_VSYNC_ACTIVE_LOW |
+		V4L2_MBUS_PCLK_SAMPLE_RISING |
+		V4L2_MBUS_PCLK_SAMPLE_FALLING |
+		V4L2_MBUS_DATA_ACTIVE_HIGH |
+		V4L2_MBUS_DATA_ACTIVE_LOW;
 
 	return 0;
 }
@@ -607,9 +589,11 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam,
 static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
 				    const unsigned int depth)
 {
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
-	unsigned long bus_flags, camera_flags;
+	struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+	unsigned long bus_flags, common_flags;
 	int ret = test_platform_param(mx3_cam, depth, &bus_flags);
 
 	dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret);
@@ -617,15 +601,21 @@ static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
 	if (ret < 0)
 		return ret;
 
-	camera_flags = icd->ops->query_bus_param(icd);
-
-	ret = soc_camera_bus_param_compatible(camera_flags, bus_flags);
-	if (ret < 0)
-		dev_warn(icd->parent,
-			 "Flags incompatible: camera %lx, host %lx\n",
-			 camera_flags, bus_flags);
+	ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+	if (!ret) {
+		common_flags = soc_mbus_config_compatible(&cfg,
+							  bus_flags);
+		if (!common_flags) {
+			dev_warn(icd->parent,
+				 "Flags incompatible: camera 0x%x, host 0x%lx\n",
+				 cfg.flags, bus_flags);
+			return -EINVAL;
+		}
+	} else if (ret != -ENOIOCTLCMD) {
+		return ret;
+	}
 
-	return ret;
+	return 0;
 }
 
 static bool chan_filter(struct dma_chan *chan, void *arg)
@@ -994,9 +984,11 @@ static int mx3_camera_querycap(struct soc_camera_host *ici,
 
 static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
 {
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct mx3_camera_dev *mx3_cam = ici->priv;
-	unsigned long bus_flags, camera_flags, common_flags;
+	struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+	unsigned long bus_flags, common_flags;
 	u32 dw, sens_conf;
 	const struct soc_mbus_pixelfmt *fmt;
 	int buswidth;
@@ -1008,83 +1000,76 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
 	if (!fmt)
 		return -EINVAL;
 
-	buswidth = fmt->bits_per_sample;
-	ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
-
 	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
 	if (!xlate) {
 		dev_warn(dev, "Format %x not found\n", pixfmt);
 		return -EINVAL;
 	}
 
+	buswidth = fmt->bits_per_sample;
+	ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
+
 	dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret);
 
 	if (ret < 0)
 		return ret;
 
-	camera_flags = icd->ops->query_bus_param(icd);
-
-	common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
-	dev_dbg(dev, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n",
-		camera_flags, bus_flags, common_flags);
-	if (!common_flags) {
-		dev_dbg(dev, "no common flags");
-		return -EINVAL;
+	ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+	if (!ret) {
+		common_flags = soc_mbus_config_compatible(&cfg,
+							  bus_flags);
+		if (!common_flags) {
+			dev_warn(icd->parent,
+				 "Flags incompatible: camera 0x%x, host 0x%lx\n",
+				 cfg.flags, bus_flags);
+			return -EINVAL;
+		}
+	} else if (ret != -ENOIOCTLCMD) {
+		return ret;
+	} else {
+		common_flags = bus_flags;
 	}
 
+	dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n",
+		cfg.flags, bus_flags, common_flags);
+
 	/* Make choices, based on platform preferences */
-	if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
-	    (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+	if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+	    (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
 		if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
-			common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+			common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
 		else
-			common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+			common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
 	}
 
-	if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
-	    (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+	if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+	    (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
 		if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
-			common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+			common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
 		else
-			common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+			common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
 	}
 
-	if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) &&
-	    (common_flags & SOCAM_DATA_ACTIVE_LOW)) {
+	if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
+	    (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
 		if (mx3_cam->platform_flags & MX3_CAMERA_DP)
-			common_flags &= ~SOCAM_DATA_ACTIVE_HIGH;
+			common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
 		else
-			common_flags &= ~SOCAM_DATA_ACTIVE_LOW;
+			common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
 	}
 
-	if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
-	    (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+	if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+	    (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
 		if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
-			common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+			common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
 		else
-			common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+			common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
 	}
 
-	/*
-	 * Make the camera work in widest common mode, we'll take care of
-	 * the rest
-	 */
-	if (common_flags & SOCAM_DATAWIDTH_15)
-		common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
-			SOCAM_DATAWIDTH_15;
-	else if (common_flags & SOCAM_DATAWIDTH_10)
-		common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
-			SOCAM_DATAWIDTH_10;
-	else if (common_flags & SOCAM_DATAWIDTH_8)
-		common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
-			SOCAM_DATAWIDTH_8;
-	else
-		common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
-			SOCAM_DATAWIDTH_4;
-
-	ret = icd->ops->set_bus_param(icd, common_flags);
-	if (ret < 0) {
-		dev_dbg(dev, "camera set_bus_param(%lx) returned %d\n",
+	cfg.flags = common_flags;
+	ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+	if (ret < 0 && ret != -ENOIOCTLCMD) {
+		dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
 			common_flags, ret);
 		return ret;
 	}
@@ -1108,13 +1093,13 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
 	/* This has been set in mx3_camera_activate(), but we clear it above */
 	sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
 
-	if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
+	if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
 		sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
-	if (common_flags & SOCAM_HSYNC_ACTIVE_LOW)
+	if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
 		sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
-	if (common_flags & SOCAM_VSYNC_ACTIVE_LOW)
+	if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
 		sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
-	if (common_flags & SOCAM_DATA_ACTIVE_LOW)
+	if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
 		sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
 
 	/* Just do what we're asked to do */
@@ -1199,6 +1184,14 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev)
 			 "data widths, using default 8 bit\n");
 		mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8;
 	}
+	if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
+		mx3_cam->width_flags = 1 << 3;
+	if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+		mx3_cam->width_flags |= 1 << 7;
+	if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+		mx3_cam->width_flags |= 1 << 9;
+	if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+		mx3_cam->width_flags |= 1 << 14;
 
 	mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000;
 	if (!mx3_cam->mclk) {
@@ -1281,8 +1274,6 @@ static int __devexit mx3_camera_remove(struct platform_device *pdev)
 
 	dmaengine_put();
 
-	dev_info(&pdev->dev, "i.MX3x Camera driver unloaded\n");
-
 	return 0;
 }
 
diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c
index 30d8896bb710..9c5c19f142de 100644
--- a/drivers/media/video/omap/omap_vout.c
+++ b/drivers/media/video/omap/omap_vout.c
@@ -833,6 +833,15 @@ static void omap_vout_buffer_release(struct videobuf_queue *q,
 /*
  *  File operations
  */
+static unsigned int omap_vout_poll(struct file *file,
+				   struct poll_table_struct *wait)
+{
+	struct omap_vout_device *vout = file->private_data;
+	struct videobuf_queue *q = &vout->vbq;
+
+	return videobuf_poll_stream(file, q, wait);
+}
+
 static void omap_vout_vm_open(struct vm_area_struct *vma)
 {
 	struct omap_vout_device *vout = vma->vm_private_data;
@@ -1861,6 +1870,7 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = {
 
 static const struct v4l2_file_operations omap_vout_fops = {
 	.owner 		= THIS_MODULE,
+	.poll		= omap_vout_poll,
 	.unlocked_ioctl	= video_ioctl2,
 	.mmap 		= omap_vout_mmap,
 	.open 		= omap_vout_open,
diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c
index 8a947e603aca..e87ae2f634b2 100644
--- a/drivers/media/video/omap1_camera.c
+++ b/drivers/media/video/omap1_camera.c
@@ -102,10 +102,10 @@
 /* end of OMAP1 Camera Interface registers */
 
 
-#define SOCAM_BUS_FLAGS	(SOCAM_MASTER | \
-			SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | \
-			SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \
-			SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8)
+#define SOCAM_BUS_FLAGS	(V4L2_MBUS_MASTER | \
+			V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+			V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+			V4L2_MBUS_DATA_ACTIVE_HIGH)
 
 
 #define FIFO_SIZE		((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1)
@@ -1438,41 +1438,55 @@ static int omap1_cam_querycap(struct soc_camera_host *ici,
 static int omap1_cam_set_bus_param(struct soc_camera_device *icd,
 		__u32 pixfmt)
 {
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	struct device *dev = icd->parent;
 	struct soc_camera_host *ici = to_soc_camera_host(dev);
 	struct omap1_cam_dev *pcdev = ici->priv;
 	const struct soc_camera_format_xlate *xlate;
 	const struct soc_mbus_pixelfmt *fmt;
-	unsigned long camera_flags, common_flags;
+	struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+	unsigned long common_flags;
 	u32 ctrlclock, mode;
 	int ret;
 
-	camera_flags = icd->ops->query_bus_param(icd);
-
-	common_flags = soc_camera_bus_param_compatible(camera_flags,
-			SOCAM_BUS_FLAGS);
-	if (!common_flags)
-		return -EINVAL;
+	ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+	if (!ret) {
+		common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS);
+		if (!common_flags) {
+			dev_warn(dev,
+				 "Flags incompatible: camera 0x%x, host 0x%x\n",
+				 cfg.flags, SOCAM_BUS_FLAGS);
+			return -EINVAL;
+		}
+	} else if (ret != -ENOIOCTLCMD) {
+		return ret;
+	} else {
+		common_flags = SOCAM_BUS_FLAGS;
+	}
 
 	/* Make choices, possibly based on platform configuration */
-	if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
-			(common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+	if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+			(common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
 		if (!pcdev->pdata ||
 				pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
-			common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+			common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
 		else
-			common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+			common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
 	}
 
-	ret = icd->ops->set_bus_param(icd, common_flags);
-	if (ret < 0)
+	cfg.flags = common_flags;
+	ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+	if (ret < 0 && ret != -ENOIOCTLCMD) {
+		dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
+			common_flags, ret);
 		return ret;
+	}
 
 	ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
 	if (ctrlclock & LCLK_EN)
 		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
 
-	if (common_flags & SOCAM_PCLK_SAMPLE_RISING) {
+	if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) {
 		dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
 		ctrlclock |= POLCLK;
 	} else {
@@ -1565,10 +1579,10 @@ static int __init omap1_cam_probe(struct platform_device *pdev)
 	pcdev->clk = clk;
 
 	pcdev->pdata = pdev->dev.platform_data;
-	pcdev->pflags = pcdev->pdata->flags;
-
-	if (pcdev->pdata)
+	if (pcdev->pdata) {
+		pcdev->pflags = pcdev->pdata->flags;
 		pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000;
+	}
 
 	switch (pcdev->camexclk) {
 	case 6000000:
@@ -1578,6 +1592,7 @@ static int __init omap1_cam_probe(struct platform_device *pdev)
 	case 24000000:
 		break;
 	default:
+		/* pcdev->camexclk != 0 => pcdev->pdata != NULL */
 		dev_warn(&pdev->dev,
 				"Incorrect sensor clock frequency %ld kHz, "
 				"should be one of 0, 6, 8, 9.6, 12 or 24 MHz, "
@@ -1585,8 +1600,7 @@ static int __init omap1_cam_probe(struct platform_device *pdev)
 				pcdev->pdata->camexclk_khz);
 		pcdev->camexclk = 0;
 	case 0:
-		dev_info(&pdev->dev,
-				"Not providing sensor clock\n");
+		dev_info(&pdev->dev, "Not providing sensor clock\n");
 	}
 
 	INIT_LIST_HEAD(&pcdev->capture);
@@ -1716,5 +1730,5 @@ MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg");
 MODULE_DESCRIPTION("OMAP1 Camera Interface driver");
 MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
 MODULE_LICENSE("GPL v2");
-MODULE_LICENSE(DRIVER_VERSION);
+MODULE_VERSION(DRIVER_VERSION);
 MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c
index 678e1252047a..b818cacf420f 100644
--- a/drivers/media/video/omap3isp/isp.c
+++ b/drivers/media/video/omap3isp/isp.c
@@ -1704,6 +1704,7 @@ static int isp_register_entities(struct isp_device *isp)
 	isp->media_dev.dev = isp->dev;
 	strlcpy(isp->media_dev.model, "TI OMAP3 ISP",
 		sizeof(isp->media_dev.model));
+	isp->media_dev.hw_revision = isp->revision;
 	isp->media_dev.link_notify = isp_pipeline_link_notify;
 	ret = media_device_register(&isp->media_dev);
 	if (ret < 0) {
@@ -2210,6 +2211,8 @@ error:
 	regulator_put(isp->isp_csiphy2.vdd);
 	regulator_put(isp->isp_csiphy1.vdd);
 	platform_set_drvdata(pdev, NULL);
+
+	mutex_destroy(&isp->isp_mutex);
 	kfree(isp);
 
 	return ret;
diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c
index 253fdcce2df2..b0b0fa5a3572 100644
--- a/drivers/media/video/omap3isp/ispccdc.c
+++ b/drivers/media/video/omap3isp/ispccdc.c
@@ -1836,7 +1836,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
 		 * callers to request an output size bigger than the input size
 		 * up to the nearest multiple of 16.
 		 */
-		fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15);
+		fmt->width = clamp_t(u32, width, 32, fmt->width + 15);
 		fmt->width &= ~15;
 		fmt->height = clamp_t(u32, height, 32, fmt->height);
 		break;
@@ -2152,6 +2152,37 @@ static const struct media_entity_operations ccdc_media_ops = {
 	.link_setup = ccdc_link_setup,
 };
 
+void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc)
+{
+	v4l2_device_unregister_subdev(&ccdc->subdev);
+	omap3isp_video_unregister(&ccdc->video_out);
+}
+
+int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
+	struct v4l2_device *vdev)
+{
+	int ret;
+
+	/* Register the subdev and video node. */
+	ret = v4l2_device_register_subdev(vdev, &ccdc->subdev);
+	if (ret < 0)
+		goto error;
+
+	ret = omap3isp_video_register(&ccdc->video_out, vdev);
+	if (ret < 0)
+		goto error;
+
+	return 0;
+
+error:
+	omap3isp_ccdc_unregister_entities(ccdc);
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP CCDC initialisation and cleanup
+ */
+
 /*
  * ccdc_init_entities - Initialize V4L2 subdev and media entity
  * @ccdc: ISP CCDC module
@@ -2193,50 +2224,23 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc)
 
 	ret = omap3isp_video_init(&ccdc->video_out, "CCDC");
 	if (ret < 0)
-		return ret;
+		goto error_video;
 
 	/* Connect the CCDC subdev to the video node. */
 	ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF,
 			&ccdc->video_out.video.entity, 0, 0);
 	if (ret < 0)
-		return ret;
-
-	return 0;
-}
-
-void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc)
-{
-	media_entity_cleanup(&ccdc->subdev.entity);
-
-	v4l2_device_unregister_subdev(&ccdc->subdev);
-	omap3isp_video_unregister(&ccdc->video_out);
-}
-
-int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
-	struct v4l2_device *vdev)
-{
-	int ret;
-
-	/* Register the subdev and video node. */
-	ret = v4l2_device_register_subdev(vdev, &ccdc->subdev);
-	if (ret < 0)
-		goto error;
-
-	ret = omap3isp_video_register(&ccdc->video_out, vdev);
-	if (ret < 0)
-		goto error;
+		goto error_link;
 
 	return 0;
 
-error:
-	omap3isp_ccdc_unregister_entities(ccdc);
+error_link:
+	omap3isp_video_cleanup(&ccdc->video_out);
+error_video:
+	media_entity_cleanup(me);
 	return ret;
 }
 
-/* -----------------------------------------------------------------------------
- * ISP CCDC initialisation and cleanup
- */
-
 /*
  * omap3isp_ccdc_init - CCDC module initialization.
  * @dev: Device pointer specific to the OMAP3 ISP.
@@ -2248,6 +2252,7 @@ error:
 int omap3isp_ccdc_init(struct isp_device *isp)
 {
 	struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
+	int ret;
 
 	spin_lock_init(&ccdc->lock);
 	init_waitqueue_head(&ccdc->wait);
@@ -2276,7 +2281,13 @@ int omap3isp_ccdc_init(struct isp_device *isp)
 	ccdc->update = OMAP3ISP_CCDC_BLCLAMP;
 	ccdc_apply_controls(ccdc);
 
-	return ccdc_init_entities(ccdc);
+	ret = ccdc_init_entities(ccdc);
+	if (ret < 0) {
+		mutex_destroy(&ccdc->ioctl_lock);
+		return ret;
+	}
+
+	return 0;
 }
 
 /*
@@ -2287,6 +2298,9 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp)
 {
 	struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
 
+	omap3isp_video_cleanup(&ccdc->video_out);
+	media_entity_cleanup(&ccdc->subdev.entity);
+
 	/* Free LSC requests. As the CCDC is stopped there's no active request,
 	 * so only the pending request and the free queue need to be handled.
 	 */
@@ -2296,4 +2310,6 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp)
 
 	if (ccdc->fpc.fpcaddr != 0)
 		omap_iommu_vfree(isp->domain, isp->iommu, ccdc->fpc.fpcaddr);
+
+	mutex_destroy(&ccdc->ioctl_lock);
 }
diff --git a/drivers/media/video/omap3isp/ispccp2.c b/drivers/media/video/omap3isp/ispccp2.c
index fa1d09b0ad98..904ca8c8b17f 100644
--- a/drivers/media/video/omap3isp/ispccp2.c
+++ b/drivers/media/video/omap3isp/ispccp2.c
@@ -1032,6 +1032,48 @@ static const struct media_entity_operations ccp2_media_ops = {
 };
 
 /*
+ * omap3isp_ccp2_unregister_entities - Unregister media entities: subdev
+ * @ccp2: Pointer to ISP CCP2 device
+ */
+void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2)
+{
+	v4l2_device_unregister_subdev(&ccp2->subdev);
+	omap3isp_video_unregister(&ccp2->video_in);
+}
+
+/*
+ * omap3isp_ccp2_register_entities - Register the subdev media entity
+ * @ccp2: Pointer to ISP CCP2 device
+ * @vdev: Pointer to v4l device
+ * return negative error code or zero on success
+ */
+
+int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2,
+				    struct v4l2_device *vdev)
+{
+	int ret;
+
+	/* Register the subdev and video nodes. */
+	ret = v4l2_device_register_subdev(vdev, &ccp2->subdev);
+	if (ret < 0)
+		goto error;
+
+	ret = omap3isp_video_register(&ccp2->video_in, vdev);
+	if (ret < 0)
+		goto error;
+
+	return 0;
+
+error:
+	omap3isp_ccp2_unregister_entities(ccp2);
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP ccp2 initialisation and cleanup
+ */
+
+/*
  * ccp2_init_entities - Initialize ccp2 subdev and media entity.
  * @ccp2: Pointer to ISP CCP2 device
  * return negative error code or zero on success
@@ -1083,72 +1125,23 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2)
 
 	ret = omap3isp_video_init(&ccp2->video_in, "CCP2");
 	if (ret < 0)
-		return ret;
+		goto error_video;
 
 	/* Connect the video node to the ccp2 subdev. */
 	ret = media_entity_create_link(&ccp2->video_in.video.entity, 0,
 				       &ccp2->subdev.entity, CCP2_PAD_SINK, 0);
 	if (ret < 0)
-		return ret;
+		goto error_link;
 
 	return 0;
-}
 
-/*
- * omap3isp_ccp2_unregister_entities - Unregister media entities: subdev
- * @ccp2: Pointer to ISP CCP2 device
- */
-void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2)
-{
+error_link:
+	omap3isp_video_cleanup(&ccp2->video_in);
+error_video:
 	media_entity_cleanup(&ccp2->subdev.entity);
-
-	v4l2_device_unregister_subdev(&ccp2->subdev);
-	omap3isp_video_unregister(&ccp2->video_in);
-}
-
-/*
- * omap3isp_ccp2_register_entities - Register the subdev media entity
- * @ccp2: Pointer to ISP CCP2 device
- * @vdev: Pointer to v4l device
- * return negative error code or zero on success
- */
-
-int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2,
-				    struct v4l2_device *vdev)
-{
-	int ret;
-
-	/* Register the subdev and video nodes. */
-	ret = v4l2_device_register_subdev(vdev, &ccp2->subdev);
-	if (ret < 0)
-		goto error;
-
-	ret = omap3isp_video_register(&ccp2->video_in, vdev);
-	if (ret < 0)
-		goto error;
-
-	return 0;
-
-error:
-	omap3isp_ccp2_unregister_entities(ccp2);
 	return ret;
 }
 
-/* -----------------------------------------------------------------------------
- * ISP ccp2 initialisation and cleanup
- */
-
-/*
- * omap3isp_ccp2_cleanup - CCP2 un-initialization
- * @isp : Pointer to ISP device
- */
-void omap3isp_ccp2_cleanup(struct isp_device *isp)
-{
-	struct isp_ccp2_device *ccp2 = &isp->isp_ccp2;
-
-	regulator_put(ccp2->vdds_csib);
-}
-
 /*
  * omap3isp_ccp2_init - CCP2 initialization.
  * @isp : Pointer to ISP device
@@ -1184,13 +1177,25 @@ int omap3isp_ccp2_init(struct isp_device *isp)
 	}
 
 	ret = ccp2_init_entities(ccp2);
-	if (ret < 0)
-		goto out;
+	if (ret < 0) {
+		regulator_put(ccp2->vdds_csib);
+		return ret;
+	}
 
 	ccp2_reset(ccp2);
-out:
-	if (ret)
-		omap3isp_ccp2_cleanup(isp);
+	return 0;
+}
 
-	return ret;
+/*
+ * omap3isp_ccp2_cleanup - CCP2 un-initialization
+ * @isp : Pointer to ISP device
+ */
+void omap3isp_ccp2_cleanup(struct isp_device *isp)
+{
+	struct isp_ccp2_device *ccp2 = &isp->isp_ccp2;
+
+	omap3isp_video_cleanup(&ccp2->video_in);
+	media_entity_cleanup(&ccp2->subdev.entity);
+
+	regulator_put(ccp2->vdds_csib);
 }
diff --git a/drivers/media/video/omap3isp/ispcsi2.c b/drivers/media/video/omap3isp/ispcsi2.c
index 69161a682b3d..0c5f1cb9d99d 100644
--- a/drivers/media/video/omap3isp/ispcsi2.c
+++ b/drivers/media/video/omap3isp/ispcsi2.c
@@ -1187,6 +1187,37 @@ static const struct media_entity_operations csi2_media_ops = {
 	.link_setup = csi2_link_setup,
 };
 
+void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2)
+{
+	v4l2_device_unregister_subdev(&csi2->subdev);
+	omap3isp_video_unregister(&csi2->video_out);
+}
+
+int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2,
+				    struct v4l2_device *vdev)
+{
+	int ret;
+
+	/* Register the subdev and video nodes. */
+	ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
+	if (ret < 0)
+		goto error;
+
+	ret = omap3isp_video_register(&csi2->video_out, vdev);
+	if (ret < 0)
+		goto error;
+
+	return 0;
+
+error:
+	omap3isp_csi2_unregister_entities(csi2);
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP CSI2 initialisation and cleanup
+ */
+
 /*
  * csi2_init_entities - Initialize subdev and media entity.
  * @csi2: Pointer to csi2 structure.
@@ -1228,57 +1259,23 @@ static int csi2_init_entities(struct isp_csi2_device *csi2)
 
 	ret = omap3isp_video_init(&csi2->video_out, "CSI2a");
 	if (ret < 0)
-		return ret;
+		goto error_video;
 
 	/* Connect the CSI2 subdev to the video node. */
 	ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
 				       &csi2->video_out.video.entity, 0, 0);
 	if (ret < 0)
-		return ret;
+		goto error_link;
 
 	return 0;
-}
 
-void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2)
-{
+error_link:
+	omap3isp_video_cleanup(&csi2->video_out);
+error_video:
 	media_entity_cleanup(&csi2->subdev.entity);
-
-	v4l2_device_unregister_subdev(&csi2->subdev);
-	omap3isp_video_unregister(&csi2->video_out);
-}
-
-int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2,
-				    struct v4l2_device *vdev)
-{
-	int ret;
-
-	/* Register the subdev and video nodes. */
-	ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
-	if (ret < 0)
-		goto error;
-
-	ret = omap3isp_video_register(&csi2->video_out, vdev);
-	if (ret < 0)
-		goto error;
-
-	return 0;
-
-error:
-	omap3isp_csi2_unregister_entities(csi2);
 	return ret;
 }
 
-/* -----------------------------------------------------------------------------
- * ISP CSI2 initialisation and cleanup
- */
-
-/*
- * omap3isp_csi2_cleanup - Routine for module driver cleanup
- */
-void omap3isp_csi2_cleanup(struct isp_device *isp)
-{
-}
-
 /*
  * omap3isp_csi2_init - Routine for module driver init
  */
@@ -1298,7 +1295,7 @@ int omap3isp_csi2_init(struct isp_device *isp)
 
 	ret = csi2_init_entities(csi2a);
 	if (ret < 0)
-		goto fail;
+		return ret;
 
 	if (isp->revision == ISP_REVISION_15_0) {
 		csi2c->isp = isp;
@@ -1311,7 +1308,15 @@ int omap3isp_csi2_init(struct isp_device *isp)
 	}
 
 	return 0;
-fail:
-	omap3isp_csi2_cleanup(isp);
-	return ret;
+}
+
+/*
+ * omap3isp_csi2_cleanup - Routine for module driver cleanup
+ */
+void omap3isp_csi2_cleanup(struct isp_device *isp)
+{
+	struct isp_csi2_device *csi2a = &isp->isp_csi2a;
+
+	omap3isp_video_cleanup(&csi2a->video_out);
+	media_entity_cleanup(&csi2a->subdev.entity);
 }
diff --git a/drivers/media/video/omap3isp/isph3a_aewb.c b/drivers/media/video/omap3isp/isph3a_aewb.c
index 8068cefd8d89..a3c76bf18175 100644
--- a/drivers/media/video/omap3isp/isph3a_aewb.c
+++ b/drivers/media/video/omap3isp/isph3a_aewb.c
@@ -370,5 +370,5 @@ void omap3isp_h3a_aewb_cleanup(struct isp_device *isp)
 {
 	kfree(isp->isp_aewb.priv);
 	kfree(isp->isp_aewb.recover_priv);
-	omap3isp_stat_free(&isp->isp_aewb);
+	omap3isp_stat_cleanup(&isp->isp_aewb);
 }
diff --git a/drivers/media/video/omap3isp/isph3a_af.c b/drivers/media/video/omap3isp/isph3a_af.c
index ba54d0acdecf..58e0bc414899 100644
--- a/drivers/media/video/omap3isp/isph3a_af.c
+++ b/drivers/media/video/omap3isp/isph3a_af.c
@@ -425,5 +425,5 @@ void omap3isp_h3a_af_cleanup(struct isp_device *isp)
 {
 	kfree(isp->isp_af.priv);
 	kfree(isp->isp_af.recover_priv);
-	omap3isp_stat_free(&isp->isp_af);
+	omap3isp_stat_cleanup(&isp->isp_af);
 }
diff --git a/drivers/media/video/omap3isp/isphist.c b/drivers/media/video/omap3isp/isphist.c
index 1743856b30d1..1163907bcddc 100644
--- a/drivers/media/video/omap3isp/isphist.c
+++ b/drivers/media/video/omap3isp/isphist.c
@@ -516,5 +516,5 @@ void omap3isp_hist_cleanup(struct isp_device *isp)
 	if (HIST_USING_DMA(&isp->isp_hist))
 		omap_free_dma(isp->isp_hist.dma_ch);
 	kfree(isp->isp_hist.priv);
-	omap3isp_stat_free(&isp->isp_hist);
+	omap3isp_stat_cleanup(&isp->isp_hist);
 }
diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c
index aba537af87e4..ccb876fe023f 100644
--- a/drivers/media/video/omap3isp/isppreview.c
+++ b/drivers/media/video/omap3isp/isppreview.c
@@ -76,9 +76,51 @@ static struct omap3isp_prev_csc flr_prev_csc = {
 
 #define DEF_DETECT_CORRECT_VAL	0xe
 
-#define PREV_MIN_WIDTH		64
-#define PREV_MIN_HEIGHT		8
-#define PREV_MAX_HEIGHT		16384
+/*
+ * Margins and image size limits.
+ *
+ * The preview engine crops several rows and columns internally depending on
+ * which filters are enabled. To avoid format changes when the filters are
+ * enabled or disabled (which would prevent them from being turned on or off
+ * during streaming), the driver assumes all the filters are enabled when
+ * computing sink crop and source format limits.
+ *
+ * If a filter is disabled, additional cropping is automatically added at the
+ * preview engine input by the driver to avoid overflow at line and frame end.
+ * This is completely transparent for applications.
+ *
+ * Median filter		4 pixels
+ * Noise filter,
+ * Faulty pixels correction	4 pixels, 4 lines
+ * CFA filter			4 pixels, 4 lines in Bayer mode
+ *					  2 lines in other modes
+ * Color suppression		2 pixels
+ * or luma enhancement
+ * -------------------------------------------------------------
+ * Maximum total		14 pixels, 8 lines
+ *
+ * The color suppression and luma enhancement filters are applied after bayer to
+ * YUV conversion. They thus can crop one pixel on the left and one pixel on the
+ * right side of the image without changing the color pattern. When both those
+ * filters are disabled, the driver must crop the two pixels on the same side of
+ * the image to avoid changing the bayer pattern. The left margin is thus set to
+ * 8 pixels and the right margin to 6 pixels.
+ */
+
+#define PREV_MARGIN_LEFT	8
+#define PREV_MARGIN_RIGHT	6
+#define PREV_MARGIN_TOP		4
+#define PREV_MARGIN_BOTTOM	4
+
+#define PREV_MIN_IN_WIDTH	64
+#define PREV_MIN_IN_HEIGHT	8
+#define PREV_MAX_IN_HEIGHT	16384
+
+#define PREV_MIN_OUT_WIDTH	0
+#define PREV_MIN_OUT_HEIGHT	0
+#define PREV_MAX_OUT_WIDTH	1280
+#define PREV_MAX_OUT_WIDTH_ES2	3300
+#define PREV_MAX_OUT_WIDTH_3630	4096
 
 /*
  * Coeficient Tables for the submodules in Preview.
@@ -979,52 +1021,36 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average)
  * enabled when reporting source pad formats to userspace. If this assumption is
  * not true, rows and columns must be manually cropped at the preview engine
  * input to avoid overflows at the end of lines and frames.
+ *
+ * See the explanation at the PREV_MARGIN_* definitions for more details.
  */
 static void preview_config_input_size(struct isp_prev_device *prev)
 {
 	struct isp_device *isp = to_isp_device(prev);
 	struct prev_params *params = &prev->params;
-	struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK];
-	unsigned int sph = 0;
-	unsigned int eph = format->width - 1;
-	unsigned int slv = 0;
-	unsigned int elv = format->height - 1;
-
-	if (prev->input == PREVIEW_INPUT_CCDC) {
-		sph += 2;
-		eph -= 2;
+	unsigned int sph = prev->crop.left;
+	unsigned int eph = prev->crop.left + prev->crop.width - 1;
+	unsigned int slv = prev->crop.top;
+	unsigned int elv = prev->crop.top + prev->crop.height - 1;
+
+	if (params->features & PREV_CFA) {
+		sph -= 2;
+		eph += 2;
+		slv -= 2;
+		elv += 2;
 	}
-
-	/*
-	 * Median filter	4 pixels
-	 * Noise filter		4 pixels, 4 lines
-	 * or faulty pixels correction
-	 * CFA filter		4 pixels, 4 lines in Bayer mode
-	 *				  2 lines in other modes
-	 * Color suppression	2 pixels
-	 * or luma enhancement
-	 * -------------------------------------------------------------
-	 * Maximum total	14 pixels, 8 lines
-	 */
-
-	if (!(params->features & PREV_CFA)) {
-		sph += 2;
-		eph -= 2;
-		slv += 2;
-		elv -= 2;
+	if (params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER)) {
+		sph -= 2;
+		eph += 2;
+		slv -= 2;
+		elv += 2;
 	}
-	if (!(params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER))) {
-		sph += 2;
-		eph -= 2;
-		slv += 2;
-		elv -= 2;
+	if (params->features & PREV_HORZ_MEDIAN_FILTER) {
+		sph -= 2;
+		eph += 2;
 	}
-	if (!(params->features & PREV_HORZ_MEDIAN_FILTER)) {
-		sph += 2;
-		eph -= 2;
-	}
-	if (!(params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE)))
-		sph += 2;
+	if (params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE))
+		sph -= 2;
 
 	isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph,
 		       OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO);
@@ -1228,7 +1254,6 @@ static void preview_init_params(struct isp_prev_device *prev)
 	/* Init values */
 	params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS;
 	params->brightness = ISPPRV_BRIGHT_DEF * ISPPRV_BRIGHT_UNITS;
-	params->average = NO_AVE;
 	params->cfa.format = OMAP3ISP_CFAFMT_BAYER;
 	memcpy(params->cfa.table, cfa_coef_table,
 	       sizeof(params->cfa.table));
@@ -1281,14 +1306,14 @@ static unsigned int preview_max_out_width(struct isp_prev_device *prev)
 
 	switch (isp->revision) {
 	case ISP_REVISION_1_0:
-		return ISPPRV_MAXOUTPUT_WIDTH;
+		return PREV_MAX_OUT_WIDTH;
 
 	case ISP_REVISION_2_0:
 	default:
-		return ISPPRV_MAXOUTPUT_WIDTH_ES2;
+		return PREV_MAX_OUT_WIDTH_ES2;
 
 	case ISP_REVISION_15_0:
-		return ISPPRV_MAXOUTPUT_WIDTH_3630;
+		return PREV_MAX_OUT_WIDTH_3630;
 	}
 }
 
@@ -1296,8 +1321,6 @@ static void preview_configure(struct isp_prev_device *prev)
 {
 	struct isp_device *isp = to_isp_device(prev);
 	struct v4l2_mbus_framefmt *format;
-	unsigned int max_out_width;
-	unsigned int format_avg;
 
 	preview_setup_hw(prev);
 
@@ -1335,10 +1358,7 @@ static void preview_configure(struct isp_prev_device *prev)
 		preview_config_outlineoffset(prev,
 				ALIGN(format->width, 0x10) * 2);
 
-	max_out_width = preview_max_out_width(prev);
-
-	format_avg = fls(DIV_ROUND_UP(format->width, max_out_width) - 1);
-	preview_config_averager(prev, format_avg);
+	preview_config_averager(prev, 0);
 	preview_config_ycpos(prev, format->code);
 }
 
@@ -1597,6 +1617,16 @@ __preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
 		return &prev->formats[pad];
 }
 
+static struct v4l2_rect *
+__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
+		   enum v4l2_subdev_format_whence which)
+{
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return v4l2_subdev_get_try_crop(fh, PREV_PAD_SINK);
+	else
+		return &prev->crop;
+}
+
 /* previewer format descriptions */
 static const unsigned int preview_input_fmts[] = {
 	V4L2_MBUS_FMT_SGRBG10_1X10,
@@ -1611,24 +1641,25 @@ static const unsigned int preview_output_fmts[] = {
 };
 
 /*
- * preview_try_format - Handle try format by pad subdev method
- * @prev: ISP preview device
- * @fh : V4L2 subdev file handle
- * @pad: pad num
- * @fmt: pointer to v4l2 format structure
+ * preview_try_format - Validate a format
+ * @prev: ISP preview engine
+ * @fh: V4L2 subdev file handle
+ * @pad: pad number
+ * @fmt: format to be validated
+ * @which: try/active format selector
+ *
+ * Validate and adjust the given format for the given pad based on the preview
+ * engine limits and the format and crop rectangles on other pads.
  */
 static void preview_try_format(struct isp_prev_device *prev,
 			       struct v4l2_subdev_fh *fh, unsigned int pad,
 			       struct v4l2_mbus_framefmt *fmt,
 			       enum v4l2_subdev_format_whence which)
 {
-	struct v4l2_mbus_framefmt *format;
-	unsigned int max_out_width;
 	enum v4l2_mbus_pixelcode pixelcode;
+	struct v4l2_rect *crop;
 	unsigned int i;
 
-	max_out_width = preview_max_out_width(prev);
-
 	switch (pad) {
 	case PREV_PAD_SINK:
 		/* When reading data from the CCDC, the input size has already
@@ -1641,10 +1672,11 @@ static void preview_try_format(struct isp_prev_device *prev,
 		 * filter array interpolation.
 		 */
 		if (prev->input == PREVIEW_INPUT_MEMORY) {
-			fmt->width = clamp_t(u32, fmt->width, PREV_MIN_WIDTH,
-					     max_out_width * 8);
-			fmt->height = clamp_t(u32, fmt->height, PREV_MIN_HEIGHT,
-					      PREV_MAX_HEIGHT);
+			fmt->width = clamp_t(u32, fmt->width, PREV_MIN_IN_WIDTH,
+					     preview_max_out_width(prev));
+			fmt->height = clamp_t(u32, fmt->height,
+					      PREV_MIN_IN_HEIGHT,
+					      PREV_MAX_IN_HEIGHT);
 		}
 
 		fmt->colorspace = V4L2_COLORSPACE_SRGB;
@@ -1661,15 +1693,8 @@ static void preview_try_format(struct isp_prev_device *prev,
 
 	case PREV_PAD_SOURCE:
 		pixelcode = fmt->code;
-		format = __preview_get_format(prev, fh, PREV_PAD_SINK, which);
-		memcpy(fmt, format, sizeof(*fmt));
+		*fmt = *__preview_get_format(prev, fh, PREV_PAD_SINK, which);
 
-		/* The preview module output size is configurable through the
-		 * input interface (horizontal and vertical cropping) and the
-		 * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). In
-		 * spite of this, hardcode the output size to the biggest
-		 * possible value for simplicity reasons.
-		 */
 		switch (pixelcode) {
 		case V4L2_MBUS_FMT_YUYV8_1X16:
 		case V4L2_MBUS_FMT_UYVY8_1X16:
@@ -1681,31 +1706,14 @@ static void preview_try_format(struct isp_prev_device *prev,
 			break;
 		}
 
-		/* The TRM states (12.1.4.7.1.2) that 2 pixels must be cropped
-		 * from the left and right sides when the input source is the
-		 * CCDC. This seems not to be needed in practice, investigation
-		 * is required.
-		 */
-		if (prev->input == PREVIEW_INPUT_CCDC)
-			fmt->width -= 4;
-
-		/* The preview module can output a maximum of 3312 pixels
-		 * horizontally due to fixed memory-line sizes. Compute the
-		 * horizontal averaging factor accordingly. Note that the limit
-		 * applies to the noise filter and CFA interpolation blocks, so
-		 * it doesn't take cropping by further blocks into account.
-		 *
-		 * ES 1.0 hardware revision is limited to 1280 pixels
-		 * horizontally.
-		 */
-		fmt->width >>= fls(DIV_ROUND_UP(fmt->width, max_out_width) - 1);
-
-		/* Assume that all blocks are enabled and crop pixels and lines
-		 * accordingly. See preview_config_input_size() for more
-		 * information.
+		/* The preview module output size is configurable through the
+		 * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). This
+		 * is not supported yet, hardcode the output size to the crop
+		 * rectangle size.
 		 */
-		fmt->width -= 14;
-		fmt->height -= 8;
+		crop = __preview_get_crop(prev, fh, which);
+		fmt->width = crop->width;
+		fmt->height = crop->height;
 
 		fmt->colorspace = V4L2_COLORSPACE_JPEG;
 		break;
@@ -1715,6 +1723,49 @@ static void preview_try_format(struct isp_prev_device *prev,
 }
 
 /*
+ * preview_try_crop - Validate a crop rectangle
+ * @prev: ISP preview engine
+ * @sink: format on the sink pad
+ * @crop: crop rectangle to be validated
+ *
+ * The preview engine crops lines and columns for its internal operation,
+ * depending on which filters are enabled. Enforce minimum crop margins to
+ * handle that transparently for userspace.
+ *
+ * See the explanation at the PREV_MARGIN_* definitions for more details.
+ */
+static void preview_try_crop(struct isp_prev_device *prev,
+			     const struct v4l2_mbus_framefmt *sink,
+			     struct v4l2_rect *crop)
+{
+	unsigned int left = PREV_MARGIN_LEFT;
+	unsigned int right = sink->width - PREV_MARGIN_RIGHT;
+	unsigned int top = PREV_MARGIN_TOP;
+	unsigned int bottom = sink->height - PREV_MARGIN_BOTTOM;
+
+	/* When processing data on-the-fly from the CCDC, at least 2 pixels must
+	 * be cropped from the left and right sides of the image. As we don't
+	 * know which filters will be enabled, increase the left and right
+	 * margins by two.
+	 */
+	if (prev->input == PREVIEW_INPUT_CCDC) {
+		left += 2;
+		right -= 2;
+	}
+
+	/* Restrict left/top to even values to keep the Bayer pattern. */
+	crop->left &= ~1;
+	crop->top &= ~1;
+
+	crop->left = clamp_t(u32, crop->left, left, right - PREV_MIN_OUT_WIDTH);
+	crop->top = clamp_t(u32, crop->top, top, bottom - PREV_MIN_OUT_HEIGHT);
+	crop->width = clamp_t(u32, crop->width, PREV_MIN_OUT_WIDTH,
+			      right - crop->left);
+	crop->height = clamp_t(u32, crop->height, PREV_MIN_OUT_HEIGHT,
+			       bottom - crop->top);
+}
+
+/*
  * preview_enum_mbus_code - Handle pixel format enumeration
  * @sd     : pointer to v4l2 subdev structure
  * @fh     : V4L2 subdev file handle
@@ -1776,6 +1827,60 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd,
 }
 
 /*
+ * preview_get_crop - Retrieve the crop rectangle on a pad
+ * @sd: ISP preview V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @crop: crop rectangle
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int preview_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			    struct v4l2_subdev_crop *crop)
+{
+	struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+
+	/* Cropping is only supported on the sink pad. */
+	if (crop->pad != PREV_PAD_SINK)
+		return -EINVAL;
+
+	crop->rect = *__preview_get_crop(prev, fh, crop->which);
+	return 0;
+}
+
+/*
+ * preview_set_crop - Retrieve the crop rectangle on a pad
+ * @sd: ISP preview V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @crop: crop rectangle
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int preview_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			    struct v4l2_subdev_crop *crop)
+{
+	struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	/* Cropping is only supported on the sink pad. */
+	if (crop->pad != PREV_PAD_SINK)
+		return -EINVAL;
+
+	/* The crop rectangle can't be changed while streaming. */
+	if (prev->state != ISP_PIPELINE_STREAM_STOPPED)
+		return -EBUSY;
+
+	format = __preview_get_format(prev, fh, PREV_PAD_SINK, crop->which);
+	preview_try_crop(prev, format, &crop->rect);
+	*__preview_get_crop(prev, fh, crop->which) = crop->rect;
+
+	/* Update the source format. */
+	format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, crop->which);
+	preview_try_format(prev, fh, PREV_PAD_SOURCE, format, crop->which);
+
+	return 0;
+}
+
+/*
  * preview_get_format - Handle get format by pads subdev method
  * @sd : pointer to v4l2 subdev structure
  * @fh : V4L2 subdev file handle
@@ -1808,6 +1913,7 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
 {
 	struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
 	struct v4l2_mbus_framefmt *format;
+	struct v4l2_rect *crop;
 
 	format = __preview_get_format(prev, fh, fmt->pad, fmt->which);
 	if (format == NULL)
@@ -1818,9 +1924,18 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
 
 	/* Propagate the format from sink to source */
 	if (fmt->pad == PREV_PAD_SINK) {
+		/* Reset the crop rectangle. */
+		crop = __preview_get_crop(prev, fh, fmt->which);
+		crop->left = 0;
+		crop->top = 0;
+		crop->width = fmt->format.width;
+		crop->height = fmt->format.height;
+
+		preview_try_crop(prev, &fmt->format, crop);
+
+		/* Update the source format. */
 		format = __preview_get_format(prev, fh, PREV_PAD_SOURCE,
 					      fmt->which);
-		*format = fmt->format;
 		preview_try_format(prev, fh, PREV_PAD_SOURCE, format,
 				   fmt->which);
 	}
@@ -1869,6 +1984,8 @@ static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = {
 	.enum_frame_size = preview_enum_frame_size,
 	.get_fmt = preview_get_format,
 	.set_fmt = preview_set_format,
+	.get_crop = preview_get_crop,
+	.set_crop = preview_set_crop,
 };
 
 /* subdev operations */
@@ -1966,8 +2083,44 @@ static const struct media_entity_operations preview_media_ops = {
 	.link_setup = preview_link_setup,
 };
 
+void omap3isp_preview_unregister_entities(struct isp_prev_device *prev)
+{
+	v4l2_device_unregister_subdev(&prev->subdev);
+	omap3isp_video_unregister(&prev->video_in);
+	omap3isp_video_unregister(&prev->video_out);
+}
+
+int omap3isp_preview_register_entities(struct isp_prev_device *prev,
+	struct v4l2_device *vdev)
+{
+	int ret;
+
+	/* Register the subdev and video nodes. */
+	ret = v4l2_device_register_subdev(vdev, &prev->subdev);
+	if (ret < 0)
+		goto error;
+
+	ret = omap3isp_video_register(&prev->video_in, vdev);
+	if (ret < 0)
+		goto error;
+
+	ret = omap3isp_video_register(&prev->video_out, vdev);
+	if (ret < 0)
+		goto error;
+
+	return 0;
+
+error:
+	omap3isp_preview_unregister_entities(prev);
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP previewer initialisation and cleanup
+ */
+
 /*
- * review_init_entities - Initialize subdev and media entity.
+ * preview_init_entities - Initialize subdev and media entity.
  * @prev : Pointer to preview structure
  * return -ENOMEM or zero on success
  */
@@ -2024,69 +2177,34 @@ static int preview_init_entities(struct isp_prev_device *prev)
 
 	ret = omap3isp_video_init(&prev->video_in, "preview");
 	if (ret < 0)
-		return ret;
+		goto error_video_in;
 
 	ret = omap3isp_video_init(&prev->video_out, "preview");
 	if (ret < 0)
-		return ret;
+		goto error_video_out;
 
 	/* Connect the video nodes to the previewer subdev. */
 	ret = media_entity_create_link(&prev->video_in.video.entity, 0,
 			&prev->subdev.entity, PREV_PAD_SINK, 0);
 	if (ret < 0)
-		return ret;
+		goto error_link;
 
 	ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE,
 			&prev->video_out.video.entity, 0, 0);
 	if (ret < 0)
-		return ret;
+		goto error_link;
 
 	return 0;
-}
 
-void omap3isp_preview_unregister_entities(struct isp_prev_device *prev)
-{
+error_link:
+	omap3isp_video_cleanup(&prev->video_out);
+error_video_out:
+	omap3isp_video_cleanup(&prev->video_in);
+error_video_in:
 	media_entity_cleanup(&prev->subdev.entity);
-
-	v4l2_device_unregister_subdev(&prev->subdev);
-	v4l2_ctrl_handler_free(&prev->ctrls);
-	omap3isp_video_unregister(&prev->video_in);
-	omap3isp_video_unregister(&prev->video_out);
-}
-
-int omap3isp_preview_register_entities(struct isp_prev_device *prev,
-	struct v4l2_device *vdev)
-{
-	int ret;
-
-	/* Register the subdev and video nodes. */
-	ret = v4l2_device_register_subdev(vdev, &prev->subdev);
-	if (ret < 0)
-		goto error;
-
-	ret = omap3isp_video_register(&prev->video_in, vdev);
-	if (ret < 0)
-		goto error;
-
-	ret = omap3isp_video_register(&prev->video_out, vdev);
-	if (ret < 0)
-		goto error;
-
-	return 0;
-
-error:
-	omap3isp_preview_unregister_entities(prev);
 	return ret;
 }
 
-/* -----------------------------------------------------------------------------
- * ISP previewer initialisation and cleanup
- */
-
-void omap3isp_preview_cleanup(struct isp_device *isp)
-{
-}
-
 /*
  * isp_preview_init - Previewer initialization.
  * @dev : Pointer to ISP device
@@ -2095,19 +2213,20 @@ void omap3isp_preview_cleanup(struct isp_device *isp)
 int omap3isp_preview_init(struct isp_device *isp)
 {
 	struct isp_prev_device *prev = &isp->isp_prev;
-	int ret;
 
 	spin_lock_init(&prev->lock);
 	init_waitqueue_head(&prev->wait);
 	preview_init_params(prev);
 
-	ret = preview_init_entities(prev);
-	if (ret < 0)
-		goto out;
+	return preview_init_entities(prev);
+}
 
-out:
-	if (ret)
-		omap3isp_preview_cleanup(isp);
+void omap3isp_preview_cleanup(struct isp_device *isp)
+{
+	struct isp_prev_device *prev = &isp->isp_prev;
 
-	return ret;
+	v4l2_ctrl_handler_free(&prev->ctrls);
+	omap3isp_video_cleanup(&prev->video_in);
+	omap3isp_video_cleanup(&prev->video_out);
+	media_entity_cleanup(&prev->subdev.entity);
 }
diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h
index fa943bd05c7f..f54e775c2df4 100644
--- a/drivers/media/video/omap3isp/isppreview.h
+++ b/drivers/media/video/omap3isp/isppreview.h
@@ -45,11 +45,6 @@
 #define ISPPRV_CONTRAST_HIGH		0xFF
 #define ISPPRV_CONTRAST_UNITS		0x1
 
-#define NO_AVE				0x0
-#define AVE_2_PIX			0x1
-#define AVE_4_PIX			0x2
-#define AVE_8_PIX			0x3
-
 /* Features list */
 #define PREV_LUMA_ENHANCE		OMAP3ISP_PREV_LUMAENH
 #define PREV_INVERSE_ALAW		OMAP3ISP_PREV_INVALAW
@@ -106,7 +101,6 @@ enum preview_ycpos_mode {
  * @rgb2ycbcr: RGB to ycbcr parameters.
  * @hmed: Horizontal median filter.
  * @yclimit: YC limits parameters.
- * @average: Downsampling rate for averager.
  * @contrast: Contrast.
  * @brightness: Brightness.
  */
@@ -124,7 +118,6 @@ struct prev_params {
 	struct omap3isp_prev_csc rgb2ycbcr;
 	struct omap3isp_prev_hmed hmed;
 	struct omap3isp_prev_yclimit yclimit;
-	u8 average;
 	u8 contrast;
 	u8 brightness;
 };
@@ -159,6 +152,7 @@ struct isptables_update {
  * @subdev: V4L2 subdevice
  * @pads: Media entity pads
  * @formats: Active formats at the subdev pad
+ * @crop: Active crop rectangle
  * @input: Module currently connected to the input pad
  * @output: Bitmask of the active output
  * @video_in: Input video entity
@@ -177,6 +171,7 @@ struct isp_prev_device {
 	struct v4l2_subdev subdev;
 	struct media_pad pads[PREV_PADS_NUM];
 	struct v4l2_mbus_framefmt formats[PREV_PADS_NUM];
+	struct v4l2_rect crop;
 
 	struct v4l2_ctrl_handler ctrls;
 
diff --git a/drivers/media/video/omap3isp/ispreg.h b/drivers/media/video/omap3isp/ispreg.h
index 69f6af6f6b9c..084ea77d65a7 100644
--- a/drivers/media/video/omap3isp/ispreg.h
+++ b/drivers/media/video/omap3isp/ispreg.h
@@ -402,9 +402,6 @@
 #define ISPPRV_YENH_TABLE_ADDR		0x1000
 #define ISPPRV_CFA_TABLE_ADDR		0x1400
 
-#define ISPPRV_MAXOUTPUT_WIDTH		1280
-#define ISPPRV_MAXOUTPUT_WIDTH_ES2	3300
-#define ISPPRV_MAXOUTPUT_WIDTH_3630	4096
 #define ISPRSZ_MIN_OUTPUT		64
 #define ISPRSZ_MAX_OUTPUT		3312
 
diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c
index 0bb0f8cd36f5..50e593bfcfaf 100644
--- a/drivers/media/video/omap3isp/ispresizer.c
+++ b/drivers/media/video/omap3isp/ispresizer.c
@@ -1608,6 +1608,42 @@ static const struct media_entity_operations resizer_media_ops = {
 	.link_setup = resizer_link_setup,
 };
 
+void omap3isp_resizer_unregister_entities(struct isp_res_device *res)
+{
+	v4l2_device_unregister_subdev(&res->subdev);
+	omap3isp_video_unregister(&res->video_in);
+	omap3isp_video_unregister(&res->video_out);
+}
+
+int omap3isp_resizer_register_entities(struct isp_res_device *res,
+				       struct v4l2_device *vdev)
+{
+	int ret;
+
+	/* Register the subdev and video nodes. */
+	ret = v4l2_device_register_subdev(vdev, &res->subdev);
+	if (ret < 0)
+		goto error;
+
+	ret = omap3isp_video_register(&res->video_in, vdev);
+	if (ret < 0)
+		goto error;
+
+	ret = omap3isp_video_register(&res->video_out, vdev);
+	if (ret < 0)
+		goto error;
+
+	return 0;
+
+error:
+	omap3isp_resizer_unregister_entities(res);
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP resizer initialization and cleanup
+ */
+
 /*
  * resizer_init_entities - Initialize resizer subdev and media entity.
  * @res : Pointer to resizer device structure
@@ -1652,68 +1688,34 @@ static int resizer_init_entities(struct isp_res_device *res)
 
 	ret = omap3isp_video_init(&res->video_in, "resizer");
 	if (ret < 0)
-		return ret;
+		goto error_video_in;
 
 	ret = omap3isp_video_init(&res->video_out, "resizer");
 	if (ret < 0)
-		return ret;
+		goto error_video_out;
 
 	/* Connect the video nodes to the resizer subdev. */
 	ret = media_entity_create_link(&res->video_in.video.entity, 0,
 			&res->subdev.entity, RESZ_PAD_SINK, 0);
 	if (ret < 0)
-		return ret;
+		goto error_link;
 
 	ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE,
 			&res->video_out.video.entity, 0, 0);
 	if (ret < 0)
-		return ret;
+		goto error_link;
 
 	return 0;
-}
 
-void omap3isp_resizer_unregister_entities(struct isp_res_device *res)
-{
+error_link:
+	omap3isp_video_cleanup(&res->video_out);
+error_video_out:
+	omap3isp_video_cleanup(&res->video_in);
+error_video_in:
 	media_entity_cleanup(&res->subdev.entity);
-
-	v4l2_device_unregister_subdev(&res->subdev);
-	omap3isp_video_unregister(&res->video_in);
-	omap3isp_video_unregister(&res->video_out);
-}
-
-int omap3isp_resizer_register_entities(struct isp_res_device *res,
-				       struct v4l2_device *vdev)
-{
-	int ret;
-
-	/* Register the subdev and video nodes. */
-	ret = v4l2_device_register_subdev(vdev, &res->subdev);
-	if (ret < 0)
-		goto error;
-
-	ret = omap3isp_video_register(&res->video_in, vdev);
-	if (ret < 0)
-		goto error;
-
-	ret = omap3isp_video_register(&res->video_out, vdev);
-	if (ret < 0)
-		goto error;
-
-	return 0;
-
-error:
-	omap3isp_resizer_unregister_entities(res);
 	return ret;
 }
 
-/* -----------------------------------------------------------------------------
- * ISP resizer initialization and cleanup
- */
-
-void omap3isp_resizer_cleanup(struct isp_device *isp)
-{
-}
-
 /*
  * isp_resizer_init - Resizer initialization.
  * @isp : Pointer to ISP device
@@ -1722,17 +1724,17 @@ void omap3isp_resizer_cleanup(struct isp_device *isp)
 int omap3isp_resizer_init(struct isp_device *isp)
 {
 	struct isp_res_device *res = &isp->isp_res;
-	int ret;
 
 	init_waitqueue_head(&res->wait);
 	atomic_set(&res->stopping, 0);
-	ret = resizer_init_entities(res);
-	if (ret < 0)
-		goto out;
+	return resizer_init_entities(res);
+}
 
-out:
-	if (ret)
-		omap3isp_resizer_cleanup(isp);
+void omap3isp_resizer_cleanup(struct isp_device *isp)
+{
+	struct isp_res_device *res = &isp->isp_res;
 
-	return ret;
+	omap3isp_video_cleanup(&res->video_in);
+	omap3isp_video_cleanup(&res->video_out);
+	media_entity_cleanup(&res->subdev.entity);
 }
diff --git a/drivers/media/video/omap3isp/ispstat.c b/drivers/media/video/omap3isp/ispstat.c
index 732905552261..68d539456c55 100644
--- a/drivers/media/video/omap3isp/ispstat.c
+++ b/drivers/media/video/omap3isp/ispstat.c
@@ -1023,24 +1023,6 @@ void omap3isp_stat_dma_isr(struct ispstat *stat)
 	__stat_isr(stat, 1);
 }
 
-static int isp_stat_init_entities(struct ispstat *stat, const char *name,
-				  const struct v4l2_subdev_ops *sd_ops)
-{
-	struct v4l2_subdev *subdev = &stat->subdev;
-	struct media_entity *me = &subdev->entity;
-
-	v4l2_subdev_init(subdev, sd_ops);
-	snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name);
-	subdev->grp_id = 1 << 16;	/* group ID for isp subdevs */
-	subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
-	v4l2_set_subdevdata(subdev, stat);
-
-	stat->pad.flags = MEDIA_PAD_FL_SINK;
-	me->ops = NULL;
-
-	return media_entity_init(me, 1, &stat->pad, 0);
-}
-
 int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
 				  struct v4l2_fh *fh,
 				  struct v4l2_event_subscription *sub)
@@ -1062,7 +1044,6 @@ int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
 
 void omap3isp_stat_unregister_entities(struct ispstat *stat)
 {
-	media_entity_cleanup(&stat->subdev.entity);
 	v4l2_device_unregister_subdev(&stat->subdev);
 }
 
@@ -1072,21 +1053,50 @@ int omap3isp_stat_register_entities(struct ispstat *stat,
 	return v4l2_device_register_subdev(vdev, &stat->subdev);
 }
 
+static int isp_stat_init_entities(struct ispstat *stat, const char *name,
+				  const struct v4l2_subdev_ops *sd_ops)
+{
+	struct v4l2_subdev *subdev = &stat->subdev;
+	struct media_entity *me = &subdev->entity;
+
+	v4l2_subdev_init(subdev, sd_ops);
+	snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name);
+	subdev->grp_id = 1 << 16;	/* group ID for isp subdevs */
+	subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
+	v4l2_set_subdevdata(subdev, stat);
+
+	stat->pad.flags = MEDIA_PAD_FL_SINK;
+	me->ops = NULL;
+
+	return media_entity_init(me, 1, &stat->pad, 0);
+}
+
 int omap3isp_stat_init(struct ispstat *stat, const char *name,
 		       const struct v4l2_subdev_ops *sd_ops)
 {
+	int ret;
+
 	stat->buf = kcalloc(STAT_MAX_BUFS, sizeof(*stat->buf), GFP_KERNEL);
 	if (!stat->buf)
 		return -ENOMEM;
+
 	isp_stat_buf_clear(stat);
 	mutex_init(&stat->ioctl_lock);
 	atomic_set(&stat->buf_err, 0);
 
-	return isp_stat_init_entities(stat, name, sd_ops);
+	ret = isp_stat_init_entities(stat, name, sd_ops);
+	if (ret < 0) {
+		mutex_destroy(&stat->ioctl_lock);
+		kfree(stat->buf);
+	}
+
+	return ret;
 }
 
-void omap3isp_stat_free(struct ispstat *stat)
+void omap3isp_stat_cleanup(struct ispstat *stat)
 {
+	media_entity_cleanup(&stat->subdev.entity);
+	mutex_destroy(&stat->ioctl_lock);
 	isp_stat_bufs_free(stat);
 	kfree(stat->buf);
 }
diff --git a/drivers/media/video/omap3isp/ispstat.h b/drivers/media/video/omap3isp/ispstat.h
index d86da94fa50d..9b7c8654dc8a 100644
--- a/drivers/media/video/omap3isp/ispstat.h
+++ b/drivers/media/video/omap3isp/ispstat.h
@@ -144,7 +144,7 @@ int omap3isp_stat_request_statistics(struct ispstat *stat,
 				     struct omap3isp_stat_data *data);
 int omap3isp_stat_init(struct ispstat *stat, const char *name,
 		       const struct v4l2_subdev_ops *sd_ops);
-void omap3isp_stat_free(struct ispstat *stat);
+void omap3isp_stat_cleanup(struct ispstat *stat);
 int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
 				  struct v4l2_fh *fh,
 				  struct v4l2_event_subscription *sub);
diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c
index 0cb8a9f9d675..d1000723c5ae 100644
--- a/drivers/media/video/omap3isp/ispvideo.c
+++ b/drivers/media/video/omap3isp/ispvideo.c
@@ -1325,6 +1325,13 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
 	return 0;
 }
 
+void omap3isp_video_cleanup(struct isp_video *video)
+{
+	media_entity_cleanup(&video->video.entity);
+	mutex_destroy(&video->stream_lock);
+	mutex_destroy(&video->mutex);
+}
+
 int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev)
 {
 	int ret;
@@ -1341,8 +1348,6 @@ int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev)
 
 void omap3isp_video_unregister(struct isp_video *video)
 {
-	if (video_is_registered(&video->video)) {
-		media_entity_cleanup(&video->video.entity);
+	if (video_is_registered(&video->video))
 		video_unregister_device(&video->video);
-	}
 }
diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h
index 53160aa24e6e..08cbfa144e6e 100644
--- a/drivers/media/video/omap3isp/ispvideo.h
+++ b/drivers/media/video/omap3isp/ispvideo.h
@@ -190,6 +190,7 @@ struct isp_video_fh {
 				container_of(q, struct isp_video_fh, queue)
 
 int omap3isp_video_init(struct isp_video *video, const char *name);
+void omap3isp_video_cleanup(struct isp_video *video);
 int omap3isp_video_register(struct isp_video *video,
 			    struct v4l2_device *vdev);
 void omap3isp_video_unregister(struct isp_video *video);
diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c
index 9ce2fa037b94..b5247cb64fde 100644
--- a/drivers/media/video/ov2640.c
+++ b/drivers/media/video/ov2640.c
@@ -18,11 +18,13 @@
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
 #include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-subdev.h>
-#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
+#include <media/v4l2-ctrls.h>
 
 #define VAL_SET(x, mask, rshift, lshift)  \
 		((((x) >> rshift) & mask) << lshift)
@@ -299,12 +301,10 @@ struct ov2640_win_size {
 
 struct ov2640_priv {
 	struct v4l2_subdev		subdev;
-	struct ov2640_camera_info	*info;
+	struct v4l2_ctrl_handler	hdl;
 	enum v4l2_mbus_pixelcode	cfmt_code;
 	const struct ov2640_win_size	*win;
 	int				model;
-	u16				flag_vflip:1;
-	u16				flag_hflip:1;
 };
 
 /*
@@ -610,29 +610,6 @@ static enum v4l2_mbus_pixelcode ov2640_codes[] = {
 };
 
 /*
- * Supported controls
- */
-static const struct v4l2_queryctrl ov2640_controls[] = {
-	{
-		.id		= V4L2_CID_VFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Vertically",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	}, {
-		.id		= V4L2_CID_HFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Horizontally",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	},
-};
-
-/*
  * General functions
  */
 static struct ov2640_priv *to_ov2640(const struct i2c_client *client)
@@ -701,81 +678,23 @@ static int ov2640_s_stream(struct v4l2_subdev *sd, int enable)
 	return 0;
 }
 
-static int ov2640_set_bus_param(struct soc_camera_device *icd,
-				unsigned long flags)
-{
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-	unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK;
-
-	/* Only one width bit may be set */
-	if (!is_power_of_2(width_flag))
-		return -EINVAL;
-
-	if (icl->set_bus_param)
-		return icl->set_bus_param(icl, width_flag);
-
-	/*
-	 * Without board specific bus width settings we support only the
-	 * sensors native bus width witch are tested working
-	 */
-	if (width_flag & (SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8))
-		return 0;
-
-	return 0;
-}
-
-static unsigned long ov2640_query_bus_param(struct soc_camera_device *icd)
-{
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-	unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
-		SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
-		SOCAM_DATA_ACTIVE_HIGH;
-
-	if (icl->query_bus_param)
-		flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
-	else
-		flags |= SOCAM_DATAWIDTH_10;
-
-	return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static int ov2640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+	struct v4l2_subdev *sd =
+		&container_of(ctrl->handler, struct ov2640_priv, hdl)->subdev;
 	struct i2c_client  *client = v4l2_get_subdevdata(sd);
-	struct ov2640_priv *priv = to_ov2640(client);
-
-	switch (ctrl->id) {
-	case V4L2_CID_VFLIP:
-		ctrl->value = priv->flag_vflip;
-		break;
-	case V4L2_CID_HFLIP:
-		ctrl->value = priv->flag_hflip;
-		break;
-	}
-	return 0;
-}
-
-static int ov2640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-	struct i2c_client  *client = v4l2_get_subdevdata(sd);
-	struct ov2640_priv *priv = to_ov2640(client);
-	int ret = 0;
 	u8 val;
 
 	switch (ctrl->id) {
 	case V4L2_CID_VFLIP:
-		val = ctrl->value ? REG04_VFLIP_IMG : 0x00;
-		priv->flag_vflip = ctrl->value ? 1 : 0;
-		ret = ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val);
-		break;
+		val = ctrl->val ? REG04_VFLIP_IMG : 0x00;
+		return ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val);
 	case V4L2_CID_HFLIP:
-		val = ctrl->value ? REG04_HFLIP_IMG : 0x00;
-		priv->flag_hflip = ctrl->value ? 1 : 0;
-		ret = ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val);
-		break;
+		val = ctrl->val ? REG04_HFLIP_IMG : 0x00;
+		return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val);
 	}
 
-	return ret;
+	return -EINVAL;
 }
 
 static int ov2640_g_chip_ident(struct v4l2_subdev *sd,
@@ -1023,18 +942,13 @@ static int ov2640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
 	return 0;
 }
 
-static int ov2640_video_probe(struct soc_camera_device *icd,
-			      struct i2c_client *client)
+static int ov2640_video_probe(struct i2c_client *client)
 {
 	struct ov2640_priv *priv = to_ov2640(client);
 	u8 pid, ver, midh, midl;
 	const char *devname;
 	int ret;
 
-	/* We must have a parent by now. And it cannot be a wrong one. */
-	BUG_ON(!icd->parent ||
-	       to_soc_camera_host(icd->parent)->nr != icd->iface);
-
 	/*
 	 * check and show product ID and manufacturer ID
 	 */
@@ -1060,22 +974,17 @@ static int ov2640_video_probe(struct soc_camera_device *icd,
 		 "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
 		 devname, pid, ver, midh, midl);
 
-	return 0;
+	return v4l2_ctrl_handler_setup(&priv->hdl);
 
 err:
 	return ret;
 }
 
-static struct soc_camera_ops ov2640_ops = {
-	.set_bus_param		= ov2640_set_bus_param,
-	.query_bus_param	= ov2640_query_bus_param,
-	.controls		= ov2640_controls,
-	.num_controls		= ARRAY_SIZE(ov2640_controls),
+static const struct v4l2_ctrl_ops ov2640_ctrl_ops = {
+	.s_ctrl = ov2640_s_ctrl,
 };
 
 static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
-	.g_ctrl		= ov2640_g_ctrl,
-	.s_ctrl		= ov2640_s_ctrl,
 	.g_chip_ident	= ov2640_g_chip_ident,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.g_register	= ov2640_g_register,
@@ -1083,6 +992,21 @@ static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
 #endif
 };
 
+static int ov2640_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+	cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+		V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+		V4L2_MBUS_DATA_ACTIVE_HIGH;
+	cfg->type = V4L2_MBUS_PARALLEL;
+	cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+	return 0;
+}
+
 static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = {
 	.s_stream	= ov2640_s_stream,
 	.g_mbus_fmt	= ov2640_g_fmt,
@@ -1091,6 +1015,7 @@ static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = {
 	.cropcap	= ov2640_cropcap,
 	.g_crop		= ov2640_g_crop,
 	.enum_mbus_fmt	= ov2640_enum_fmt,
+	.g_mbus_config	= ov2640_g_mbus_config,
 };
 
 static struct v4l2_subdev_ops ov2640_subdev_ops = {
@@ -1104,18 +1029,11 @@ static struct v4l2_subdev_ops ov2640_subdev_ops = {
 static int ov2640_probe(struct i2c_client *client,
 			const struct i2c_device_id *did)
 {
-	struct ov2640_priv        *priv;
-	struct soc_camera_device  *icd = client->dev.platform_data;
-	struct i2c_adapter        *adapter = to_i2c_adapter(client->dev.parent);
-	struct soc_camera_link    *icl;
-	int                        ret;
-
-	if (!icd) {
-		dev_err(&adapter->dev, "OV2640: missing soc-camera data!\n");
-		return -EINVAL;
-	}
+	struct ov2640_priv	*priv;
+	struct soc_camera_link	*icl = soc_camera_i2c_to_link(client);
+	struct i2c_adapter	*adapter = to_i2c_adapter(client->dev.parent);
+	int			ret;
 
-	icl = to_soc_camera_link(icd);
 	if (!icl) {
 		dev_err(&adapter->dev,
 			"OV2640: Missing platform_data for driver\n");
@@ -1135,15 +1053,23 @@ static int ov2640_probe(struct i2c_client *client,
 		return -ENOMEM;
 	}
 
-	priv->info = icl->priv;
-
 	v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);
+	v4l2_ctrl_handler_init(&priv->hdl, 2);
+	v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+	priv->subdev.ctrl_handler = &priv->hdl;
+	if (priv->hdl.error) {
+		int err = priv->hdl.error;
 
-	icd->ops = &ov2640_ops;
+		kfree(priv);
+		return err;
+	}
 
-	ret = ov2640_video_probe(icd, client);
+	ret = ov2640_video_probe(client);
 	if (ret) {
-		icd->ops = NULL;
+		v4l2_ctrl_handler_free(&priv->hdl);
 		kfree(priv);
 	} else {
 		dev_info(&adapter->dev, "OV2640 Probed\n");
@@ -1155,9 +1081,9 @@ static int ov2640_probe(struct i2c_client *client,
 static int ov2640_remove(struct i2c_client *client)
 {
 	struct ov2640_priv       *priv = to_ov2640(client);
-	struct soc_camera_device *icd = client->dev.platform_data;
 
-	icd->ops = NULL;
+	v4l2_device_unregister_subdev(&priv->subdev);
+	v4l2_ctrl_handler_free(&priv->hdl);
 	kfree(priv);
 	return 0;
 }
diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c
index 349a4ad3ccc1..bb37ec80f274 100644
--- a/drivers/media/video/ov5642.c
+++ b/drivers/media/video/ov5642.c
@@ -14,14 +14,16 @@
  * published by the Free Software Foundation.
  */
 
+#include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
+#include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/videodev2.h>
 #include <linux/module.h>
+#include <linux/v4l2-mediabus.h>
 
 #include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-subdev.h>
 
@@ -35,7 +37,7 @@
 #define REG_WINDOW_START_Y_LOW		0x3803
 #define REG_WINDOW_WIDTH_HIGH		0x3804
 #define REG_WINDOW_WIDTH_LOW		0x3805
-#define REG_WINDOW_HEIGHT_HIGH 		0x3806
+#define REG_WINDOW_HEIGHT_HIGH		0x3806
 #define REG_WINDOW_HEIGHT_LOW		0x3807
 #define REG_OUT_WIDTH_HIGH		0x3808
 #define REG_OUT_WIDTH_LOW		0x3809
@@ -45,19 +47,44 @@
 #define REG_OUT_TOTAL_WIDTH_LOW		0x380d
 #define REG_OUT_TOTAL_HEIGHT_HIGH	0x380e
 #define REG_OUT_TOTAL_HEIGHT_LOW	0x380f
+#define REG_OUTPUT_FORMAT		0x4300
+#define REG_ISP_CTRL_01			0x5001
+#define REG_AVG_WINDOW_END_X_HIGH	0x5682
+#define REG_AVG_WINDOW_END_X_LOW	0x5683
+#define REG_AVG_WINDOW_END_Y_HIGH	0x5686
+#define REG_AVG_WINDOW_END_Y_LOW	0x5687
+
+/* active pixel array size */
+#define OV5642_SENSOR_SIZE_X	2592
+#define OV5642_SENSOR_SIZE_Y	1944
 
 /*
- * define standard resolution.
- * Works currently only for up to 720 lines
- * eg. 320x240, 640x480, 800x600, 1280x720, 2048x720
+ * About OV5642 resolution, cropping and binning:
+ * This sensor supports it all, at least in the feature description.
+ * Unfortunately, no combination of appropriate registers settings could make
+ * the chip work the intended way. As it works with predefined register lists,
+ * some undocumented registers are presumably changed there to achieve their
+ * goals.
+ * This driver currently only works for resolutions up to 720 lines with a
+ * 1:1 scale. Hopefully these restrictions will be removed in the future.
  */
+#define OV5642_MAX_WIDTH	OV5642_SENSOR_SIZE_X
+#define OV5642_MAX_HEIGHT	720
 
-#define OV5642_WIDTH		1280
-#define OV5642_HEIGHT		720
-#define OV5642_TOTAL_WIDTH	3200
-#define OV5642_TOTAL_HEIGHT	2000
-#define OV5642_SENSOR_SIZE_X	2592
-#define OV5642_SENSOR_SIZE_Y	1944
+/* default sizes */
+#define OV5642_DEFAULT_WIDTH	1280
+#define OV5642_DEFAULT_HEIGHT	OV5642_MAX_HEIGHT
+
+/* minimum extra blanking */
+#define BLANKING_EXTRA_WIDTH		500
+#define BLANKING_EXTRA_HEIGHT		20
+
+/*
+ * the sensor's autoexposure is buggy when setting total_height low.
+ * It tries to expose longer than 1 frame period without taking care of it
+ * and this leads to weird output. So we set 1000 lines as minimum.
+ */
+#define BLANKING_MIN_HEIGHT		1000
 
 struct regval_list {
 	u16 reg_num;
@@ -582,6 +609,11 @@ struct ov5642_datafmt {
 struct ov5642 {
 	struct v4l2_subdev		subdev;
 	const struct ov5642_datafmt	*fmt;
+	struct v4l2_rect                crop_rect;
+
+	/* blanking information */
+	int total_width;
+	int total_height;
 };
 
 static const struct ov5642_datafmt ov5642_colour_fmts[] = {
@@ -642,6 +674,21 @@ static int reg_write(struct i2c_client *client, u16 reg, u8 val)
 
 	return 0;
 }
+
+/*
+ * convenience function to write 16 bit register values that are split up
+ * into two consecutive high and low parts
+ */
+static int reg_write16(struct i2c_client *client, u16 reg, u16 val16)
+{
+	int ret;
+
+	ret = reg_write(client, reg, val16 >> 8);
+	if (ret)
+		return ret;
+	return reg_write(client, reg + 1, val16 & 0x00ff);
+}
+
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
 {
@@ -685,58 +732,55 @@ static int ov5642_write_array(struct i2c_client *client,
 	return 0;
 }
 
-static int ov5642_set_resolution(struct i2c_client *client)
+static int ov5642_set_resolution(struct v4l2_subdev *sd)
 {
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5642 *priv = to_ov5642(client);
+	int width = priv->crop_rect.width;
+	int height = priv->crop_rect.height;
+	int total_width = priv->total_width;
+	int total_height = priv->total_height;
+	int start_x = (OV5642_SENSOR_SIZE_X - width) / 2;
+	int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2;
 	int ret;
-	u8 start_x_high = ((OV5642_SENSOR_SIZE_X - OV5642_WIDTH) / 2) >> 8;
-	u8 start_x_low  = ((OV5642_SENSOR_SIZE_X - OV5642_WIDTH) / 2) & 0xff;
-	u8 start_y_high = ((OV5642_SENSOR_SIZE_Y - OV5642_HEIGHT) / 2) >> 8;
-	u8 start_y_low  = ((OV5642_SENSOR_SIZE_Y - OV5642_HEIGHT) / 2) & 0xff;
-
-	u8 width_high	= OV5642_WIDTH  >> 8;
-	u8 width_low	= OV5642_WIDTH  & 0xff;
-	u8 height_high	= OV5642_HEIGHT >> 8;
-	u8 height_low	= OV5642_HEIGHT & 0xff;
-
-	u8 total_width_high  = OV5642_TOTAL_WIDTH  >> 8;
-	u8 total_width_low   = OV5642_TOTAL_WIDTH  & 0xff;
-	u8 total_height_high = OV5642_TOTAL_HEIGHT >> 8;
-	u8 total_height_low  = OV5642_TOTAL_HEIGHT & 0xff;
-
-	ret = reg_write(client, REG_WINDOW_START_X_HIGH, start_x_high);
-	if (!ret)
-		ret = reg_write(client, REG_WINDOW_START_X_LOW, start_x_low);
-	if (!ret)
-		ret = reg_write(client, REG_WINDOW_START_Y_HIGH, start_y_high);
-	if (!ret)
-		ret = reg_write(client, REG_WINDOW_START_Y_LOW, start_y_low);
 
+	/*
+	 * This should set the starting point for cropping.
+	 * Doesn't work so far.
+	 */
+	ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x);
 	if (!ret)
-		ret = reg_write(client, REG_WINDOW_WIDTH_HIGH, width_high);
-	if (!ret)
-		ret = reg_write(client, REG_WINDOW_WIDTH_LOW , width_low);
-	if (!ret)
-		ret = reg_write(client, REG_WINDOW_HEIGHT_HIGH, height_high);
-	if (!ret)
-		ret = reg_write(client, REG_WINDOW_HEIGHT_LOW,  height_low);
+		ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y);
+	if (!ret) {
+		priv->crop_rect.left = start_x;
+		priv->crop_rect.top = start_y;
+	}
 
 	if (!ret)
-		ret = reg_write(client, REG_OUT_WIDTH_HIGH, width_high);
+		ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width);
 	if (!ret)
-		ret = reg_write(client, REG_OUT_WIDTH_LOW , width_low);
-	if (!ret)
-		ret = reg_write(client, REG_OUT_HEIGHT_HIGH, height_high);
+		ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height);
+	if (ret)
+		return ret;
+	priv->crop_rect.width = width;
+	priv->crop_rect.height = height;
+
+	/* Set the output window size. Only 1:1 scale is supported so far. */
+	ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width);
 	if (!ret)
-		ret = reg_write(client, REG_OUT_HEIGHT_LOW,  height_low);
+		ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height);
 
+	/* Total width = output size + blanking */
 	if (!ret)
-		ret = reg_write(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width_high);
+		ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width);
 	if (!ret)
-		ret = reg_write(client, REG_OUT_TOTAL_WIDTH_LOW, total_width_low);
+		ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height);
+
+	/* Sets the window for AWB calculations */
 	if (!ret)
-		ret = reg_write(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height_high);
+		ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width);
 	if (!ret)
-		ret = reg_write(client, REG_OUT_TOTAL_HEIGHT_LOW,  total_height_low);
+		ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height);
 
 	return ret;
 }
@@ -744,18 +788,18 @@ static int ov5642_set_resolution(struct i2c_client *client)
 static int ov5642_try_fmt(struct v4l2_subdev *sd,
 			  struct v4l2_mbus_framefmt *mf)
 {
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5642 *priv = to_ov5642(client);
 	const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code);
 
-	dev_dbg(sd->v4l2_dev->dev, "%s(%u) width: %u heigth: %u\n",
-			__func__, mf->code, mf->width, mf->height);
+	mf->width = priv->crop_rect.width;
+	mf->height = priv->crop_rect.height;
 
 	if (!fmt) {
 		mf->code	= ov5642_colour_fmts[0].code;
 		mf->colorspace	= ov5642_colour_fmts[0].colorspace;
 	}
 
-	mf->width	= OV5642_WIDTH;
-	mf->height	= OV5642_HEIGHT;
 	mf->field	= V4L2_FIELD_NONE;
 
 	return 0;
@@ -767,20 +811,13 @@ static int ov5642_s_fmt(struct v4l2_subdev *sd,
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct ov5642 *priv = to_ov5642(client);
 
-	dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
-
 	/* MIPI CSI could have changed the format, double-check */
 	if (!ov5642_find_datafmt(mf->code))
 		return -EINVAL;
 
 	ov5642_try_fmt(sd, mf);
-
 	priv->fmt = ov5642_find_datafmt(mf->code);
 
-	ov5642_write_array(client, ov5642_default_regs_init);
-	ov5642_set_resolution(client);
-	ov5642_write_array(client, ov5642_default_regs_finalise);
-
 	return 0;
 }
 
@@ -794,8 +831,8 @@ static int ov5642_g_fmt(struct v4l2_subdev *sd,
 
 	mf->code	= fmt->code;
 	mf->colorspace	= fmt->colorspace;
-	mf->width	= OV5642_WIDTH;
-	mf->height	= OV5642_HEIGHT;
+	mf->width	= priv->crop_rect.width;
+	mf->height	= priv->crop_rect.height;
 	mf->field	= V4L2_FIELD_NONE;
 
 	return 0;
@@ -828,15 +865,44 @@ static int ov5642_g_chip_ident(struct v4l2_subdev *sd,
 	return 0;
 }
 
+static int ov5642_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5642 *priv = to_ov5642(client);
+	struct v4l2_rect *rect = &a->c;
+	int ret;
+
+	v4l_bound_align_image(&rect->width, 48, OV5642_MAX_WIDTH, 1,
+			      &rect->height, 32, OV5642_MAX_HEIGHT, 1, 0);
+
+	priv->crop_rect.width	= rect->width;
+	priv->crop_rect.height	= rect->height;
+	priv->total_width	= rect->width + BLANKING_EXTRA_WIDTH;
+	priv->total_height	= max_t(int, rect->height +
+							BLANKING_EXTRA_HEIGHT,
+							BLANKING_MIN_HEIGHT);
+	priv->crop_rect.width		= rect->width;
+	priv->crop_rect.height		= rect->height;
+
+	ret = ov5642_write_array(client, ov5642_default_regs_init);
+	if (!ret)
+		ret = ov5642_set_resolution(sd);
+	if (!ret)
+		ret = ov5642_write_array(client, ov5642_default_regs_finalise);
+
+	return ret;
+}
+
 static int ov5642_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5642 *priv = to_ov5642(client);
 	struct v4l2_rect *rect = &a->c;
 
-	a->type		= V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	rect->top	= 0;
-	rect->left	= 0;
-	rect->width	= OV5642_WIDTH;
-	rect->height	= OV5642_HEIGHT;
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	*rect = priv->crop_rect;
 
 	return 0;
 }
@@ -845,8 +911,8 @@ static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
 {
 	a->bounds.left			= 0;
 	a->bounds.top			= 0;
-	a->bounds.width			= OV5642_WIDTH;
-	a->bounds.height		= OV5642_HEIGHT;
+	a->bounds.width			= OV5642_MAX_WIDTH;
+	a->bounds.height		= OV5642_MAX_HEIGHT;
 	a->defrect			= a->bounds;
 	a->type				= V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	a->pixelaspect.numerator	= 1;
@@ -855,16 +921,47 @@ static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
 	return 0;
 }
 
+static int ov5642_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	cfg->type = V4L2_MBUS_CSI2;
+	cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
+					V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+	return 0;
+}
+
+static int ov5642_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct i2c_client *client;
+	int ret;
+
+	if (!on)
+		return 0;
+
+	client = v4l2_get_subdevdata(sd);
+	ret = ov5642_write_array(client, ov5642_default_regs_init);
+	if (!ret)
+		ret = ov5642_set_resolution(sd);
+	if (!ret)
+		ret = ov5642_write_array(client, ov5642_default_regs_finalise);
+
+	return ret;
+}
+
 static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = {
 	.s_mbus_fmt	= ov5642_s_fmt,
 	.g_mbus_fmt	= ov5642_g_fmt,
 	.try_mbus_fmt	= ov5642_try_fmt,
 	.enum_mbus_fmt	= ov5642_enum_fmt,
+	.s_crop		= ov5642_s_crop,
 	.g_crop		= ov5642_g_crop,
 	.cropcap	= ov5642_cropcap,
+	.g_mbus_config	= ov5642_g_mbus_config,
 };
 
 static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = {
+	.s_power	= ov5642_s_power,
 	.g_chip_ident	= ov5642_g_chip_ident,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.g_register	= ov5642_get_register,
@@ -877,28 +974,7 @@ static struct v4l2_subdev_ops ov5642_subdev_ops = {
 	.video	= &ov5642_subdev_video_ops,
 };
 
-/*
- * We have to provide soc-camera operations, but we don't have anything to say
- * there. The MIPI CSI2 driver will provide .query_bus_param and .set_bus_param
- */
-static unsigned long soc_ov5642_query_bus_param(struct soc_camera_device *icd)
-{
-	return 0;
-}
-
-static int soc_ov5642_set_bus_param(struct soc_camera_device *icd,
-				 unsigned long flags)
-{
-	return -EINVAL;
-}
-
-static struct soc_camera_ops soc_ov5642_ops = {
-	.query_bus_param	= soc_ov5642_query_bus_param,
-	.set_bus_param		= soc_ov5642_set_bus_param,
-};
-
-static int ov5642_video_probe(struct soc_camera_device *icd,
-			      struct i2c_client *client)
+static int ov5642_video_probe(struct i2c_client *client)
 {
 	int ret;
 	u8 id_high, id_low;
@@ -929,16 +1005,9 @@ static int ov5642_probe(struct i2c_client *client,
 			const struct i2c_device_id *did)
 {
 	struct ov5642 *priv;
-	struct soc_camera_device *icd = client->dev.platform_data;
-	struct soc_camera_link *icl;
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 	int ret;
 
-	if (!icd) {
-		dev_err(&client->dev, "OV5642: missing soc-camera data!\n");
-		return -EINVAL;
-	}
-
-	icl = to_soc_camera_link(icd);
 	if (!icl) {
 		dev_err(&client->dev, "OV5642: missing platform data!\n");
 		return -EINVAL;
@@ -950,17 +1019,24 @@ static int ov5642_probe(struct i2c_client *client,
 
 	v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops);
 
-	icd->ops	= &soc_ov5642_ops;
-	priv->fmt	= &ov5642_colour_fmts[0];
+	priv->fmt		= &ov5642_colour_fmts[0];
+
+	priv->crop_rect.width	= OV5642_DEFAULT_WIDTH;
+	priv->crop_rect.height	= OV5642_DEFAULT_HEIGHT;
+	priv->crop_rect.left	= (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2;
+	priv->crop_rect.top	= (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2;
+	priv->crop_rect.width	= OV5642_DEFAULT_WIDTH;
+	priv->crop_rect.height	= OV5642_DEFAULT_HEIGHT;
+	priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH;
+	priv->total_height = BLANKING_MIN_HEIGHT;
 
-	ret = ov5642_video_probe(icd, client);
+	ret = ov5642_video_probe(client);
 	if (ret < 0)
 		goto error;
 
 	return 0;
 
 error:
-	icd->ops = NULL;
 	kfree(priv);
 	return ret;
 }
@@ -968,10 +1044,8 @@ error:
 static int ov5642_remove(struct i2c_client *client)
 {
 	struct ov5642 *priv = to_ov5642(client);
-	struct soc_camera_device *icd = client->dev.platform_data;
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 
-	icd->ops = NULL;
 	if (icl->free_bus)
 		icl->free_bus(icl);
 	kfree(priv);
diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c
index 456d9ad9ae5a..d5b057207a7b 100644
--- a/drivers/media/video/ov6650.c
+++ b/drivers/media/video/ov6650.c
@@ -28,10 +28,11 @@
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
 
 #include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
-
+#include <media/v4l2-ctrls.h>
 
 /* Register definitions */
 #define REG_GAIN		0x00	/* range 00 - 3F */
@@ -177,20 +178,23 @@ struct ov6650_reg {
 
 struct ov6650 {
 	struct v4l2_subdev	subdev;
-
-	int			gain;
-	int			blue;
-	int			red;
-	int			saturation;
-	int			hue;
-	int			brightness;
-	int			exposure;
-	int			gamma;
-	int			aec;
-	bool			vflip;
-	bool			hflip;
-	bool			awb;
-	bool			agc;
+	struct v4l2_ctrl_handler hdl;
+	struct {
+		/* exposure/autoexposure cluster */
+		struct v4l2_ctrl *autoexposure;
+		struct v4l2_ctrl *exposure;
+	};
+	struct {
+		/* gain/autogain cluster */
+		struct v4l2_ctrl *autogain;
+		struct v4l2_ctrl *gain;
+	};
+	struct {
+		/* blue/red/autowhitebalance cluster */
+		struct v4l2_ctrl *autowb;
+		struct v4l2_ctrl *blue;
+		struct v4l2_ctrl *red;
+	};
 	bool			half_scale;	/* scale down output by 2 */
 	struct v4l2_rect	rect;		/* sensor cropping window */
 	unsigned long		pclk_limit;	/* from host */
@@ -210,126 +214,6 @@ static enum v4l2_mbus_pixelcode ov6650_codes[] = {
 	V4L2_MBUS_FMT_Y8_1X8,
 };
 
-static const struct v4l2_queryctrl ov6650_controls[] = {
-	{
-		.id		= V4L2_CID_AUTOGAIN,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "AGC",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 1,
-	},
-	{
-		.id		= V4L2_CID_GAIN,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Gain",
-		.minimum	= 0,
-		.maximum	= 0x3f,
-		.step		= 1,
-		.default_value	= DEF_GAIN,
-	},
-	{
-		.id		= V4L2_CID_AUTO_WHITE_BALANCE,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "AWB",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 1,
-	},
-	{
-		.id		= V4L2_CID_BLUE_BALANCE,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Blue",
-		.minimum	= 0,
-		.maximum	= 0xff,
-		.step		= 1,
-		.default_value	= DEF_BLUE,
-	},
-	{
-		.id		= V4L2_CID_RED_BALANCE,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Red",
-		.minimum	= 0,
-		.maximum	= 0xff,
-		.step		= 1,
-		.default_value	= DEF_RED,
-	},
-	{
-		.id		= V4L2_CID_SATURATION,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Saturation",
-		.minimum	= 0,
-		.maximum	= 0xf,
-		.step		= 1,
-		.default_value	= 0x8,
-	},
-	{
-		.id		= V4L2_CID_HUE,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Hue",
-		.minimum	= 0,
-		.maximum	= HUE_MASK,
-		.step		= 1,
-		.default_value	= DEF_HUE,
-	},
-	{
-		.id		= V4L2_CID_BRIGHTNESS,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Brightness",
-		.minimum	= 0,
-		.maximum	= 0xff,
-		.step		= 1,
-		.default_value	= 0x80,
-	},
-	{
-		.id		= V4L2_CID_EXPOSURE_AUTO,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "AEC",
-		.minimum	= 0,
-		.maximum	= 3,
-		.step		= 1,
-		.default_value	= 0,
-	},
-	{
-		.id		= V4L2_CID_EXPOSURE,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Exposure",
-		.minimum	= 0,
-		.maximum	= 0xff,
-		.step		= 1,
-		.default_value	= DEF_AECH,
-	},
-	{
-		.id		= V4L2_CID_GAMMA,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Gamma",
-		.minimum	= 0,
-		.maximum	= 0xff,
-		.step		= 1,
-		.default_value	= 0x12,
-	},
-	{
-		.id		= V4L2_CID_VFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Vertically",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	},
-	{
-		.id		= V4L2_CID_HFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Horizontally",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	},
-};
-
 /* read a register */
 static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val)
 {
@@ -419,213 +303,90 @@ static int ov6650_s_stream(struct v4l2_subdev *sd, int enable)
 	return 0;
 }
 
-/* Alter bus settings on camera side */
-static int ov6650_set_bus_param(struct soc_camera_device *icd,
-				unsigned long flags)
-{
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-	struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-	int ret;
-
-	flags = soc_camera_apply_sensor_flags(icl, flags);
-
-	if (flags & SOCAM_PCLK_SAMPLE_RISING)
-		ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
-	else
-		ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
-	if (ret)
-		return ret;
-
-	if (flags & SOCAM_HSYNC_ACTIVE_LOW)
-		ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
-	else
-		ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
-	if (ret)
-		return ret;
-
-	if (flags & SOCAM_VSYNC_ACTIVE_HIGH)
-		ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
-	else
-		ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
-
-	return ret;
-}
-
-/* Request bus settings on camera side */
-static unsigned long ov6650_query_bus_param(struct soc_camera_device *icd)
-{
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-
-	unsigned long flags = SOCAM_MASTER |
-		SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
-		SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW |
-		SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW |
-		SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
-
-	return soc_camera_apply_sensor_flags(icl, flags);
-}
-
 /* Get status of additional camera capabilities */
-static int ov6650_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov6550_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 {
+	struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
+	struct v4l2_subdev *sd = &priv->subdev;
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct ov6650 *priv = to_ov6650(client);
-	uint8_t reg;
-	int ret = 0;
+	uint8_t reg, reg2;
+	int ret;
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUTOGAIN:
-		ctrl->value = priv->agc;
-		break;
-	case V4L2_CID_GAIN:
-		if (priv->agc) {
-			ret = ov6650_reg_read(client, REG_GAIN, &reg);
-			ctrl->value = reg;
-		} else {
-			ctrl->value = priv->gain;
-		}
-		break;
+		ret = ov6650_reg_read(client, REG_GAIN, &reg);
+		if (!ret)
+			priv->gain->val = reg;
+		return ret;
 	case V4L2_CID_AUTO_WHITE_BALANCE:
-		ctrl->value = priv->awb;
-		break;
-	case V4L2_CID_BLUE_BALANCE:
-		if (priv->awb) {
-			ret = ov6650_reg_read(client, REG_BLUE, &reg);
-			ctrl->value = reg;
-		} else {
-			ctrl->value = priv->blue;
-		}
-		break;
-	case V4L2_CID_RED_BALANCE:
-		if (priv->awb) {
-			ret = ov6650_reg_read(client, REG_RED, &reg);
-			ctrl->value = reg;
-		} else {
-			ctrl->value = priv->red;
+		ret = ov6650_reg_read(client, REG_BLUE, &reg);
+		if (!ret)
+			ret = ov6650_reg_read(client, REG_RED, &reg2);
+		if (!ret) {
+			priv->blue->val = reg;
+			priv->red->val = reg2;
 		}
-		break;
-	case V4L2_CID_SATURATION:
-		ctrl->value = priv->saturation;
-		break;
-	case V4L2_CID_HUE:
-		ctrl->value = priv->hue;
-		break;
-	case V4L2_CID_BRIGHTNESS:
-		ctrl->value = priv->brightness;
-		break;
+		return ret;
 	case V4L2_CID_EXPOSURE_AUTO:
-		ctrl->value = priv->aec;
-		break;
-	case V4L2_CID_EXPOSURE:
-		if (priv->aec) {
-			ret = ov6650_reg_read(client, REG_AECH, &reg);
-			ctrl->value = reg;
-		} else {
-			ctrl->value = priv->exposure;
-		}
-		break;
-	case V4L2_CID_GAMMA:
-		ctrl->value = priv->gamma;
-		break;
-	case V4L2_CID_VFLIP:
-		ctrl->value = priv->vflip;
-		break;
-	case V4L2_CID_HFLIP:
-		ctrl->value = priv->hflip;
-		break;
+		ret = ov6650_reg_read(client, REG_AECH, &reg);
+		if (!ret)
+			priv->exposure->val = reg;
+		return ret;
 	}
-	return ret;
+	return -EINVAL;
 }
 
 /* Set status of additional camera capabilities */
-static int ov6650_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov6550_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+	struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
+	struct v4l2_subdev *sd = &priv->subdev;
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct ov6650 *priv = to_ov6650(client);
-	int ret = 0;
+	int ret;
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUTOGAIN:
 		ret = ov6650_reg_rmw(client, REG_COMB,
-				ctrl->value ? COMB_AGC : 0, COMB_AGC);
-		if (!ret)
-			priv->agc = ctrl->value;
-		break;
-	case V4L2_CID_GAIN:
-		ret = ov6650_reg_write(client, REG_GAIN, ctrl->value);
-		if (!ret)
-			priv->gain = ctrl->value;
-		break;
+				ctrl->val ? COMB_AGC : 0, COMB_AGC);
+		if (!ret && !ctrl->val)
+			ret = ov6650_reg_write(client, REG_GAIN, priv->gain->val);
+		return ret;
 	case V4L2_CID_AUTO_WHITE_BALANCE:
 		ret = ov6650_reg_rmw(client, REG_COMB,
-				ctrl->value ? COMB_AWB : 0, COMB_AWB);
-		if (!ret)
-			priv->awb = ctrl->value;
-		break;
-	case V4L2_CID_BLUE_BALANCE:
-		ret = ov6650_reg_write(client, REG_BLUE, ctrl->value);
-		if (!ret)
-			priv->blue = ctrl->value;
-		break;
-	case V4L2_CID_RED_BALANCE:
-		ret = ov6650_reg_write(client, REG_RED, ctrl->value);
-		if (!ret)
-			priv->red = ctrl->value;
-		break;
+				ctrl->val ? COMB_AWB : 0, COMB_AWB);
+		if (!ret && !ctrl->val) {
+			ret = ov6650_reg_write(client, REG_BLUE, priv->blue->val);
+			if (!ret)
+				ret = ov6650_reg_write(client, REG_RED,
+							priv->red->val);
+		}
+		return ret;
 	case V4L2_CID_SATURATION:
-		ret = ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->value),
+		return ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->val),
 				SAT_MASK);
-		if (!ret)
-			priv->saturation = ctrl->value;
-		break;
 	case V4L2_CID_HUE:
-		ret = ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->value),
+		return ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->val),
 				HUE_MASK);
-		if (!ret)
-			priv->hue = ctrl->value;
-		break;
 	case V4L2_CID_BRIGHTNESS:
-		ret = ov6650_reg_write(client, REG_BRT, ctrl->value);
-		if (!ret)
-			priv->brightness = ctrl->value;
-		break;
+		return ov6650_reg_write(client, REG_BRT, ctrl->val);
 	case V4L2_CID_EXPOSURE_AUTO:
-		switch (ctrl->value) {
-		case V4L2_EXPOSURE_AUTO:
-			ret = ov6650_reg_rmw(client, REG_COMB, COMB_AEC, 0);
-			break;
-		default:
-			ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_AEC);
-			break;
-		}
-		if (!ret)
-			priv->aec = ctrl->value;
-		break;
-	case V4L2_CID_EXPOSURE:
-		ret = ov6650_reg_write(client, REG_AECH, ctrl->value);
-		if (!ret)
-			priv->exposure = ctrl->value;
-		break;
+		ret = ov6650_reg_rmw(client, REG_COMB, ctrl->val ==
+				V4L2_EXPOSURE_AUTO ? COMB_AEC : 0, COMB_AEC);
+		if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
+			ret = ov6650_reg_write(client, REG_AECH,
+						priv->exposure->val);
+		return ret;
 	case V4L2_CID_GAMMA:
-		ret = ov6650_reg_write(client, REG_GAM1, ctrl->value);
-		if (!ret)
-			priv->gamma = ctrl->value;
-		break;
+		return ov6650_reg_write(client, REG_GAM1, ctrl->val);
 	case V4L2_CID_VFLIP:
-		ret = ov6650_reg_rmw(client, REG_COMB,
-				ctrl->value ? COMB_FLIP_V : 0, COMB_FLIP_V);
-		if (!ret)
-			priv->vflip = ctrl->value;
-		break;
+		return ov6650_reg_rmw(client, REG_COMB,
+				ctrl->val ? COMB_FLIP_V : 0, COMB_FLIP_V);
 	case V4L2_CID_HFLIP:
-		ret = ov6650_reg_rmw(client, REG_COMB,
-				ctrl->value ? COMB_FLIP_H : 0, COMB_FLIP_H);
-		if (!ret)
-			priv->hflip = ctrl->value;
-		break;
+		return ov6650_reg_rmw(client, REG_COMB,
+				ctrl->val ? COMB_FLIP_H : 0, COMB_FLIP_H);
 	}
 
-	return ret;
+	return -EINVAL;
 }
 
 /* Get chip identification */
@@ -778,7 +539,7 @@ static u8 to_clkrc(struct v4l2_fract *timeperframe,
 static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct soc_camera_device *icd = client->dev.platform_data;
+	struct soc_camera_device *icd = (struct soc_camera_device *)sd->grp_id;
 	struct soc_camera_sense *sense = icd->sense;
 	struct ov6650 *priv = to_ov6650(client);
 	bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect);
@@ -1057,8 +818,7 @@ static int ov6650_prog_dflt(struct i2c_client *client)
 	return ret;
 }
 
-static int ov6650_video_probe(struct soc_camera_device *icd,
-				struct i2c_client *client)
+static int ov6650_video_probe(struct i2c_client *client)
 {
 	u8		pidh, pidl, midh, midl;
 	int		ret = 0;
@@ -1094,16 +854,12 @@ static int ov6650_video_probe(struct soc_camera_device *icd,
 	return ret;
 }
 
-static struct soc_camera_ops ov6650_ops = {
-	.set_bus_param		= ov6650_set_bus_param,
-	.query_bus_param	= ov6650_query_bus_param,
-	.controls		= ov6650_controls,
-	.num_controls		= ARRAY_SIZE(ov6650_controls),
+static const struct v4l2_ctrl_ops ov6550_ctrl_ops = {
+	.g_volatile_ctrl = ov6550_g_volatile_ctrl,
+	.s_ctrl = ov6550_s_ctrl,
 };
 
 static struct v4l2_subdev_core_ops ov6650_core_ops = {
-	.g_ctrl			= ov6650_g_ctrl,
-	.s_ctrl			= ov6650_s_ctrl,
 	.g_chip_ident		= ov6650_g_chip_ident,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.g_register		= ov6650_get_register,
@@ -1111,6 +867,55 @@ static struct v4l2_subdev_core_ops ov6650_core_ops = {
 #endif
 };
 
+/* Request bus settings on camera side */
+static int ov6650_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+	cfg->flags = V4L2_MBUS_MASTER |
+		V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+		V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+		V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+		V4L2_MBUS_DATA_ACTIVE_HIGH;
+	cfg->type = V4L2_MBUS_PARALLEL;
+	cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+	return 0;
+}
+
+/* Alter bus settings on camera side */
+static int ov6650_s_mbus_config(struct v4l2_subdev *sd,
+				const struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+	unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+	int ret;
+
+	if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+		ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
+	else
+		ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
+	if (ret)
+		return ret;
+
+	if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+		ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
+	else
+		ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
+	if (ret)
+		return ret;
+
+	if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+		ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
+	else
+		ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
+
+	return ret;
+}
+
 static struct v4l2_subdev_video_ops ov6650_video_ops = {
 	.s_stream	= ov6650_s_stream,
 	.g_mbus_fmt	= ov6650_g_fmt,
@@ -1122,6 +927,8 @@ static struct v4l2_subdev_video_ops ov6650_video_ops = {
 	.s_crop		= ov6650_s_crop,
 	.g_parm		= ov6650_g_parm,
 	.s_parm		= ov6650_s_parm,
+	.g_mbus_config	= ov6650_g_mbus_config,
+	.s_mbus_config	= ov6650_s_mbus_config,
 };
 
 static struct v4l2_subdev_ops ov6650_subdev_ops = {
@@ -1136,16 +943,9 @@ static int ov6650_probe(struct i2c_client *client,
 			const struct i2c_device_id *did)
 {
 	struct ov6650 *priv;
-	struct soc_camera_device *icd = client->dev.platform_data;
-	struct soc_camera_link *icl;
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 	int ret;
 
-	if (!icd) {
-		dev_err(&client->dev, "Missing soc-camera data!\n");
-		return -EINVAL;
-	}
-
-	icl = to_soc_camera_link(icd);
 	if (!icl) {
 		dev_err(&client->dev, "Missing platform_data for driver\n");
 		return -EINVAL;
@@ -1159,8 +959,46 @@ static int ov6650_probe(struct i2c_client *client,
 	}
 
 	v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops);
+	v4l2_ctrl_handler_init(&priv->hdl, 13);
+	v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+	priv->autogain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	priv->gain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+			V4L2_CID_GAIN, 0, 0x3f, 1, DEF_GAIN);
+	priv->autowb = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+			V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+	priv->blue = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+			V4L2_CID_BLUE_BALANCE, 0, 0xff, 1, DEF_BLUE);
+	priv->red = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+			V4L2_CID_RED_BALANCE, 0, 0xff, 1, DEF_RED);
+	v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 0xf, 1, 0x8);
+	v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+			V4L2_CID_HUE, 0, HUE_MASK, 1, DEF_HUE);
+	v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x80);
+	priv->autoexposure = v4l2_ctrl_new_std_menu(&priv->hdl,
+			&ov6550_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
+			V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
+	priv->exposure = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+			V4L2_CID_EXPOSURE, 0, 0xff, 1, DEF_AECH);
+	v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+			V4L2_CID_GAMMA, 0, 0xff, 1, 0x12);
+
+	priv->subdev.ctrl_handler = &priv->hdl;
+	if (priv->hdl.error) {
+		int err = priv->hdl.error;
 
-	icd->ops = &ov6650_ops;
+		kfree(priv);
+		return err;
+	}
+	v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true);
+	v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true);
+	v4l2_ctrl_auto_cluster(2, &priv->autoexposure,
+				V4L2_EXPOSURE_MANUAL, true);
 
 	priv->rect.left	  = DEF_HSTRT << 1;
 	priv->rect.top	  = DEF_VSTRT << 1;
@@ -1170,10 +1008,12 @@ static int ov6650_probe(struct i2c_client *client,
 	priv->code	  = V4L2_MBUS_FMT_YUYV8_2X8;
 	priv->colorspace  = V4L2_COLORSPACE_JPEG;
 
-	ret = ov6650_video_probe(icd, client);
+	ret = ov6650_video_probe(client);
+	if (!ret)
+		ret = v4l2_ctrl_handler_setup(&priv->hdl);
 
 	if (ret) {
-		icd->ops = NULL;
+		v4l2_ctrl_handler_free(&priv->hdl);
 		kfree(priv);
 	}
 
@@ -1184,6 +1024,8 @@ static int ov6650_remove(struct i2c_client *client)
 {
 	struct ov6650 *priv = to_ov6650(client);
 
+	v4l2_device_unregister_subdev(&priv->subdev);
+	v4l2_ctrl_handler_free(&priv->hdl);
 	kfree(priv);
 	return 0;
 }
diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
index 397870f076c1..9f6ce3d8a29e 100644
--- a/drivers/media/video/ov772x.c
+++ b/drivers/media/video/ov772x.c
@@ -20,12 +20,14 @@
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
 #include <linux/videodev2.h>
+
+#include <media/ov772x.h>
+#include <media/soc_camera.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-subdev.h>
-#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
-#include <media/ov772x.h>
 
 /*
  * register offset
@@ -400,6 +402,7 @@ struct ov772x_win_size {
 
 struct ov772x_priv {
 	struct v4l2_subdev                subdev;
+	struct v4l2_ctrl_handler	  hdl;
 	struct ov772x_camera_info        *info;
 	const struct ov772x_color_format *cfmt;
 	const struct ov772x_win_size     *win;
@@ -517,36 +520,6 @@ static const struct ov772x_win_size ov772x_win_qvga = {
 	.regs     = ov772x_qvga_regs,
 };
 
-static const struct v4l2_queryctrl ov772x_controls[] = {
-	{
-		.id		= V4L2_CID_VFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Vertically",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	},
-	{
-		.id		= V4L2_CID_HFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Horizontally",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	},
-	{
-		.id		= V4L2_CID_BAND_STOP_FILTER,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Band-stop filter",
-		.minimum	= 0,
-		.maximum	= 256,
-		.step		= 1,
-		.default_value	= 0,
-	},
-};
-
 /*
  * general function
  */
@@ -620,75 +593,30 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
 	return 0;
 }
 
-static int ov772x_set_bus_param(struct soc_camera_device *icd,
-				unsigned long		  flags)
-{
-	return 0;
-}
-
-static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd)
-{
-	struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
-	struct ov772x_priv *priv = i2c_get_clientdata(client);
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-	unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
-		SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
-		SOCAM_DATA_ACTIVE_HIGH;
-
-	if (priv->info->flags & OV772X_FLAG_8BIT)
-		flags |= SOCAM_DATAWIDTH_8;
-	else
-		flags |= SOCAM_DATAWIDTH_10;
-
-	return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static int ov772x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-	struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
-
-	switch (ctrl->id) {
-	case V4L2_CID_VFLIP:
-		ctrl->value = priv->flag_vflip;
-		break;
-	case V4L2_CID_HFLIP:
-		ctrl->value = priv->flag_hflip;
-		break;
-	case V4L2_CID_BAND_STOP_FILTER:
-		ctrl->value = priv->band_filter;
-		break;
-	}
-	return 0;
-}
-
-static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+	struct ov772x_priv *priv = container_of(ctrl->handler,
+						struct ov772x_priv, hdl);
+	struct v4l2_subdev *sd = &priv->subdev;
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
 	int ret = 0;
 	u8 val;
 
 	switch (ctrl->id) {
 	case V4L2_CID_VFLIP:
-		val = ctrl->value ? VFLIP_IMG : 0x00;
-		priv->flag_vflip = ctrl->value;
+		val = ctrl->val ? VFLIP_IMG : 0x00;
+		priv->flag_vflip = ctrl->val;
 		if (priv->info->flags & OV772X_FLAG_VFLIP)
 			val ^= VFLIP_IMG;
-		ret = ov772x_mask_set(client, COM3, VFLIP_IMG, val);
-		break;
+		return ov772x_mask_set(client, COM3, VFLIP_IMG, val);
 	case V4L2_CID_HFLIP:
-		val = ctrl->value ? HFLIP_IMG : 0x00;
-		priv->flag_hflip = ctrl->value;
+		val = ctrl->val ? HFLIP_IMG : 0x00;
+		priv->flag_hflip = ctrl->val;
 		if (priv->info->flags & OV772X_FLAG_HFLIP)
 			val ^= HFLIP_IMG;
-		ret = ov772x_mask_set(client, COM3, HFLIP_IMG, val);
-		break;
+		return ov772x_mask_set(client, COM3, HFLIP_IMG, val);
 	case V4L2_CID_BAND_STOP_FILTER:
-		if ((unsigned)ctrl->value > 256)
-			ctrl->value = 256;
-		if (ctrl->value == priv->band_filter)
-			break;
-		if (!ctrl->value) {
+		if (!ctrl->val) {
 			/* Switch the filter off, it is on now */
 			ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff);
 			if (!ret)
@@ -696,7 +624,7 @@ static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 						      BNDF_ON_OFF, 0);
 		} else {
 			/* Switch the filter on, set AEC low limit */
-			val = 256 - ctrl->value;
+			val = 256 - ctrl->val;
 			ret = ov772x_mask_set(client, COM8,
 					      BNDF_ON_OFF, BNDF_ON_OFF);
 			if (!ret)
@@ -704,11 +632,11 @@ static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 						      0xff, val);
 		}
 		if (!ret)
-			priv->band_filter = ctrl->value;
-		break;
+			priv->band_filter = ctrl->val;
+		return ret;
 	}
 
-	return ret;
+	return -EINVAL;
 }
 
 static int ov772x_g_chip_ident(struct v4l2_subdev *sd,
@@ -822,13 +750,13 @@ static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height,
 			goto ov772x_set_fmt_error;
 
 		ret = ov772x_mask_set(client,
-				      EDGE_TRSHLD, EDGE_THRESHOLD_MASK,
+				      EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK,
 				      priv->info->edgectrl.threshold);
 		if (ret < 0)
 			goto ov772x_set_fmt_error;
 
 		ret = ov772x_mask_set(client,
-				      EDGE_STRNGT, EDGE_STRENGTH_MASK,
+				      EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK,
 				      priv->info->edgectrl.strength);
 		if (ret < 0)
 			goto ov772x_set_fmt_error;
@@ -840,13 +768,13 @@ static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height,
 		 * set upper and lower limit
 		 */
 		ret = ov772x_mask_set(client,
-				      EDGE_UPPER, EDGE_UPPER_MASK,
+				      EDGE_UPPER, OV772X_EDGE_UPPER_MASK,
 				      priv->info->edgectrl.upper);
 		if (ret < 0)
 			goto ov772x_set_fmt_error;
 
 		ret = ov772x_mask_set(client,
-				      EDGE_LOWER, EDGE_LOWER_MASK,
+				      EDGE_LOWER, OV772X_EDGE_LOWER_MASK,
 				      priv->info->edgectrl.lower);
 		if (ret < 0)
 			goto ov772x_set_fmt_error;
@@ -1025,17 +953,12 @@ static int ov772x_try_fmt(struct v4l2_subdev *sd,
 	return 0;
 }
 
-static int ov772x_video_probe(struct soc_camera_device *icd,
-			      struct i2c_client *client)
+static int ov772x_video_probe(struct i2c_client *client)
 {
 	struct ov772x_priv *priv = to_ov772x(client);
 	u8                  pid, ver;
 	const char         *devname;
 
-	/* We must have a parent by now. And it cannot be a wrong one. */
-	BUG_ON(!icd->parent ||
-	       to_soc_camera_host(icd->parent)->nr != icd->iface);
-
 	/*
 	 * check and show product ID and manufacturer ID
 	 */
@@ -1064,20 +987,14 @@ static int ov772x_video_probe(struct soc_camera_device *icd,
 		 ver,
 		 i2c_smbus_read_byte_data(client, MIDH),
 		 i2c_smbus_read_byte_data(client, MIDL));
-
-	return 0;
+	return v4l2_ctrl_handler_setup(&priv->hdl);
 }
 
-static struct soc_camera_ops ov772x_ops = {
-	.set_bus_param		= ov772x_set_bus_param,
-	.query_bus_param	= ov772x_query_bus_param,
-	.controls		= ov772x_controls,
-	.num_controls		= ARRAY_SIZE(ov772x_controls),
+static const struct v4l2_ctrl_ops ov772x_ctrl_ops = {
+	.s_ctrl = ov772x_s_ctrl,
 };
 
 static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
-	.g_ctrl		= ov772x_g_ctrl,
-	.s_ctrl		= ov772x_s_ctrl,
 	.g_chip_ident	= ov772x_g_chip_ident,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.g_register	= ov772x_g_register,
@@ -1095,6 +1012,21 @@ static int ov772x_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
 	return 0;
 }
 
+static int ov772x_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+	cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+		V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+		V4L2_MBUS_DATA_ACTIVE_HIGH;
+	cfg->type = V4L2_MBUS_PARALLEL;
+	cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+	return 0;
+}
+
 static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
 	.s_stream	= ov772x_s_stream,
 	.g_mbus_fmt	= ov772x_g_fmt,
@@ -1103,6 +1035,7 @@ static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
 	.cropcap	= ov772x_cropcap,
 	.g_crop		= ov772x_g_crop,
 	.enum_mbus_fmt	= ov772x_enum_fmt,
+	.g_mbus_config	= ov772x_g_mbus_config,
 };
 
 static struct v4l2_subdev_ops ov772x_subdev_ops = {
@@ -1117,20 +1050,15 @@ static struct v4l2_subdev_ops ov772x_subdev_ops = {
 static int ov772x_probe(struct i2c_client *client,
 			const struct i2c_device_id *did)
 {
-	struct ov772x_priv        *priv;
-	struct soc_camera_device  *icd = client->dev.platform_data;
-	struct i2c_adapter        *adapter = to_i2c_adapter(client->dev.parent);
-	struct soc_camera_link    *icl;
-	int                        ret;
-
-	if (!icd) {
-		dev_err(&client->dev, "OV772X: missing soc-camera data!\n");
-		return -EINVAL;
-	}
+	struct ov772x_priv	*priv;
+	struct soc_camera_link	*icl = soc_camera_i2c_to_link(client);
+	struct i2c_adapter	*adapter = to_i2c_adapter(client->dev.parent);
+	int			ret;
 
-	icl = to_soc_camera_link(icd);
-	if (!icl || !icl->priv)
+	if (!icl || !icl->priv) {
+		dev_err(&client->dev, "OV772X: missing platform data!\n");
 		return -EINVAL;
+	}
 
 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
 		dev_err(&adapter->dev,
@@ -1146,12 +1074,24 @@ static int ov772x_probe(struct i2c_client *client,
 	priv->info = icl->priv;
 
 	v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
+	v4l2_ctrl_handler_init(&priv->hdl, 3);
+	v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+			V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0);
+	priv->subdev.ctrl_handler = &priv->hdl;
+	if (priv->hdl.error) {
+		int err = priv->hdl.error;
 
-	icd->ops		= &ov772x_ops;
+		kfree(priv);
+		return err;
+	}
 
-	ret = ov772x_video_probe(icd, client);
+	ret = ov772x_video_probe(client);
 	if (ret) {
-		icd->ops = NULL;
+		v4l2_ctrl_handler_free(&priv->hdl);
 		kfree(priv);
 	}
 
@@ -1161,9 +1101,9 @@ static int ov772x_probe(struct i2c_client *client,
 static int ov772x_remove(struct i2c_client *client)
 {
 	struct ov772x_priv *priv = to_ov772x(client);
-	struct soc_camera_device *icd = client->dev.platform_data;
 
-	icd->ops = NULL;
+	v4l2_device_unregister_subdev(&priv->subdev);
+	v4l2_ctrl_handler_free(&priv->hdl);
 	kfree(priv);
 	return 0;
 }
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
index 3681a6ff0815..a4f99797eb56 100644
--- a/drivers/media/video/ov9640.c
+++ b/drivers/media/video/ov9640.c
@@ -24,10 +24,13 @@
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
 #include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
 #include <media/v4l2-chip-ident.h>
 #include <media/v4l2-common.h>
-#include <media/soc_camera.h>
+#include <media/v4l2-ctrls.h>
 
 #include "ov9640.h"
 
@@ -162,27 +165,6 @@ static enum v4l2_mbus_pixelcode ov9640_codes[] = {
 	V4L2_MBUS_FMT_RGB565_2X8_LE,
 };
 
-static const struct v4l2_queryctrl ov9640_controls[] = {
-	{
-		.id		= V4L2_CID_VFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Vertically",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	},
-	{
-		.id		= V4L2_CID_HFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Horizontally",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	},
-};
-
 /* read a register */
 static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val)
 {
@@ -284,75 +266,25 @@ static int ov9640_s_stream(struct v4l2_subdev *sd, int enable)
 	return 0;
 }
 
-/* Alter bus settings on camera side */
-static int ov9640_set_bus_param(struct soc_camera_device *icd,
-				unsigned long flags)
-{
-	return 0;
-}
-
-/* Request bus settings on camera side */
-static unsigned long ov9640_query_bus_param(struct soc_camera_device *icd)
-{
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-
-	/*
-	 * REVISIT: the camera probably can do 10 bit transfers, but I don't
-	 *          have those pins connected on my hardware.
-	 */
-	unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
-		SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
-		SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
-
-	return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-/* Get status of additional camera capabilities */
-static int ov9640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-	struct ov9640_priv *priv = to_ov9640_sensor(sd);
-
-	switch (ctrl->id) {
-	case V4L2_CID_VFLIP:
-		ctrl->value = priv->flag_vflip;
-		break;
-	case V4L2_CID_HFLIP:
-		ctrl->value = priv->flag_hflip;
-		break;
-	}
-	return 0;
-}
-
 /* Set status of additional camera capabilities */
-static int ov9640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct ov9640_priv *priv = to_ov9640_sensor(sd);
-
-	int ret = 0;
+	struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl);
+	struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
 
 	switch (ctrl->id) {
 	case V4L2_CID_VFLIP:
-		priv->flag_vflip = ctrl->value;
-		if (ctrl->value)
-			ret = ov9640_reg_rmw(client, OV9640_MVFP,
+		if (ctrl->val)
+			return ov9640_reg_rmw(client, OV9640_MVFP,
 							OV9640_MVFP_V, 0);
-		else
-			ret = ov9640_reg_rmw(client, OV9640_MVFP,
-							0, OV9640_MVFP_V);
-		break;
+		return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V);
 	case V4L2_CID_HFLIP:
-		priv->flag_hflip = ctrl->value;
-		if (ctrl->value)
-			ret = ov9640_reg_rmw(client, OV9640_MVFP,
+		if (ctrl->val)
+			return ov9640_reg_rmw(client, OV9640_MVFP,
 							OV9640_MVFP_H, 0);
-		else
-			ret = ov9640_reg_rmw(client, OV9640_MVFP,
-							0, OV9640_MVFP_H);
-		break;
+		return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H);
 	}
-
-	return ret;
+	return -EINVAL;
 }
 
 /* Get chip identification */
@@ -646,10 +578,7 @@ static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
 	return 0;
 }
 
-
-
-static int ov9640_video_probe(struct soc_camera_device *icd,
-				struct i2c_client *client)
+static int ov9640_video_probe(struct i2c_client *client)
 {
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 	struct ov9640_priv *priv = to_ov9640_sensor(sd);
@@ -657,29 +586,19 @@ static int ov9640_video_probe(struct soc_camera_device *icd,
 	const char	*devname;
 	int		ret = 0;
 
-	/* We must have a parent by now. And it cannot be a wrong one. */
-	BUG_ON(!icd->parent ||
-	       to_soc_camera_host(icd->parent)->nr != icd->iface);
-
 	/*
 	 * check and show product ID and manufacturer ID
 	 */
 
 	ret = ov9640_reg_read(client, OV9640_PID, &pid);
+	if (!ret)
+		ret = ov9640_reg_read(client, OV9640_VER, &ver);
+	if (!ret)
+		ret = ov9640_reg_read(client, OV9640_MIDH, &midh);
+	if (!ret)
+		ret = ov9640_reg_read(client, OV9640_MIDL, &midl);
 	if (ret)
-		goto err;
-
-	ret = ov9640_reg_read(client, OV9640_VER, &ver);
-	if (ret)
-		goto err;
-
-	ret = ov9640_reg_read(client, OV9640_MIDH, &midh);
-	if (ret)
-		goto err;
-
-	ret = ov9640_reg_read(client, OV9640_MIDL, &midl);
-	if (ret)
-		goto err;
+		return ret;
 
 	switch (VERSION(pid, ver)) {
 	case OV9640_V2:
@@ -693,27 +612,20 @@ static int ov9640_video_probe(struct soc_camera_device *icd,
 		break;
 	default:
 		dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver);
-		ret = -ENODEV;
-		goto err;
+		return -ENODEV;
 	}
 
 	dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
 		 devname, pid, ver, midh, midl);
 
-err:
-	return ret;
+	return v4l2_ctrl_handler_setup(&priv->hdl);
 }
 
-static struct soc_camera_ops ov9640_ops = {
-	.set_bus_param		= ov9640_set_bus_param,
-	.query_bus_param	= ov9640_query_bus_param,
-	.controls		= ov9640_controls,
-	.num_controls		= ARRAY_SIZE(ov9640_controls),
+static const struct v4l2_ctrl_ops ov9640_ctrl_ops = {
+	.s_ctrl = ov9640_s_ctrl,
 };
 
 static struct v4l2_subdev_core_ops ov9640_core_ops = {
-	.g_ctrl			= ov9640_g_ctrl,
-	.s_ctrl			= ov9640_s_ctrl,
 	.g_chip_ident		= ov9640_g_chip_ident,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.g_register		= ov9640_get_register,
@@ -722,6 +634,22 @@ static struct v4l2_subdev_core_ops ov9640_core_ops = {
 
 };
 
+/* Request bus settings on camera side */
+static int ov9640_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+	cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+		V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+		V4L2_MBUS_DATA_ACTIVE_HIGH;
+	cfg->type = V4L2_MBUS_PARALLEL;
+	cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+	return 0;
+}
+
 static struct v4l2_subdev_video_ops ov9640_video_ops = {
 	.s_stream	= ov9640_s_stream,
 	.s_mbus_fmt	= ov9640_s_fmt,
@@ -729,7 +657,7 @@ static struct v4l2_subdev_video_ops ov9640_video_ops = {
 	.enum_mbus_fmt	= ov9640_enum_fmt,
 	.cropcap	= ov9640_cropcap,
 	.g_crop		= ov9640_g_crop,
-
+	.g_mbus_config	= ov9640_g_mbus_config,
 };
 
 static struct v4l2_subdev_ops ov9640_subdev_ops = {
@@ -744,16 +672,9 @@ static int ov9640_probe(struct i2c_client *client,
 			const struct i2c_device_id *did)
 {
 	struct ov9640_priv *priv;
-	struct soc_camera_device *icd	= client->dev.platform_data;
-	struct soc_camera_link *icl;
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 	int ret;
 
-	if (!icd) {
-		dev_err(&client->dev, "Missing soc-camera data!\n");
-		return -EINVAL;
-	}
-
-	icl = to_soc_camera_link(icd);
 	if (!icl) {
 		dev_err(&client->dev, "Missing platform_data for driver\n");
 		return -EINVAL;
@@ -768,12 +689,23 @@ static int ov9640_probe(struct i2c_client *client,
 
 	v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops);
 
-	icd->ops	= &ov9640_ops;
+	v4l2_ctrl_handler_init(&priv->hdl, 2);
+	v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+	priv->subdev.ctrl_handler = &priv->hdl;
+	if (priv->hdl.error) {
+		int err = priv->hdl.error;
+
+		kfree(priv);
+		return err;
+	}
 
-	ret = ov9640_video_probe(icd, client);
+	ret = ov9640_video_probe(client);
 
 	if (ret) {
-		icd->ops = NULL;
+		v4l2_ctrl_handler_free(&priv->hdl);
 		kfree(priv);
 	}
 
@@ -785,6 +717,8 @@ static int ov9640_remove(struct i2c_client *client)
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 	struct ov9640_priv *priv = to_ov9640_sensor(sd);
 
+	v4l2_device_unregister_subdev(&priv->subdev);
+	v4l2_ctrl_handler_free(&priv->hdl);
 	kfree(priv);
 	return 0;
 }
diff --git a/drivers/media/video/ov9640.h b/drivers/media/video/ov9640.h
index f8a51b70792e..6b33a972c83c 100644
--- a/drivers/media/video/ov9640.h
+++ b/drivers/media/video/ov9640.h
@@ -198,12 +198,10 @@ struct ov9640_reg {
 
 struct ov9640_priv {
 	struct v4l2_subdev		subdev;
+	struct v4l2_ctrl_handler	hdl;
 
 	int				model;
 	int				revision;
-
-	bool				flag_vflip;
-	bool				flag_hflip;
 };
 
 #endif	/* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */
diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c
index edd1ffcca30b..d9a9f7174f7a 100644
--- a/drivers/media/video/ov9740.c
+++ b/drivers/media/video/ov9740.c
@@ -14,8 +14,11 @@
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
-#include <media/v4l2-chip-ident.h>
+#include <linux/v4l2-mediabus.h>
+
 #include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
 
 #define to_ov9740(sd)		container_of(sd, struct ov9740_priv, subdev)
 
@@ -192,6 +195,7 @@ struct ov9740_reg {
 
 struct ov9740_priv {
 	struct v4l2_subdev		subdev;
+	struct v4l2_ctrl_handler	hdl;
 
 	int				ident;
 	u16				model;
@@ -392,27 +396,6 @@ static enum v4l2_mbus_pixelcode ov9740_codes[] = {
 	V4L2_MBUS_FMT_YUYV8_2X8,
 };
 
-static const struct v4l2_queryctrl ov9740_controls[] = {
-	{
-		.id		= V4L2_CID_VFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Vertically",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	},
-	{
-		.id		= V4L2_CID_HFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Horizontally",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	},
-};
-
 /* read a register */
 static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val)
 {
@@ -560,25 +543,6 @@ static int ov9740_s_stream(struct v4l2_subdev *sd, int enable)
 	return ret;
 }
 
-/* Alter bus settings on camera side */
-static int ov9740_set_bus_param(struct soc_camera_device *icd,
-				unsigned long flags)
-{
-	return 0;
-}
-
-/* Request bus settings on camera side */
-static unsigned long ov9740_query_bus_param(struct soc_camera_device *icd)
-{
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-
-	unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
-		SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
-		SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
-
-	return soc_camera_apply_sensor_flags(icl, flags);
-}
-
 /* select nearest higher resolution for capture */
 static void ov9740_res_roundup(u32 *width, u32 *height)
 {
@@ -788,36 +752,18 @@ static int ov9740_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 	return 0;
 }
 
-/* Get status of additional camera capabilities */
-static int ov9740_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-	struct ov9740_priv *priv = to_ov9740(sd);
-
-	switch (ctrl->id) {
-	case V4L2_CID_VFLIP:
-		ctrl->value = priv->flag_vflip;
-		break;
-	case V4L2_CID_HFLIP:
-		ctrl->value = priv->flag_hflip;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
 /* Set status of additional camera capabilities */
-static int ov9740_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct ov9740_priv *priv = to_ov9740(sd);
+	struct ov9740_priv *priv =
+		container_of(ctrl->handler, struct ov9740_priv, hdl);
 
 	switch (ctrl->id) {
 	case V4L2_CID_VFLIP:
-		priv->flag_vflip = ctrl->value;
+		priv->flag_vflip = ctrl->val;
 		break;
 	case V4L2_CID_HFLIP:
-		priv->flag_hflip = ctrl->value;
+		priv->flag_hflip = ctrl->val;
 		break;
 	default:
 		return -EINVAL;
@@ -890,18 +836,13 @@ static int ov9740_set_register(struct v4l2_subdev *sd,
 }
 #endif
 
-static int ov9740_video_probe(struct soc_camera_device *icd,
-			      struct i2c_client *client)
+static int ov9740_video_probe(struct i2c_client *client)
 {
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 	struct ov9740_priv *priv = to_ov9740(sd);
 	u8 modelhi, modello;
 	int ret;
 
-	/* We must have a parent by now. And it cannot be a wrong one. */
-	BUG_ON(!icd->parent ||
-	       to_soc_camera_host(icd->parent)->nr != icd->iface);
-
 	/*
 	 * check and show product ID and manufacturer ID
 	 */
@@ -942,25 +883,33 @@ err:
 	return ret;
 }
 
-static struct soc_camera_ops ov9740_ops = {
-	.set_bus_param		= ov9740_set_bus_param,
-	.query_bus_param	= ov9740_query_bus_param,
-	.controls		= ov9740_controls,
-	.num_controls		= ARRAY_SIZE(ov9740_controls),
-};
+/* Request bus settings on camera side */
+static int ov9740_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+	cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+		V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+		V4L2_MBUS_DATA_ACTIVE_HIGH;
+	cfg->type = V4L2_MBUS_PARALLEL;
+	cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+	return 0;
+}
 
 static struct v4l2_subdev_video_ops ov9740_video_ops = {
-	.s_stream		= ov9740_s_stream,
-	.s_mbus_fmt		= ov9740_s_fmt,
-	.try_mbus_fmt		= ov9740_try_fmt,
-	.enum_mbus_fmt		= ov9740_enum_fmt,
-	.cropcap		= ov9740_cropcap,
-	.g_crop			= ov9740_g_crop,
+	.s_stream	= ov9740_s_stream,
+	.s_mbus_fmt	= ov9740_s_fmt,
+	.try_mbus_fmt	= ov9740_try_fmt,
+	.enum_mbus_fmt	= ov9740_enum_fmt,
+	.cropcap	= ov9740_cropcap,
+	.g_crop		= ov9740_g_crop,
+	.g_mbus_config	= ov9740_g_mbus_config,
 };
 
 static struct v4l2_subdev_core_ops ov9740_core_ops = {
-	.g_ctrl			= ov9740_g_ctrl,
-	.s_ctrl			= ov9740_s_ctrl,
 	.g_chip_ident		= ov9740_g_chip_ident,
 	.s_power		= ov9740_s_power,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -974,6 +923,10 @@ static struct v4l2_subdev_ops ov9740_subdev_ops = {
 	.video			= &ov9740_video_ops,
 };
 
+static const struct v4l2_ctrl_ops ov9740_ctrl_ops = {
+	.s_ctrl = ov9740_s_ctrl,
+};
+
 /*
  * i2c_driver function
  */
@@ -981,16 +934,9 @@ static int ov9740_probe(struct i2c_client *client,
 			const struct i2c_device_id *did)
 {
 	struct ov9740_priv *priv;
-	struct soc_camera_device *icd	= client->dev.platform_data;
-	struct soc_camera_link *icl;
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 	int ret;
 
-	if (!icd) {
-		dev_err(&client->dev, "Missing soc-camera data!\n");
-		return -EINVAL;
-	}
-
-	icl = to_soc_camera_link(icd);
 	if (!icl) {
 		dev_err(&client->dev, "Missing platform_data for driver\n");
 		return -EINVAL;
@@ -1003,12 +949,24 @@ static int ov9740_probe(struct i2c_client *client,
 	}
 
 	v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops);
+	v4l2_ctrl_handler_init(&priv->hdl, 13);
+	v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+	priv->subdev.ctrl_handler = &priv->hdl;
+	if (priv->hdl.error) {
+		int err = priv->hdl.error;
 
-	icd->ops = &ov9740_ops;
+		kfree(priv);
+		return err;
+	}
 
-	ret = ov9740_video_probe(icd, client);
+	ret = ov9740_video_probe(client);
+	if (!ret)
+		ret = v4l2_ctrl_handler_setup(&priv->hdl);
 	if (ret < 0) {
-		icd->ops = NULL;
+		v4l2_ctrl_handler_free(&priv->hdl);
 		kfree(priv);
 	}
 
@@ -1019,8 +977,9 @@ static int ov9740_remove(struct i2c_client *client)
 {
 	struct ov9740_priv *priv = i2c_get_clientdata(client);
 
+	v4l2_device_unregister_subdev(&priv->subdev);
+	v4l2_ctrl_handler_free(&priv->hdl);
 	kfree(priv);
-
 	return 0;
 }
 
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index 360be226718d..01ff643e682d 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -744,9 +744,9 @@ static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
 /***************************************************************************/
 /* Videobuf2 operations */
 
-static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
-				unsigned int *nplanes, unsigned int sizes[],
-				void *alloc_ctxs[])
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+				unsigned int *nbuffers, unsigned int *nplanes,
+				unsigned int sizes[], void *alloc_ctxs[])
 {
 	struct pwc_device *pdev = vb2_get_drv_priv(vq);
 
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
index d07df22a5ec6..79fb22c89ae9 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/video/pxa_camera.c
@@ -214,6 +214,7 @@ struct pxa_camera_dev {
 	unsigned long		ciclk;
 	unsigned long		mclk;
 	u32			mclk_divisor;
+	u16			width_flags;	/* max 10 bits */
 
 	struct list_head	capture;
 
@@ -1020,37 +1021,20 @@ static int test_platform_param(struct pxa_camera_dev *pcdev,
 	 * quick capture interface supports both.
 	 */
 	*flags = (pcdev->platform_flags & PXA_CAMERA_MASTER ?
-		  SOCAM_MASTER : SOCAM_SLAVE) |
-		SOCAM_HSYNC_ACTIVE_HIGH |
-		SOCAM_HSYNC_ACTIVE_LOW |
-		SOCAM_VSYNC_ACTIVE_HIGH |
-		SOCAM_VSYNC_ACTIVE_LOW |
-		SOCAM_DATA_ACTIVE_HIGH |
-		SOCAM_PCLK_SAMPLE_RISING |
-		SOCAM_PCLK_SAMPLE_FALLING;
+		  V4L2_MBUS_MASTER : V4L2_MBUS_SLAVE) |
+		V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+		V4L2_MBUS_HSYNC_ACTIVE_LOW |
+		V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+		V4L2_MBUS_VSYNC_ACTIVE_LOW |
+		V4L2_MBUS_DATA_ACTIVE_HIGH |
+		V4L2_MBUS_PCLK_SAMPLE_RISING |
+		V4L2_MBUS_PCLK_SAMPLE_FALLING;
 
 	/* If requested data width is supported by the platform, use it */
-	switch (buswidth) {
-	case 10:
-		if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10))
-			return -EINVAL;
-		*flags |= SOCAM_DATAWIDTH_10;
-		break;
-	case 9:
-		if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9))
-			return -EINVAL;
-		*flags |= SOCAM_DATAWIDTH_9;
-		break;
-	case 8:
-		if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8))
-			return -EINVAL;
-		*flags |= SOCAM_DATAWIDTH_8;
-		break;
-	default:
-		return -EINVAL;
-	}
+	if ((1 << (buswidth - 1)) & pcdev->width_flags)
+		return 0;
 
-	return 0;
+	return -EINVAL;
 }
 
 static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
@@ -1070,12 +1054,12 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
 	 * Datawidth is now guaranteed to be equal to one of the three values.
 	 * We fix bit-per-pixel equal to data-width...
 	 */
-	switch (flags & SOCAM_DATAWIDTH_MASK) {
-	case SOCAM_DATAWIDTH_10:
+	switch (icd->current_fmt->host_fmt->bits_per_sample) {
+	case 10:
 		dw = 4;
 		bpp = 0x40;
 		break;
-	case SOCAM_DATAWIDTH_9:
+	case 9:
 		dw = 3;
 		bpp = 0x20;
 		break;
@@ -1084,7 +1068,7 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
 		 * Actually it can only be 8 now,
 		 * default is just to silence compiler warnings
 		 */
-	case SOCAM_DATAWIDTH_8:
+	case 8:
 		dw = 2;
 		bpp = 0;
 	}
@@ -1093,11 +1077,11 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
 		cicr4 |= CICR4_PCLK_EN;
 	if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
 		cicr4 |= CICR4_MCLK_EN;
-	if (flags & SOCAM_PCLK_SAMPLE_FALLING)
+	if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
 		cicr4 |= CICR4_PCP;
-	if (flags & SOCAM_HSYNC_ACTIVE_LOW)
+	if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
 		cicr4 |= CICR4_HSP;
-	if (flags & SOCAM_VSYNC_ACTIVE_LOW)
+	if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
 		cicr4 |= CICR4_VSP;
 
 	cicr0 = __raw_readl(pcdev->base + CICR0);
@@ -1151,9 +1135,11 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
 
 static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
 {
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
-	unsigned long bus_flags, camera_flags, common_flags;
+	struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+	unsigned long bus_flags, common_flags;
 	int ret;
 	struct pxa_cam *cam = icd->host_priv;
 
@@ -1162,44 +1148,58 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
 	if (ret < 0)
 		return ret;
 
-	camera_flags = icd->ops->query_bus_param(icd);
-
-	common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
-	if (!common_flags)
-		return -EINVAL;
+	ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+	if (!ret) {
+		common_flags = soc_mbus_config_compatible(&cfg,
+							  bus_flags);
+		if (!common_flags) {
+			dev_warn(icd->parent,
+				 "Flags incompatible: camera 0x%x, host 0x%lx\n",
+				 cfg.flags, bus_flags);
+			return -EINVAL;
+		}
+	} else if (ret != -ENOIOCTLCMD) {
+		return ret;
+	} else {
+		common_flags = bus_flags;
+	}
 
 	pcdev->channels = 1;
 
 	/* Make choises, based on platform preferences */
-	if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
-	    (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+	if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+	    (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
 		if (pcdev->platform_flags & PXA_CAMERA_HSP)
-			common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+			common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
 		else
-			common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+			common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
 	}
 
-	if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
-	    (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+	if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+	    (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
 		if (pcdev->platform_flags & PXA_CAMERA_VSP)
-			common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+			common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
 		else
-			common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+			common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
 	}
 
-	if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
-	    (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+	if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+	    (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
 		if (pcdev->platform_flags & PXA_CAMERA_PCP)
-			common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+			common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
 		else
-			common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+			common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
 	}
 
-	cam->flags = common_flags;
-
-	ret = icd->ops->set_bus_param(icd, common_flags);
-	if (ret < 0)
+	cfg.flags = common_flags;
+	ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+	if (ret < 0 && ret != -ENOIOCTLCMD) {
+		dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+			common_flags, ret);
 		return ret;
+	}
+
+	cam->flags = common_flags;
 
 	pxa_camera_setup_cicr(icd, common_flags, pixfmt);
 
@@ -1209,17 +1209,31 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
 static int pxa_camera_try_bus_param(struct soc_camera_device *icd,
 				    unsigned char buswidth)
 {
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct pxa_camera_dev *pcdev = ici->priv;
-	unsigned long bus_flags, camera_flags;
+	struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+	unsigned long bus_flags, common_flags;
 	int ret = test_platform_param(pcdev, buswidth, &bus_flags);
 
 	if (ret < 0)
 		return ret;
 
-	camera_flags = icd->ops->query_bus_param(icd);
+	ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+	if (!ret) {
+		common_flags = soc_mbus_config_compatible(&cfg,
+							  bus_flags);
+		if (!common_flags) {
+			dev_warn(icd->parent,
+				 "Flags incompatible: camera 0x%x, host 0x%lx\n",
+				 cfg.flags, bus_flags);
+			return -EINVAL;
+		}
+	} else if (ret == -ENOIOCTLCMD) {
+		ret = 0;
+	}
 
-	return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL;
+	return ret;
 }
 
 static const struct soc_mbus_pixelfmt pxa_camera_formats[] = {
@@ -1687,6 +1701,12 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev)
 			 "data widths, using default 10 bit\n");
 		pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10;
 	}
+	if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8)
+		pcdev->width_flags = 1 << 7;
+	if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9)
+		pcdev->width_flags |= 1 << 8;
+	if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10)
+		pcdev->width_flags |= 1 << 9;
 	pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
 	if (!pcdev->mclk) {
 		dev_warn(&pdev->dev,
diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c
index 847ccc067e87..6afc61689549 100644
--- a/drivers/media/video/rj54n1cb0c.c
+++ b/drivers/media/video/rj54n1cb0c.c
@@ -11,13 +11,14 @@
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
 #include <linux/videodev2.h>
 
 #include <media/rj54n1cb0c.h>
 #include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
 
 #define RJ54N1_DEV_CODE			0x0400
 #define RJ54N1_DEV_CODE2		0x0401
@@ -148,6 +149,7 @@ struct rj54n1_clock_div {
 
 struct rj54n1 {
 	struct v4l2_subdev subdev;
+	struct v4l2_ctrl_handler hdl;
 	struct rj54n1_clock_div clk_div;
 	const struct rj54n1_datafmt *fmt;
 	struct v4l2_rect rect;	/* Sensor window */
@@ -499,31 +501,6 @@ static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable)
 	return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80);
 }
 
-static int rj54n1_set_bus_param(struct soc_camera_device *icd,
-				unsigned long flags)
-{
-	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	/* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
-
-	if (flags & SOCAM_PCLK_SAMPLE_RISING)
-		return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4);
-	else
-		return reg_write(client, RJ54N1_OUT_SIGPO, 0);
-}
-
-static unsigned long rj54n1_query_bus_param(struct soc_camera_device *icd)
-{
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-	const unsigned long flags =
-		SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
-		SOCAM_MASTER | SOCAM_DATAWIDTH_8 |
-		SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
-		SOCAM_DATA_ACTIVE_HIGH;
-
-	return soc_camera_apply_sensor_flags(icl, flags);
-}
-
 static int rj54n1_set_rect(struct i2c_client *client,
 			   u16 reg_x, u16 reg_y, u16 reg_xy,
 			   u32 width, u32 height)
@@ -1202,134 +1179,51 @@ static int rj54n1_s_register(struct v4l2_subdev *sd,
 }
 #endif
 
-static const struct v4l2_queryctrl rj54n1_controls[] = {
-	{
-		.id		= V4L2_CID_VFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Vertically",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	}, {
-		.id		= V4L2_CID_HFLIP,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Flip Horizontally",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	}, {
-		.id		= V4L2_CID_GAIN,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Gain",
-		.minimum	= 0,
-		.maximum	= 127,
-		.step		= 1,
-		.default_value	= 66,
-		.flags		= V4L2_CTRL_FLAG_SLIDER,
-	}, {
-		.id		= V4L2_CID_AUTO_WHITE_BALANCE,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Auto white balance",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 1,
-	},
-};
-
-static struct soc_camera_ops rj54n1_ops = {
-	.set_bus_param		= rj54n1_set_bus_param,
-	.query_bus_param	= rj54n1_query_bus_param,
-	.controls		= rj54n1_controls,
-	.num_controls		= ARRAY_SIZE(rj54n1_controls),
-};
-
-static int rj54n1_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+	struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl);
+	struct v4l2_subdev *sd = &rj54n1->subdev;
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct rj54n1 *rj54n1 = to_rj54n1(client);
 	int data;
 
 	switch (ctrl->id) {
 	case V4L2_CID_VFLIP:
-		data = reg_read(client, RJ54N1_MIRROR_STILL_MODE);
-		if (data < 0)
-			return -EIO;
-		ctrl->value = !(data & 1);
-		break;
-	case V4L2_CID_HFLIP:
-		data = reg_read(client, RJ54N1_MIRROR_STILL_MODE);
-		if (data < 0)
-			return -EIO;
-		ctrl->value = !(data & 2);
-		break;
-	case V4L2_CID_GAIN:
-		data = reg_read(client, RJ54N1_Y_GAIN);
-		if (data < 0)
-			return -EIO;
-
-		ctrl->value = data / 2;
-		break;
-	case V4L2_CID_AUTO_WHITE_BALANCE:
-		ctrl->value = rj54n1->auto_wb;
-		break;
-	}
-
-	return 0;
-}
-
-static int rj54n1_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
-	int data;
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct rj54n1 *rj54n1 = to_rj54n1(client);
-	const struct v4l2_queryctrl *qctrl;
-
-	qctrl = soc_camera_find_qctrl(&rj54n1_ops, ctrl->id);
-	if (!qctrl)
-		return -EINVAL;
-
-	switch (ctrl->id) {
-	case V4L2_CID_VFLIP:
-		if (ctrl->value)
+		if (ctrl->val)
 			data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1);
 		else
 			data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1);
 		if (data < 0)
 			return -EIO;
-		break;
+		return 0;
 	case V4L2_CID_HFLIP:
-		if (ctrl->value)
+		if (ctrl->val)
 			data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2);
 		else
 			data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2);
 		if (data < 0)
 			return -EIO;
-		break;
+		return 0;
 	case V4L2_CID_GAIN:
-		if (ctrl->value > qctrl->maximum ||
-		    ctrl->value < qctrl->minimum)
-			return -EINVAL;
-		else if (reg_write(client, RJ54N1_Y_GAIN, ctrl->value * 2) < 0)
+		if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0)
 			return -EIO;
-		break;
+		return 0;
 	case V4L2_CID_AUTO_WHITE_BALANCE:
 		/* Auto WB area - whole image */
-		if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->value << 7,
+		if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7,
 			    0x80) < 0)
 			return -EIO;
-		rj54n1->auto_wb = ctrl->value;
-		break;
+		rj54n1->auto_wb = ctrl->val;
+		return 0;
 	}
 
-	return 0;
+	return -EINVAL;
 }
 
+static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = {
+	.s_ctrl = rj54n1_s_ctrl,
+};
+
 static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
-	.g_ctrl		= rj54n1_g_ctrl,
-	.s_ctrl		= rj54n1_s_ctrl,
 	.g_chip_ident	= rj54n1_g_chip_ident,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.g_register	= rj54n1_g_register,
@@ -1337,6 +1231,36 @@ static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
 #endif
 };
 
+static int rj54n1_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+	cfg->flags =
+		V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+		V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH |
+		V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+	cfg->type = V4L2_MBUS_PARALLEL;
+	cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+	return 0;
+}
+
+static int rj54n1_s_mbus_config(struct v4l2_subdev *sd,
+				const struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+	/* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
+	if (soc_camera_apply_board_flags(icl, cfg) &
+	    V4L2_MBUS_PCLK_SAMPLE_RISING)
+		return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4);
+	else
+		return reg_write(client, RJ54N1_OUT_SIGPO, 0);
+}
+
 static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
 	.s_stream	= rj54n1_s_stream,
 	.s_mbus_fmt	= rj54n1_s_fmt,
@@ -1346,6 +1270,8 @@ static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
 	.g_crop		= rj54n1_g_crop,
 	.s_crop		= rj54n1_s_crop,
 	.cropcap	= rj54n1_cropcap,
+	.g_mbus_config	= rj54n1_g_mbus_config,
+	.s_mbus_config	= rj54n1_s_mbus_config,
 };
 
 static struct v4l2_subdev_ops rj54n1_subdev_ops = {
@@ -1357,17 +1283,12 @@ static struct v4l2_subdev_ops rj54n1_subdev_ops = {
  * Interface active, can use i2c. If it fails, it can indeed mean, that
  * this wasn't our capture interface, so, we wait for the right one
  */
-static int rj54n1_video_probe(struct soc_camera_device *icd,
-			      struct i2c_client *client,
+static int rj54n1_video_probe(struct i2c_client *client,
 			      struct rj54n1_pdata *priv)
 {
 	int data1, data2;
 	int ret;
 
-	/* We must have a parent by now. And it cannot be a wrong one. */
-	BUG_ON(!icd->parent ||
-	       to_soc_camera_host(icd->parent)->nr != icd->iface);
-
 	/* Read out the chip version register */
 	data1 = reg_read(client, RJ54N1_DEV_CODE);
 	data2 = reg_read(client, RJ54N1_DEV_CODE2);
@@ -1395,18 +1316,11 @@ static int rj54n1_probe(struct i2c_client *client,
 			const struct i2c_device_id *did)
 {
 	struct rj54n1 *rj54n1;
-	struct soc_camera_device *icd = client->dev.platform_data;
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
-	struct soc_camera_link *icl;
 	struct rj54n1_pdata *rj54n1_priv;
 	int ret;
 
-	if (!icd) {
-		dev_err(&client->dev, "RJ54N1CB0C: missing soc-camera data!\n");
-		return -EINVAL;
-	}
-
-	icl = to_soc_camera_link(icd);
 	if (!icl || !icl->priv) {
 		dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n");
 		return -EINVAL;
@@ -1425,8 +1339,22 @@ static int rj54n1_probe(struct i2c_client *client,
 		return -ENOMEM;
 
 	v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops);
+	v4l2_ctrl_handler_init(&rj54n1->hdl, 4);
+	v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+			V4L2_CID_GAIN, 0, 127, 1, 66);
+	v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+			V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+	rj54n1->subdev.ctrl_handler = &rj54n1->hdl;
+	if (rj54n1->hdl.error) {
+		int err = rj54n1->hdl.error;
 
-	icd->ops		= &rj54n1_ops;
+		kfree(rj54n1);
+		return err;
+	}
 
 	rj54n1->clk_div		= clk_div;
 	rj54n1->rect.left	= RJ54N1_COLUMN_SKIP;
@@ -1440,25 +1368,24 @@ static int rj54n1_probe(struct i2c_client *client,
 	rj54n1->tgclk_mhz	= (rj54n1_priv->mclk_freq / PLL_L * PLL_N) /
 		(clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1);
 
-	ret = rj54n1_video_probe(icd, client, rj54n1_priv);
+	ret = rj54n1_video_probe(client, rj54n1_priv);
 	if (ret < 0) {
-		icd->ops = NULL;
+		v4l2_ctrl_handler_free(&rj54n1->hdl);
 		kfree(rj54n1);
 		return ret;
 	}
-
-	return ret;
+	return v4l2_ctrl_handler_setup(&rj54n1->hdl);
 }
 
 static int rj54n1_remove(struct i2c_client *client)
 {
 	struct rj54n1 *rj54n1 = to_rj54n1(client);
-	struct soc_camera_device *icd = client->dev.platform_data;
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
 
-	icd->ops = NULL;
+	v4l2_device_unregister_subdev(&rj54n1->subdev);
 	if (icl->free_bus)
 		icl->free_bus(icl);
+	v4l2_ctrl_handler_free(&rj54n1->hdl);
 	kfree(rj54n1);
 
 	return 0;
diff --git a/drivers/media/video/s5k6aa.c b/drivers/media/video/s5k6aa.c
new file mode 100644
index 000000000000..2446736b7871
--- /dev/null
+++ b/drivers/media/video/s5k6aa.c
@@ -0,0 +1,1680 @@
+/*
+ * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor
+ * with embedded SoC ISP.
+ *
+ * Copyright (C) 2011, Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * Based on a driver authored by Dongsoo Nathaniel Kim.
+ * Copyright (C) 2009, Dongsoo Nathaniel Kim <dongsoo45.kim@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/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/media.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-mediabus.h>
+#include <media/s5k6aa.h>
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define DRIVER_NAME			"S5K6AA"
+
+/* The token to indicate array termination */
+#define S5K6AA_TERM			0xffff
+#define S5K6AA_OUT_WIDTH_DEF		640
+#define S5K6AA_OUT_HEIGHT_DEF		480
+#define S5K6AA_WIN_WIDTH_MAX		1280
+#define S5K6AA_WIN_HEIGHT_MAX		1024
+#define S5K6AA_WIN_WIDTH_MIN		8
+#define S5K6AA_WIN_HEIGHT_MIN		8
+
+/*
+ * H/W register Interface (0xD0000000 - 0xD0000FFF)
+ */
+#define AHB_MSB_ADDR_PTR		0xfcfc
+#define GEN_REG_OFFSH			0xd000
+#define REG_CMDWR_ADDRH			0x0028
+#define REG_CMDWR_ADDRL			0x002a
+#define REG_CMDRD_ADDRH			0x002c
+#define REG_CMDRD_ADDRL			0x002e
+#define REG_CMDBUF0_ADDR		0x0f12
+#define REG_CMDBUF1_ADDR		0x0f10
+
+/*
+ * Host S/W Register interface (0x70000000 - 0x70002000)
+ * The value of the two most significant address bytes is 0x7000,
+ * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs.
+ */
+#define HOST_SWIF_OFFSH			0x7000
+
+/* Initialization parameters */
+/* Master clock frequency in KHz */
+#define REG_I_INCLK_FREQ_L		0x01b8
+#define REG_I_INCLK_FREQ_H		0x01ba
+#define  MIN_MCLK_FREQ_KHZ		6000U
+#define  MAX_MCLK_FREQ_KHZ		27000U
+#define REG_I_USE_NPVI_CLOCKS		0x01c6
+#define REG_I_USE_NMIPI_CLOCKS		0x01c8
+
+/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */
+#define REG_I_OPCLK_4KHZ(n)		((n) * 6 + 0x01cc)
+#define REG_I_MIN_OUTRATE_4KHZ(n)	((n) * 6 + 0x01ce)
+#define REG_I_MAX_OUTRATE_4KHZ(n)	((n) * 6 + 0x01d0)
+#define  SYS_PLL_OUT_FREQ		(48000000 / 4000)
+#define  PCLK_FREQ_MIN			(24000000 / 4000)
+#define  PCLK_FREQ_MAX			(48000000 / 4000)
+#define REG_I_INIT_PARAMS_UPDATED	0x01e0
+#define REG_I_ERROR_INFO		0x01e2
+
+/* General purpose parameters */
+#define REG_USER_BRIGHTNESS		0x01e4
+#define REG_USER_CONTRAST		0x01e6
+#define REG_USER_SATURATION		0x01e8
+#define REG_USER_SHARPBLUR		0x01ea
+
+#define REG_G_SPEC_EFFECTS		0x01ee
+#define REG_G_ENABLE_PREV		0x01f0
+#define REG_G_ENABLE_PREV_CHG		0x01f2
+#define REG_G_NEW_CFG_SYNC		0x01f8
+#define REG_G_PREVZOOM_IN_WIDTH		0x020a
+#define REG_G_PREVZOOM_IN_HEIGHT	0x020c
+#define REG_G_PREVZOOM_IN_XOFFS		0x020e
+#define REG_G_PREVZOOM_IN_YOFFS		0x0210
+#define REG_G_INPUTS_CHANGE_REQ		0x021a
+#define REG_G_ACTIVE_PREV_CFG		0x021c
+#define REG_G_PREV_CFG_CHG		0x021e
+#define REG_G_PREV_OPEN_AFTER_CH	0x0220
+#define REG_G_PREV_CFG_ERROR		0x0222
+
+/* Preview control section. n = 0...4. */
+#define PREG(n, x)			((n) * 0x26 + x)
+#define REG_P_OUT_WIDTH(n)		PREG(n, 0x0242)
+#define REG_P_OUT_HEIGHT(n)		PREG(n, 0x0244)
+#define REG_P_FMT(n)			PREG(n, 0x0246)
+#define REG_P_MAX_OUT_RATE(n)		PREG(n, 0x0248)
+#define REG_P_MIN_OUT_RATE(n)		PREG(n, 0x024a)
+#define REG_P_PVI_MASK(n)		PREG(n, 0x024c)
+#define REG_P_CLK_INDEX(n)		PREG(n, 0x024e)
+#define REG_P_FR_RATE_TYPE(n)		PREG(n, 0x0250)
+#define  FR_RATE_DYNAMIC		0
+#define  FR_RATE_FIXED			1
+#define  FR_RATE_FIXED_ACCURATE		2
+#define REG_P_FR_RATE_Q_TYPE(n)		PREG(n, 0x0252)
+#define  FR_RATE_Q_BEST_FRRATE		1 /* Binning enabled */
+#define  FR_RATE_Q_BEST_QUALITY		2 /* Binning disabled */
+/* Frame period in 0.1 ms units */
+#define REG_P_MAX_FR_TIME(n)		PREG(n, 0x0254)
+#define REG_P_MIN_FR_TIME(n)		PREG(n, 0x0256)
+/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */
+#define  US_TO_FR_TIME(__t)		((__t) / 100)
+#define  S5K6AA_MIN_FR_TIME		33300  /* us */
+#define  S5K6AA_MAX_FR_TIME		650000 /* us */
+#define  S5K6AA_MAX_HIGHRES_FR_TIME	666    /* x100 us */
+/* The below 5 registers are for "device correction" values */
+#define REG_P_COLORTEMP(n)		PREG(n, 0x025e)
+#define REG_P_PREV_MIRROR(n)		PREG(n, 0x0262)
+
+/* Extended image property controls */
+/* Exposure time in 10 us units */
+#define REG_SF_USR_EXPOSURE_L		0x03c6
+#define REG_SF_USR_EXPOSURE_H		0x03c8
+#define REG_SF_USR_EXPOSURE_CHG		0x03ca
+#define REG_SF_USR_TOT_GAIN		0x03cc
+#define REG_SF_USR_TOT_GAIN_CHG		0x03ce
+#define REG_SF_RGAIN			0x03d0
+#define REG_SF_RGAIN_CHG		0x03d2
+#define REG_SF_GGAIN			0x03d4
+#define REG_SF_GGAIN_CHG		0x03d6
+#define REG_SF_BGAIN			0x03d8
+#define REG_SF_BGAIN_CHG		0x03da
+#define REG_SF_FLICKER_QUANT		0x03dc
+#define REG_SF_FLICKER_QUANT_CHG	0x03de
+
+/* Output interface (parallel/MIPI) setup */
+#define REG_OIF_EN_MIPI_LANES		0x03fa
+#define REG_OIF_EN_PACKETS		0x03fc
+#define REG_OIF_CFG_CHG			0x03fe
+
+/* Auto-algorithms enable mask */
+#define REG_DBG_AUTOALG_EN		0x0400
+#define  AALG_ALL_EN_MASK		(1 << 0)
+#define  AALG_AE_EN_MASK		(1 << 1)
+#define  AALG_DIVLEI_EN_MASK		(1 << 2)
+#define  AALG_WB_EN_MASK		(1 << 3)
+#define  AALG_FLICKER_EN_MASK		(1 << 5)
+#define  AALG_FIT_EN_MASK		(1 << 6)
+#define  AALG_WRHW_EN_MASK		(1 << 7)
+
+/* Firmware revision information */
+#define REG_FW_APIVER			0x012e
+#define  S5K6AAFX_FW_APIVER		0x0001
+#define REG_FW_REVISION			0x0130
+
+/* For now we use only one user configuration register set */
+#define S5K6AA_MAX_PRESETS		1
+
+static const char * const s5k6aa_supply_names[] = {
+	"vdd_core",	/* Digital core supply 1.5V (1.4V to 1.6V) */
+	"vdda",		/* Analog power supply 2.8V (2.6V to 3.0V) */
+	"vdd_reg",	/* Regulator input power 1.8V (1.7V to 1.9V)
+			   or 2.8V (2.6V to 3.0) */
+	"vddio",	/* I/O supply 1.8V (1.65V to 1.95V)
+			   or 2.8V (2.5V to 3.1V) */
+};
+#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names)
+
+enum s5k6aa_gpio_id {
+	STBY,
+	RST,
+	GPIO_NUM,
+};
+
+struct s5k6aa_regval {
+	u16 addr;
+	u16 val;
+};
+
+struct s5k6aa_pixfmt {
+	enum v4l2_mbus_pixelcode code;
+	u32 colorspace;
+	/* REG_P_FMT(x) register value */
+	u16 reg_p_fmt;
+};
+
+struct s5k6aa_preset {
+	/* output pixel format and resolution */
+	struct v4l2_mbus_framefmt mbus_fmt;
+	u8 clk_id;
+	u8 index;
+};
+
+struct s5k6aa_ctrls {
+	struct v4l2_ctrl_handler handler;
+	/* Auto / manual white balance cluster */
+	struct v4l2_ctrl *awb;
+	struct v4l2_ctrl *gain_red;
+	struct v4l2_ctrl *gain_blue;
+	struct v4l2_ctrl *gain_green;
+	/* Mirror cluster */
+	struct v4l2_ctrl *hflip;
+	struct v4l2_ctrl *vflip;
+	/* Auto exposure / manual exposure and gain cluster */
+	struct v4l2_ctrl *auto_exp;
+	struct v4l2_ctrl *exposure;
+	struct v4l2_ctrl *gain;
+};
+
+struct s5k6aa_interval {
+	u16 reg_fr_time;
+	struct v4l2_fract interval;
+	/* Maximum rectangle for the interval */
+	struct v4l2_frmsize_discrete size;
+};
+
+struct s5k6aa {
+	struct v4l2_subdev sd;
+	struct media_pad pad;
+
+	enum v4l2_mbus_type bus_type;
+	u8 mipi_lanes;
+
+	int (*s_power)(int enable);
+	struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES];
+	struct s5k6aa_gpio gpio[GPIO_NUM];
+
+	/* external master clock frequency */
+	unsigned long mclk_frequency;
+	/* ISP internal master clock frequency */
+	u16 clk_fop;
+	/* output pixel clock frequency range */
+	u16 pclk_fmin;
+	u16 pclk_fmax;
+
+	unsigned int inv_hflip:1;
+	unsigned int inv_vflip:1;
+
+	/* protects the struct members below */
+	struct mutex lock;
+
+	/* sensor matrix scan window */
+	struct v4l2_rect ccd_rect;
+
+	struct s5k6aa_ctrls ctrls;
+	struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS];
+	struct s5k6aa_preset *preset;
+	const struct s5k6aa_interval *fiv;
+
+	unsigned int streaming:1;
+	unsigned int apply_cfg:1;
+	unsigned int apply_crop:1;
+	unsigned int power;
+};
+
+static struct s5k6aa_regval s5k6aa_analog_config[] = {
+	/* Analog settings */
+	{ 0x112a, 0x0000 }, { 0x1132, 0x0000 },
+	{ 0x113e, 0x0000 }, { 0x115c, 0x0000 },
+	{ 0x1164, 0x0000 }, { 0x1174, 0x0000 },
+	{ 0x1178, 0x0000 }, { 0x077a, 0x0000 },
+	{ 0x077c, 0x0000 }, { 0x077e, 0x0000 },
+	{ 0x0780, 0x0000 }, { 0x0782, 0x0000 },
+	{ 0x0784, 0x0000 }, { 0x0786, 0x0000 },
+	{ 0x0788, 0x0000 }, { 0x07a2, 0x0000 },
+	{ 0x07a4, 0x0000 }, { 0x07a6, 0x0000 },
+	{ 0x07a8, 0x0000 }, { 0x07b6, 0x0000 },
+	{ 0x07b8, 0x0002 }, { 0x07ba, 0x0004 },
+	{ 0x07bc, 0x0004 }, { 0x07be, 0x0005 },
+	{ 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 },
+};
+
+/* TODO: Add RGB888 and Bayer format */
+static const struct s5k6aa_pixfmt s5k6aa_formats[] = {
+	{ V4L2_MBUS_FMT_YUYV8_2X8,	V4L2_COLORSPACE_JPEG,	5 },
+	/* range 16-240 */
+	{ V4L2_MBUS_FMT_YUYV8_2X8,	V4L2_COLORSPACE_REC709,	6 },
+	{ V4L2_MBUS_FMT_RGB565_2X8_BE,	V4L2_COLORSPACE_JPEG,	0 },
+};
+
+static const struct s5k6aa_interval s5k6aa_intervals[] = {
+	{ 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */
+	{ 666,  {15000, 1000000}, {1280, 1024} }, /* 15 fps */
+	{ 500,  {20000, 1000000}, {1280, 720} },  /* 20 fps */
+	{ 400,  {25000, 1000000}, {640, 480} },   /* 25 fps */
+	{ 333,  {33300, 1000000}, {640, 480} },   /* 30 fps */
+};
+
+#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */
+
+static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+	return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd;
+}
+
+static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct s5k6aa, sd);
+}
+
+/* Set initial values for all preview presets */
+static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa)
+{
+	struct s5k6aa_preset *preset = &s5k6aa->presets[0];
+	int i;
+
+	for (i = 0; i < S5K6AA_MAX_PRESETS; i++) {
+		preset->mbus_fmt.width	= S5K6AA_OUT_WIDTH_DEF;
+		preset->mbus_fmt.height	= S5K6AA_OUT_HEIGHT_DEF;
+		preset->mbus_fmt.code	= s5k6aa_formats[0].code;
+		preset->index		= i;
+		preset->clk_id		= 0;
+		preset++;
+	}
+
+	s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX];
+	s5k6aa->preset = &s5k6aa->presets[0];
+}
+
+static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val)
+{
+	u8 wbuf[2] = {addr >> 8, addr & 0xFF};
+	struct i2c_msg msg[2];
+	u8 rbuf[2];
+	int ret;
+
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 2;
+	msg[0].buf = wbuf;
+
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = 2;
+	msg[1].buf = rbuf;
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	*val = be16_to_cpu(*((u16 *)rbuf));
+
+	v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val);
+
+	return ret == 2 ? 0 : ret;
+}
+
+static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val)
+{
+	u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF};
+
+	int ret = i2c_master_send(client, buf, 4);
+	v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val);
+
+	return ret == 4 ? 0 : ret;
+}
+
+/* The command register write, assumes Command_Wr_addH = 0x7000. */
+static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val)
+{
+	int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr);
+	if (ret)
+		return ret;
+	return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val);
+}
+
+/* The command register read, assumes Command_Rd_addH = 0x7000. */
+static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val)
+{
+	int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr);
+	if (ret)
+		return ret;
+	return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val);
+}
+
+static int s5k6aa_write_array(struct v4l2_subdev *sd,
+			      const struct s5k6aa_regval *msg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u16 addr_incr = 0;
+	int ret = 0;
+
+	while (msg->addr != S5K6AA_TERM) {
+		if (addr_incr != 2)
+			ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL,
+					       msg->addr);
+		if (ret)
+			break;
+		ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val);
+		if (ret)
+			break;
+		/* Assume that msg->addr is always less than 0xfffc */
+		addr_incr = (msg + 1)->addr - msg->addr;
+		msg++;
+	}
+
+	return ret;
+}
+
+/* Configure the AHB high address bytes for GTG registers access */
+static int s5k6aa_set_ahb_address(struct i2c_client *client)
+{
+	int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH);
+	if (ret)
+		return ret;
+	ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH);
+	if (ret)
+		return ret;
+	return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH);
+}
+
+/**
+ * s5k6aa_configure_pixel_clock - apply ISP main clock/PLL configuration
+ *
+ * Configure the internal ISP PLL for the required output frequency.
+ * Locking: called with s5k6aa.lock mutex held.
+ */
+static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa)
+{
+	struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+	unsigned long fmclk = s5k6aa->mclk_frequency / 1000;
+	u16 status;
+	int ret;
+
+	if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ,
+		 "Invalid clock frequency: %ld\n", fmclk))
+		return -EINVAL;
+
+	s5k6aa->pclk_fmin = PCLK_FREQ_MIN;
+	s5k6aa->pclk_fmax = PCLK_FREQ_MAX;
+	s5k6aa->clk_fop = SYS_PLL_OUT_FREQ;
+
+	/* External input clock frequency in kHz */
+	ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16);
+	if (!ret)
+		ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF);
+	if (!ret)
+		ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1);
+	/* Internal PLL frequency */
+	if (!ret)
+		ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop);
+	if (!ret)
+		ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0),
+				   s5k6aa->pclk_fmin);
+	if (!ret)
+		ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0),
+				   s5k6aa->pclk_fmax);
+	if (!ret)
+		ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1);
+	if (!ret)
+		ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status);
+
+	return ret ? ret : (status ? -EINVAL : 0);
+}
+
+/* Set horizontal and vertical image flipping */
+static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+	int index = s5k6aa->preset->index;
+
+	unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip;
+	unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1);
+
+	return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip);
+}
+
+/* Configure auto/manual white balance and R/G/B gains */
+static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb)
+{
+	struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+	struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls;
+	u16 reg;
+
+	int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &reg);
+
+	if (!ret && !awb) {
+		ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val);
+		if (!ret)
+			ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1);
+		if (ret)
+			return ret;
+
+		ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val);
+		if (!ret)
+			ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1);
+		if (ret)
+			return ret;
+
+		ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val);
+		if (!ret)
+			ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1);
+	}
+	if (!ret) {
+		reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK;
+		ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg);
+	}
+
+	return ret;
+}
+
+/* Program FW with exposure time, 'exposure' in us units */
+static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure)
+{
+	unsigned int time = exposure / 10;
+
+	int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff);
+	if (!ret)
+		ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16);
+	if (ret)
+		return ret;
+	return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1);
+}
+
+static int s5k6aa_set_user_gain(struct i2c_client *client, int gain)
+{
+	int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain);
+	if (ret)
+		return ret;
+	return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1);
+}
+
+/* Set auto/manual exposure and total gain */
+static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value)
+{
+	struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+	unsigned int exp_time = s5k6aa->ctrls.exposure->val;
+	u16 auto_alg;
+
+	int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg);
+	if (ret)
+		return ret;
+
+	v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n",
+		 exp_time, value, auto_alg);
+
+	if (value == V4L2_EXPOSURE_AUTO) {
+		auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK;
+	} else {
+		ret = s5k6aa_set_user_exposure(c, exp_time);
+		if (ret)
+			return ret;
+		ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val);
+		if (ret)
+			return ret;
+		auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK);
+	}
+
+	return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg);
+}
+
+static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+	u16 auto_alg;
+	int ret;
+
+	ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg);
+	if (ret)
+		return ret;
+
+	if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) {
+		auto_alg |= AALG_FLICKER_EN_MASK;
+	} else {
+		auto_alg &= ~AALG_FLICKER_EN_MASK;
+		/* The V4L2_CID_LINE_FREQUENCY control values match
+		 * the register values */
+		ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value);
+		if (ret)
+			return ret;
+		ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1);
+		if (ret)
+			return ret;
+	}
+
+	return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg);
+}
+
+static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+	static const struct v4l2_control colorfx[] = {
+		{ V4L2_COLORFX_NONE,	 0 },
+		{ V4L2_COLORFX_BW,	 1 },
+		{ V4L2_COLORFX_NEGATIVE, 2 },
+		{ V4L2_COLORFX_SEPIA,	 3 },
+		{ V4L2_COLORFX_SKY_BLUE, 4 },
+		{ V4L2_COLORFX_SKETCH,	 5 },
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(colorfx); i++) {
+		if (colorfx[i].id == val)
+			return s5k6aa_write(client, REG_G_SPEC_EFFECTS,
+					    colorfx[i].value);
+	}
+	return -EINVAL;
+}
+
+static int s5k6aa_preview_config_status(struct i2c_client *client)
+{
+	u16 error = 0;
+	int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error);
+
+	v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret);
+	return ret ? ret : (error ? -EINVAL : 0);
+}
+
+static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa,
+				   struct v4l2_mbus_framefmt *mf)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++)
+		if (mf->colorspace == s5k6aa_formats[i].colorspace &&
+		    mf->code == s5k6aa_formats[i].code)
+			return i;
+	return 0;
+}
+
+static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa,
+				      struct s5k6aa_preset *preset)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+	int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt);
+	int ret;
+
+	ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index),
+			   preset->mbus_fmt.width);
+	if (!ret)
+		ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index),
+				   preset->mbus_fmt.height);
+	if (!ret)
+		ret = s5k6aa_write(client, REG_P_FMT(preset->index),
+				   s5k6aa_formats[fmt_index].reg_p_fmt);
+	return ret;
+}
+
+static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa)
+{
+	struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+	struct v4l2_rect *r = &s5k6aa->ccd_rect;
+	int ret;
+
+	ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width);
+	if (!ret)
+		ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height);
+	if (!ret)
+		ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left);
+	if (!ret)
+		ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top);
+	if (!ret)
+		ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1);
+	if (!ret)
+		s5k6aa->apply_crop = 0;
+
+	return ret;
+}
+
+/**
+ * s5k6aa_configure_video_bus - configure the video output interface
+ * @bus_type: video bus type: parallel or MIPI-CSI
+ * @nlanes: number of MIPI lanes to be used (MIPI-CSI only)
+ *
+ * Note: Only parallel bus operation has been tested.
+ */
+static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa,
+				      enum v4l2_mbus_type bus_type, int nlanes)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+	u16 cfg = 0;
+	int ret;
+
+	/*
+	 * TODO: The sensor is supposed to support BT.601 and BT.656
+	 * but there is nothing indicating how to switch between both
+	 * in the datasheet. For now default BT.601 interface is assumed.
+	 */
+	if (bus_type == V4L2_MBUS_CSI2)
+		cfg = nlanes;
+	else if (bus_type != V4L2_MBUS_PARALLEL)
+		return -EINVAL;
+
+	ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg);
+	if (ret)
+		return ret;
+	return s5k6aa_write(client, REG_OIF_CFG_CHG, 1);
+}
+
+/* This function should be called when switching to new user configuration set*/
+static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout,
+				  int cid)
+{
+	unsigned long end = jiffies + msecs_to_jiffies(timeout);
+	u16 reg = 1;
+	int ret;
+
+	ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid);
+	if (!ret)
+		ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+	if (!ret)
+		ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1);
+	if (timeout == 0)
+		return ret;
+
+	while (ret >= 0 && time_is_after_jiffies(end)) {
+		ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, &reg);
+		if (!reg)
+			return 0;
+		usleep_range(1000, 5000);
+	}
+	return ret ? ret : -ETIMEDOUT;
+}
+
+/**
+ * s5k6aa_set_prev_config - write user preview register set
+ *
+ * Configure output resolution and color fromat, pixel clock
+ * frequency range, device frame rate type and frame period range.
+ */
+static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa,
+				  struct s5k6aa_preset *preset)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+	int idx = preset->index;
+	u16 frame_rate_q;
+	int ret;
+
+	if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME)
+		frame_rate_q = FR_RATE_Q_BEST_FRRATE;
+	else
+		frame_rate_q = FR_RATE_Q_BEST_QUALITY;
+
+	ret = s5k6aa_set_output_framefmt(s5k6aa, preset);
+	if (!ret)
+		ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx),
+				   s5k6aa->pclk_fmax);
+	if (!ret)
+		ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx),
+				   s5k6aa->pclk_fmin);
+	if (!ret)
+		ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx),
+				   preset->clk_id);
+	if (!ret)
+		ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx),
+				   FR_RATE_DYNAMIC);
+	if (!ret)
+		ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx),
+				   frame_rate_q);
+	if (!ret)
+		ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx),
+				   s5k6aa->fiv->reg_fr_time + 33);
+	if (!ret)
+		ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx),
+				   s5k6aa->fiv->reg_fr_time - 33);
+	if (!ret)
+		ret = s5k6aa_new_config_sync(client, 250, idx);
+	if (!ret)
+		ret = s5k6aa_preview_config_status(client);
+	if (!ret)
+		s5k6aa->apply_cfg = 0;
+
+	v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n",
+		 s5k6aa->fiv->reg_fr_time, ret);
+	return ret;
+}
+
+/**
+ * s5k6aa_initialize_isp - basic ISP MCU initialization
+ *
+ * Configure AHB addresses for registers read/write; configure PLLs for
+ * required output pixel clock. The ISP power supply needs to be already
+ * enabled, with an optional H/W reset.
+ * Locking: called with s5k6aa.lock mutex held.
+ */
+static int s5k6aa_initialize_isp(struct v4l2_subdev *sd)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+	int ret;
+
+	s5k6aa->apply_crop = 1;
+	s5k6aa->apply_cfg = 1;
+	msleep(100);
+
+	ret = s5k6aa_set_ahb_address(client);
+	if (ret)
+		return ret;
+	ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type,
+					 s5k6aa->mipi_lanes);
+	if (ret)
+		return ret;
+	ret = s5k6aa_write_array(sd, s5k6aa_analog_config);
+	if (ret)
+		return ret;
+	msleep(20);
+
+	return s5k6aa_configure_pixel_clocks(s5k6aa);
+}
+
+static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val)
+{
+	if (!gpio_is_valid(priv->gpio[id].gpio))
+		return 0;
+	gpio_set_value(priv->gpio[id].gpio, !!val);
+	return 1;
+}
+
+static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id)
+{
+	return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level);
+}
+
+static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id)
+{
+	return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level);
+}
+
+static int __s5k6aa_power_on(struct s5k6aa *s5k6aa)
+{
+	int ret;
+
+	ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+	if (ret)
+		return ret;
+	if (s5k6aa_gpio_deassert(s5k6aa, STBY))
+		usleep_range(150, 200);
+
+	if (s5k6aa->s_power)
+		ret = s5k6aa->s_power(1);
+	usleep_range(4000, 4000);
+
+	if (s5k6aa_gpio_deassert(s5k6aa, RST))
+		msleep(20);
+
+	return ret;
+}
+
+static int __s5k6aa_power_off(struct s5k6aa *s5k6aa)
+{
+	int ret;
+
+	if (s5k6aa_gpio_assert(s5k6aa, RST))
+		usleep_range(100, 150);
+
+	if (s5k6aa->s_power) {
+		ret = s5k6aa->s_power(0);
+		if (ret)
+			return ret;
+	}
+	if (s5k6aa_gpio_assert(s5k6aa, STBY))
+		usleep_range(50, 100);
+	s5k6aa->streaming = 0;
+
+	return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+}
+
+/*
+ * V4L2 subdev core and video operations
+ */
+static int s5k6aa_set_power(struct v4l2_subdev *sd, int on)
+{
+	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+	int ret = 0;
+
+	mutex_lock(&s5k6aa->lock);
+
+	if (!on == s5k6aa->power) {
+		if (on) {
+			ret = __s5k6aa_power_on(s5k6aa);
+			if (!ret)
+				ret = s5k6aa_initialize_isp(sd);
+		} else {
+			ret = __s5k6aa_power_off(s5k6aa);
+		}
+
+		if (!ret)
+			s5k6aa->power += on ? 1 : -1;
+	}
+
+	mutex_unlock(&s5k6aa->lock);
+
+	if (!on || ret || s5k6aa->power != 1)
+		return ret;
+
+	return v4l2_ctrl_handler_setup(sd->ctrl_handler);
+}
+
+static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+	int ret = 0;
+
+	ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable);
+	if (!ret)
+		ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1);
+	if (!ret)
+		s5k6aa->streaming = enable;
+
+	return ret;
+}
+
+static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on)
+{
+	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+	int ret = 0;
+
+	mutex_lock(&s5k6aa->lock);
+
+	if (s5k6aa->streaming == !on) {
+		if (!ret && s5k6aa->apply_cfg)
+			ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset);
+		if (s5k6aa->apply_crop)
+			ret = s5k6aa_set_input_params(s5k6aa);
+		if (!ret)
+			ret = __s5k6aa_stream(s5k6aa, !!on);
+	}
+	mutex_unlock(&s5k6aa->lock);
+
+	return ret;
+}
+
+static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_frame_interval *fi)
+{
+	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+
+	mutex_lock(&s5k6aa->lock);
+	fi->interval = s5k6aa->fiv->interval;
+	mutex_unlock(&s5k6aa->lock);
+
+	return 0;
+}
+
+static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa,
+				       struct v4l2_subdev_frame_interval *fi)
+{
+	struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt;
+	const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0];
+	unsigned int err, min_err = UINT_MAX;
+	unsigned int i, fr_time;
+
+	if (fi->interval.denominator == 0)
+		return -EINVAL;
+
+	fr_time = fi->interval.numerator * 10000 / fi->interval.denominator;
+
+	for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) {
+		const struct s5k6aa_interval *iv = &s5k6aa_intervals[i];
+
+		if (mbus_fmt->width > iv->size.width ||
+		    mbus_fmt->height > iv->size.height)
+			continue;
+
+		err = abs(iv->reg_fr_time - fr_time);
+		if (err < min_err) {
+			fiv = iv;
+			min_err = err;
+		}
+	}
+	s5k6aa->fiv = fiv;
+
+	v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n",
+		 fiv->reg_fr_time * 100);
+	return 0;
+}
+
+static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_frame_interval *fi)
+{
+	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+	int ret;
+
+	v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n",
+		 fi->interval.numerator, fi->interval.denominator);
+
+	mutex_lock(&s5k6aa->lock);
+	ret = __s5k6aa_set_frame_interval(s5k6aa, fi);
+	s5k6aa->apply_cfg = 1;
+
+	mutex_unlock(&s5k6aa->lock);
+	return ret;
+}
+
+/*
+ * V4L2 subdev pad level and video operations
+ */
+static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_fh *fh,
+			      struct v4l2_subdev_frame_interval_enum *fie)
+{
+	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+	const struct s5k6aa_interval *fi;
+	int ret = 0;
+
+	if (fie->index > ARRAY_SIZE(s5k6aa_intervals))
+		return -EINVAL;
+
+	v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN,
+			      S5K6AA_WIN_WIDTH_MAX, 1,
+			      &fie->height, S5K6AA_WIN_HEIGHT_MIN,
+			      S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+	mutex_lock(&s5k6aa->lock);
+	fi = &s5k6aa_intervals[fie->index];
+	if (fie->width > fi->size.width || fie->height > fi->size.height)
+		ret = -EINVAL;
+	else
+		fie->interval = fi->interval;
+	mutex_unlock(&s5k6aa->lock);
+
+	return ret;
+}
+
+static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index >= ARRAY_SIZE(s5k6aa_formats))
+		return -EINVAL;
+
+	code->code = s5k6aa_formats[code->index].code;
+	return 0;
+}
+
+static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	int i = ARRAY_SIZE(s5k6aa_formats);
+
+	if (fse->index > 0)
+		return -EINVAL;
+
+	while (--i)
+		if (fse->code == s5k6aa_formats[i].code)
+			break;
+
+	fse->code = s5k6aa_formats[i].code;
+	fse->min_width  = S5K6AA_WIN_WIDTH_MIN;
+	fse->max_width  = S5K6AA_WIN_WIDTH_MAX;
+	fse->max_height = S5K6AA_WIN_HEIGHT_MIN;
+	fse->min_height = S5K6AA_WIN_HEIGHT_MAX;
+
+	return 0;
+}
+
+static struct v4l2_rect *
+__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh,
+		       enum v4l2_subdev_format_whence which)
+{
+	if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
+		return &s5k6aa->ccd_rect;
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return v4l2_subdev_get_try_crop(fh, 0);
+
+	return NULL;
+}
+
+static void s5k6aa_try_format(struct s5k6aa *s5k6aa,
+			      struct v4l2_mbus_framefmt *mf)
+{
+	unsigned int index;
+
+	v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN,
+			      S5K6AA_WIN_WIDTH_MAX, 1,
+			      &mf->height, S5K6AA_WIN_HEIGHT_MIN,
+			      S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+	if (mf->colorspace != V4L2_COLORSPACE_JPEG &&
+	    mf->colorspace != V4L2_COLORSPACE_REC709)
+		mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+	index = s5k6aa_get_pixfmt_index(s5k6aa, mf);
+
+	mf->colorspace	= s5k6aa_formats[index].colorspace;
+	mf->code	= s5k6aa_formats[index].code;
+	mf->field	= V4L2_FIELD_NONE;
+}
+
+static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+	struct v4l2_mbus_framefmt *mf;
+
+	memset(fmt->reserved, 0, sizeof(fmt->reserved));
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		mf = v4l2_subdev_get_try_format(fh, 0);
+		fmt->format = *mf;
+		return 0;
+	}
+
+	mutex_lock(&s5k6aa->lock);
+	fmt->format = s5k6aa->preset->mbus_fmt;
+	mutex_unlock(&s5k6aa->lock);
+
+	return 0;
+}
+
+static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+	struct s5k6aa_preset *preset = s5k6aa->preset;
+	struct v4l2_mbus_framefmt *mf;
+	struct v4l2_rect *crop;
+	int ret = 0;
+
+	mutex_lock(&s5k6aa->lock);
+	s5k6aa_try_format(s5k6aa, &fmt->format);
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+		crop = v4l2_subdev_get_try_crop(fh, 0);
+	} else {
+		if (s5k6aa->streaming) {
+			ret = -EBUSY;
+		} else {
+			mf = &preset->mbus_fmt;
+			crop = &s5k6aa->ccd_rect;
+			s5k6aa->apply_cfg = 1;
+		}
+	}
+
+	if (ret == 0) {
+		struct v4l2_subdev_frame_interval fiv = {
+			.interval = {0, 1}
+		};
+
+		*mf = fmt->format;
+		/*
+		 * Make sure the crop window is valid, i.e. its size is
+		 * greater than the output window, as the ISP supports
+		 * only down-scaling.
+		 */
+		crop->width = clamp_t(unsigned int, crop->width, mf->width,
+				      S5K6AA_WIN_WIDTH_MAX);
+		crop->height = clamp_t(unsigned int, crop->height, mf->height,
+				       S5K6AA_WIN_HEIGHT_MAX);
+		crop->left = clamp_t(unsigned int, crop->left, 0,
+				     S5K6AA_WIN_WIDTH_MAX - crop->width);
+		crop->top  = clamp_t(unsigned int, crop->top, 0,
+				     S5K6AA_WIN_HEIGHT_MAX - crop->height);
+
+		/* Reset to minimum possible frame interval */
+		ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv);
+	}
+	mutex_unlock(&s5k6aa->lock);
+
+	return ret;
+}
+
+static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_crop *crop)
+{
+	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+	struct v4l2_rect *rect;
+
+	memset(crop->reserved, 0, sizeof(crop->reserved));
+	mutex_lock(&s5k6aa->lock);
+
+	rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which);
+	if (rect)
+		crop->rect = *rect;
+
+	mutex_unlock(&s5k6aa->lock);
+
+	v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n",
+		 rect->left, rect->top, rect->width, rect->height);
+
+	return 0;
+}
+
+static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			   struct v4l2_subdev_crop *crop)
+{
+	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+	struct v4l2_mbus_framefmt *mf;
+	unsigned int max_x, max_y;
+	struct v4l2_rect *crop_r;
+
+	mutex_lock(&s5k6aa->lock);
+	crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which);
+
+	if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		mf = &s5k6aa->preset->mbus_fmt;
+		s5k6aa->apply_crop = 1;
+	} else {
+		mf = v4l2_subdev_get_try_format(fh, 0);
+	}
+	v4l_bound_align_image(&crop->rect.width, mf->width,
+			      S5K6AA_WIN_WIDTH_MAX, 1,
+			      &crop->rect.height, mf->height,
+			      S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+	max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1;
+	max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1;
+
+	crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x);
+	crop->rect.top  = clamp_t(unsigned int, crop->rect.top, 0, max_y);
+
+	*crop_r = crop->rect;
+
+	mutex_unlock(&s5k6aa->lock);
+
+	v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n",
+		 crop_r->left, crop_r->top, crop_r->width, crop_r->height);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = {
+	.enum_mbus_code		= s5k6aa_enum_mbus_code,
+	.enum_frame_size	= s5k6aa_enum_frame_size,
+	.enum_frame_interval	= s5k6aa_enum_frame_interval,
+	.get_fmt		= s5k6aa_get_fmt,
+	.set_fmt		= s5k6aa_set_fmt,
+	.get_crop		= s5k6aa_get_crop,
+	.set_crop		= s5k6aa_set_crop,
+};
+
+static const struct v4l2_subdev_video_ops s5k6aa_video_ops = {
+	.g_frame_interval	= s5k6aa_g_frame_interval,
+	.s_frame_interval	= s5k6aa_s_frame_interval,
+	.s_stream		= s5k6aa_s_stream,
+};
+
+/*
+ * V4L2 subdev controls
+ */
+
+static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+	int idx, err = 0;
+
+	v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val);
+
+	mutex_lock(&s5k6aa->lock);
+	/*
+	 * If the device is not powered up by the host driver do
+	 * not apply any controls to H/W at this time. Instead
+	 * the controls will be restored right after power-up.
+	 */
+	if (s5k6aa->power == 0)
+		goto unlock;
+	idx = s5k6aa->preset->index;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		err = s5k6aa_set_awb(s5k6aa, ctrl->val);
+		break;
+
+	case V4L2_CID_BRIGHTNESS:
+		err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val);
+		break;
+
+	case V4L2_CID_COLORFX:
+		err = s5k6aa_set_colorfx(s5k6aa, ctrl->val);
+		break;
+
+	case V4L2_CID_CONTRAST:
+		err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val);
+		break;
+
+	case V4L2_CID_EXPOSURE_AUTO:
+		err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val);
+		break;
+
+	case V4L2_CID_HFLIP:
+		err = s5k6aa_set_mirror(s5k6aa, ctrl->val);
+		if (err)
+			break;
+		err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+		break;
+
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val);
+		break;
+
+	case V4L2_CID_SATURATION:
+		err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val);
+		break;
+
+	case V4L2_CID_SHARPNESS:
+		err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val);
+		break;
+
+	case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+		err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val);
+		if (err)
+			break;
+		err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+		break;
+	}
+unlock:
+	mutex_unlock(&s5k6aa->lock);
+	return err;
+}
+
+static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = {
+	.s_ctrl	= s5k6aa_s_ctrl,
+};
+
+static int s5k6aa_log_status(struct v4l2_subdev *sd)
+{
+	v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name);
+	return 0;
+}
+
+#define V4L2_CID_RED_GAIN	(V4L2_CTRL_CLASS_CAMERA | 0x1001)
+#define V4L2_CID_GREEN_GAIN	(V4L2_CTRL_CLASS_CAMERA | 0x1002)
+#define V4L2_CID_BLUE_GAIN	(V4L2_CTRL_CLASS_CAMERA | 0x1003)
+
+static const struct v4l2_ctrl_config s5k6aa_ctrls[] = {
+	{
+		.ops	= &s5k6aa_ctrl_ops,
+		.id	= V4L2_CID_RED_GAIN,
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.name	= "Gain, Red",
+		.min	= 0,
+		.max	= 256,
+		.def	= 127,
+		.step	= 1,
+	}, {
+		.ops	= &s5k6aa_ctrl_ops,
+		.id	= V4L2_CID_GREEN_GAIN,
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.name	= "Gain, Green",
+		.min	= 0,
+		.max	= 256,
+		.def	= 127,
+		.step	= 1,
+	}, {
+		.ops	= &s5k6aa_ctrl_ops,
+		.id	= V4L2_CID_BLUE_GAIN,
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.name	= "Gain, Blue",
+		.min	= 0,
+		.max	= 256,
+		.def	= 127,
+		.step	= 1,
+	},
+};
+
+static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa)
+{
+	const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops;
+	struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls;
+	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+
+	int ret = v4l2_ctrl_handler_init(hdl, 16);
+	if (ret)
+		return ret;
+	/* Auto white balance cluster */
+	ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE,
+				       0, 1, 1, 1);
+	ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL);
+	ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL);
+	ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL);
+	v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false);
+
+	ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+	ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_cluster(2, &ctrls->hflip);
+
+	ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
+				V4L2_CID_EXPOSURE_AUTO,
+				V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
+	/* Exposure time: x 1 us */
+	ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
+					    0, 6000000U, 1, 100000U);
+	/* Total gain: 256 <=> 1x */
+	ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
+					0, 256, 1, 256);
+	v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false);
+
+	v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY,
+			       V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
+			       V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
+
+	v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX,
+			       V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE);
+
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+			  0, 256, 1, 0);
+
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0);
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0);
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0);
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0);
+
+	if (hdl->error) {
+		ret = hdl->error;
+		v4l2_ctrl_handler_free(hdl);
+		return ret;
+	}
+
+	s5k6aa->sd.ctrl_handler = hdl;
+	return 0;
+}
+
+/*
+ * V4L2 subdev internal operations
+ */
+static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+	struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0);
+
+	format->colorspace = s5k6aa_formats[0].colorspace;
+	format->code = s5k6aa_formats[0].code;
+	format->width = S5K6AA_OUT_WIDTH_DEF;
+	format->height = S5K6AA_OUT_HEIGHT_DEF;
+	format->field = V4L2_FIELD_NONE;
+
+	crop->width = S5K6AA_WIN_WIDTH_MAX;
+	crop->height = S5K6AA_WIN_HEIGHT_MAX;
+	crop->left = 0;
+	crop->top = 0;
+
+	return 0;
+}
+
+int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+	u16 api_ver = 0, fw_rev = 0;
+
+	int ret = s5k6aa_set_ahb_address(client);
+
+	if (!ret)
+		ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver);
+	if (!ret)
+		ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev);
+	if (ret) {
+		v4l2_err(&s5k6aa->sd, "FW revision check failed!\n");
+		return ret;
+	}
+
+	v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n",
+		  api_ver, fw_rev);
+
+	return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV;
+}
+
+static int s5k6aa_registered(struct v4l2_subdev *sd)
+{
+	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+	int ret;
+
+	mutex_lock(&s5k6aa->lock);
+	ret = __s5k6aa_power_on(s5k6aa);
+	if (!ret) {
+		msleep(100);
+		ret = s5k6aa_check_fw_revision(s5k6aa);
+		__s5k6aa_power_off(s5k6aa);
+	}
+	mutex_unlock(&s5k6aa->lock);
+
+	return ret;
+}
+
+static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = {
+	.registered = s5k6aa_registered,
+	.open = s5k6aa_open,
+};
+
+static const struct v4l2_subdev_core_ops s5k6aa_core_ops = {
+	.s_power = s5k6aa_set_power,
+	.log_status = s5k6aa_log_status,
+};
+
+static const struct v4l2_subdev_ops s5k6aa_subdev_ops = {
+	.core = &s5k6aa_core_ops,
+	.pad = &s5k6aa_pad_ops,
+	.video = &s5k6aa_video_ops,
+};
+
+/*
+ * GPIO setup
+ */
+static int s5k6aa_configure_gpio(int nr, int val, const char *name)
+{
+	unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+	int ret;
+
+	if (!gpio_is_valid(nr))
+		return 0;
+	ret = gpio_request_one(nr, flags, name);
+	if (!ret)
+		gpio_export(nr, 0);
+	return ret;
+}
+
+static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(s5k6aa->gpio); i++) {
+		if (!gpio_is_valid(s5k6aa->gpio[i].gpio))
+			continue;
+		gpio_free(s5k6aa->gpio[i].gpio);
+		s5k6aa->gpio[i].gpio = -EINVAL;
+	}
+}
+
+static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa,
+				  const struct s5k6aa_platform_data *pdata)
+{
+	const struct s5k6aa_gpio *gpio = &pdata->gpio_stby;
+	int ret;
+
+	s5k6aa->gpio[STBY].gpio = -EINVAL;
+	s5k6aa->gpio[RST].gpio  = -EINVAL;
+
+	ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY");
+	if (ret) {
+		s5k6aa_free_gpios(s5k6aa);
+		return ret;
+	}
+	s5k6aa->gpio[STBY] = *gpio;
+	if (gpio_is_valid(gpio->gpio))
+		gpio_set_value(gpio->gpio, 0);
+
+	gpio = &pdata->gpio_reset;
+	ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST");
+	if (ret) {
+		s5k6aa_free_gpios(s5k6aa);
+		return ret;
+	}
+	s5k6aa->gpio[RST] = *gpio;
+	if (gpio_is_valid(gpio->gpio))
+		gpio_set_value(gpio->gpio, 0);
+
+	return 0;
+}
+
+static int s5k6aa_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	const struct s5k6aa_platform_data *pdata = client->dev.platform_data;
+	struct v4l2_subdev *sd;
+	struct s5k6aa *s5k6aa;
+	int i, ret;
+
+	if (pdata == NULL) {
+		dev_err(&client->dev, "Platform data not specified\n");
+		return -EINVAL;
+	}
+
+	if (pdata->mclk_frequency == 0) {
+		dev_err(&client->dev, "MCLK frequency not specified\n");
+		return -EINVAL;
+	}
+
+	s5k6aa = kzalloc(sizeof(*s5k6aa), GFP_KERNEL);
+	if (!s5k6aa)
+		return -ENOMEM;
+
+	mutex_init(&s5k6aa->lock);
+
+	s5k6aa->mclk_frequency = pdata->mclk_frequency;
+	s5k6aa->bus_type = pdata->bus_type;
+	s5k6aa->mipi_lanes = pdata->nlanes;
+	s5k6aa->s_power	= pdata->set_power;
+	s5k6aa->inv_hflip = pdata->horiz_flip;
+	s5k6aa->inv_vflip = pdata->vert_flip;
+
+	sd = &s5k6aa->sd;
+	strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name));
+	v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops);
+
+	sd->internal_ops = &s5k6aa_subdev_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+	ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0);
+	if (ret)
+		goto out_err1;
+
+	ret = s5k6aa_configure_gpios(s5k6aa, pdata);
+	if (ret)
+		goto out_err2;
+
+	for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++)
+		s5k6aa->supplies[i].supply = s5k6aa_supply_names[i];
+
+	ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES,
+				 s5k6aa->supplies);
+	if (ret) {
+		dev_err(&client->dev, "Failed to get regulators\n");
+		goto out_err3;
+	}
+
+	ret = s5k6aa_initialize_ctrls(s5k6aa);
+	if (ret)
+		goto out_err4;
+
+	s5k6aa_presets_data_init(s5k6aa);
+
+	s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX;
+	s5k6aa->ccd_rect.height	= S5K6AA_WIN_HEIGHT_MAX;
+	s5k6aa->ccd_rect.left = 0;
+	s5k6aa->ccd_rect.top = 0;
+
+	return 0;
+
+out_err4:
+	regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+out_err3:
+	s5k6aa_free_gpios(s5k6aa);
+out_err2:
+	media_entity_cleanup(&s5k6aa->sd.entity);
+out_err1:
+	kfree(s5k6aa);
+	return ret;
+}
+
+static int s5k6aa_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+
+	v4l2_device_unregister_subdev(sd);
+	v4l2_ctrl_handler_free(sd->ctrl_handler);
+	media_entity_cleanup(&sd->entity);
+	regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+	s5k6aa_free_gpios(s5k6aa);
+	kfree(s5k6aa);
+
+	return 0;
+}
+
+static const struct i2c_device_id s5k6aa_id[] = {
+	{ DRIVER_NAME, 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, s5k6aa_id);
+
+
+static struct i2c_driver s5k6aa_i2c_driver = {
+	.driver = {
+		.name = DRIVER_NAME
+	},
+	.probe		= s5k6aa_probe,
+	.remove		= s5k6aa_remove,
+	.id_table	= s5k6aa_id,
+};
+
+static int __init s5k6aa_init(void)
+{
+	return i2c_add_driver(&s5k6aa_i2c_driver);
+}
+
+static void __exit s5k6aa_exit(void)
+{
+	i2c_del_driver(&s5k6aa_i2c_driver);
+}
+
+module_init(s5k6aa_init);
+module_exit(s5k6aa_exit);
+
+MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c
index 931f469604b0..c8d91b0cd9bd 100644
--- a/drivers/media/video/s5p-fimc/fimc-capture.c
+++ b/drivers/media/video/s5p-fimc/fimc-capture.c
@@ -246,9 +246,9 @@ static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane)
 	return fr->f_width * fr->f_height * fr->fmt->depth[plane] / 8;
 }
 
-static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
-		       unsigned int *num_planes, unsigned int sizes[],
-		       void *allocators[])
+static int queue_setup(struct vb2_queue *vq,  const struct v4l2_format *pfmt,
+		       unsigned int *num_buffers, unsigned int *num_planes,
+		       unsigned int sizes[], void *allocators[])
 {
 	struct fimc_ctx *ctx = vq->drv_priv;
 	struct fimc_fmt *fmt = ctx->d_frame.fmt;
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
index 6c1c9cb55378..19ca6db38b2f 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -670,9 +670,9 @@ static void fimc_job_abort(void *priv)
 	fimc_m2m_shutdown(priv);
 }
 
-static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
-			    unsigned int *num_planes, unsigned int sizes[],
-			    void *allocators[])
+static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+			    unsigned int *num_buffers, unsigned int *num_planes,
+			    unsigned int sizes[], void *allocators[])
 {
 	struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
 	struct fimc_frame *f;
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
index 5f4da80051bb..f2481a85e0a2 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
@@ -38,7 +38,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
 	 * into kernel. */
 	mfc_debug_enter();
 	err = request_firmware((const struct firmware **)&fw_blob,
-				     "s5pc110-mfc.fw", dev->v4l2_dev.dev);
+				     "s5p-mfc.fw", dev->v4l2_dev.dev);
 	if (err != 0) {
 		mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
 		return -EINVAL;
@@ -116,7 +116,7 @@ int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev)
 	 * into kernel. */
 	mfc_debug_enter();
 	err = request_firmware((const struct firmware **)&fw_blob,
-				     "s5pc110-mfc.fw", dev->v4l2_dev.dev);
+				     "s5p-mfc.fw", dev->v4l2_dev.dev);
 	if (err != 0) {
 		mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
 		return -EINVAL;
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
index bfbe08432050..725634d9736d 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
@@ -744,9 +744,10 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
 	.vidioc_g_crop = vidioc_g_crop,
 };
 
-static int s5p_mfc_queue_setup(struct vb2_queue *vq, unsigned int *buf_count,
-			       unsigned int *plane_count, unsigned int psize[],
-			       void *allocators[])
+static int s5p_mfc_queue_setup(struct vb2_queue *vq,
+			const struct v4l2_format *fmt, unsigned int *buf_count,
+			unsigned int *plane_count, unsigned int psize[],
+			void *allocators[])
 {
 	struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
 
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
index 4c90e53bd964..ecef127dbc66 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
@@ -1513,8 +1513,9 @@ static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb)
 }
 
 static int s5p_mfc_queue_setup(struct vb2_queue *vq,
-		       unsigned int *buf_count, unsigned int *plane_count,
-		       unsigned int psize[], void *allocators[])
+			const struct v4l2_format *fmt,
+			unsigned int *buf_count, unsigned int *plane_count,
+			unsigned int psize[], void *allocators[])
 {
 	struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
 
diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c
index 4917e2c2b321..e16d3a4bc1dc 100644
--- a/drivers/media/video/s5p-tv/mixer_video.c
+++ b/drivers/media/video/s5p-tv/mixer_video.c
@@ -727,8 +727,8 @@ static const struct v4l2_file_operations mxr_fops = {
 	.unlocked_ioctl = video_ioctl2,
 };
 
-static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
-	unsigned int *nplanes, unsigned int sizes[],
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
+	unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[],
 	void *alloc_ctxs[])
 {
 	struct mxr_layer *layer = vb2_get_drv_priv(vq);
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index bc8d6bba8ee5..9b550687213a 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -843,10 +843,10 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev);
 int saa7134_ir_start(struct saa7134_dev *dev);
 void saa7134_ir_stop(struct saa7134_dev *dev);
 #else
-#define saa7134_input_init1(dev)	(0)
-#define saa7134_input_fini(dev)		(0)
-#define saa7134_input_irq(dev)		(0)
-#define saa7134_probe_i2c_ir(dev)	(0)
-#define saa7134_ir_start(dev)		(0)
-#define saa7134_ir_stop(dev)		(0)
+#define saa7134_input_init1(dev)	((void)0)
+#define saa7134_input_fini(dev)		((void)0)
+#define saa7134_input_irq(dev)		((void)0)
+#define saa7134_probe_i2c_ir(dev)	((void)0)
+#define saa7134_ir_start(dev)		((void)0)
+#define saa7134_ir_stop(dev)		((void)0)
 #endif
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index 8615fb81775f..f390682629cf 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -90,7 +90,6 @@
 struct sh_mobile_ceu_buffer {
 	struct vb2_buffer vb; /* v4l buffer must be first */
 	struct list_head queue;
-	enum v4l2_mbus_pixelcode code;
 };
 
 struct sh_mobile_ceu_dev {
@@ -100,7 +99,8 @@ struct sh_mobile_ceu_dev {
 
 	unsigned int irq;
 	void __iomem *base;
-	unsigned long video_limit;
+	size_t video_limit;
+	size_t buf_total;
 
 	spinlock_t lock;		/* Protects video buffer lists */
 	struct list_head capture;
@@ -121,7 +121,7 @@ struct sh_mobile_ceu_dev {
 };
 
 struct sh_mobile_ceu_cam {
-	/* CEU offsets within scaled by the CEU camera output */
+	/* CEU offsets within the camera output, before the CEU scaler */
 	unsigned int ceu_left;
 	unsigned int ceu_top;
 	/* Client output, as seen by the CEU */
@@ -144,30 +144,6 @@ static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_buffer *vb)
 	return container_of(vb, struct sh_mobile_ceu_buffer, vb);
 }
 
-static unsigned long make_bus_param(struct sh_mobile_ceu_dev *pcdev)
-{
-	unsigned long flags;
-
-	flags = SOCAM_MASTER |
-		SOCAM_PCLK_SAMPLE_RISING |
-		SOCAM_HSYNC_ACTIVE_HIGH |
-		SOCAM_HSYNC_ACTIVE_LOW |
-		SOCAM_VSYNC_ACTIVE_HIGH |
-		SOCAM_VSYNC_ACTIVE_LOW |
-		SOCAM_DATA_ACTIVE_HIGH;
-
-	if (pcdev->pdata->flags & SH_CEU_FLAG_USE_8BIT_BUS)
-		flags |= SOCAM_DATAWIDTH_8;
-
-	if (pcdev->pdata->flags & SH_CEU_FLAG_USE_16BIT_BUS)
-		flags |= SOCAM_DATAWIDTH_16;
-
-	if (flags & SOCAM_DATAWIDTH_MASK)
-		return flags;
-
-	return 0;
-}
-
 static void ceu_write(struct sh_mobile_ceu_dev *priv,
 		      unsigned long reg_offs, u32 data)
 {
@@ -216,33 +192,61 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
 /*
  *  Videobuf operations
  */
+
+/*
+ * .queue_setup() is called to check, whether the driver can accept the
+ *		  requested number of buffers and to fill in plane sizes
+ *		  for the current frame format if required
+ */
 static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
+			const struct v4l2_format *fmt,
 			unsigned int *count, unsigned int *num_planes,
 			unsigned int sizes[], void *alloc_ctxs[])
 {
 	struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq);
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
-	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
-						icd->current_fmt->host_fmt);
+	int bytes_per_line;
+	unsigned int height;
 
+	if (fmt) {
+		const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
+								fmt->fmt.pix.pixelformat);
+		if (!xlate)
+			return -EINVAL;
+		bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+							 xlate->host_fmt);
+		height = fmt->fmt.pix.height;
+	} else {
+		/* Called from VIDIOC_REQBUFS or in compatibility mode */
+		bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+						icd->current_fmt->host_fmt);
+		height = icd->user_height;
+	}
 	if (bytes_per_line < 0)
 		return bytes_per_line;
 
-	*num_planes = 1;
+	sizes[0] = bytes_per_line * height;
 
-	pcdev->sequence = 0;
-	sizes[0] = bytes_per_line * icd->user_height;
 	alloc_ctxs[0] = pcdev->alloc_ctx;
 
+	if (!vq->num_buffers)
+		pcdev->sequence = 0;
+
 	if (!*count)
 		*count = 2;
 
-	if (pcdev->video_limit) {
-		if (PAGE_ALIGN(sizes[0]) * *count > pcdev->video_limit)
-			*count = pcdev->video_limit / PAGE_ALIGN(sizes[0]);
+	/* If *num_planes != 0, we have already verified *count. */
+	if (pcdev->video_limit && !*num_planes) {
+		size_t size = PAGE_ALIGN(sizes[0]) * *count;
+
+		if (size + pcdev->buf_total > pcdev->video_limit)
+			*count = (pcdev->video_limit - pcdev->buf_total) /
+				PAGE_ALIGN(sizes[0]);
 	}
 
+	*num_planes = 1;
+
 	dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]);
 
 	return 0;
@@ -267,6 +271,7 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
 	unsigned long top1, top2;
 	unsigned long bottom1, bottom2;
 	u32 status;
+	bool planar;
 	int ret = 0;
 
 	/*
@@ -314,17 +319,29 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
 
 	phys_addr_top = vb2_dma_contig_plane_dma_addr(pcdev->active, 0);
 
-	ceu_write(pcdev, top1, phys_addr_top);
-	if (V4L2_FIELD_NONE != pcdev->field) {
-		phys_addr_bottom = phys_addr_top + icd->user_width;
-		ceu_write(pcdev, bottom1, phys_addr_bottom);
-	}
-
 	switch (icd->current_fmt->host_fmt->fourcc) {
 	case V4L2_PIX_FMT_NV12:
 	case V4L2_PIX_FMT_NV21:
 	case V4L2_PIX_FMT_NV16:
 	case V4L2_PIX_FMT_NV61:
+		planar = true;
+		break;
+	default:
+		planar = false;
+	}
+
+	ceu_write(pcdev, top1, phys_addr_top);
+	if (V4L2_FIELD_NONE != pcdev->field) {
+		if (planar)
+			phys_addr_bottom = phys_addr_top + icd->user_width;
+		else
+			phys_addr_bottom = phys_addr_top +
+				soc_mbus_bytes_per_line(icd->user_width,
+							icd->current_fmt->host_fmt);
+		ceu_write(pcdev, bottom1, phys_addr_bottom);
+	}
+
+	if (planar) {
 		phys_addr_top += icd->user_width *
 			icd->user_height;
 		ceu_write(pcdev, top2, phys_addr_top);
@@ -341,23 +358,40 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
 
 static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb)
 {
+	struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
+
+	/* Added list head initialization on alloc */
+	WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb);
+
+	return 0;
+}
+
+static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
+{
 	struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
-	struct sh_mobile_ceu_buffer *buf;
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct sh_mobile_ceu_dev *pcdev = ici->priv;
+	struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
+	unsigned long size;
 	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
 						icd->current_fmt->host_fmt);
-	unsigned long size;
 
 	if (bytes_per_line < 0)
-		return bytes_per_line;
+		goto error;
 
-	buf = to_ceu_vb(vb);
+	size = icd->user_height * bytes_per_line;
+
+	if (vb2_plane_size(vb, 0) < size) {
+		dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n",
+			vb->v4l2_buf.index, vb2_plane_size(vb, 0), size);
+		goto error;
+	}
+
+	vb2_set_plane_payload(vb, 0, size);
 
 	dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
 		vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
 
-	/* Added list head initialization on alloc */
-	WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb);
-
 #ifdef DEBUG
 	/*
 	 * This can be useful if you want to see if we actually fill
@@ -367,31 +401,6 @@ static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb)
 		memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
 #endif
 
-	BUG_ON(NULL == icd->current_fmt);
-
-	size = icd->user_height * bytes_per_line;
-
-	if (vb2_plane_size(vb, 0) < size) {
-		dev_err(icd->parent, "Buffer too small (%lu < %lu)\n",
-			vb2_plane_size(vb, 0), size);
-		return -ENOBUFS;
-	}
-
-	vb2_set_plane_payload(vb, 0, size);
-
-	return 0;
-}
-
-static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
-{
-	struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-	struct sh_mobile_ceu_dev *pcdev = ici->priv;
-	struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
-
-	dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
-		vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
 	spin_lock_irq(&pcdev->lock);
 	list_add_tail(&buf->queue, &pcdev->capture);
 
@@ -405,6 +414,11 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
 		sh_mobile_ceu_capture(pcdev);
 	}
 	spin_unlock_irq(&pcdev->lock);
+
+	return;
+
+error:
+	vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
 }
 
 static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
@@ -429,11 +443,23 @@ static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
 	if (buf->queue.next)
 		list_del_init(&buf->queue);
 
+	pcdev->buf_total -= PAGE_ALIGN(vb2_plane_size(vb, 0));
+	dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
+		pcdev->buf_total);
+
 	spin_unlock_irq(&pcdev->lock);
 }
 
 static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)
 {
+	struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+	pcdev->buf_total += PAGE_ALIGN(vb2_plane_size(vb, 0));
+	dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
+		pcdev->buf_total);
+
 	/* This is for locking debugging only */
 	INIT_LIST_HEAD(&to_ceu_vb(vb)->queue);
 	return 0;
@@ -535,19 +561,29 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 
 	pm_runtime_get_sync(ici->v4l2_dev.dev);
 
+	pcdev->buf_total = 0;
+
 	ret = sh_mobile_ceu_soft_reset(pcdev);
 
 	csi2_sd = find_csi2(pcdev);
+	if (csi2_sd)
+		csi2_sd->grp_id = (long)icd;
 
 	ret = v4l2_subdev_call(csi2_sd, core, s_power, 1);
-	if (ret != -ENODEV && ret != -ENOIOCTLCMD && ret < 0) {
+	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
 		pm_runtime_put_sync(ici->v4l2_dev.dev);
-	} else {
-		pcdev->icd = icd;
-		ret = 0;
+		return ret;
 	}
 
-	return ret;
+	/*
+	 * -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver
+	 * has not found this soc-camera device among its clients
+	 */
+	if (ret == -ENODEV && csi2_sd)
+		csi2_sd->grp_id = 0;
+	pcdev->icd = icd;
+
+	return 0;
 }
 
 /* Called with .video_lock held */
@@ -560,6 +596,8 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
 	BUG_ON(icd != pcdev->icd);
 
 	v4l2_subdev_call(csi2_sd, core, s_power, 0);
+	if (csi2_sd)
+		csi2_sd->grp_id = 0;
 	/* disable capture, disable interrupts */
 	ceu_write(pcdev, CEIER, 0);
 	sh_mobile_ceu_soft_reset(pcdev);
@@ -628,22 +666,22 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
 	left_offset	= cam->ceu_left;
 	top_offset	= cam->ceu_top;
 
-	/* CEU cropping (CFSZR) is applied _after_ the scaling filter (CFLCR) */
+	WARN_ON(icd->user_width & 3 || icd->user_height & 3);
+
+	width = icd->user_width;
+
 	if (pcdev->image_mode) {
 		in_width = cam->width;
 		if (!pcdev->is_16bit) {
 			in_width *= 2;
 			left_offset *= 2;
 		}
-		width = icd->user_width;
-		cdwdr_width = icd->user_width;
+		cdwdr_width = width;
 	} else {
-		int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+		int bytes_per_line = soc_mbus_bytes_per_line(width,
 						icd->current_fmt->host_fmt);
 		unsigned int w_factor;
 
-		width = icd->user_width;
-
 		switch (icd->current_fmt->host_fmt->packing) {
 		case SOC_MBUS_PACKING_2X8_PADHI:
 			w_factor = 2;
@@ -653,10 +691,10 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
 		}
 
 		in_width = cam->width * w_factor;
-		left_offset = left_offset * w_factor;
+		left_offset *= w_factor;
 
 		if (bytes_per_line < 0)
-			cdwdr_width = icd->user_width;
+			cdwdr_width = width;
 		else
 			cdwdr_width = bytes_per_line;
 	}
@@ -664,7 +702,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
 	height = icd->user_height;
 	in_height = cam->height;
 	if (V4L2_FIELD_NONE != pcdev->field) {
-		height /= 2;
+		height = (height / 2) & ~3;
 		in_height /= 2;
 		top_offset /= 2;
 		cdwdr_width *= 2;
@@ -686,6 +724,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
 
 	ceu_write(pcdev, CAMOR, camor);
 	ceu_write(pcdev, CAPWR, (in_height << 16) | in_width);
+	/* CFSZR clipping is applied _after_ the scaling filter (CFLCR) */
 	ceu_write(pcdev, CFSZR, (height << 16) | width);
 	ceu_write(pcdev, CDWDR, cdwdr_width);
 }
@@ -723,66 +762,93 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
 		ceu_write(pcdev, CAPSR, capsr);
 }
 
+/* Find the bus subdevice driver, e.g., CSI2 */
+static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev,
+					   struct soc_camera_device *icd)
+{
+	if (pcdev->csi2_pdev) {
+		struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
+		if (csi2_sd && csi2_sd->grp_id == (u32)icd)
+			return csi2_sd;
+	}
+
+	return soc_camera_to_subdev(icd);
+}
+
+#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER |	\
+		V4L2_MBUS_PCLK_SAMPLE_RISING |	\
+		V4L2_MBUS_HSYNC_ACTIVE_HIGH |	\
+		V4L2_MBUS_HSYNC_ACTIVE_LOW |	\
+		V4L2_MBUS_VSYNC_ACTIVE_HIGH |	\
+		V4L2_MBUS_VSYNC_ACTIVE_LOW |	\
+		V4L2_MBUS_DATA_ACTIVE_HIGH)
+
 /* Capture is not running, no interrupts, no locking needed */
 static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
 				       __u32 pixfmt)
 {
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
-	int ret;
-	unsigned long camera_flags, common_flags, value;
-	int yuv_lineskip;
+	struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
 	struct sh_mobile_ceu_cam *cam = icd->host_priv;
+	struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+	unsigned long value, common_flags = CEU_BUS_FLAGS;
 	u32 capsr = capture_save_reset(pcdev);
+	unsigned int yuv_lineskip;
+	int ret;
 
-	camera_flags = icd->ops->query_bus_param(icd);
-	common_flags = soc_camera_bus_param_compatible(camera_flags,
-						       make_bus_param(pcdev));
-	if (!common_flags)
-		return -EINVAL;
+	/*
+	 * If the client doesn't implement g_mbus_config, we just use our
+	 * platform data
+	 */
+	ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+	if (!ret) {
+		common_flags = soc_mbus_config_compatible(&cfg,
+							  common_flags);
+		if (!common_flags)
+			return -EINVAL;
+	} else if (ret != -ENOIOCTLCMD) {
+		return ret;
+	}
 
 	/* Make choises, based on platform preferences */
-	if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
-	    (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+	if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+	    (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
 		if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW)
-			common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+			common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
 		else
-			common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+			common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
 	}
 
-	if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
-	    (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+	if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+	    (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
 		if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW)
-			common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+			common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
 		else
-			common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+			common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
 	}
 
-	ret = icd->ops->set_bus_param(icd, common_flags);
-	if (ret < 0)
+	cfg.flags = common_flags;
+	ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+	if (ret < 0 && ret != -ENOIOCTLCMD)
 		return ret;
 
-	switch (common_flags & SOCAM_DATAWIDTH_MASK) {
-	case SOCAM_DATAWIDTH_8:
-		pcdev->is_16bit = 0;
-		break;
-	case SOCAM_DATAWIDTH_16:
+	if (icd->current_fmt->host_fmt->bits_per_sample > 8)
 		pcdev->is_16bit = 1;
-		break;
-	default:
-		return -EINVAL;
-	}
+	else
+		pcdev->is_16bit = 0;
 
 	ceu_write(pcdev, CRCNTR, 0);
 	ceu_write(pcdev, CRCMPR, 0);
 
 	value = 0x00000010; /* data fetch by default */
-	yuv_lineskip = 0;
+	yuv_lineskip = 0x10;
 
 	switch (icd->current_fmt->host_fmt->fourcc) {
 	case V4L2_PIX_FMT_NV12:
 	case V4L2_PIX_FMT_NV21:
-		yuv_lineskip = 1; /* skip for NV12/21, no skip for NV16/61 */
+		/* convert 4:2:2 -> 4:2:0 */
+		yuv_lineskip = 0; /* skip for NV12/21, no skip for NV16/61 */
 		/* fall-through */
 	case V4L2_PIX_FMT_NV16:
 	case V4L2_PIX_FMT_NV61:
@@ -808,8 +874,8 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
 	    icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61)
 		value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */
 
-	value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
-	value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
+	value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
+	value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
 	value |= pcdev->is_16bit ? 1 << 12 : 0;
 
 	/* CSI2 mode */
@@ -852,9 +918,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
 	 * using 7 we swap the data bytes to match the incoming order:
 	 * D0, D1, D2, D3, D4, D5, D6, D7
 	 */
-	value = 0x00000017;
-	if (yuv_lineskip)
-		value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */
+	value = 0x00000007 | yuv_lineskip;
 
 	ceu_write(pcdev, CDOCR, value);
 	ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
@@ -875,13 +939,19 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
 {
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
 	struct sh_mobile_ceu_dev *pcdev = ici->priv;
-	unsigned long camera_flags, common_flags;
+	struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
+	unsigned long common_flags = CEU_BUS_FLAGS;
+	struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+	int ret;
 
-	camera_flags = icd->ops->query_bus_param(icd);
-	common_flags = soc_camera_bus_param_compatible(camera_flags,
-						       make_bus_param(pcdev));
-	if (!common_flags || buswidth > 16 ||
-	    (buswidth > 8 && !(common_flags & SOCAM_DATAWIDTH_16)))
+	ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+	if (!ret)
+		common_flags = soc_mbus_config_compatible(&cfg,
+							  common_flags);
+	else if (ret != -ENOIOCTLCMD)
+		return ret;
+
+	if (!common_flags || buswidth > 16)
 		return -EINVAL;
 
 	return 0;
@@ -891,26 +961,26 @@ static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
 	{
 		.fourcc			= V4L2_PIX_FMT_NV12,
 		.name			= "NV12",
-		.bits_per_sample	= 12,
-		.packing		= SOC_MBUS_PACKING_NONE,
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_1_5X8,
 		.order			= SOC_MBUS_ORDER_LE,
 	}, {
 		.fourcc			= V4L2_PIX_FMT_NV21,
 		.name			= "NV21",
-		.bits_per_sample	= 12,
-		.packing		= SOC_MBUS_PACKING_NONE,
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_1_5X8,
 		.order			= SOC_MBUS_ORDER_LE,
 	}, {
 		.fourcc			= V4L2_PIX_FMT_NV16,
 		.name			= "NV16",
-		.bits_per_sample	= 16,
-		.packing		= SOC_MBUS_PACKING_NONE,
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
 		.order			= SOC_MBUS_ORDER_LE,
 	}, {
 		.fourcc			= V4L2_PIX_FMT_NV61,
 		.name			= "NV61",
-		.bits_per_sample	= 16,
-		.packing		= SOC_MBUS_PACKING_NONE,
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
 		.order			= SOC_MBUS_ORDER_LE,
 	},
 };
@@ -920,6 +990,8 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
 {
 	return	fmt->packing == SOC_MBUS_PACKING_NONE ||
 		(fmt->bits_per_sample == 8 &&
+		 fmt->packing == SOC_MBUS_PACKING_1_5X8) ||
+		(fmt->bits_per_sample == 8 &&
 		 fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
 		(fmt->bits_per_sample > 8 &&
 		 fmt->packing == SOC_MBUS_PACKING_EXTEND16);
@@ -927,6 +999,38 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
 
 static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect);
 
+static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl)
+{
+	return container_of(ctrl->handler, struct soc_camera_device,
+							ctrl_handler);
+}
+
+static int sh_mobile_ceu_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct soc_camera_device *icd = ctrl_to_icd(ctrl);
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+	struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_SHARPNESS:
+		switch (icd->current_fmt->host_fmt->fourcc) {
+		case V4L2_PIX_FMT_NV12:
+		case V4L2_PIX_FMT_NV21:
+		case V4L2_PIX_FMT_NV16:
+		case V4L2_PIX_FMT_NV61:
+			ceu_write(pcdev, CLFCR, !ctrl->val);
+			return 0;
+		}
+		break;
+	}
+
+	return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops sh_mobile_ceu_ctrl_ops = {
+	.s_ctrl = sh_mobile_ceu_s_ctrl,
+};
+
 static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int idx,
 				     struct soc_camera_format_xlate *xlate)
 {
@@ -952,6 +1056,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
 	}
 
 	if (!pcdev->pdata->csi2) {
+		/* Are there any restrictions in the CSI-2 case? */
 		ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
 		if (ret < 0)
 			return 0;
@@ -962,6 +1067,12 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
 		struct v4l2_rect rect;
 		int shift = 0;
 
+		/* Add our control */
+		v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops,
+				  V4L2_CID_SHARPNESS, 0, 1, 1, 0);
+		if (icd->ctrl_handler.error)
+			return icd->ctrl_handler.error;
+
 		/* FIXME: subwindow is lost between close / open */
 
 		/* Cache current client geometry */
@@ -1004,9 +1115,6 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
 		cam->width	= mf.width;
 		cam->height	= mf.height;
 
-		cam->width	= mf.width;
-		cam->height	= mf.height;
-
 		icd->host_priv = cam;
 	} else {
 		cam = icd->host_priv;
@@ -1278,6 +1386,7 @@ static int client_s_fmt(struct soc_camera_device *icd,
 	unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
 	unsigned int max_width, max_height;
 	struct v4l2_cropcap cap;
+	bool ceu_1to1;
 	int ret;
 
 	ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video,
@@ -1287,7 +1396,14 @@ static int client_s_fmt(struct soc_camera_device *icd,
 
 	dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
 
-	if ((width == mf->width && height == mf->height) || !ceu_can_scale)
+	if (width == mf->width && height == mf->height) {
+		/* Perfect! The client has done it all. */
+		ceu_1to1 = true;
+		goto update_cache;
+	}
+
+	ceu_1to1 = false;
+	if (!ceu_can_scale)
 		goto update_cache;
 
 	cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -1327,7 +1443,10 @@ update_cache:
 	if (ret < 0)
 		return ret;
 
-	update_subrect(cam);
+	if (ceu_1to1)
+		cam->subrect = cam->rect;
+	else
+		update_subrect(cam);
 
 	return 0;
 }
@@ -1414,7 +1533,10 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
 	capsr = capture_save_reset(pcdev);
 	dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr);
 
-	/* 1. - 2. Apply iterative camera S_CROP for new input window. */
+	/*
+	 * 1. - 2. Apply iterative camera S_CROP for new input window, read back
+	 * actual camera rectangle.
+	 */
 	ret = client_s_crop(icd, a, &cam_crop);
 	if (ret < 0)
 		return ret;
@@ -1498,8 +1620,9 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
 		ceu_write(pcdev, CFLCR, cflcr);
 	}
 
-	icd->user_width	 = out_width;
-	icd->user_height = out_height;
+	icd->user_width	 = out_width & ~3;
+	icd->user_height = out_height & ~3;
+	/* Offsets are applied at the CEU scaling filter input */
 	cam->ceu_left	 = scale_down(rect->left - cam_rect->left, scale_cam_h) & ~1;
 	cam->ceu_top	 = scale_down(rect->top - cam_rect->top, scale_cam_v) & ~1;
 
@@ -1538,7 +1661,7 @@ static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd,
  * CEU crop, mapped backed onto the client input (subrect).
  */
 static void calculate_client_output(struct soc_camera_device *icd,
-		struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
+		const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
 {
 	struct sh_mobile_ceu_cam *cam = icd->host_priv;
 	struct device *dev = icd->parent;
@@ -1574,8 +1697,8 @@ static void calculate_client_output(struct soc_camera_device *icd,
 	dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v);
 
 	/*
-	 * 4. Calculate client output window by applying combined scales to real
-	 *    input window.
+	 * 4. Calculate desired client output window by applying combined scales
+	 *    to client (real) input window.
 	 */
 	mf->width	= scale_down(cam->rect.width, scale_h);
 	mf->height	= scale_down(cam->rect.height, scale_v);
@@ -1600,8 +1723,6 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
 	bool image_mode;
 	enum v4l2_field field;
 
-	dev_geo(dev, "S_FMT(pix=0x%x, %ux%u)\n", pixfmt, pix->width, pix->height);
-
 	switch (pix->field) {
 	default:
 		pix->field = V4L2_FIELD_NONE;
@@ -1622,8 +1743,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
 		return -EINVAL;
 	}
 
-	/* 1.-4. Calculate client output geometry */
-	calculate_client_output(icd, &f->fmt.pix, &mf);
+	/* 1.-4. Calculate desired client output geometry */
+	calculate_client_output(icd, pix, &mf);
 	mf.field	= pix->field;
 	mf.colorspace	= pix->colorspace;
 	mf.code		= xlate->code;
@@ -1639,6 +1760,9 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
 		image_mode = false;
 	}
 
+	dev_geo(dev, "S_FMT(pix=0x%x, fld 0x%x, code 0x%x, %ux%u)\n", pixfmt, mf.field, mf.code,
+		pix->width, pix->height);
+
 	dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height);
 
 	/* 5. - 9. */
@@ -1700,6 +1824,10 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
 	pcdev->field = field;
 	pcdev->image_mode = image_mode;
 
+	/* CFSZR requirement */
+	pix->width	&= ~3;
+	pix->height	&= ~3;
+
 	return 0;
 }
 
@@ -1725,7 +1853,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
 
 	/* FIXME: calculate using depth and bus width */
 
-	v4l_bound_align_image(&pix->width, 2, 2560, 1,
+	/* CFSZR requires height and width to be 4-pixel aligned */
+	v4l_bound_align_image(&pix->width, 2, 2560, 2,
 			      &pix->height, 4, 1920, 2, 0);
 
 	width = pix->width;
@@ -1778,6 +1907,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
 			pix->height = height;
 	}
 
+	pix->width	&= ~3;
+	pix->height	&= ~3;
+
 	dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
 		__func__, ret, pix->pixelformat, pix->width, pix->height);
 
@@ -1824,8 +1956,8 @@ static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd,
 			     out_height != f.fmt.pix.height))
 			ret = -EINVAL;
 		if (!ret) {
-			icd->user_width		= out_width;
-			icd->user_height	= out_height;
+			icd->user_width		= out_width & ~3;
+			icd->user_height	= out_height & ~3;
 			ret = sh_mobile_ceu_set_bus_param(icd,
 					icd->current_fmt->host_fmt->fourcc);
 		}
@@ -1869,55 +2001,6 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
 	return vb2_queue_init(q);
 }
 
-static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd,
-				  struct v4l2_control *ctrl)
-{
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-	struct sh_mobile_ceu_dev *pcdev = ici->priv;
-	u32 val;
-
-	switch (ctrl->id) {
-	case V4L2_CID_SHARPNESS:
-		val = ceu_read(pcdev, CLFCR);
-		ctrl->value = val ^ 1;
-		return 0;
-	}
-	return -ENOIOCTLCMD;
-}
-
-static int sh_mobile_ceu_set_ctrl(struct soc_camera_device *icd,
-				  struct v4l2_control *ctrl)
-{
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-	struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
-	switch (ctrl->id) {
-	case V4L2_CID_SHARPNESS:
-		switch (icd->current_fmt->host_fmt->fourcc) {
-		case V4L2_PIX_FMT_NV12:
-		case V4L2_PIX_FMT_NV21:
-		case V4L2_PIX_FMT_NV16:
-		case V4L2_PIX_FMT_NV61:
-			ceu_write(pcdev, CLFCR, !ctrl->value);
-			return 0;
-		}
-		return -EINVAL;
-	}
-	return -ENOIOCTLCMD;
-}
-
-static const struct v4l2_queryctrl sh_mobile_ceu_controls[] = {
-	{
-		.id		= V4L2_CID_SHARPNESS,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Low-pass filter",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 0,
-	},
-};
-
 static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= sh_mobile_ceu_add_device,
@@ -1929,14 +2012,10 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
 	.set_livecrop	= sh_mobile_ceu_set_livecrop,
 	.set_fmt	= sh_mobile_ceu_set_fmt,
 	.try_fmt	= sh_mobile_ceu_try_fmt,
-	.set_ctrl	= sh_mobile_ceu_set_ctrl,
-	.get_ctrl	= sh_mobile_ceu_get_ctrl,
 	.poll		= sh_mobile_ceu_poll,
 	.querycap	= sh_mobile_ceu_querycap,
 	.set_bus_param	= sh_mobile_ceu_set_bus_param,
 	.init_videobuf2	= sh_mobile_ceu_init_videobuf,
-	.controls	= sh_mobile_ceu_controls,
-	.num_controls	= ARRAY_SIZE(sh_mobile_ceu_controls),
 };
 
 struct bus_wait {
diff --git a/drivers/media/video/sh_mobile_csi2.c b/drivers/media/video/sh_mobile_csi2.c
index 2893a0134c7e..37706eb81f25 100644
--- a/drivers/media/video/sh_mobile_csi2.c
+++ b/drivers/media/video/sh_mobile_csi2.c
@@ -19,6 +19,7 @@
 #include <media/sh_mobile_ceu.h>
 #include <media/sh_mobile_csi2.h>
 #include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-device.h>
@@ -35,11 +36,10 @@ struct sh_csi2 {
 	struct v4l2_subdev		subdev;
 	struct list_head		list;
 	unsigned int			irq;
+	unsigned long			mipi_flags;
 	void __iomem			*base;
 	struct platform_device		*pdev;
 	struct sh_csi2_client_config	*client;
-	unsigned long (*query_bus_param)(struct soc_camera_device *);
-	int (*set_bus_param)(struct soc_camera_device *, unsigned long);
 };
 
 static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
@@ -127,9 +127,34 @@ static int sh_csi2_s_fmt(struct v4l2_subdev *sd,
 	return 0;
 }
 
+static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd,
+				 struct v4l2_mbus_config *cfg)
+{
+	cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
+		V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+		V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
+	cfg->type = V4L2_MBUS_PARALLEL;
+
+	return 0;
+}
+
+static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd,
+				 const struct v4l2_mbus_config *cfg)
+{
+	struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
+	struct soc_camera_device *icd = (struct soc_camera_device *)sd->grp_id;
+	struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
+	struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,
+					      .flags = priv->mipi_flags};
+
+	return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg);
+}
+
 static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = {
 	.s_mbus_fmt	= sh_csi2_s_fmt,
 	.try_mbus_fmt	= sh_csi2_try_fmt,
+	.g_mbus_config	= sh_csi2_g_mbus_config,
+	.s_mbus_config	= sh_csi2_s_mbus_config,
 };
 
 static void sh_csi2_hwinit(struct sh_csi2 *priv)
@@ -144,11 +169,21 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv)
 	udelay(5);
 	iowrite32(0x00000000, priv->base + SH_CSI2_SRST);
 
-	if (priv->client->lanes & 3)
-		tmp |= priv->client->lanes & 3;
-	else
-		/* Default - both lanes */
-		tmp |= 3;
+	switch (pdata->type) {
+	case SH_CSI2C:
+		if (priv->client->lanes == 1)
+			tmp |= 1;
+		else
+			/* Default - both lanes */
+			tmp |= 3;
+		break;
+	case SH_CSI2I:
+		if (!priv->client->lanes || priv->client->lanes > 4)
+			/* Default - all 4 lanes */
+			tmp |= 0xf;
+		else
+			tmp |= (1 << priv->client->lanes) - 1;
+	}
 
 	if (priv->client->phy == SH_CSI2_PHY_MAIN)
 		tmp |= 0x8000;
@@ -163,38 +198,18 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv)
 	iowrite32(tmp, priv->base + SH_CSI2_CHKSUM);
 }
 
-static int sh_csi2_set_bus_param(struct soc_camera_device *icd,
-				 unsigned long flags)
-{
-	return 0;
-}
-
-static unsigned long sh_csi2_query_bus_param(struct soc_camera_device *icd)
-{
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-	const unsigned long flags = SOCAM_PCLK_SAMPLE_RISING |
-		SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
-		SOCAM_MASTER | SOCAM_DATAWIDTH_8 | SOCAM_DATA_ACTIVE_HIGH;
-
-	return soc_camera_apply_sensor_flags(icl, flags);
-}
-
 static int sh_csi2_client_connect(struct sh_csi2 *priv)
 {
 	struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
-	struct v4l2_subdev *sd, *csi2_sd = &priv->subdev;
-	struct soc_camera_device *icd = NULL;
+	struct soc_camera_device *icd = (struct soc_camera_device *)priv->subdev.grp_id;
+	struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
 	struct device *dev = v4l2_get_subdevdata(&priv->subdev);
-	int i;
+	struct v4l2_mbus_config cfg;
+	unsigned long common_flags, csi2_flags;
+	int i, ret;
 
-	v4l2_device_for_each_subdev(sd, csi2_sd->v4l2_dev)
-		if (sd->grp_id) {
-			icd = (struct soc_camera_device *)sd->grp_id;
-			break;
-		}
-
-	if (!icd)
-		return -EINVAL;
+	if (priv->client)
+		return -EBUSY;
 
 	for (i = 0; i < pdata->num_clients; i++)
 		if (&pdata->clients[i].pdev->dev == icd->pdev)
@@ -205,14 +220,41 @@ static int sh_csi2_client_connect(struct sh_csi2 *priv)
 	if (i == pdata->num_clients)
 		return -ENODEV;
 
-	priv->client = pdata->clients + i;
+	/* Check if we can support this camera */
+	csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_1_LANE;
+
+	switch (pdata->type) {
+	case SH_CSI2C:
+		if (pdata->clients[i].lanes != 1)
+			csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+		break;
+	case SH_CSI2I:
+		switch (pdata->clients[i].lanes) {
+		default:
+			csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
+		case 3:
+			csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
+		case 2:
+			csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+		}
+	}
 
-	priv->set_bus_param		= icd->ops->set_bus_param;
-	priv->query_bus_param		= icd->ops->query_bus_param;
-	icd->ops->set_bus_param		= sh_csi2_set_bus_param;
-	icd->ops->query_bus_param	= sh_csi2_query_bus_param;
+	cfg.type = V4L2_MBUS_CSI2;
+	ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &cfg);
+	if (ret == -ENOIOCTLCMD)
+		common_flags = csi2_flags;
+	else if (!ret)
+		common_flags = soc_mbus_config_compatible(&cfg,
+							  csi2_flags);
+	else
+		common_flags = 0;
 
-	csi2_sd->grp_id = (long)icd;
+	if (!common_flags)
+		return -EINVAL;
+
+	/* All good: camera MIPI configuration supported */
+	priv->mipi_flags = common_flags;
+	priv->client = pdata->clients + i;
 
 	pm_runtime_get_sync(dev);
 
@@ -223,16 +265,10 @@ static int sh_csi2_client_connect(struct sh_csi2 *priv)
 
 static void sh_csi2_client_disconnect(struct sh_csi2 *priv)
 {
-	struct soc_camera_device *icd = (struct soc_camera_device *)priv->subdev.grp_id;
+	if (!priv->client)
+		return;
 
 	priv->client = NULL;
-	priv->subdev.grp_id = 0;
-
-	/* Driver is about to be unbound */
-	icd->ops->set_bus_param		= priv->set_bus_param;
-	icd->ops->query_bus_param	= priv->query_bus_param;
-	priv->set_bus_param		= NULL;
-	priv->query_bus_param		= NULL;
 
 	pm_runtime_put(v4l2_get_subdevdata(&priv->subdev));
 }
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 5bdfe7e16bc1..b72580c38957 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -50,49 +50,65 @@ static LIST_HEAD(hosts);
 static LIST_HEAD(devices);
 static DEFINE_MUTEX(list_lock);		/* Protects the list of hosts */
 
-static int soc_camera_power_set(struct soc_camera_device *icd,
-				struct soc_camera_link *icl,
-				int power_on)
+static int soc_camera_power_on(struct soc_camera_device *icd,
+			       struct soc_camera_link *icl)
 {
-	int ret;
-
-	if (power_on) {
-		ret = regulator_bulk_enable(icl->num_regulators,
-					    icl->regulators);
-		if (ret < 0) {
-			dev_err(icd->pdev, "Cannot enable regulators\n");
-			return ret;
-		}
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	int ret = regulator_bulk_enable(icl->num_regulators,
+					icl->regulators);
+	if (ret < 0) {
+		dev_err(icd->pdev, "Cannot enable regulators\n");
+		return ret;
+	}
 
-		if (icl->power)
-			ret = icl->power(icd->pdev, power_on);
+	if (icl->power) {
+		ret = icl->power(icd->pdev, 1);
 		if (ret < 0) {
 			dev_err(icd->pdev,
 				"Platform failed to power-on the camera.\n");
-
-			regulator_bulk_disable(icl->num_regulators,
-					       icl->regulators);
-			return ret;
+			goto elinkpwr;
 		}
-	} else {
-		ret = 0;
-		if (icl->power)
-			ret = icl->power(icd->pdev, 0);
+	}
+
+	ret = v4l2_subdev_call(sd, core, s_power, 1);
+	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		goto esdpwr;
+
+	return 0;
+
+esdpwr:
+	if (icl->power)
+		icl->power(icd->pdev, 0);
+elinkpwr:
+	regulator_bulk_disable(icl->num_regulators,
+			       icl->regulators);
+	return ret;
+}
+
+static int soc_camera_power_off(struct soc_camera_device *icd,
+				struct soc_camera_link *icl)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	int ret = v4l2_subdev_call(sd, core, s_power, 0);
+
+	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		return ret;
+
+	if (icl->power) {
+		ret = icl->power(icd->pdev, 0);
 		if (ret < 0) {
 			dev_err(icd->pdev,
 				"Platform failed to power-off the camera.\n");
 			return ret;
 		}
-
-		ret = regulator_bulk_disable(icl->num_regulators,
-					     icl->regulators);
-		if (ret < 0) {
-			dev_err(icd->pdev, "Cannot disable regulators\n");
-			return ret;
-		}
 	}
 
-	return 0;
+	ret = regulator_bulk_disable(icl->num_regulators,
+				     icl->regulators);
+	if (ret < 0)
+		dev_err(icd->pdev, "Cannot disable regulators\n");
+
+	return ret;
 }
 
 const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
@@ -108,38 +124,38 @@ const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
 EXPORT_SYMBOL(soc_camera_xlate_by_fourcc);
 
 /**
- * soc_camera_apply_sensor_flags() - apply platform SOCAM_SENSOR_INVERT_* flags
+ * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags
  * @icl:	camera platform parameters
- * @flags:	flags to be inverted according to platform configuration
+ * @cfg:	media bus configuration
  * @return:	resulting flags
  */
-unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl,
-					    unsigned long flags)
+unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl,
+					   const struct v4l2_mbus_config *cfg)
 {
-	unsigned long f;
+	unsigned long f, flags = cfg->flags;
 
 	/* If only one of the two polarities is supported, switch to the opposite */
 	if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) {
-		f = flags & (SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW);
-		if (f == SOCAM_HSYNC_ACTIVE_HIGH || f == SOCAM_HSYNC_ACTIVE_LOW)
-			flags ^= SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW;
+		f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW);
+		if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW)
+			flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW;
 	}
 
 	if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) {
-		f = flags & (SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW);
-		if (f == SOCAM_VSYNC_ACTIVE_HIGH || f == SOCAM_VSYNC_ACTIVE_LOW)
-			flags ^= SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW;
+		f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW);
+		if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW)
+			flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW;
 	}
 
 	if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) {
-		f = flags & (SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING);
-		if (f == SOCAM_PCLK_SAMPLE_RISING || f == SOCAM_PCLK_SAMPLE_FALLING)
-			flags ^= SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING;
+		f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING);
+		if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING)
+			flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
 	}
 
 	return flags;
 }
-EXPORT_SYMBOL(soc_camera_apply_sensor_flags);
+EXPORT_SYMBOL(soc_camera_apply_board_flags);
 
 #define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
 	((x) >> 24) & 0xff
@@ -233,6 +249,14 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a)
 	return v4l2_subdev_call(sd, core, s_std, *a);
 }
 
+static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a)
+{
+	struct soc_camera_device *icd = file->private_data;
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+	return v4l2_subdev_call(sd, core, g_std, a);
+}
+
 static int soc_camera_enum_fsizes(struct file *file, void *fh,
 					 struct v4l2_frmsizeenum *fsize)
 {
@@ -318,6 +342,32 @@ static int soc_camera_dqbuf(struct file *file, void *priv,
 		return vb2_dqbuf(&icd->vb2_vidq, p, file->f_flags & O_NONBLOCK);
 }
 
+static int soc_camera_create_bufs(struct file *file, void *priv,
+			    struct v4l2_create_buffers *create)
+{
+	struct soc_camera_device *icd = file->private_data;
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+	/* videobuf2 only */
+	if (ici->ops->init_videobuf)
+		return -EINVAL;
+	else
+		return vb2_create_bufs(&icd->vb2_vidq, create);
+}
+
+static int soc_camera_prepare_buf(struct file *file, void *priv,
+				  struct v4l2_buffer *b)
+{
+	struct soc_camera_device *icd = file->private_data;
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+	/* videobuf2 only */
+	if (ici->ops->init_videobuf)
+		return -EINVAL;
+	else
+		return vb2_prepare_buf(&icd->vb2_vidq, b);
+}
+
 /* Always entered with .video_lock held */
 static int soc_camera_init_user_formats(struct soc_camera_device *icd)
 {
@@ -448,7 +498,7 @@ static int soc_camera_open(struct file *file)
 	struct soc_camera_host *ici;
 	int ret;
 
-	if (!icd->ops)
+	if (!to_soc_camera_control(icd))
 		/* No device driver attached */
 		return -ENODEV;
 
@@ -476,7 +526,7 @@ static int soc_camera_open(struct file *file)
 			},
 		};
 
-		ret = soc_camera_power_set(icd, icl, 1);
+		ret = soc_camera_power_on(icd, icl);
 		if (ret < 0)
 			goto epower;
 
@@ -512,6 +562,7 @@ static int soc_camera_open(struct file *file)
 			if (ret < 0)
 				goto einitvb;
 		}
+		v4l2_ctrl_handler_setup(&icd->ctrl_handler);
 	}
 
 	file->private_data = icd;
@@ -529,7 +580,7 @@ esfmt:
 eresume:
 	ici->ops->remove(icd);
 eiciadd:
-	soc_camera_power_set(icd, icl, 0);
+	soc_camera_power_off(icd, icl);
 epower:
 	icd->use_count--;
 	module_put(ici->ops->owner);
@@ -553,7 +604,7 @@ static int soc_camera_close(struct file *file)
 		if (ici->ops->init_videobuf2)
 			vb2_queue_release(&icd->vb2_vidq);
 
-		soc_camera_power_set(icd, icl, 0);
+		soc_camera_power_off(icd, icl);
 	}
 
 	if (icd->streamer == file)
@@ -781,75 +832,6 @@ static int soc_camera_streamoff(struct file *file, void *priv,
 	return 0;
 }
 
-static int soc_camera_queryctrl(struct file *file, void *priv,
-				struct v4l2_queryctrl *qc)
-{
-	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-	int i;
-
-	WARN_ON(priv != file->private_data);
-
-	if (!qc->id)
-		return -EINVAL;
-
-	/* First check host controls */
-	for (i = 0; i < ici->ops->num_controls; i++)
-		if (qc->id == ici->ops->controls[i].id) {
-			memcpy(qc, &(ici->ops->controls[i]),
-				sizeof(*qc));
-			return 0;
-		}
-
-	/* Then device controls */
-	for (i = 0; i < icd->ops->num_controls; i++)
-		if (qc->id == icd->ops->controls[i].id) {
-			memcpy(qc, &(icd->ops->controls[i]),
-				sizeof(*qc));
-			return 0;
-		}
-
-	return -EINVAL;
-}
-
-static int soc_camera_g_ctrl(struct file *file, void *priv,
-			     struct v4l2_control *ctrl)
-{
-	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	int ret;
-
-	WARN_ON(priv != file->private_data);
-
-	if (ici->ops->get_ctrl) {
-		ret = ici->ops->get_ctrl(icd, ctrl);
-		if (ret != -ENOIOCTLCMD)
-			return ret;
-	}
-
-	return v4l2_subdev_call(sd, core, g_ctrl, ctrl);
-}
-
-static int soc_camera_s_ctrl(struct file *file, void *priv,
-			     struct v4l2_control *ctrl)
-{
-	struct soc_camera_device *icd = file->private_data;
-	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-	int ret;
-
-	WARN_ON(priv != file->private_data);
-
-	if (ici->ops->set_ctrl) {
-		ret = ici->ops->set_ctrl(icd, ctrl);
-		if (ret != -ENOIOCTLCMD)
-			return ret;
-	}
-
-	return v4l2_subdev_call(sd, core, s_ctrl, ctrl);
-}
-
 static int soc_camera_cropcap(struct file *file, void *fh,
 			      struct v4l2_cropcap *a)
 {
@@ -1003,7 +985,7 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd,
 		goto ei2cga;
 	}
 
-	icl->board_info->platform_data = icd;
+	icl->board_info->platform_data = icl;
 
 	subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
 				icl->board_info, NULL);
@@ -1052,12 +1034,29 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 
 	dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev));
 
+	/*
+	 * Currently the subdev with the largest number of controls (13) is
+	 * ov6550. So let's pick 16 as a hint for the control handler. Note
+	 * that this is a hint only: too large and you waste some memory, too
+	 * small and there is a (very) small performance hit when looking up
+	 * controls in the internal hash.
+	 */
+	ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16);
+	if (ret < 0)
+		return ret;
+
 	ret = regulator_bulk_get(icd->pdev, icl->num_regulators,
 				 icl->regulators);
 	if (ret < 0)
 		goto ereg;
 
-	ret = soc_camera_power_set(icd, icl, 1);
+	/*
+	 * This will not yet call v4l2_subdev_core_ops::s_power(1), because the
+	 * subdevice has not been initialised yet. We'll have to call it once
+	 * again after initialisation, even though it shouldn't be needed, we
+	 * don't do any IO here.
+	 */
+	ret = soc_camera_power_on(icd, icl);
 	if (ret < 0)
 		goto epower;
 
@@ -1098,6 +1097,7 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 		if (!control || !control->driver || !dev_get_drvdata(control) ||
 		    !try_module_get(control->driver->owner)) {
 			icl->del_device(icd);
+			ret = -ENODEV;
 			goto enodrv;
 		}
 	}
@@ -1105,6 +1105,9 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 	sd = soc_camera_to_subdev(icd);
 	sd->grp_id = (long)icd;
 
+	if (v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler))
+		goto ectrl;
+
 	/* At this point client .probe() should have run already */
 	ret = soc_camera_init_user_formats(icd);
 	if (ret < 0)
@@ -1123,6 +1126,10 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 	if (ret < 0)
 		goto evidstart;
 
+	ret = v4l2_subdev_call(sd, core, s_power, 1);
+	if (ret < 0 && ret != -ENOIOCTLCMD)
+		goto esdpwr;
+
 	/* Try to improve our guess of a reasonable window format */
 	if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
 		icd->user_width		= mf.width;
@@ -1133,16 +1140,19 @@ static int soc_camera_probe(struct soc_camera_device *icd)
 
 	ici->ops->remove(icd);
 
-	soc_camera_power_set(icd, icl, 0);
+	soc_camera_power_off(icd, icl);
 
 	mutex_unlock(&icd->video_lock);
 
 	return 0;
 
+esdpwr:
+	video_unregister_device(icd->vdev);
 evidstart:
 	mutex_unlock(&icd->video_lock);
 	soc_camera_free_user_formats(icd);
 eiufmt:
+ectrl:
 	if (icl->board_info) {
 		soc_camera_free_i2c(icd);
 	} else {
@@ -1152,13 +1162,15 @@ eiufmt:
 enodrv:
 eadddev:
 	video_device_release(icd->vdev);
+	icd->vdev = NULL;
 evdc:
 	ici->ops->remove(icd);
 eadd:
-	soc_camera_power_set(icd, icl, 0);
+	soc_camera_power_off(icd, icl);
 epower:
 	regulator_bulk_free(icl->num_regulators, icl->regulators);
 ereg:
+	v4l2_ctrl_handler_free(&icd->ctrl_handler);
 	return ret;
 }
 
@@ -1173,6 +1185,7 @@ static int soc_camera_remove(struct soc_camera_device *icd)
 
 	BUG_ON(!icd->parent);
 
+	v4l2_ctrl_handler_free(&icd->ctrl_handler);
 	if (vdev) {
 		video_unregister_device(vdev);
 		icd->vdev = NULL;
@@ -1363,24 +1376,24 @@ static int soc_camera_device_register(struct soc_camera_device *icd)
 
 static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
 	.vidioc_querycap	 = soc_camera_querycap,
+	.vidioc_try_fmt_vid_cap  = soc_camera_try_fmt_vid_cap,
 	.vidioc_g_fmt_vid_cap    = soc_camera_g_fmt_vid_cap,
-	.vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,
 	.vidioc_s_fmt_vid_cap    = soc_camera_s_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,
 	.vidioc_enum_input	 = soc_camera_enum_input,
 	.vidioc_g_input		 = soc_camera_g_input,
 	.vidioc_s_input		 = soc_camera_s_input,
 	.vidioc_s_std		 = soc_camera_s_std,
+	.vidioc_g_std		 = soc_camera_g_std,
 	.vidioc_enum_framesizes  = soc_camera_enum_fsizes,
 	.vidioc_reqbufs		 = soc_camera_reqbufs,
-	.vidioc_try_fmt_vid_cap  = soc_camera_try_fmt_vid_cap,
 	.vidioc_querybuf	 = soc_camera_querybuf,
 	.vidioc_qbuf		 = soc_camera_qbuf,
 	.vidioc_dqbuf		 = soc_camera_dqbuf,
+	.vidioc_create_bufs	 = soc_camera_create_bufs,
+	.vidioc_prepare_buf	 = soc_camera_prepare_buf,
 	.vidioc_streamon	 = soc_camera_streamon,
 	.vidioc_streamoff	 = soc_camera_streamoff,
-	.vidioc_queryctrl	 = soc_camera_queryctrl,
-	.vidioc_g_ctrl		 = soc_camera_g_ctrl,
-	.vidioc_s_ctrl		 = soc_camera_s_ctrl,
 	.vidioc_cropcap		 = soc_camera_cropcap,
 	.vidioc_g_crop		 = soc_camera_g_crop,
 	.vidioc_s_crop		 = soc_camera_s_crop,
@@ -1409,6 +1422,7 @@ static int video_dev_create(struct soc_camera_device *icd)
 	vdev->ioctl_ops		= &soc_camera_ioctl_ops;
 	vdev->release		= video_device_release;
 	vdev->tvnorms		= V4L2_STD_UNKNOWN;
+	vdev->ctrl_handler	= &icd->ctrl_handler;
 	vdev->lock		= &icd->video_lock;
 
 	icd->vdev = vdev;
@@ -1427,11 +1441,6 @@ static int soc_camera_video_start(struct soc_camera_device *icd)
 	if (!icd->parent)
 		return -ENODEV;
 
-	if (!icd->ops ||
-	    !icd->ops->query_bus_param ||
-	    !icd->ops->set_bus_param)
-		return -EINVAL;
-
 	ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);
 	if (ret < 0) {
 		dev_err(icd->pdev, "video_register_device failed: %d\n", ret);
diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c
index 8069cd6bc5e8..4402a8a74f7a 100644
--- a/drivers/media/video/soc_camera_platform.c
+++ b/drivers/media/video/soc_camera_platform.c
@@ -30,32 +30,12 @@ static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev)
 	return container_of(subdev, struct soc_camera_platform_priv, subdev);
 }
 
-static struct soc_camera_platform_info *get_info(struct soc_camera_device *icd)
-{
-	struct platform_device *pdev =
-		to_platform_device(to_soc_camera_control(icd));
-	return pdev->dev.platform_data;
-}
-
 static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable)
 {
 	struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
 	return p->set_capture(p, enable);
 }
 
-static int soc_camera_platform_set_bus_param(struct soc_camera_device *icd,
-					     unsigned long flags)
-{
-	return 0;
-}
-
-static unsigned long
-soc_camera_platform_query_bus_param(struct soc_camera_device *icd)
-{
-	struct soc_camera_platform_info *p = get_info(icd);
-	return p->bus_param;
-}
-
 static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd,
 					struct v4l2_mbus_framefmt *mf)
 {
@@ -115,6 +95,17 @@ static int soc_camera_platform_cropcap(struct v4l2_subdev *sd,
 	return 0;
 }
 
+static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd,
+					     struct v4l2_mbus_config *cfg)
+{
+	struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+
+	cfg->flags = p->mbus_param;
+	cfg->type = p->mbus_type;
+
+	return 0;
+}
+
 static struct v4l2_subdev_video_ops platform_subdev_video_ops = {
 	.s_stream	= soc_camera_platform_s_stream,
 	.enum_mbus_fmt	= soc_camera_platform_enum_fmt,
@@ -123,6 +114,7 @@ static struct v4l2_subdev_video_ops platform_subdev_video_ops = {
 	.try_mbus_fmt	= soc_camera_platform_fill_fmt,
 	.g_mbus_fmt	= soc_camera_platform_fill_fmt,
 	.s_mbus_fmt	= soc_camera_platform_fill_fmt,
+	.g_mbus_config	= soc_camera_platform_g_mbus_config,
 };
 
 static struct v4l2_subdev_ops platform_subdev_ops = {
@@ -130,11 +122,6 @@ static struct v4l2_subdev_ops platform_subdev_ops = {
 	.video	= &platform_subdev_video_ops,
 };
 
-static struct soc_camera_ops soc_camera_platform_ops = {
-	.set_bus_param		= soc_camera_platform_set_bus_param,
-	.query_bus_param	= soc_camera_platform_query_bus_param,
-};
-
 static int soc_camera_platform_probe(struct platform_device *pdev)
 {
 	struct soc_camera_host *ici;
@@ -163,8 +150,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev)
 	/* Set the control device reference */
 	icd->control = &pdev->dev;
 
-	icd->ops = &soc_camera_platform_ops;
-
 	ici = to_soc_camera_host(icd->parent);
 
 	v4l2_subdev_init(&priv->subdev, &platform_subdev_ops);
@@ -178,7 +163,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev)
 	return ret;
 
 evdrs:
-	icd->ops = NULL;
 	platform_set_drvdata(pdev, NULL);
 	kfree(priv);
 	return ret;
@@ -187,11 +171,10 @@ evdrs:
 static int soc_camera_platform_remove(struct platform_device *pdev)
 {
 	struct soc_camera_platform_priv *priv = get_priv(pdev);
-	struct soc_camera_platform_info *p = pdev->dev.platform_data;
-	struct soc_camera_device *icd = p->icd;
+	struct soc_camera_platform_info *p = v4l2_get_subdevdata(&priv->subdev);
 
+	p->icd->control = NULL;
 	v4l2_device_unregister_subdev(&priv->subdev);
-	icd->ops = NULL;
 	platform_set_drvdata(pdev, NULL);
 	kfree(priv);
 	return 0;
diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c
index bea7c9cf4f88..cf7f2194ded4 100644
--- a/drivers/media/video/soc_mediabus.c
+++ b/drivers/media/video/soc_mediabus.c
@@ -383,6 +383,39 @@ const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc(
 }
 EXPORT_SYMBOL(soc_mbus_get_fmtdesc);
 
+unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg,
+					unsigned int flags)
+{
+	unsigned long common_flags;
+	bool hsync = true, vsync = true, pclk, data, mode;
+	bool mipi_lanes, mipi_clock;
+
+	common_flags = cfg->flags & flags;
+
+	switch (cfg->type) {
+	case V4L2_MBUS_PARALLEL:
+		hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+					V4L2_MBUS_HSYNC_ACTIVE_LOW);
+		vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+					V4L2_MBUS_VSYNC_ACTIVE_LOW);
+	case V4L2_MBUS_BT656:
+		pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING |
+				       V4L2_MBUS_PCLK_SAMPLE_FALLING);
+		data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH |
+				       V4L2_MBUS_DATA_ACTIVE_LOW);
+		mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE);
+		return (!hsync || !vsync || !pclk || !data || !mode) ?
+			0 : common_flags;
+	case V4L2_MBUS_CSI2:
+		mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES;
+		mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK |
+					     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK);
+		return (!mipi_lanes || !mipi_clock) ? 0 : common_flags;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(soc_mbus_config_compatible);
+
 static int __init soc_mbus_init(void)
 {
 	return 0;
diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c
index 742482e30011..a514fa61116c 100644
--- a/drivers/media/video/tw9910.c
+++ b/drivers/media/video/tw9910.c
@@ -22,11 +22,13 @@
 #include <linux/slab.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
 #include <linux/videodev2.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-subdev.h>
+
 #include <media/soc_camera.h>
 #include <media/tw9910.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
 
 #define GET_ID(val)  ((val & 0xF8) >> 3)
 #define GET_REV(val) (val & 0x07)
@@ -203,6 +205,10 @@
 #define RTSEL_FIELD 0x06 /* 0110 = FIELD */
 #define RTSEL_RTCO  0x07 /* 0111 = RTCO ( Real Time Control ) */
 
+/* HSYNC start and end are constant for now */
+#define HSYNC_START	0x0260
+#define HSYNC_END	0x0300
+
 /*
  * structure
  */
@@ -220,22 +226,11 @@ struct tw9910_scale_ctrl {
 	u16             vscale;
 };
 
-struct tw9910_cropping_ctrl {
-	u16 vdelay;
-	u16 vactive;
-	u16 hdelay;
-	u16 hactive;
-};
-
-struct tw9910_hsync_ctrl {
-	u16 start;
-	u16 end;
-};
-
 struct tw9910_priv {
 	struct v4l2_subdev		subdev;
 	struct tw9910_video_info	*info;
 	const struct tw9910_scale_ctrl	*scale;
+	v4l2_std_id			norm;
 	u32				revision;
 };
 
@@ -329,11 +324,6 @@ static const struct tw9910_scale_ctrl tw9910_pal_scales[] = {
 	},
 };
 
-static const struct tw9910_hsync_ctrl tw9910_hsync_ctrl = {
-	.start = 0x0260,
-	.end   = 0x0300,
-};
-
 /*
  * general function
  */
@@ -378,21 +368,20 @@ static int tw9910_set_scale(struct i2c_client *client,
 	return ret;
 }
 
-static int tw9910_set_hsync(struct i2c_client *client,
-			    const struct tw9910_hsync_ctrl *hsync)
+static int tw9910_set_hsync(struct i2c_client *client)
 {
 	struct tw9910_priv *priv = to_tw9910(client);
 	int ret;
 
 	/* bit 10 - 3 */
 	ret = i2c_smbus_write_byte_data(client, HSBEGIN,
-					(hsync->start & 0x07F8) >> 3);
+					(HSYNC_START & 0x07F8) >> 3);
 	if (ret < 0)
 		return ret;
 
 	/* bit 10 - 3 */
 	ret = i2c_smbus_write_byte_data(client, HSEND,
-					(hsync->end & 0x07F8) >> 3);
+					(HSYNC_END & 0x07F8) >> 3);
 	if (ret < 0)
 		return ret;
 
@@ -400,8 +389,8 @@ static int tw9910_set_hsync(struct i2c_client *client,
 	/* bit 2 - 0 */
 	if (1 == priv->revision)
 		ret = tw9910_mask_set(client, HSLOWCTL, 0x77,
-				      (hsync->start & 0x0007) << 4 |
-				      (hsync->end   & 0x0007));
+				      (HSYNC_START & 0x0007) << 4 |
+				      (HSYNC_END   & 0x0007));
 
 	return ret;
 }
@@ -433,12 +422,11 @@ static int tw9910_power(struct i2c_client *client, int enable)
 	return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2);
 }
 
-static const struct tw9910_scale_ctrl*
-tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height)
+static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm,
+							  u32 width, u32 height)
 {
 	const struct tw9910_scale_ctrl *scale;
 	const struct tw9910_scale_ctrl *ret = NULL;
-	v4l2_std_id norm = icd->vdev->current_norm;
 	__u32 diff = 0xffffffff, tmp;
 	int size, i;
 
@@ -465,7 +453,7 @@ tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height)
 }
 
 /*
- * soc_camera_ops function
+ * subdevice operations
  */
 static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
 {
@@ -507,49 +495,27 @@ static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
 	return tw9910_power(client, enable);
 }
 
-static int tw9910_set_bus_param(struct soc_camera_device *icd,
-				unsigned long flags)
+static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
 {
-	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	u8 val = VSSL_VVALID | HSSL_DVALID;
+	struct tw9910_priv *priv = to_tw9910(client);
 
-	/*
-	 * set OUTCTR1
-	 *
-	 * We use VVALID and DVALID signals to control VSYNC and HSYNC
-	 * outputs, in this mode their polarity is inverted.
-	 */
-	if (flags & SOCAM_HSYNC_ACTIVE_LOW)
-		val |= HSP_HI;
+	*norm = priv->norm;
 
-	if (flags & SOCAM_VSYNC_ACTIVE_LOW)
-		val |= VSP_HI;
-
-	return i2c_smbus_write_byte_data(client, OUTCTR1, val);
+	return 0;
 }
 
-static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd)
+static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
 {
-	struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct tw9910_priv *priv = to_tw9910(client);
-	struct soc_camera_link *icl = to_soc_camera_link(icd);
-	unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
-		SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
-		SOCAM_VSYNC_ACTIVE_LOW  | SOCAM_HSYNC_ACTIVE_LOW  |
-		SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth;
 
-	return soc_camera_apply_sensor_flags(icl, flags);
-}
-
-static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
-{
-	int ret = -EINVAL;
+	if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL)))
+		return -EINVAL;
 
-	if (norm & (V4L2_STD_NTSC | V4L2_STD_PAL))
-		ret = 0;
+	priv->norm = norm;
 
-	return ret;
+	return 0;
 }
 
 static int tw9910_g_chip_ident(struct v4l2_subdev *sd,
@@ -600,19 +566,17 @@ static int tw9910_s_register(struct v4l2_subdev *sd,
 }
 #endif
 
-static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height)
 {
-	struct v4l2_rect *rect = &a->c;
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct tw9910_priv *priv = to_tw9910(client);
-	struct soc_camera_device *icd = client->dev.platform_data;
-	int                 ret  = -EINVAL;
-	u8                  val;
+	int ret = -EINVAL;
+	u8 val;
 
 	/*
 	 * select suitable norm
 	 */
-	priv->scale = tw9910_select_norm(icd, rect->width, rect->height);
+	priv->scale = tw9910_select_norm(priv->norm, *width, *height);
 	if (!priv->scale)
 		goto tw9910_set_fmt_error;
 
@@ -670,14 +634,12 @@ static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 	/*
 	 * set hsync
 	 */
-	ret = tw9910_set_hsync(client, &tw9910_hsync_ctrl);
+	ret = tw9910_set_hsync(client);
 	if (ret < 0)
 		goto tw9910_set_fmt_error;
 
-	rect->width = priv->scale->width;
-	rect->height = priv->scale->height;
-	rect->left = 0;
-	rect->top = 0;
+	*width = priv->scale->width;
+	*height = priv->scale->height;
 
 	return ret;
 
@@ -694,25 +656,15 @@ static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	struct tw9910_priv *priv = to_tw9910(client);
 
-	if (!priv->scale) {
-		int ret;
-		struct v4l2_crop crop = {
-			.c = {
-				.left	= 0,
-				.top	= 0,
-				.width	= 640,
-				.height	= 480,
-			},
-		};
-		ret = tw9910_s_crop(sd, &crop);
-		if (ret < 0)
-			return ret;
-	}
-
 	a->c.left	= 0;
 	a->c.top	= 0;
-	a->c.width	= priv->scale->width;
-	a->c.height	= priv->scale->height;
+	if (priv->norm & V4L2_STD_NTSC) {
+		a->c.width	= 640;
+		a->c.height	= 480;
+	} else {
+		a->c.width	= 768;
+		a->c.height	= 576;
+	}
 	a->type		= V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
 	return 0;
@@ -720,14 +672,19 @@ static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 
 static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
 {
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct tw9910_priv *priv = to_tw9910(client);
+
 	a->bounds.left			= 0;
 	a->bounds.top			= 0;
-	a->bounds.width			= 768;
-	a->bounds.height		= 576;
-	a->defrect.left			= 0;
-	a->defrect.top			= 0;
-	a->defrect.width		= 640;
-	a->defrect.height		= 480;
+	if (priv->norm & V4L2_STD_NTSC) {
+		a->bounds.width		= 640;
+		a->bounds.height	= 480;
+	} else {
+		a->bounds.width		= 768;
+		a->bounds.height	= 576;
+	}
+	a->defrect			= a->bounds;
 	a->type				= V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	a->pixelaspect.numerator	= 1;
 	a->pixelaspect.denominator	= 1;
@@ -743,15 +700,8 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd,
 
 	if (!priv->scale) {
 		int ret;
-		struct v4l2_crop crop = {
-			.c = {
-				.left	= 0,
-				.top	= 0,
-				.width	= 640,
-				.height	= 480,
-			},
-		};
-		ret = tw9910_s_crop(sd, &crop);
+		u32 width = 640, height = 480;
+		ret = tw9910_set_frame(sd, &width, &height);
 		if (ret < 0)
 			return ret;
 	}
@@ -768,17 +718,7 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd,
 static int tw9910_s_fmt(struct v4l2_subdev *sd,
 			struct v4l2_mbus_framefmt *mf)
 {
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct tw9910_priv *priv = to_tw9910(client);
-	/* See tw9910_s_crop() - no proper cropping support */
-	struct v4l2_crop a = {
-		.c = {
-			.left	= 0,
-			.top	= 0,
-			.width	= mf->width,
-			.height	= mf->height,
-		},
-	};
+	u32 width = mf->width, height = mf->height;
 	int ret;
 
 	WARN_ON(mf->field != V4L2_FIELD_ANY &&
@@ -792,10 +732,10 @@ static int tw9910_s_fmt(struct v4l2_subdev *sd,
 
 	mf->colorspace = V4L2_COLORSPACE_JPEG;
 
-	ret = tw9910_s_crop(sd, &a);
+	ret = tw9910_set_frame(sd, &width, &height);
 	if (!ret) {
-		mf->width	= priv->scale->width;
-		mf->height	= priv->scale->height;
+		mf->width	= width;
+		mf->height	= height;
 	}
 	return ret;
 }
@@ -804,7 +744,7 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd,
 			  struct v4l2_mbus_framefmt *mf)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
-	struct soc_camera_device *icd = client->dev.platform_data;
+	struct tw9910_priv *priv = to_tw9910(client);
 	const struct tw9910_scale_ctrl *scale;
 
 	if (V4L2_FIELD_ANY == mf->field) {
@@ -820,7 +760,7 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd,
 	/*
 	 * select suitable norm
 	 */
-	scale = tw9910_select_norm(icd, mf->width, mf->height);
+	scale = tw9910_select_norm(priv->norm, mf->width, mf->height);
 	if (!scale)
 		return -EINVAL;
 
@@ -830,16 +770,11 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd,
 	return 0;
 }
 
-static int tw9910_video_probe(struct soc_camera_device *icd,
-			      struct i2c_client *client)
+static int tw9910_video_probe(struct i2c_client *client)
 {
 	struct tw9910_priv *priv = to_tw9910(client);
 	s32 id;
 
-	/* We must have a parent by now. And it cannot be a wrong one. */
-	BUG_ON(!icd->parent ||
-	       to_soc_camera_host(icd->parent)->nr != icd->iface);
-
 	/*
 	 * tw9910 only use 8 or 16 bit bus width
 	 */
@@ -868,20 +803,15 @@ static int tw9910_video_probe(struct soc_camera_device *icd,
 	dev_info(&client->dev,
 		 "tw9910 Product ID %0x:%0x\n", id, priv->revision);
 
-	icd->vdev->tvnorms      = V4L2_STD_NTSC | V4L2_STD_PAL;
-	icd->vdev->current_norm = V4L2_STD_NTSC;
+	priv->norm = V4L2_STD_NTSC;
 
 	return 0;
 }
 
-static struct soc_camera_ops tw9910_ops = {
-	.set_bus_param		= tw9910_set_bus_param,
-	.query_bus_param	= tw9910_query_bus_param,
-};
-
 static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
 	.g_chip_ident	= tw9910_g_chip_ident,
 	.s_std		= tw9910_s_std,
+	.g_std		= tw9910_g_std,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 	.g_register	= tw9910_g_register,
 	.s_register	= tw9910_s_register,
@@ -898,6 +828,45 @@ static int tw9910_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
 	return 0;
 }
 
+static int tw9910_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+	cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+		V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+		V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+		V4L2_MBUS_DATA_ACTIVE_HIGH;
+	cfg->type = V4L2_MBUS_PARALLEL;
+	cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+	return 0;
+}
+
+static int tw9910_s_mbus_config(struct v4l2_subdev *sd,
+				const struct v4l2_mbus_config *cfg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+	u8 val = VSSL_VVALID | HSSL_DVALID;
+	unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+
+	/*
+	 * set OUTCTR1
+	 *
+	 * We use VVALID and DVALID signals to control VSYNC and HSYNC
+	 * outputs, in this mode their polarity is inverted.
+	 */
+	if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+		val |= HSP_HI;
+
+	if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+		val |= VSP_HI;
+
+	return i2c_smbus_write_byte_data(client, OUTCTR1, val);
+}
+
 static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
 	.s_stream	= tw9910_s_stream,
 	.g_mbus_fmt	= tw9910_g_fmt,
@@ -905,8 +874,9 @@ static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
 	.try_mbus_fmt	= tw9910_try_fmt,
 	.cropcap	= tw9910_cropcap,
 	.g_crop		= tw9910_g_crop,
-	.s_crop		= tw9910_s_crop,
 	.enum_mbus_fmt	= tw9910_enum_fmt,
+	.g_mbus_config	= tw9910_g_mbus_config,
+	.s_mbus_config	= tw9910_s_mbus_config,
 };
 
 static struct v4l2_subdev_ops tw9910_subdev_ops = {
@@ -922,23 +892,18 @@ static int tw9910_probe(struct i2c_client *client,
 			const struct i2c_device_id *did)
 
 {
-	struct tw9910_priv             *priv;
-	struct tw9910_video_info       *info;
-	struct soc_camera_device       *icd = client->dev.platform_data;
-	struct i2c_adapter             *adapter =
+	struct tw9910_priv		*priv;
+	struct tw9910_video_info	*info;
+	struct i2c_adapter		*adapter =
 		to_i2c_adapter(client->dev.parent);
-	struct soc_camera_link         *icl;
-	int                             ret;
+	struct soc_camera_link		*icl = soc_camera_i2c_to_link(client);
+	int				ret;
 
-	if (!icd) {
-		dev_err(&client->dev, "TW9910: missing soc-camera data!\n");
+	if (!icl || !icl->priv) {
+		dev_err(&client->dev, "TW9910: missing platform data!\n");
 		return -EINVAL;
 	}
 
-	icl = to_soc_camera_link(icd);
-	if (!icl || !icl->priv)
-		return -EINVAL;
-
 	info = icl->priv;
 
 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
@@ -956,14 +921,9 @@ static int tw9910_probe(struct i2c_client *client,
 
 	v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
 
-	icd->ops     = &tw9910_ops;
-	icd->iface   = icl->bus_id;
-
-	ret = tw9910_video_probe(icd, client);
-	if (ret) {
-		icd->ops = NULL;
+	ret = tw9910_video_probe(client);
+	if (ret)
 		kfree(priv);
-	}
 
 	return ret;
 }
@@ -971,9 +931,7 @@ static int tw9910_probe(struct i2c_client *client,
 static int tw9910_remove(struct i2c_client *client)
 {
 	struct tw9910_priv *priv = to_tw9910(client);
-	struct soc_camera_device *icd = client->dev.platform_data;
 
-	icd->ops = NULL;
 	kfree(priv);
 	return 0;
 }
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
index 61979b70f388..c68531b88279 100644
--- a/drivers/media/video/v4l2-compat-ioctl32.c
+++ b/drivers/media/video/v4l2-compat-ioctl32.c
@@ -159,11 +159,25 @@ struct v4l2_format32 {
 	} fmt;
 };
 
-static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+/**
+ * struct v4l2_create_buffers32 - VIDIOC_CREATE_BUFS32 argument
+ * @index:	on return, index of the first created buffer
+ * @count:	entry: number of requested buffers,
+ *		return: number of created buffers
+ * @memory:	buffer memory type
+ * @format:	frame format, for which buffers are requested
+ * @reserved:	future extensions
+ */
+struct v4l2_create_buffers32 {
+	__u32			index;
+	__u32			count;
+	enum v4l2_memory        memory;
+	struct v4l2_format32	format;
+	__u32			reserved[8];
+};
+
+static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
 {
-	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) ||
-			get_user(kp->type, &up->type))
-			return -EFAULT;
 	switch (kp->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
@@ -192,11 +206,24 @@ static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user
 	}
 }
 
-static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) ||
+			get_user(kp->type, &up->type))
+			return -EFAULT;
+	return __get_v4l2_format32(kp, up);
+}
+
+static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+{
+	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) ||
+	    copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format.fmt)))
+			return -EFAULT;
+	return __get_v4l2_format32(&kp->format, &up->format);
+}
+
+static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
 {
-	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
-		put_user(kp->type, &up->type))
-		return -EFAULT;
 	switch (kp->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
@@ -225,6 +252,22 @@ static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user
 	}
 }
 
+static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
+		put_user(kp->type, &up->type))
+		return -EFAULT;
+	return __put_v4l2_format32(kp, up);
+}
+
+static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+{
+	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) ||
+	    copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt)))
+			return -EFAULT;
+	return __put_v4l2_format32(&kp->format, &up->format);
+}
+
 struct v4l2_standard32 {
 	__u32		     index;
 	__u32		     id[2]; /* __u64 would get the alignment wrong */
@@ -702,6 +745,8 @@ static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *u
 #define VIDIOC_S_EXT_CTRLS32    _IOWR('V', 72, struct v4l2_ext_controls32)
 #define VIDIOC_TRY_EXT_CTRLS32  _IOWR('V', 73, struct v4l2_ext_controls32)
 #define	VIDIOC_DQEVENT32	_IOR ('V', 89, struct v4l2_event32)
+#define VIDIOC_CREATE_BUFS32	_IOWR('V', 92, struct v4l2_create_buffers32)
+#define VIDIOC_PREPARE_BUF32	_IOWR('V', 93, struct v4l2_buffer32)
 
 #define VIDIOC_OVERLAY32	_IOW ('V', 14, s32)
 #define VIDIOC_STREAMON32	_IOW ('V', 18, s32)
@@ -721,6 +766,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
 		struct v4l2_standard v2s;
 		struct v4l2_ext_controls v2ecs;
 		struct v4l2_event v2ev;
+		struct v4l2_create_buffers v2crt;
 		unsigned long vx;
 		int vi;
 	} karg;
@@ -751,6 +797,8 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
 	case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break;
 	case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break;
 	case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break;
+	case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break;
+	case VIDIOC_PREPARE_BUF32: cmd = VIDIOC_PREPARE_BUF; break;
 	}
 
 	switch (cmd) {
@@ -775,6 +823,12 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
 		compatible_arg = 0;
 		break;
 
+	case VIDIOC_CREATE_BUFS:
+		err = get_v4l2_create32(&karg.v2crt, up);
+		compatible_arg = 0;
+		break;
+
+	case VIDIOC_PREPARE_BUF:
 	case VIDIOC_QUERYBUF:
 	case VIDIOC_QBUF:
 	case VIDIOC_DQBUF:
@@ -860,6 +914,10 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
 		err = put_v4l2_format32(&karg.v2f, up);
 		break;
 
+	case VIDIOC_CREATE_BUFS:
+		err = put_v4l2_create32(&karg.v2crt, up);
+		break;
+
 	case VIDIOC_QUERYBUF:
 	case VIDIOC_QBUF:
 	case VIDIOC_DQBUF:
@@ -959,6 +1017,8 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
 	case VIDIOC_DQEVENT32:
 	case VIDIOC_SUBSCRIBE_EVENT:
 	case VIDIOC_UNSUBSCRIBE_EVENT:
+	case VIDIOC_CREATE_BUFS32:
+	case VIDIOC_PREPARE_BUF32:
 		ret = do_video_ioctl(file, cmd, arg);
 		break;
 
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index fc8666ae408f..5552f8137571 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -210,6 +210,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
 		"Disabled",
 		"50 Hz",
 		"60 Hz",
+		"Auto",
 		NULL
 	};
 	static const char * const camera_exposure_auto[] = {
diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c
index e6a2c3b302d4..9fc0ae8a526a 100644
--- a/drivers/media/video/v4l2-device.c
+++ b/drivers/media/video/v4l2-device.c
@@ -21,6 +21,7 @@
 #include <linux/types.h>
 #include <linux/ioctl.h>
 #include <linux/i2c.h>
+#include <linux/slab.h>
 #if defined(CONFIG_SPI)
 #include <linux/spi/spi.h>
 #endif
@@ -193,6 +194,13 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
 }
 EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
 
+static void v4l2_device_release_subdev_node(struct video_device *vdev)
+{
+	struct v4l2_subdev *sd = video_get_drvdata(vdev);
+	sd->devnode = NULL;
+	kfree(vdev);
+}
+
 int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
 {
 	struct video_device *vdev;
@@ -206,22 +214,40 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
 		if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
 			continue;
 
-		vdev = &sd->devnode;
+		vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+		if (!vdev) {
+			err = -ENOMEM;
+			goto clean_up;
+		}
+
+		video_set_drvdata(vdev, sd);
 		strlcpy(vdev->name, sd->name, sizeof(vdev->name));
 		vdev->v4l2_dev = v4l2_dev;
 		vdev->fops = &v4l2_subdev_fops;
-		vdev->release = video_device_release_empty;
+		vdev->release = v4l2_device_release_subdev_node;
 		vdev->ctrl_handler = sd->ctrl_handler;
 		err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
 					      sd->owner);
-		if (err < 0)
-			return err;
+		if (err < 0) {
+			kfree(vdev);
+			goto clean_up;
+		}
 #if defined(CONFIG_MEDIA_CONTROLLER)
 		sd->entity.v4l.major = VIDEO_MAJOR;
 		sd->entity.v4l.minor = vdev->minor;
 #endif
+		sd->devnode = vdev;
 	}
 	return 0;
+
+clean_up:
+	list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
+		if (!sd->devnode)
+			break;
+		video_unregister_device(sd->devnode);
+	}
+
+	return err;
 }
 EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes);
 
@@ -247,7 +273,7 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
 	if (v4l2_dev->mdev)
 		media_device_unregister_entity(&sd->entity);
 #endif
-	video_unregister_device(&sd->devnode);
+	video_unregister_device(sd->devnode);
 	module_put(sd->owner);
 }
 EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index 24fd43322150..e1da8fc9dd2f 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -273,6 +273,8 @@ static const char *v4l2_ioctls[] = {
 	[_IOC_NR(VIDIOC_DQEVENT)]	   = "VIDIOC_DQEVENT",
 	[_IOC_NR(VIDIOC_SUBSCRIBE_EVENT)]  = "VIDIOC_SUBSCRIBE_EVENT",
 	[_IOC_NR(VIDIOC_UNSUBSCRIBE_EVENT)] = "VIDIOC_UNSUBSCRIBE_EVENT",
+	[_IOC_NR(VIDIOC_CREATE_BUFS)]      = "VIDIOC_CREATE_BUFS",
+	[_IOC_NR(VIDIOC_PREPARE_BUF)]      = "VIDIOC_PREPARE_BUF",
 };
 #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
 
@@ -2104,6 +2106,40 @@ static long __video_do_ioctl(struct file *file,
 		dbgarg(cmd, "type=0x%8.8x", sub->type);
 		break;
 	}
+	case VIDIOC_CREATE_BUFS:
+	{
+		struct v4l2_create_buffers *create = arg;
+
+		if (!ops->vidioc_create_bufs)
+			break;
+		if (ret_prio) {
+			ret = ret_prio;
+			break;
+		}
+		ret = check_fmt(ops, create->format.type);
+		if (ret)
+			break;
+
+		ret = ops->vidioc_create_bufs(file, fh, create);
+
+		dbgarg(cmd, "count=%d @ %d\n", create->count, create->index);
+		break;
+	}
+	case VIDIOC_PREPARE_BUF:
+	{
+		struct v4l2_buffer *b = arg;
+
+		if (!ops->vidioc_prepare_buf)
+			break;
+		ret = check_fmt(ops, b->type);
+		if (ret)
+			break;
+
+		ret = ops->vidioc_prepare_buf(file, fh, b);
+
+		dbgarg(cmd, "index=%d", b->index);
+		break;
+	}
 	default:
 		if (!ops->vidioc_default)
 			break;
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 3f5c7a38e6e8..979e544388cb 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -38,7 +38,8 @@ module_param(debug, int, 0644);
 	(((q)->ops->op) ? ((q)->ops->op(args)) : 0)
 
 #define V4L2_BUFFER_STATE_FLAGS	(V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
-				 V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR)
+				 V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \
+				 V4L2_BUF_FLAG_PREPARED)
 
 /**
  * __vb2_buf_mem_alloc() - allocate video memory for the given buffer
@@ -109,13 +110,22 @@ static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
  * __setup_offsets() - setup unique offsets ("cookies") for every plane in
  * every buffer on the queue
  */
-static void __setup_offsets(struct vb2_queue *q)
+static void __setup_offsets(struct vb2_queue *q, unsigned int n)
 {
 	unsigned int buffer, plane;
 	struct vb2_buffer *vb;
-	unsigned long off = 0;
+	unsigned long off;
 
-	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+	if (q->num_buffers) {
+		struct v4l2_plane *p;
+		vb = q->bufs[q->num_buffers - 1];
+		p = &vb->v4l2_planes[vb->num_planes - 1];
+		off = PAGE_ALIGN(p->m.mem_offset + p->length);
+	} else {
+		off = 0;
+	}
+
+	for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) {
 		vb = q->bufs[buffer];
 		if (!vb)
 			continue;
@@ -161,7 +171,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
 		vb->state = VB2_BUF_STATE_DEQUEUED;
 		vb->vb2_queue = q;
 		vb->num_planes = num_planes;
-		vb->v4l2_buf.index = buffer;
+		vb->v4l2_buf.index = q->num_buffers + buffer;
 		vb->v4l2_buf.type = q->type;
 		vb->v4l2_buf.memory = memory;
 
@@ -189,15 +199,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
 			}
 		}
 
-		q->bufs[buffer] = vb;
+		q->bufs[q->num_buffers + buffer] = vb;
 	}
 
-	q->num_buffers = buffer;
-
-	__setup_offsets(q);
+	__setup_offsets(q, buffer);
 
 	dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
-			q->num_buffers, num_planes);
+			buffer, num_planes);
 
 	return buffer;
 }
@@ -205,12 +213,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
 /**
  * __vb2_free_mem() - release all video buffer memory for a given queue
  */
-static void __vb2_free_mem(struct vb2_queue *q)
+static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
 {
 	unsigned int buffer;
 	struct vb2_buffer *vb;
 
-	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+	for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+	     ++buffer) {
 		vb = q->bufs[buffer];
 		if (!vb)
 			continue;
@@ -224,17 +233,18 @@ static void __vb2_free_mem(struct vb2_queue *q)
 }
 
 /**
- * __vb2_queue_free() - free the queue - video memory and related information
- * and return the queue to an uninitialized state. Might be called even if the
- * queue has already been freed.
+ * __vb2_queue_free() - free buffers at the end of the queue - video memory and
+ * related information, if no buffers are left return the queue to an
+ * uninitialized state. Might be called even if the queue has already been freed.
  */
-static void __vb2_queue_free(struct vb2_queue *q)
+static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
 {
 	unsigned int buffer;
 
 	/* Call driver-provided cleanup function for each buffer, if provided */
 	if (q->ops->buf_cleanup) {
-		for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+		for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+		     ++buffer) {
 			if (NULL == q->bufs[buffer])
 				continue;
 			q->ops->buf_cleanup(q->bufs[buffer]);
@@ -242,23 +252,25 @@ static void __vb2_queue_free(struct vb2_queue *q)
 	}
 
 	/* Release video buffer memory */
-	__vb2_free_mem(q);
+	__vb2_free_mem(q, buffers);
 
 	/* Free videobuf buffers */
-	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+	for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+	     ++buffer) {
 		kfree(q->bufs[buffer]);
 		q->bufs[buffer] = NULL;
 	}
 
-	q->num_buffers = 0;
-	q->memory = 0;
+	q->num_buffers -= buffers;
+	if (!q->num_buffers)
+		q->memory = 0;
 }
 
 /**
  * __verify_planes_array() - verify that the planes array passed in struct
  * v4l2_buffer from userspace can be safely used
  */
-static int __verify_planes_array(struct vb2_buffer *vb, struct v4l2_buffer *b)
+static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 {
 	/* Is memory for copying plane information present? */
 	if (NULL == b->m.planes) {
@@ -318,7 +330,7 @@ static bool __buffers_in_use(struct vb2_queue *q)
 static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
 {
 	struct vb2_queue *q = vb->vb2_queue;
-	int ret = 0;
+	int ret;
 
 	/* Copy back data such as timestamp, flags, input, etc. */
 	memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
@@ -365,6 +377,9 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
 	case VB2_BUF_STATE_DONE:
 		b->flags |= V4L2_BUF_FLAG_DONE;
 		break;
+	case VB2_BUF_STATE_PREPARED:
+		b->flags |= V4L2_BUF_FLAG_PREPARED;
+		break;
 	case VB2_BUF_STATE_DEQUEUED:
 		/* nothing */
 		break;
@@ -373,7 +388,7 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
 	if (__buffer_in_use(q, vb))
 		b->flags |= V4L2_BUF_FLAG_MAPPED;
 
-	return ret;
+	return 0;
 }
 
 /**
@@ -459,7 +474,7 @@ static int __verify_mmap_ops(struct vb2_queue *q)
  */
 int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
 {
-	unsigned int num_buffers, num_planes;
+	unsigned int num_buffers, allocated_buffers, num_planes = 0;
 	int ret = 0;
 
 	if (q->fileio) {
@@ -507,7 +522,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
 			return -EBUSY;
 		}
 
-		__vb2_queue_free(q);
+		__vb2_queue_free(q, q->num_buffers);
 
 		/*
 		 * In case of REQBUFS(0) return immediately without calling
@@ -529,7 +544,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
 	 * Ask the driver how many buffers and planes per buffer it requires.
 	 * Driver also sets the size and allocator context for each plane.
 	 */
-	ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
+	ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes,
 		       q->plane_sizes, q->alloc_ctx);
 	if (ret)
 		return ret;
@@ -541,44 +556,168 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
 		return -ENOMEM;
 	}
 
+	allocated_buffers = ret;
+
 	/*
 	 * Check if driver can handle the allocated number of buffers.
 	 */
-	if (ret < num_buffers) {
-		unsigned int orig_num_buffers;
+	if (allocated_buffers < num_buffers) {
+		num_buffers = allocated_buffers;
 
-		orig_num_buffers = num_buffers = ret;
-		ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
-			       q->plane_sizes, q->alloc_ctx);
-		if (ret)
-			goto free_mem;
+		ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
+			       &num_planes, q->plane_sizes, q->alloc_ctx);
 
-		if (orig_num_buffers < num_buffers) {
+		if (!ret && allocated_buffers < num_buffers)
 			ret = -ENOMEM;
-			goto free_mem;
-		}
 
 		/*
-		 * Ok, driver accepted smaller number of buffers.
+		 * Either the driver has accepted a smaller number of buffers,
+		 * or .queue_setup() returned an error
 		 */
-		ret = num_buffers;
+	}
+
+	q->num_buffers = allocated_buffers;
+
+	if (ret < 0) {
+		__vb2_queue_free(q, allocated_buffers);
+		return ret;
 	}
 
 	/*
 	 * Return the number of successfully allocated buffers
 	 * to the userspace.
 	 */
-	req->count = ret;
+	req->count = allocated_buffers;
 
 	return 0;
-
-free_mem:
-	__vb2_queue_free(q);
-	return ret;
 }
 EXPORT_SYMBOL_GPL(vb2_reqbufs);
 
 /**
+ * vb2_create_bufs() - Allocate buffers and any required auxiliary structs
+ * @q:		videobuf2 queue
+ * @create:	creation parameters, passed from userspace to vidioc_create_bufs
+ *		handler in driver
+ *
+ * Should be called from vidioc_create_bufs ioctl handler of a driver.
+ * This function:
+ * 1) verifies parameter sanity
+ * 2) calls the .queue_setup() queue operation
+ * 3) performs any necessary memory allocations
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_create_bufs handler in driver.
+ */
+int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
+{
+	unsigned int num_planes = 0, num_buffers, allocated_buffers;
+	int ret = 0;
+
+	if (q->fileio) {
+		dprintk(1, "%s(): file io in progress\n", __func__);
+		return -EBUSY;
+	}
+
+	if (create->memory != V4L2_MEMORY_MMAP
+			&& create->memory != V4L2_MEMORY_USERPTR) {
+		dprintk(1, "%s(): unsupported memory type\n", __func__);
+		return -EINVAL;
+	}
+
+	if (create->format.type != q->type) {
+		dprintk(1, "%s(): requested type is incorrect\n", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * Make sure all the required memory ops for given memory type
+	 * are available.
+	 */
+	if (create->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
+		dprintk(1, "%s(): MMAP for current setup unsupported\n", __func__);
+		return -EINVAL;
+	}
+
+	if (create->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
+		dprintk(1, "%s(): USERPTR for current setup unsupported\n", __func__);
+		return -EINVAL;
+	}
+
+	if (q->num_buffers == VIDEO_MAX_FRAME) {
+		dprintk(1, "%s(): maximum number of buffers already allocated\n",
+			__func__);
+		return -ENOBUFS;
+	}
+
+	create->index = q->num_buffers;
+
+	if (!q->num_buffers) {
+		memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
+		memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
+		q->memory = create->memory;
+	}
+
+	num_buffers = min(create->count, VIDEO_MAX_FRAME - q->num_buffers);
+
+	/*
+	 * Ask the driver, whether the requested number of buffers, planes per
+	 * buffer and their sizes are acceptable
+	 */
+	ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
+		       &num_planes, q->plane_sizes, q->alloc_ctx);
+	if (ret)
+		return ret;
+
+	/* Finally, allocate buffers and video memory */
+	ret = __vb2_queue_alloc(q, create->memory, num_buffers,
+				num_planes);
+	if (ret < 0) {
+		dprintk(1, "Memory allocation failed with error: %d\n", ret);
+		return ret;
+	}
+
+	allocated_buffers = ret;
+
+	/*
+	 * Check if driver can handle the so far allocated number of buffers.
+	 */
+	if (ret < num_buffers) {
+		num_buffers = ret;
+
+		/*
+		 * q->num_buffers contains the total number of buffers, that the
+		 * queue driver has set up
+		 */
+		ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
+			       &num_planes, q->plane_sizes, q->alloc_ctx);
+
+		if (!ret && allocated_buffers < num_buffers)
+			ret = -ENOMEM;
+
+		/*
+		 * Either the driver has accepted a smaller number of buffers,
+		 * or .queue_setup() returned an error
+		 */
+	}
+
+	q->num_buffers += allocated_buffers;
+
+	if (ret < 0) {
+		__vb2_queue_free(q, allocated_buffers);
+		return ret;
+	}
+
+	/*
+	 * Return the number of successfully allocated buffers
+	 * to the userspace.
+	 */
+	create->count = allocated_buffers;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_create_bufs);
+
+/**
  * vb2_plane_vaddr() - Return a kernel virtual address of a given plane
  * @vb:		vb2_buffer to which the plane in question belongs to
  * @plane_no:	plane number for which the address is to be returned
@@ -662,7 +801,7 @@ EXPORT_SYMBOL_GPL(vb2_buffer_done);
  * __fill_vb2_buffer() - fill a vb2_buffer with information provided in
  * a v4l2_buffer by the userspace
  */
-static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
+static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b,
 				struct v4l2_plane *v4l2_planes)
 {
 	unsigned int plane;
@@ -726,7 +865,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
 /**
  * __qbuf_userptr() - handle qbuf of a USERPTR buffer
  */
-static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b)
+static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 {
 	struct v4l2_plane planes[VIDEO_MAX_PLANES];
 	struct vb2_queue *q = vb->vb2_queue;
@@ -815,7 +954,7 @@ err:
 /**
  * __qbuf_mmap() - handle qbuf of an MMAP buffer
  */
-static int __qbuf_mmap(struct vb2_buffer *vb, struct v4l2_buffer *b)
+static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 {
 	return __fill_vb2_buffer(vb, b, vb->v4l2_planes);
 }
@@ -832,6 +971,95 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
 	q->ops->buf_queue(vb);
 }
 
+static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+	struct vb2_queue *q = vb->vb2_queue;
+	int ret;
+
+	switch (q->memory) {
+	case V4L2_MEMORY_MMAP:
+		ret = __qbuf_mmap(vb, b);
+		break;
+	case V4L2_MEMORY_USERPTR:
+		ret = __qbuf_userptr(vb, b);
+		break;
+	default:
+		WARN(1, "Invalid queue type\n");
+		ret = -EINVAL;
+	}
+
+	if (!ret)
+		ret = call_qop(q, buf_prepare, vb);
+	if (ret)
+		dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
+	else
+		vb->state = VB2_BUF_STATE_PREPARED;
+
+	return ret;
+}
+
+/**
+ * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel
+ * @q:		videobuf2 queue
+ * @b:		buffer structure passed from userspace to vidioc_prepare_buf
+ *		handler in driver
+ *
+ * Should be called from vidioc_prepare_buf ioctl handler of a driver.
+ * This function:
+ * 1) verifies the passed buffer,
+ * 2) calls buf_prepare callback in the driver (if provided), in which
+ *    driver-specific buffer initialization can be performed,
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_prepare_buf handler in driver.
+ */
+int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+	struct vb2_buffer *vb;
+	int ret;
+
+	if (q->fileio) {
+		dprintk(1, "%s(): file io in progress\n", __func__);
+		return -EBUSY;
+	}
+
+	if (b->type != q->type) {
+		dprintk(1, "%s(): invalid buffer type\n", __func__);
+		return -EINVAL;
+	}
+
+	if (b->index >= q->num_buffers) {
+		dprintk(1, "%s(): buffer index out of range\n", __func__);
+		return -EINVAL;
+	}
+
+	vb = q->bufs[b->index];
+	if (NULL == vb) {
+		/* Should never happen */
+		dprintk(1, "%s(): buffer is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (b->memory != q->memory) {
+		dprintk(1, "%s(): invalid memory type\n", __func__);
+		return -EINVAL;
+	}
+
+	if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+		dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state);
+		return -EINVAL;
+	}
+
+	ret = __buf_prepare(vb, b);
+	if (ret < 0)
+		return ret;
+
+	__fill_v4l2_buffer(vb, b);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_prepare_buf);
+
 /**
  * vb2_qbuf() - Queue a buffer from userspace
  * @q:		videobuf2 queue
@@ -841,8 +1069,8 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
  * Should be called from vidioc_qbuf ioctl handler of a driver.
  * This function:
  * 1) verifies the passed buffer,
- * 2) calls buf_prepare callback in the driver (if provided), in which
- *    driver-specific buffer initialization can be performed,
+ * 2) if necessary, calls buf_prepare callback in the driver (if provided), in
+ *    which driver-specific buffer initialization can be performed,
  * 3) if streaming is on, queues the buffer in driver by the means of buf_queue
  *    callback for processing.
  *
@@ -852,7 +1080,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
 int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
 {
 	struct vb2_buffer *vb;
-	int ret = 0;
+	int ret;
 
 	if (q->fileio) {
 		dprintk(1, "qbuf: file io in progress\n");
@@ -881,29 +1109,18 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
 		return -EINVAL;
 	}
 
-	if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+	switch (vb->state) {
+	case VB2_BUF_STATE_DEQUEUED:
+		ret = __buf_prepare(vb, b);
+		if (ret)
+			return ret;
+	case VB2_BUF_STATE_PREPARED:
+		break;
+	default:
 		dprintk(1, "qbuf: buffer already in use\n");
 		return -EINVAL;
 	}
 
-	if (q->memory == V4L2_MEMORY_MMAP)
-		ret = __qbuf_mmap(vb, b);
-	else if (q->memory == V4L2_MEMORY_USERPTR)
-		ret = __qbuf_userptr(vb, b);
-	else {
-		WARN(1, "Invalid queue type\n");
-		return -EINVAL;
-	}
-
-	if (ret)
-		return ret;
-
-	ret = call_qop(q, buf_prepare, vb);
-	if (ret) {
-		dprintk(1, "qbuf: buffer preparation failed\n");
-		return ret;
-	}
-
 	/*
 	 * Add to the queued buffers list, a buffer will stay on it until
 	 * dequeued in dqbuf.
@@ -918,6 +1135,9 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
 	if (q->streaming)
 		__enqueue_in_driver(vb);
 
+	/* Fill buffer information for the userspace */
+	__fill_v4l2_buffer(vb, b);
+
 	dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
 	return 0;
 }
@@ -1347,6 +1567,37 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
 }
 EXPORT_SYMBOL_GPL(vb2_mmap);
 
+#ifndef CONFIG_MMU
+unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
+				    unsigned long addr,
+				    unsigned long len,
+				    unsigned long pgoff,
+				    unsigned long flags)
+{
+	unsigned long off = pgoff << PAGE_SHIFT;
+	struct vb2_buffer *vb;
+	unsigned int buffer, plane;
+	int ret;
+
+	if (q->memory != V4L2_MEMORY_MMAP) {
+		dprintk(1, "Queue is not currently set up for mmap\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Find the plane corresponding to the offset passed by userspace.
+	 */
+	ret = __find_plane_by_offset(q, off, &buffer, &plane);
+	if (ret)
+		return ret;
+
+	vb = q->bufs[buffer];
+
+	return (unsigned long)vb2_plane_vaddr(vb, plane);
+}
+EXPORT_SYMBOL_GPL(vb2_get_unmapped_area);
+#endif
+
 static int __vb2_init_fileio(struct vb2_queue *q, int read);
 static int __vb2_cleanup_fileio(struct vb2_queue *q);
 
@@ -1464,7 +1715,7 @@ void vb2_queue_release(struct vb2_queue *q)
 {
 	__vb2_cleanup_fileio(q);
 	__vb2_queue_cancel(q);
-	__vb2_queue_free(q);
+	__vb2_queue_free(q, q->num_buffers);
 }
 EXPORT_SYMBOL_GPL(vb2_queue_release);
 
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 7cf94c09d99a..7d754fbcccbf 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -650,9 +650,9 @@ static void vivi_stop_generating(struct vivi_dev *dev)
 /* ------------------------------------------------------------------
 	Videobuf operations
    ------------------------------------------------------------------*/
-static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
-				unsigned int *nplanes, unsigned int sizes[],
-				void *alloc_ctxs[])
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+				unsigned int *nbuffers, unsigned int *nplanes,
+				unsigned int sizes[], void *alloc_ctxs[])
 {
 	struct vivi_dev *dev = vb2_get_drv_priv(vq);
 	unsigned long size;
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index d132c27dfb3f..25cdff36a78a 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -30,12 +30,6 @@ source "drivers/staging/et131x/Kconfig"
 
 source "drivers/staging/slicoss/Kconfig"
 
-source "drivers/staging/go7007/Kconfig"
-
-source "drivers/staging/cx25821/Kconfig"
-
-source "drivers/staging/cxd2099/Kconfig"
-
 source "drivers/staging/usbip/Kconfig"
 
 source "drivers/staging/winbond/Kconfig"
@@ -104,20 +98,12 @@ source "drivers/staging/wlags49_h25/Kconfig"
 
 source "drivers/staging/sm7xx/Kconfig"
 
-source "drivers/staging/dt3155v4l/Kconfig"
-
 source "drivers/staging/crystalhd/Kconfig"
 
 source "drivers/staging/cxt1e1/Kconfig"
 
 source "drivers/staging/xgifb/Kconfig"
 
-source "drivers/staging/lirc/Kconfig"
-
-source "drivers/staging/easycap/Kconfig"
-
-source "drivers/staging/solo6x10/Kconfig"
-
 source "drivers/staging/tidspbridge/Kconfig"
 
 source "drivers/staging/quickstart/Kconfig"
@@ -144,4 +130,6 @@ source "drivers/staging/mei/Kconfig"
 
 source "drivers/staging/nvec/Kconfig"
 
+source "drivers/staging/media/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 936b7c22e18e..a25f3f26c7ff 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -4,12 +4,9 @@
 obj-$(CONFIG_STAGING)		+= staging.o
 
 obj-y				+= serial/
+obj-y				+= media/
 obj-$(CONFIG_ET131X)		+= et131x/
 obj-$(CONFIG_SLICOSS)		+= slicoss/
-obj-$(CONFIG_VIDEO_GO7007)	+= go7007/
-obj-$(CONFIG_VIDEO_CX25821)	+= cx25821/
-obj-$(CONFIG_DVB_CXD2099)	+= cxd2099/
-obj-$(CONFIG_LIRC_STAGING)	+= lirc/
 obj-$(CONFIG_USBIP_CORE)	+= usbip/
 obj-$(CONFIG_W35UND)		+= winbond/
 obj-$(CONFIG_PRISM2_USB)	+= wlan-ng/
@@ -44,12 +41,9 @@ obj-$(CONFIG_ZCACHE)		+= zcache/
 obj-$(CONFIG_WLAGS49_H2)	+= wlags49_h2/
 obj-$(CONFIG_WLAGS49_H25)	+= wlags49_h25/
 obj-$(CONFIG_FB_SM7XX)		+= sm7xx/
-obj-$(CONFIG_VIDEO_DT3155)	+= dt3155v4l/
 obj-$(CONFIG_CRYSTALHD)		+= crystalhd/
 obj-$(CONFIG_CXT1E1)		+= cxt1e1/
 obj-$(CONFIG_FB_XGI)		+= xgifb/
-obj-$(CONFIG_EASYCAP)		+= easycap/
-obj-$(CONFIG_SOLO6X10)		+= solo6x10/
 obj-$(CONFIG_TIDSPBRIDGE)	+= tidspbridge/
 obj-$(CONFIG_ACPI_QUICKSTART)	+= quickstart/
 obj-$(CONFIG_SBE_2T3E3)		+= sbe-2t3e3/
diff --git a/drivers/staging/cx25821/README b/drivers/staging/cx25821/README
deleted file mode 100644
index a9ba50b9888b..000000000000
--- a/drivers/staging/cx25821/README
+++ /dev/null
@@ -1,6 +0,0 @@
-Todo:
-	- checkpatch.pl cleanups
-	- sparse cleanups
-
-Please send patches to linux-media@vger.kernel.org
-
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
new file mode 100644
index 000000000000..7e5caa39ed3f
--- /dev/null
+++ b/drivers/staging/media/Kconfig
@@ -0,0 +1,37 @@
+menuconfig STAGING_MEDIA
+        bool "Media staging drivers"
+        default n
+        ---help---
+          This option allows you to select a number of media drivers that
+	  don't have the "normal" Linux kernel quality level.
+	  Most of them don't follow properly the V4L, DVB and/or RC API's,
+	  so, they won't likely work fine with the existing applications.
+	  That also means that, one fixed, their API's will change to match
+	  the existing ones.
+
+          If you wish to work on these drivers, to help improve them, or
+          to report problems you have with them, please use the
+	  linux-media@vger.kernel.org mailing list.
+
+          If in doubt, say N here.
+
+
+if STAGING_MEDIA
+
+# Please keep them in alphabetic order
+source "drivers/staging/media/as102/Kconfig"
+
+source "drivers/staging/media/cxd2099/Kconfig"
+
+source "drivers/staging/media/dt3155v4l/Kconfig"
+
+source "drivers/staging/media/easycap/Kconfig"
+
+source "drivers/staging/media/go7007/Kconfig"
+
+source "drivers/staging/media/solo6x10/Kconfig"
+
+# Keep LIRC at the end, as it has sub-menus
+source "drivers/staging/media/lirc/Kconfig"
+
+endif
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
new file mode 100644
index 000000000000..c69124cdb0d3
--- /dev/null
+++ b/drivers/staging/media/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_DVB_AS102)		+= as102/
+obj-$(CONFIG_DVB_CXD2099)	+= cxd2099/
+obj-$(CONFIG_EASYCAP)		+= easycap/
+obj-$(CONFIG_LIRC_STAGING)	+= lirc/
+obj-$(CONFIG_SOLO6X10)		+= solo6x10/
+obj-$(CONFIG_VIDEO_DT3155)	+= dt3155v4l/
+obj-$(CONFIG_VIDEO_GO7007)	+= go7007/
diff --git a/drivers/staging/media/as102/Kconfig b/drivers/staging/media/as102/Kconfig
new file mode 100644
index 000000000000..5865029db0f6
--- /dev/null
+++ b/drivers/staging/media/as102/Kconfig
@@ -0,0 +1,7 @@
+config DVB_AS102
+	tristate "Abilis AS102 DVB receiver"
+	depends on DVB_CORE && USB && I2C && INPUT
+	help
+	  Choose Y or M here if you have a device containing an AS102
+
+	  To compile this driver as a module, choose M here
diff --git a/drivers/staging/media/as102/Makefile b/drivers/staging/media/as102/Makefile
new file mode 100644
index 000000000000..e7dbb6f814d5
--- /dev/null
+++ b/drivers/staging/media/as102/Makefile
@@ -0,0 +1,6 @@
+dvb-as102-objs := as102_drv.o as102_fw.o as10x_cmd.o as10x_cmd_stream.o \
+		as102_fe.o as102_usb_drv.o as10x_cmd_cfg.o
+
+obj-$(CONFIG_DVB_AS102) += dvb-as102.o
+
+EXTRA_CFLAGS += -DCONFIG_AS102_USB -Idrivers/media/dvb/dvb-core
diff --git a/drivers/staging/media/as102/as102_drv.c b/drivers/staging/media/as102/as102_drv.c
new file mode 100644
index 000000000000..d335c7d6fa0f
--- /dev/null
+++ b/drivers/staging/media/as102/as102_drv.c
@@ -0,0 +1,351 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/kref.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+
+/* header file for Usb device driver*/
+#include "as102_drv.h"
+#include "as102_fw.h"
+#include "dvbdev.h"
+
+int debug;
+module_param_named(debug, debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default: off)");
+
+int dual_tuner;
+module_param_named(dual_tuner, dual_tuner, int, 0644);
+MODULE_PARM_DESC(dual_tuner, "Activate Dual-Tuner config (default: off)");
+
+static int fw_upload = 1;
+module_param_named(fw_upload, fw_upload, int, 0644);
+MODULE_PARM_DESC(fw_upload, "Turn on/off default FW upload (default: on)");
+
+static int pid_filtering;
+module_param_named(pid_filtering, pid_filtering, int, 0644);
+MODULE_PARM_DESC(pid_filtering, "Activate HW PID filtering (default: off)");
+
+static int ts_auto_disable;
+module_param_named(ts_auto_disable, ts_auto_disable, int, 0644);
+MODULE_PARM_DESC(ts_auto_disable, "Stream Auto Enable on FW (default: off)");
+
+int elna_enable = 1;
+module_param_named(elna_enable, elna_enable, int, 0644);
+MODULE_PARM_DESC(elna_enable, "Activate eLNA (default: on)");
+
+#ifdef DVB_DEFINE_MOD_OPT_ADAPTER_NR
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+#endif
+
+static void as102_stop_stream(struct as102_dev_t *dev)
+{
+	struct as102_bus_adapter_t *bus_adap;
+
+	if (dev != NULL)
+		bus_adap = &dev->bus_adap;
+	else
+		return;
+
+	if (bus_adap->ops->stop_stream != NULL)
+		bus_adap->ops->stop_stream(dev);
+
+	if (ts_auto_disable) {
+		if (mutex_lock_interruptible(&dev->bus_adap.lock))
+			return;
+
+		if (as10x_cmd_stop_streaming(bus_adap) < 0)
+			dprintk(debug, "as10x_cmd_stop_streaming failed\n");
+
+		mutex_unlock(&dev->bus_adap.lock);
+	}
+}
+
+static int as102_start_stream(struct as102_dev_t *dev)
+{
+	struct as102_bus_adapter_t *bus_adap;
+	int ret = -EFAULT;
+
+	if (dev != NULL)
+		bus_adap = &dev->bus_adap;
+	else
+		return ret;
+
+	if (bus_adap->ops->start_stream != NULL)
+		ret = bus_adap->ops->start_stream(dev);
+
+	if (ts_auto_disable) {
+		if (mutex_lock_interruptible(&dev->bus_adap.lock))
+			return -EFAULT;
+
+		ret = as10x_cmd_start_streaming(bus_adap);
+
+		mutex_unlock(&dev->bus_adap.lock);
+	}
+
+	return ret;
+}
+
+static int as10x_pid_filter(struct as102_dev_t *dev,
+			    int index, u16 pid, int onoff) {
+
+	struct as102_bus_adapter_t *bus_adap = &dev->bus_adap;
+	int ret = -EFAULT;
+
+	ENTER();
+
+	if (mutex_lock_interruptible(&dev->bus_adap.lock)) {
+		dprintk(debug, "mutex_lock_interruptible(lock) failed !\n");
+		return -EBUSY;
+	}
+
+	switch (onoff) {
+	case 0:
+	    ret = as10x_cmd_del_PID_filter(bus_adap, (uint16_t) pid);
+	    dprintk(debug, "DEL_PID_FILTER([%02d] 0x%04x) ret = %d\n",
+		    index, pid, ret);
+	    break;
+	case 1:
+	{
+	    struct as10x_ts_filter filter;
+
+	    filter.type = TS_PID_TYPE_TS;
+	    filter.idx = 0xFF;
+	    filter.pid = pid;
+
+	    ret = as10x_cmd_add_PID_filter(bus_adap, &filter);
+	    dprintk(debug, "ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n",
+		    index, filter.idx, filter.pid, ret);
+	    break;
+	}
+	}
+
+	mutex_unlock(&dev->bus_adap.lock);
+
+	LEAVE();
+	return ret;
+}
+
+static int as102_dvb_dmx_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	int ret = 0;
+	struct dvb_demux *demux = dvbdmxfeed->demux;
+	struct as102_dev_t *as102_dev = demux->priv;
+
+	ENTER();
+
+	if (mutex_lock_interruptible(&as102_dev->sem))
+		return -ERESTARTSYS;
+
+	if (pid_filtering) {
+		as10x_pid_filter(as102_dev,
+				dvbdmxfeed->index, dvbdmxfeed->pid, 1);
+	}
+
+	if (as102_dev->streaming++ == 0)
+		ret = as102_start_stream(as102_dev);
+
+	mutex_unlock(&as102_dev->sem);
+	LEAVE();
+	return ret;
+}
+
+static int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *demux = dvbdmxfeed->demux;
+	struct as102_dev_t *as102_dev = demux->priv;
+
+	ENTER();
+
+	if (mutex_lock_interruptible(&as102_dev->sem))
+		return -ERESTARTSYS;
+
+	if (--as102_dev->streaming == 0)
+		as102_stop_stream(as102_dev);
+
+	if (pid_filtering) {
+		as10x_pid_filter(as102_dev,
+				dvbdmxfeed->index, dvbdmxfeed->pid, 0);
+	}
+
+	mutex_unlock(&as102_dev->sem);
+	LEAVE();
+	return 0;
+}
+
+int as102_dvb_register(struct as102_dev_t *as102_dev)
+{
+	int ret = 0;
+	ENTER();
+
+	ret = dvb_register_adapter(&as102_dev->dvb_adap,
+				   as102_dev->name,
+				   THIS_MODULE,
+#if defined(CONFIG_AS102_USB)
+				   &as102_dev->bus_adap.usb_dev->dev
+#elif defined(CONFIG_AS102_SPI)
+				   &as102_dev->bus_adap.spi_dev->dev
+#else
+#error >>> dvb_register_adapter <<<
+#endif
+#ifdef DVB_DEFINE_MOD_OPT_ADAPTER_NR
+				   , adapter_nr
+#endif
+				   );
+	if (ret < 0) {
+		err("%s: dvb_register_adapter() failed (errno = %d)",
+		    __func__, ret);
+		goto failed;
+	}
+
+	as102_dev->dvb_dmx.priv = as102_dev;
+	as102_dev->dvb_dmx.filternum = pid_filtering ? 16 : 256;
+	as102_dev->dvb_dmx.feednum = 256;
+	as102_dev->dvb_dmx.start_feed = as102_dvb_dmx_start_feed;
+	as102_dev->dvb_dmx.stop_feed = as102_dvb_dmx_stop_feed;
+
+	as102_dev->dvb_dmx.dmx.capabilities = DMX_TS_FILTERING |
+					      DMX_SECTION_FILTERING;
+
+	as102_dev->dvb_dmxdev.filternum = as102_dev->dvb_dmx.filternum;
+	as102_dev->dvb_dmxdev.demux = &as102_dev->dvb_dmx.dmx;
+	as102_dev->dvb_dmxdev.capabilities = 0;
+
+	ret = dvb_dmx_init(&as102_dev->dvb_dmx);
+	if (ret < 0) {
+		err("%s: dvb_dmx_init() failed (errno = %d)", __func__, ret);
+		goto failed;
+	}
+
+	ret = dvb_dmxdev_init(&as102_dev->dvb_dmxdev, &as102_dev->dvb_adap);
+	if (ret < 0) {
+		err("%s: dvb_dmxdev_init() failed (errno = %d)", __func__,
+		    ret);
+		goto failed;
+	}
+
+	ret = as102_dvb_register_fe(as102_dev, &as102_dev->dvb_fe);
+	if (ret < 0) {
+		err("%s: as102_dvb_register_frontend() failed (errno = %d)",
+		    __func__, ret);
+		goto failed;
+	}
+
+	/* init bus mutex for token locking */
+	mutex_init(&as102_dev->bus_adap.lock);
+
+	/* init start / stop stream mutex */
+	mutex_init(&as102_dev->sem);
+
+#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
+	/*
+	 * try to load as102 firmware. If firmware upload failed, we'll be
+	 * able to upload it later.
+	 */
+	if (fw_upload)
+		try_then_request_module(as102_fw_upload(&as102_dev->bus_adap),
+				"firmware_class");
+#endif
+
+failed:
+	LEAVE();
+	/* FIXME: free dvb_XXX */
+	return ret;
+}
+
+void as102_dvb_unregister(struct as102_dev_t *as102_dev)
+{
+	ENTER();
+
+	/* unregister as102 frontend */
+	as102_dvb_unregister_fe(&as102_dev->dvb_fe);
+
+	/* unregister demux device */
+	dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
+	dvb_dmx_release(&as102_dev->dvb_dmx);
+
+	/* unregister dvb adapter */
+	dvb_unregister_adapter(&as102_dev->dvb_adap);
+
+	LEAVE();
+}
+
+static int __init as102_driver_init(void)
+{
+	int ret = 0;
+
+	ENTER();
+
+	/* register this driver with the low level subsystem */
+#if defined(CONFIG_AS102_USB)
+	ret = usb_register(&as102_usb_driver);
+	if (ret)
+		err("usb_register failed (ret = %d)", ret);
+#endif
+#if defined(CONFIG_AS102_SPI)
+	ret = spi_register_driver(&as102_spi_driver);
+	if (ret)
+		printk(KERN_ERR "spi_register failed (ret = %d)", ret);
+#endif
+
+	LEAVE();
+	return ret;
+}
+
+/*
+ * Mandatory function : Adds a special section to the module indicating
+ * where initialisation function is defined
+ */
+module_init(as102_driver_init);
+
+/**
+ * as102_driver_exit - as102 driver exit point
+ *
+ * This function is called when device has to be removed.
+ */
+static void __exit as102_driver_exit(void)
+{
+	ENTER();
+	/* deregister this driver with the low level bus subsystem */
+#if defined(CONFIG_AS102_USB)
+	usb_deregister(&as102_usb_driver);
+#endif
+#if defined(CONFIG_AS102_SPI)
+	spi_unregister_driver(&as102_spi_driver);
+#endif
+	LEAVE();
+}
+
+/*
+ * required function for unload: Adds a special section to the module
+ * indicating where unload function is defined
+ */
+module_exit(as102_driver_exit);
+/* modinfo details */
+MODULE_DESCRIPTION(DRIVER_FULL_NAME);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>");
+
+/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */
diff --git a/drivers/staging/media/as102/as102_drv.h b/drivers/staging/media/as102/as102_drv.h
new file mode 100644
index 000000000000..bcda635b5a99
--- /dev/null
+++ b/drivers/staging/media/as102/as102_drv.h
@@ -0,0 +1,141 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if defined(CONFIG_AS102_USB)
+#include <linux/usb.h>
+extern struct usb_driver as102_usb_driver;
+#endif
+
+#if defined(CONFIG_AS102_SPI)
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/cdev.h>
+
+extern struct spi_driver as102_spi_driver;
+#endif
+
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dmxdev.h"
+
+#define DRIVER_FULL_NAME "Abilis Systems as10x usb driver"
+#define DRIVER_NAME "as10x_usb"
+
+extern int debug;
+
+#define dprintk(debug, args...) \
+	do { if (debug) {	\
+		printk(KERN_DEBUG "%s: ",__FUNCTION__);	\
+		printk(args);	\
+	} } while (0)
+
+#ifdef TRACE
+#define ENTER()                 printk(">> enter %s\n", __FUNCTION__)
+#define LEAVE()                 printk("<< leave %s\n", __FUNCTION__)
+#else
+#define ENTER()
+#define LEAVE()
+#endif
+
+#define AS102_DEVICE_MAJOR	192
+
+#define AS102_USB_BUF_SIZE	512
+#define MAX_STREAM_URB		32
+
+#include "as10x_cmd.h"
+
+#if defined(CONFIG_AS102_USB)
+#include "as102_usb_drv.h"
+#endif
+
+#if defined(CONFIG_AS102_SPI)
+#include "as10x_spi_drv.h"
+#endif
+
+
+struct as102_bus_adapter_t {
+#if defined(CONFIG_AS102_USB)
+	struct usb_device *usb_dev;
+#elif defined(CONFIG_AS102_SPI)
+	struct spi_device *spi_dev;
+	struct cdev cdev; /* spidev raw device */
+
+	struct timer_list timer;
+	struct completion xfer_done;
+#endif
+	/* bus token lock */
+	struct mutex lock;
+	/* low level interface for bus adapter */
+	union as10x_bus_token_t {
+#if defined(CONFIG_AS102_USB)
+		/* usb token */
+		struct as10x_usb_token_cmd_t usb;
+#endif
+#if defined(CONFIG_AS102_SPI)
+		/* spi token */
+		struct as10x_spi_token_cmd_t spi;
+#endif
+	} token;
+
+	/* token cmd xfer id */
+	uint16_t cmd_xid;
+
+	/* as10x command and response for dvb interface*/
+	struct as10x_cmd_t *cmd, *rsp;
+
+	/* bus adapter private ops callback */
+	struct as102_priv_ops_t *ops;
+};
+
+struct as102_dev_t {
+	const char *name;
+	struct as102_bus_adapter_t bus_adap;
+	struct list_head device_entry;
+	struct kref kref;
+	unsigned long minor;
+
+	struct dvb_adapter dvb_adap;
+	struct dvb_frontend dvb_fe;
+	struct dvb_demux dvb_dmx;
+	struct dmxdev dvb_dmxdev;
+
+	/* demodulator stats */
+	struct as10x_demod_stats demod_stats;
+	/* signal strength */
+	uint16_t signal_strength;
+	/* bit error rate */
+	uint32_t ber;
+
+	/* timer handle to trig ts stream download */
+	struct timer_list timer_handle;
+
+	struct mutex sem;
+	dma_addr_t dma_addr;
+	void *stream;
+	int streaming;
+	struct urb *stream_urb[MAX_STREAM_URB];
+};
+
+int as102_dvb_register(struct as102_dev_t *dev);
+void as102_dvb_unregister(struct as102_dev_t *dev);
+
+int as102_dvb_register_fe(struct as102_dev_t *dev, struct dvb_frontend *fe);
+int as102_dvb_unregister_fe(struct dvb_frontend *dev);
+
+/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */
diff --git a/drivers/staging/media/as102/as102_fe.c b/drivers/staging/media/as102/as102_fe.c
new file mode 100644
index 000000000000..3550f905367e
--- /dev/null
+++ b/drivers/staging/media/as102/as102_fe.c
@@ -0,0 +1,603 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/version.h>
+
+#include "as102_drv.h"
+#include "as10x_types.h"
+#include "as10x_cmd.h"
+
+extern int elna_enable;
+
+static void as10x_fe_copy_tps_parameters(struct dvb_frontend_parameters *dst,
+					 struct as10x_tps *src);
+
+static void as102_fe_copy_tune_parameters(struct as10x_tune_args *dst,
+					  struct dvb_frontend_parameters *src);
+
+static int as102_fe_set_frontend(struct dvb_frontend *fe,
+				 struct dvb_frontend_parameters *params)
+{
+	int ret = 0;
+	struct as102_dev_t *dev;
+	struct as10x_tune_args tune_args = { 0 };
+
+	ENTER();
+
+	dev = (struct as102_dev_t *) fe->tuner_priv;
+	if (dev == NULL)
+		return -ENODEV;
+
+	if (mutex_lock_interruptible(&dev->bus_adap.lock))
+		return -EBUSY;
+
+	as102_fe_copy_tune_parameters(&tune_args, params);
+
+	/* send abilis command: SET_TUNE */
+	ret =  as10x_cmd_set_tune(&dev->bus_adap, &tune_args);
+	if (ret != 0)
+		dprintk(debug, "as10x_cmd_set_tune failed. (err = %d)\n", ret);
+
+	mutex_unlock(&dev->bus_adap.lock);
+
+	LEAVE();
+	return (ret < 0) ? -EINVAL : 0;
+}
+
+static int as102_fe_get_frontend(struct dvb_frontend *fe,
+				 struct dvb_frontend_parameters *p) {
+	int ret = 0;
+	struct as102_dev_t *dev;
+	struct as10x_tps tps = { 0 };
+
+	ENTER();
+
+	dev = (struct as102_dev_t *) fe->tuner_priv;
+	if (dev == NULL)
+		return -EINVAL;
+
+	if (mutex_lock_interruptible(&dev->bus_adap.lock))
+		return -EBUSY;
+
+	/* send abilis command: GET_TPS */
+	ret = as10x_cmd_get_tps(&dev->bus_adap, &tps);
+
+	if (ret == 0)
+		as10x_fe_copy_tps_parameters(p, &tps);
+
+	mutex_unlock(&dev->bus_adap.lock);
+
+	LEAVE();
+	return (ret < 0) ? -EINVAL : 0;
+}
+
+static int as102_fe_get_tune_settings(struct dvb_frontend *fe,
+			struct dvb_frontend_tune_settings *settings) {
+	ENTER();
+
+#if 0
+	dprintk(debug, "step_size    = %d\n", settings->step_size);
+	dprintk(debug, "max_drift    = %d\n", settings->max_drift);
+	dprintk(debug, "min_delay_ms = %d -> %d\n", settings->min_delay_ms,
+		1000);
+#endif
+
+	settings->min_delay_ms = 1000;
+
+	LEAVE();
+	return 0;
+}
+
+
+static int as102_fe_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	int ret = 0;
+	struct as102_dev_t *dev;
+	struct as10x_tune_status tstate = { 0 };
+
+	ENTER();
+
+	dev = (struct as102_dev_t *) fe->tuner_priv;
+	if (dev == NULL)
+		return -ENODEV;
+
+	if (mutex_lock_interruptible(&dev->bus_adap.lock))
+		return -EBUSY;
+
+	/* send abilis command: GET_TUNE_STATUS */
+	ret = as10x_cmd_get_tune_status(&dev->bus_adap, &tstate);
+	if (ret < 0) {
+		dprintk(debug, "as10x_cmd_get_tune_status failed (err = %d)\n",
+			ret);
+		goto out;
+	}
+
+	dev->signal_strength  = tstate.signal_strength;
+	dev->ber  = tstate.BER;
+
+	switch (tstate.tune_state) {
+	case TUNE_STATUS_SIGNAL_DVB_OK:
+		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+		break;
+	case TUNE_STATUS_STREAM_DETECTED:
+		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC;
+		break;
+	case TUNE_STATUS_STREAM_TUNED:
+		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC |
+			FE_HAS_LOCK;
+		break;
+	default:
+		*status = TUNE_STATUS_NOT_TUNED;
+	}
+
+	dprintk(debug, "tuner status: 0x%02x, strength %d, per: %d, ber: %d\n",
+			tstate.tune_state, tstate.signal_strength,
+			tstate.PER, tstate.BER);
+
+	if (*status & FE_HAS_LOCK) {
+		if (as10x_cmd_get_demod_stats(&dev->bus_adap,
+			(struct as10x_demod_stats *) &dev->demod_stats) < 0) {
+			memset(&dev->demod_stats, 0, sizeof(dev->demod_stats));
+			dprintk(debug, "as10x_cmd_get_demod_stats failed "
+				"(probably not tuned)\n");
+		} else {
+			dprintk(debug,
+				"demod status: fc: 0x%08x, bad fc: 0x%08x, "
+				"bytes corrected: 0x%08x , MER: 0x%04x\n",
+				dev->demod_stats.frame_count,
+				dev->demod_stats.bad_frame_count,
+				dev->demod_stats.bytes_fixed_by_rs,
+				dev->demod_stats.mer);
+		}
+	} else {
+		memset(&dev->demod_stats, 0, sizeof(dev->demod_stats));
+	}
+
+out:
+	mutex_unlock(&dev->bus_adap.lock);
+	LEAVE();
+	return ret;
+}
+
+/*
+ * Note:
+ * - in AS102 SNR=MER
+ *   - the SNR will be returned in linear terms, i.e. not in dB
+ *   - the accuracy equals ±2dB for a SNR range from 4dB to 30dB
+ *   - the accuracy is >2dB for SNR values outside this range
+ */
+static int as102_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct as102_dev_t *dev;
+
+	ENTER();
+
+	dev = (struct as102_dev_t *) fe->tuner_priv;
+	if (dev == NULL)
+		return -ENODEV;
+
+	*snr = dev->demod_stats.mer;
+
+	LEAVE();
+	return 0;
+}
+
+static int as102_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	struct as102_dev_t *dev;
+
+	ENTER();
+
+	dev = (struct as102_dev_t *) fe->tuner_priv;
+	if (dev == NULL)
+		return -ENODEV;
+
+	*ber = dev->ber;
+
+	LEAVE();
+	return 0;
+}
+
+static int as102_fe_read_signal_strength(struct dvb_frontend *fe,
+					 u16 *strength)
+{
+	struct as102_dev_t *dev;
+
+	ENTER();
+
+	dev = (struct as102_dev_t *) fe->tuner_priv;
+	if (dev == NULL)
+		return -ENODEV;
+
+	*strength = (((0xffff * 400) * dev->signal_strength + 41000) * 2);
+
+	LEAVE();
+	return 0;
+}
+
+static int as102_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	struct as102_dev_t *dev;
+
+	ENTER();
+
+	dev = (struct as102_dev_t *) fe->tuner_priv;
+	if (dev == NULL)
+		return -ENODEV;
+
+	if (dev->demod_stats.has_started)
+		*ucblocks = dev->demod_stats.bad_frame_count;
+	else
+		*ucblocks = 0;
+
+	LEAVE();
+	return 0;
+}
+
+static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+	struct as102_dev_t *dev;
+	int ret;
+
+	ENTER();
+
+	dev = (struct as102_dev_t *) fe->tuner_priv;
+	if (dev == NULL)
+		return -ENODEV;
+
+	if (mutex_lock_interruptible(&dev->bus_adap.lock))
+		return -EBUSY;
+
+	if (acquire) {
+		if (elna_enable)
+			as10x_cmd_set_context(&dev->bus_adap, 1010, 0xC0);
+
+		ret = as10x_cmd_turn_on(&dev->bus_adap);
+	} else {
+		ret = as10x_cmd_turn_off(&dev->bus_adap);
+	}
+
+	mutex_unlock(&dev->bus_adap.lock);
+
+	LEAVE();
+	return ret;
+}
+
+static struct dvb_frontend_ops as102_fe_ops = {
+	.info = {
+		.name			= "Unknown AS102 device",
+		.type			= FE_OFDM,
+		.frequency_min		= 174000000,
+		.frequency_max		= 862000000,
+		.frequency_stepsize	= 166667,
+		.caps = FE_CAN_INVERSION_AUTO
+			| FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4
+			| FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO
+			| FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QPSK
+			| FE_CAN_QAM_AUTO
+			| FE_CAN_TRANSMISSION_MODE_AUTO
+			| FE_CAN_GUARD_INTERVAL_AUTO
+			| FE_CAN_HIERARCHY_AUTO
+			| FE_CAN_RECOVER
+			| FE_CAN_MUTE_TS
+	},
+
+	.set_frontend		= as102_fe_set_frontend,
+	.get_frontend		= as102_fe_get_frontend,
+	.get_tune_settings	= as102_fe_get_tune_settings,
+
+	.read_status		= as102_fe_read_status,
+	.read_snr		= as102_fe_read_snr,
+	.read_ber		= as102_fe_read_ber,
+	.read_signal_strength	= as102_fe_read_signal_strength,
+	.read_ucblocks		= as102_fe_read_ucblocks,
+	.ts_bus_ctrl		= as102_fe_ts_bus_ctrl,
+};
+
+int as102_dvb_unregister_fe(struct dvb_frontend *fe)
+{
+	/* unregister frontend */
+	dvb_unregister_frontend(fe);
+
+	/* detach frontend */
+	dvb_frontend_detach(fe);
+
+	return 0;
+}
+
+int as102_dvb_register_fe(struct as102_dev_t *as102_dev,
+			  struct dvb_frontend *dvb_fe)
+{
+	int errno;
+	struct dvb_adapter *dvb_adap;
+
+	if (as102_dev == NULL)
+		return -EINVAL;
+
+	/* extract dvb_adapter */
+	dvb_adap = &as102_dev->dvb_adap;
+
+	/* init frontend callback ops */
+	memcpy(&dvb_fe->ops, &as102_fe_ops, sizeof(struct dvb_frontend_ops));
+	strncpy(dvb_fe->ops.info.name, as102_dev->name,
+		sizeof(dvb_fe->ops.info.name));
+
+	/* register dbvb frontend */
+	errno = dvb_register_frontend(dvb_adap, dvb_fe);
+	if (errno == 0)
+		dvb_fe->tuner_priv = as102_dev;
+
+	return errno;
+}
+
+static void as10x_fe_copy_tps_parameters(struct dvb_frontend_parameters *dst,
+					 struct as10x_tps *as10x_tps)
+{
+
+	struct dvb_ofdm_parameters *fe_tps = &dst->u.ofdm;
+
+	/* extract consteallation */
+	switch (as10x_tps->constellation) {
+	case CONST_QPSK:
+		fe_tps->constellation = QPSK;
+		break;
+	case CONST_QAM16:
+		fe_tps->constellation = QAM_16;
+		break;
+	case CONST_QAM64:
+		fe_tps->constellation = QAM_64;
+		break;
+	}
+
+	/* extract hierarchy */
+	switch (as10x_tps->hierarchy) {
+	case HIER_NONE:
+		fe_tps->hierarchy_information = HIERARCHY_NONE;
+		break;
+	case HIER_ALPHA_1:
+		fe_tps->hierarchy_information = HIERARCHY_1;
+		break;
+	case HIER_ALPHA_2:
+		fe_tps->hierarchy_information = HIERARCHY_2;
+		break;
+	case HIER_ALPHA_4:
+		fe_tps->hierarchy_information = HIERARCHY_4;
+		break;
+	}
+
+	/* extract code rate HP */
+	switch (as10x_tps->code_rate_HP) {
+	case CODE_RATE_1_2:
+		fe_tps->code_rate_HP = FEC_1_2;
+		break;
+	case CODE_RATE_2_3:
+		fe_tps->code_rate_HP = FEC_2_3;
+		break;
+	case CODE_RATE_3_4:
+		fe_tps->code_rate_HP = FEC_3_4;
+		break;
+	case CODE_RATE_5_6:
+		fe_tps->code_rate_HP = FEC_5_6;
+		break;
+	case CODE_RATE_7_8:
+		fe_tps->code_rate_HP = FEC_7_8;
+		break;
+	}
+
+	/* extract code rate LP */
+	switch (as10x_tps->code_rate_LP) {
+	case CODE_RATE_1_2:
+		fe_tps->code_rate_LP = FEC_1_2;
+		break;
+	case CODE_RATE_2_3:
+		fe_tps->code_rate_LP = FEC_2_3;
+		break;
+	case CODE_RATE_3_4:
+		fe_tps->code_rate_LP = FEC_3_4;
+		break;
+	case CODE_RATE_5_6:
+		fe_tps->code_rate_LP = FEC_5_6;
+		break;
+	case CODE_RATE_7_8:
+		fe_tps->code_rate_LP = FEC_7_8;
+		break;
+	}
+
+	/* extract guard interval */
+	switch (as10x_tps->guard_interval) {
+	case GUARD_INT_1_32:
+		fe_tps->guard_interval = GUARD_INTERVAL_1_32;
+		break;
+	case GUARD_INT_1_16:
+		fe_tps->guard_interval = GUARD_INTERVAL_1_16;
+		break;
+	case GUARD_INT_1_8:
+		fe_tps->guard_interval = GUARD_INTERVAL_1_8;
+		break;
+	case GUARD_INT_1_4:
+		fe_tps->guard_interval = GUARD_INTERVAL_1_4;
+		break;
+	}
+
+	/* extract transmission mode */
+	switch (as10x_tps->transmission_mode) {
+	case TRANS_MODE_2K:
+		fe_tps->transmission_mode = TRANSMISSION_MODE_2K;
+		break;
+	case TRANS_MODE_8K:
+		fe_tps->transmission_mode = TRANSMISSION_MODE_8K;
+		break;
+	}
+}
+
+static uint8_t as102_fe_get_code_rate(fe_code_rate_t arg)
+{
+	uint8_t c;
+
+	switch (arg) {
+	case FEC_1_2:
+		c = CODE_RATE_1_2;
+		break;
+	case FEC_2_3:
+		c = CODE_RATE_2_3;
+		break;
+	case FEC_3_4:
+		c = CODE_RATE_3_4;
+		break;
+	case FEC_5_6:
+		c = CODE_RATE_5_6;
+		break;
+	case FEC_7_8:
+		c = CODE_RATE_7_8;
+		break;
+	default:
+		c = CODE_RATE_UNKNOWN;
+		break;
+	}
+
+	return c;
+}
+
+static void as102_fe_copy_tune_parameters(struct as10x_tune_args *tune_args,
+			  struct dvb_frontend_parameters *params)
+{
+
+	/* set frequency */
+	tune_args->freq = params->frequency / 1000;
+
+	/* fix interleaving_mode */
+	tune_args->interleaving_mode = INTLV_NATIVE;
+
+	switch (params->u.ofdm.bandwidth) {
+	case BANDWIDTH_8_MHZ:
+		tune_args->bandwidth = BW_8_MHZ;
+		break;
+	case BANDWIDTH_7_MHZ:
+		tune_args->bandwidth = BW_7_MHZ;
+		break;
+	case BANDWIDTH_6_MHZ:
+		tune_args->bandwidth = BW_6_MHZ;
+		break;
+	default:
+		tune_args->bandwidth = BW_8_MHZ;
+	}
+
+	switch (params->u.ofdm.guard_interval) {
+	case GUARD_INTERVAL_1_32:
+		tune_args->guard_interval = GUARD_INT_1_32;
+		break;
+	case GUARD_INTERVAL_1_16:
+		tune_args->guard_interval = GUARD_INT_1_16;
+		break;
+	case GUARD_INTERVAL_1_8:
+		tune_args->guard_interval = GUARD_INT_1_8;
+		break;
+	case GUARD_INTERVAL_1_4:
+		tune_args->guard_interval = GUARD_INT_1_4;
+		break;
+	case GUARD_INTERVAL_AUTO:
+	default:
+		tune_args->guard_interval = GUARD_UNKNOWN;
+		break;
+	}
+
+	switch (params->u.ofdm.constellation) {
+	case QPSK:
+		tune_args->constellation = CONST_QPSK;
+		break;
+	case QAM_16:
+		tune_args->constellation = CONST_QAM16;
+		break;
+	case QAM_64:
+		tune_args->constellation = CONST_QAM64;
+		break;
+	default:
+		tune_args->constellation = CONST_UNKNOWN;
+		break;
+	}
+
+	switch (params->u.ofdm.transmission_mode) {
+	case TRANSMISSION_MODE_2K:
+		tune_args->transmission_mode = TRANS_MODE_2K;
+		break;
+	case TRANSMISSION_MODE_8K:
+		tune_args->transmission_mode = TRANS_MODE_8K;
+		break;
+	default:
+		tune_args->transmission_mode = TRANS_MODE_UNKNOWN;
+	}
+
+	switch (params->u.ofdm.hierarchy_information) {
+	case HIERARCHY_NONE:
+		tune_args->hierarchy = HIER_NONE;
+		break;
+	case HIERARCHY_1:
+		tune_args->hierarchy = HIER_ALPHA_1;
+		break;
+	case HIERARCHY_2:
+		tune_args->hierarchy = HIER_ALPHA_2;
+		break;
+	case HIERARCHY_4:
+		tune_args->hierarchy = HIER_ALPHA_4;
+		break;
+	case HIERARCHY_AUTO:
+		tune_args->hierarchy = HIER_UNKNOWN;
+		break;
+	}
+
+	dprintk(debug, "tuner parameters: freq: %d  bw: 0x%02x  gi: 0x%02x\n",
+			params->frequency,
+			tune_args->bandwidth,
+			tune_args->guard_interval);
+
+	/*
+	 * Detect a hierarchy selection
+	 * if HP/LP are both set to FEC_NONE, HP will be selected.
+	 */
+	if ((tune_args->hierarchy != HIER_NONE) &&
+		       ((params->u.ofdm.code_rate_LP == FEC_NONE) ||
+			(params->u.ofdm.code_rate_HP == FEC_NONE))) {
+
+		if (params->u.ofdm.code_rate_LP == FEC_NONE) {
+			tune_args->hier_select = HIER_HIGH_PRIORITY;
+			tune_args->code_rate =
+			   as102_fe_get_code_rate(params->u.ofdm.code_rate_HP);
+		}
+
+		if (params->u.ofdm.code_rate_HP == FEC_NONE) {
+			tune_args->hier_select = HIER_LOW_PRIORITY;
+			tune_args->code_rate =
+			   as102_fe_get_code_rate(params->u.ofdm.code_rate_LP);
+		}
+
+		dprintk(debug, "\thierarchy: 0x%02x  "
+				"selected: %s  code_rate_%s: 0x%02x\n",
+			tune_args->hierarchy,
+			tune_args->hier_select == HIER_HIGH_PRIORITY ?
+			"HP" : "LP",
+			tune_args->hier_select == HIER_HIGH_PRIORITY ?
+			"HP" : "LP",
+			tune_args->code_rate);
+	} else {
+		tune_args->code_rate =
+			as102_fe_get_code_rate(params->u.ofdm.code_rate_HP);
+	}
+}
+
+/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */
diff --git a/drivers/staging/media/as102/as102_fw.c b/drivers/staging/media/as102/as102_fw.c
new file mode 100644
index 000000000000..c019df933cc9
--- /dev/null
+++ b/drivers/staging/media/as102/as102_fw.c
@@ -0,0 +1,251 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+
+#include "as102_drv.h"
+#include "as102_fw.h"
+
+#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
+char as102_st_fw1[] = "as102_data1_st.hex";
+char as102_st_fw2[] = "as102_data2_st.hex";
+char as102_dt_fw1[] = "as102_data1_dt.hex";
+char as102_dt_fw2[] = "as102_data2_dt.hex";
+
+static unsigned char atohx(unsigned char *dst, char *src)
+{
+	unsigned char value = 0;
+
+	char msb = tolower(*src) - '0';
+	char lsb = tolower(*(src + 1)) - '0';
+
+	if (msb > 9)
+		msb -= 7;
+	if (lsb > 9)
+		lsb -= 7;
+
+	*dst = value = ((msb & 0xF) << 4) | (lsb & 0xF);
+	return value;
+}
+
+/*
+ * Parse INTEL HEX firmware file to extract address and data.
+ */
+static int parse_hex_line(unsigned char *fw_data, unsigned char *addr,
+			  unsigned char *data, int *dataLength,
+			  unsigned char *addr_has_changed) {
+
+	int count = 0;
+	unsigned char *src, dst;
+
+	if (*fw_data++ != ':') {
+		printk(KERN_ERR "invalid firmware file\n");
+		return -EFAULT;
+	}
+
+	/* locate end of line */
+	for (src = fw_data; *src != '\n'; src += 2) {
+		atohx(&dst, src);
+		/* parse line to split addr / data */
+		switch (count) {
+		case 0:
+			*dataLength = dst;
+			break;
+		case 1:
+			addr[2] = dst;
+			break;
+		case 2:
+			addr[3] = dst;
+			break;
+		case 3:
+			/* check if data is an address */
+			if (dst == 0x04)
+				*addr_has_changed = 1;
+			else
+				*addr_has_changed = 0;
+			break;
+		case  4:
+		case  5:
+			if (*addr_has_changed)
+				addr[(count - 4)] = dst;
+			else
+				data[(count - 4)] = dst;
+			break;
+		default:
+			data[(count - 4)] = dst;
+			break;
+		}
+		count++;
+	}
+
+	/* return read value + ':' + '\n' */
+	return (count * 2) + 2;
+}
+
+static int as102_firmware_upload(struct as102_bus_adapter_t *bus_adap,
+				 unsigned char *cmd,
+				 const struct firmware *firmware) {
+
+	struct as10x_fw_pkt_t fw_pkt;
+	int total_read_bytes = 0, errno = 0;
+	unsigned char addr_has_changed = 0;
+
+	ENTER();
+
+	for (total_read_bytes = 0; total_read_bytes < firmware->size; ) {
+		int read_bytes = 0, data_len = 0;
+
+		/* parse intel hex line */
+		read_bytes = parse_hex_line(
+				(u8 *) (firmware->data + total_read_bytes),
+				fw_pkt.raw.address,
+				fw_pkt.raw.data,
+				&data_len,
+				&addr_has_changed);
+
+		if (read_bytes <= 0)
+			goto error;
+
+		/* detect the end of file */
+		total_read_bytes += read_bytes;
+		if (total_read_bytes == firmware->size) {
+			fw_pkt.u.request[0] = 0x00;
+			fw_pkt.u.request[1] = 0x03;
+
+			/* send EOF command */
+			errno = bus_adap->ops->upload_fw_pkt(bus_adap,
+							     (uint8_t *)
+							     &fw_pkt, 2, 0);
+			if (errno < 0)
+				goto error;
+		} else {
+			if (!addr_has_changed) {
+				/* prepare command to send */
+				fw_pkt.u.request[0] = 0x00;
+				fw_pkt.u.request[1] = 0x01;
+
+				data_len += sizeof(fw_pkt.u.request);
+				data_len += sizeof(fw_pkt.raw.address);
+
+				/* send cmd to device */
+				errno = bus_adap->ops->upload_fw_pkt(bus_adap,
+								     (uint8_t *)
+								     &fw_pkt,
+								     data_len,
+								     0);
+				if (errno < 0)
+					goto error;
+			}
+		}
+	}
+error:
+	LEAVE();
+	return (errno == 0) ? total_read_bytes : errno;
+}
+
+int as102_fw_upload(struct as102_bus_adapter_t *bus_adap)
+{
+	int errno = -EFAULT;
+	const struct firmware *firmware;
+	unsigned char *cmd_buf = NULL;
+	char *fw1, *fw2;
+
+#if defined(CONFIG_AS102_USB)
+	struct usb_device *dev = bus_adap->usb_dev;
+#endif
+#if defined(CONFIG_AS102_SPI)
+	struct spi_device *dev = bus_adap->spi_dev;
+#endif
+	ENTER();
+
+	/* select fw file to upload */
+	if (dual_tuner) {
+		fw1 = as102_dt_fw1;
+		fw2 = as102_dt_fw2;
+	} else {
+		fw1 = as102_st_fw1;
+		fw2 = as102_st_fw2;
+	}
+
+#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
+	/* allocate buffer to store firmware upload command and data */
+	cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL);
+	if (cmd_buf == NULL) {
+		errno = -ENOMEM;
+		goto error;
+	}
+
+	/* request kernel to locate firmware file: part1 */
+	errno = request_firmware(&firmware, fw1, &dev->dev);
+	if (errno < 0) {
+		printk(KERN_ERR "%s: unable to locate firmware file: %s\n",
+				 DRIVER_NAME, fw1);
+		goto error;
+	}
+
+	/* initiate firmware upload */
+	errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
+	if (errno < 0) {
+		printk(KERN_ERR "%s: error during firmware upload part1\n",
+				 DRIVER_NAME);
+		goto error;
+	}
+
+	printk(KERN_INFO "%s: fimrware: %s loaded with success\n",
+			 DRIVER_NAME, fw1);
+	release_firmware(firmware);
+
+	/* wait for boot to complete */
+	mdelay(100);
+
+	/* request kernel to locate firmware file: part2 */
+	errno = request_firmware(&firmware, fw2, &dev->dev);
+	if (errno < 0) {
+		printk(KERN_ERR "%s: unable to locate firmware file: %s\n",
+				 DRIVER_NAME, fw2);
+		goto error;
+	}
+
+	/* initiate firmware upload */
+	errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
+	if (errno < 0) {
+		printk(KERN_ERR "%s: error during firmware upload part2\n",
+				 DRIVER_NAME);
+		goto error;
+	}
+
+	printk(KERN_INFO "%s: fimrware: %s loaded with success\n",
+			DRIVER_NAME, fw2);
+error:
+	/* free data buffer */
+	kfree(cmd_buf);
+	/* release firmware if needed */
+	if (firmware != NULL)
+		release_firmware(firmware);
+#endif
+	LEAVE();
+	return errno;
+}
+#endif
+
+/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */
diff --git a/drivers/staging/media/as102/as102_fw.h b/drivers/staging/media/as102/as102_fw.h
new file mode 100644
index 000000000000..27e5347e2e19
--- /dev/null
+++ b/drivers/staging/media/as102/as102_fw.h
@@ -0,0 +1,42 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#define MAX_FW_PKT_SIZE	64
+
+extern int dual_tuner;
+
+#pragma pack(1)
+struct as10x_raw_fw_pkt {
+	unsigned char address[4];
+	unsigned char data[MAX_FW_PKT_SIZE - 6];
+};
+
+struct as10x_fw_pkt_t {
+	union {
+		unsigned char request[2];
+		unsigned char length[2];
+	} u;
+	struct as10x_raw_fw_pkt raw;
+};
+#pragma pack()
+
+#ifdef __KERNEL__
+int as102_fw_upload(struct as102_bus_adapter_t *bus_adap);
+#endif
+
+/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */
diff --git a/drivers/staging/media/as102/as102_usb_drv.c b/drivers/staging/media/as102/as102_usb_drv.c
new file mode 100644
index 000000000000..264be2dbd2a4
--- /dev/null
+++ b/drivers/staging/media/as102/as102_usb_drv.c
@@ -0,0 +1,478 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/usb.h>
+
+#include "as102_drv.h"
+#include "as102_usb_drv.h"
+#include "as102_fw.h"
+
+static void as102_usb_disconnect(struct usb_interface *interface);
+static int as102_usb_probe(struct usb_interface *interface,
+			   const struct usb_device_id *id);
+
+static int as102_usb_start_stream(struct as102_dev_t *dev);
+static void as102_usb_stop_stream(struct as102_dev_t *dev);
+
+static int as102_open(struct inode *inode, struct file *file);
+static int as102_release(struct inode *inode, struct file *file);
+
+static struct usb_device_id as102_usb_id_table[] = {
+	{ USB_DEVICE(AS102_USB_DEVICE_VENDOR_ID, AS102_USB_DEVICE_PID_0001) },
+	{ USB_DEVICE(PCTV_74E_USB_VID, PCTV_74E_USB_PID) },
+	{ USB_DEVICE(ELGATO_EYETV_DTT_USB_VID, ELGATO_EYETV_DTT_USB_PID) },
+	{ USB_DEVICE(NBOX_DVBT_DONGLE_USB_VID, NBOX_DVBT_DONGLE_USB_PID) },
+	{ } /* Terminating entry */
+};
+
+/* Note that this table must always have the same number of entries as the
+   as102_usb_id_table struct */
+static const char *as102_device_names[] = {
+	AS102_REFERENCE_DESIGN,
+	AS102_PCTV_74E,
+	AS102_ELGATO_EYETV_DTT_NAME,
+	AS102_NBOX_DVBT_DONGLE_NAME,
+	NULL /* Terminating entry */
+};
+
+struct usb_driver as102_usb_driver = {
+	.name       =  DRIVER_FULL_NAME,
+	.probe      =  as102_usb_probe,
+	.disconnect =  as102_usb_disconnect,
+	.id_table   =  as102_usb_id_table
+};
+
+static const struct file_operations as102_dev_fops = {
+	.owner   = THIS_MODULE,
+	.open    = as102_open,
+	.release = as102_release,
+};
+
+static struct usb_class_driver as102_usb_class_driver = {
+	.name		= "aton2-%d",
+	.fops		= &as102_dev_fops,
+	.minor_base	= AS102_DEVICE_MAJOR,
+};
+
+static int as102_usb_xfer_cmd(struct as102_bus_adapter_t *bus_adap,
+			      unsigned char *send_buf, int send_buf_len,
+			      unsigned char *recv_buf, int recv_buf_len)
+{
+	int ret = 0;
+	ENTER();
+
+	if (send_buf != NULL) {
+		ret = usb_control_msg(bus_adap->usb_dev,
+				      usb_sndctrlpipe(bus_adap->usb_dev, 0),
+				      AS102_USB_DEVICE_TX_CTRL_CMD,
+				      USB_DIR_OUT | USB_TYPE_VENDOR |
+				      USB_RECIP_DEVICE,
+				      bus_adap->cmd_xid, /* value */
+				      0, /* index */
+				      send_buf, send_buf_len,
+				      USB_CTRL_SET_TIMEOUT /* 200 */);
+		if (ret < 0) {
+			dprintk(debug, "usb_control_msg(send) failed, err %i\n",
+					ret);
+			return ret;
+		}
+
+		if (ret != send_buf_len) {
+			dprintk(debug, "only wrote %d of %d bytes\n",
+					ret, send_buf_len);
+			return -1;
+		}
+	}
+
+	if (recv_buf != NULL) {
+#ifdef TRACE
+		dprintk(debug, "want to read: %d bytes\n", recv_buf_len);
+#endif
+		ret = usb_control_msg(bus_adap->usb_dev,
+				      usb_rcvctrlpipe(bus_adap->usb_dev, 0),
+				      AS102_USB_DEVICE_RX_CTRL_CMD,
+				      USB_DIR_IN | USB_TYPE_VENDOR |
+				      USB_RECIP_DEVICE,
+				      bus_adap->cmd_xid, /* value */
+				      0, /* index */
+				      recv_buf, recv_buf_len,
+				      USB_CTRL_GET_TIMEOUT /* 200 */);
+		if (ret < 0) {
+			dprintk(debug, "usb_control_msg(recv) failed, err %i\n",
+					ret);
+			return ret;
+		}
+#ifdef TRACE
+		dprintk(debug, "read %d bytes\n", recv_buf_len);
+#endif
+	}
+
+	LEAVE();
+	return ret;
+}
+
+static int as102_send_ep1(struct as102_bus_adapter_t *bus_adap,
+			  unsigned char *send_buf,
+			  int send_buf_len,
+			  int swap32)
+{
+	int ret = 0, actual_len;
+
+	ret = usb_bulk_msg(bus_adap->usb_dev,
+			   usb_sndbulkpipe(bus_adap->usb_dev, 1),
+			   send_buf, send_buf_len, &actual_len, 200);
+	if (ret) {
+		dprintk(debug, "usb_bulk_msg(send) failed, err %i\n", ret);
+		return ret;
+	}
+
+	if (actual_len != send_buf_len) {
+		dprintk(debug, "only wrote %d of %d bytes\n",
+				actual_len, send_buf_len);
+		return -1;
+	}
+	return ret ? ret : actual_len;
+}
+
+static int as102_read_ep2(struct as102_bus_adapter_t *bus_adap,
+		   unsigned char *recv_buf, int recv_buf_len)
+{
+	int ret = 0, actual_len;
+
+	if (recv_buf == NULL)
+		return -EINVAL;
+
+	ret = usb_bulk_msg(bus_adap->usb_dev,
+			   usb_rcvbulkpipe(bus_adap->usb_dev, 2),
+			   recv_buf, recv_buf_len, &actual_len, 200);
+	if (ret) {
+		dprintk(debug, "usb_bulk_msg(recv) failed, err %i\n", ret);
+		return ret;
+	}
+
+	if (actual_len != recv_buf_len) {
+		dprintk(debug, "only read %d of %d bytes\n",
+				actual_len, recv_buf_len);
+		return -1;
+	}
+	return ret ? ret : actual_len;
+}
+
+struct as102_priv_ops_t as102_priv_ops = {
+	.upload_fw_pkt	= as102_send_ep1,
+	.xfer_cmd	= as102_usb_xfer_cmd,
+	.as102_read_ep2	= as102_read_ep2,
+	.start_stream	= as102_usb_start_stream,
+	.stop_stream	= as102_usb_stop_stream,
+};
+
+static int as102_submit_urb_stream(struct as102_dev_t *dev, struct urb *urb)
+{
+	int err;
+
+	usb_fill_bulk_urb(urb,
+			  dev->bus_adap.usb_dev,
+			  usb_rcvbulkpipe(dev->bus_adap.usb_dev, 0x2),
+			  urb->transfer_buffer,
+			  AS102_USB_BUF_SIZE,
+			  as102_urb_stream_irq,
+			  dev);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err)
+		dprintk(debug, "%s: usb_submit_urb failed\n", __func__);
+
+	return err;
+}
+
+void as102_urb_stream_irq(struct urb *urb)
+{
+	struct as102_dev_t *as102_dev = urb->context;
+
+	if (urb->actual_length > 0) {
+		dvb_dmx_swfilter(&as102_dev->dvb_dmx,
+				 urb->transfer_buffer,
+				 urb->actual_length);
+	} else {
+		if (urb->actual_length == 0)
+			memset(urb->transfer_buffer, 0, AS102_USB_BUF_SIZE);
+	}
+
+	/* is not stopped, re-submit urb */
+	if (as102_dev->streaming)
+		as102_submit_urb_stream(as102_dev, urb);
+}
+
+static void as102_free_usb_stream_buffer(struct as102_dev_t *dev)
+{
+	int i;
+
+	ENTER();
+
+	for (i = 0; i < MAX_STREAM_URB; i++)
+		usb_free_urb(dev->stream_urb[i]);
+
+	usb_free_coherent(dev->bus_adap.usb_dev,
+			MAX_STREAM_URB * AS102_USB_BUF_SIZE,
+			dev->stream,
+			dev->dma_addr);
+	LEAVE();
+}
+
+static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev)
+{
+	int i, ret = 0;
+
+	ENTER();
+
+	dev->stream = usb_alloc_coherent(dev->bus_adap.usb_dev,
+				       MAX_STREAM_URB * AS102_USB_BUF_SIZE,
+				       GFP_KERNEL,
+				       &dev->dma_addr);
+	if (!dev->stream) {
+		dprintk(debug, "%s: usb_buffer_alloc failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	memset(dev->stream, 0, MAX_STREAM_URB * AS102_USB_BUF_SIZE);
+
+	/* init urb buffers */
+	for (i = 0; i < MAX_STREAM_URB; i++) {
+		struct urb *urb;
+
+		urb = usb_alloc_urb(0, GFP_ATOMIC);
+		if (urb == NULL) {
+			dprintk(debug, "%s: usb_alloc_urb failed\n", __func__);
+			as102_free_usb_stream_buffer(dev);
+			return -ENOMEM;
+		}
+
+		urb->transfer_buffer = dev->stream + (i * AS102_USB_BUF_SIZE);
+		urb->transfer_buffer_length = AS102_USB_BUF_SIZE;
+
+		dev->stream_urb[i] = urb;
+	}
+	LEAVE();
+	return ret;
+}
+
+static void as102_usb_stop_stream(struct as102_dev_t *dev)
+{
+	int i;
+
+	for (i = 0; i < MAX_STREAM_URB; i++)
+		usb_kill_urb(dev->stream_urb[i]);
+}
+
+static int as102_usb_start_stream(struct as102_dev_t *dev)
+{
+	int i, ret = 0;
+
+	for (i = 0; i < MAX_STREAM_URB; i++) {
+		ret = as102_submit_urb_stream(dev, dev->stream_urb[i]);
+		if (ret) {
+			as102_usb_stop_stream(dev);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void as102_usb_release(struct kref *kref)
+{
+	struct as102_dev_t *as102_dev;
+
+	ENTER();
+
+	as102_dev = container_of(kref, struct as102_dev_t, kref);
+	if (as102_dev != NULL) {
+		usb_put_dev(as102_dev->bus_adap.usb_dev);
+		kfree(as102_dev);
+	}
+
+	LEAVE();
+}
+
+static void as102_usb_disconnect(struct usb_interface *intf)
+{
+	struct as102_dev_t *as102_dev;
+
+	ENTER();
+
+	/* extract as102_dev_t from usb_device private data */
+	as102_dev = usb_get_intfdata(intf);
+
+	/* unregister dvb layer */
+	as102_dvb_unregister(as102_dev);
+
+	/* free usb buffers */
+	as102_free_usb_stream_buffer(as102_dev);
+
+	usb_set_intfdata(intf, NULL);
+
+	/* usb unregister device */
+	usb_deregister_dev(intf, &as102_usb_class_driver);
+
+	/* decrement usage counter */
+	kref_put(&as102_dev->kref, as102_usb_release);
+
+	printk(KERN_INFO "%s: device has been disconnected\n", DRIVER_NAME);
+
+	LEAVE();
+}
+
+static int as102_usb_probe(struct usb_interface *intf,
+			   const struct usb_device_id *id)
+{
+	int ret;
+	struct as102_dev_t *as102_dev;
+	int i;
+
+	ENTER();
+
+	as102_dev = kzalloc(sizeof(struct as102_dev_t), GFP_KERNEL);
+	if (as102_dev == NULL) {
+		err("%s: kzalloc failed", __func__);
+		return -ENOMEM;
+	}
+
+	/* This should never actually happen */
+	if ((sizeof(as102_usb_id_table) / sizeof(struct usb_device_id)) !=
+	    (sizeof(as102_device_names) / sizeof(const char *))) {
+		printk(KERN_ERR "Device names table invalid size");
+		return -EINVAL;
+	}
+
+	/* Assign the user-friendly device name */
+	for (i = 0; i < (sizeof(as102_usb_id_table) /
+			 sizeof(struct usb_device_id)); i++) {
+		if (id == &as102_usb_id_table[i])
+			as102_dev->name = as102_device_names[i];
+	}
+
+	if (as102_dev->name == NULL)
+		as102_dev->name = "Unknown AS102 device";
+
+	/* set private callback functions */
+	as102_dev->bus_adap.ops = &as102_priv_ops;
+
+	/* init cmd token for usb bus */
+	as102_dev->bus_adap.cmd = &as102_dev->bus_adap.token.usb.c;
+	as102_dev->bus_adap.rsp = &as102_dev->bus_adap.token.usb.r;
+
+	/* init kernel device reference */
+	kref_init(&as102_dev->kref);
+
+	/* store as102 device to usb_device private data */
+	usb_set_intfdata(intf, (void *) as102_dev);
+
+	/* store in as102 device the usb_device pointer */
+	as102_dev->bus_adap.usb_dev = usb_get_dev(interface_to_usbdev(intf));
+
+	/* we can register the device now, as it is ready */
+	ret = usb_register_dev(intf, &as102_usb_class_driver);
+	if (ret < 0) {
+		/* something prevented us from registering this driver */
+		err("%s: usb_register_dev() failed (errno = %d)",
+		    __func__, ret);
+		goto failed;
+	}
+
+	printk(KERN_INFO "%s: device has been detected\n", DRIVER_NAME);
+
+	/* request buffer allocation for streaming */
+	ret = as102_alloc_usb_stream_buffer(as102_dev);
+	if (ret != 0)
+		goto failed;
+
+	/* register dvb layer */
+	ret = as102_dvb_register(as102_dev);
+
+	LEAVE();
+	return ret;
+
+failed:
+	usb_set_intfdata(intf, NULL);
+	kfree(as102_dev);
+	return ret;
+}
+
+static int as102_open(struct inode *inode, struct file *file)
+{
+	int ret = 0, minor = 0;
+	struct usb_interface *intf = NULL;
+	struct as102_dev_t *dev = NULL;
+
+	ENTER();
+
+	/* read minor from inode */
+	minor = iminor(inode);
+
+	/* fetch device from usb interface */
+	intf = usb_find_interface(&as102_usb_driver, minor);
+	if (intf == NULL) {
+		printk(KERN_ERR "%s: can't find device for minor %d\n",
+				__func__, minor);
+		ret = -ENODEV;
+		goto exit;
+	}
+
+	/* get our device */
+	dev = usb_get_intfdata(intf);
+	if (dev == NULL) {
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	/* save our device object in the file's private structure */
+	file->private_data = dev;
+
+	/* increment our usage count for the device */
+	kref_get(&dev->kref);
+
+exit:
+	LEAVE();
+	return ret;
+}
+
+static int as102_release(struct inode *inode, struct file *file)
+{
+	int ret = 0;
+	struct as102_dev_t *dev = NULL;
+
+	ENTER();
+
+	dev = file->private_data;
+	if (dev != NULL) {
+		/* decrement the count on our device */
+		kref_put(&dev->kref, as102_usb_release);
+	}
+
+	LEAVE();
+	return ret;
+}
+
+MODULE_DEVICE_TABLE(usb, as102_usb_id_table);
+
+/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */
diff --git a/drivers/staging/media/as102/as102_usb_drv.h b/drivers/staging/media/as102/as102_usb_drv.h
new file mode 100644
index 000000000000..fb1fc41dcd79
--- /dev/null
+++ b/drivers/staging/media/as102/as102_usb_drv.h
@@ -0,0 +1,59 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/version.h>
+
+#ifndef _AS102_USB_DRV_H_
+#define _AS102_USB_DRV_H_
+
+#define AS102_USB_DEVICE_TX_CTRL_CMD	0xF1
+#define AS102_USB_DEVICE_RX_CTRL_CMD	0xF2
+
+/* define these values to match the supported devices */
+
+/* Abilis system: "TITAN" */
+#define AS102_REFERENCE_DESIGN		"Abilis Systems DVB-Titan"
+#define AS102_USB_DEVICE_VENDOR_ID	0x1BA6
+#define AS102_USB_DEVICE_PID_0001	0x0001
+
+/* PCTV Systems: PCTV picoStick (74e) */
+#define AS102_PCTV_74E			"PCTV Systems picoStick (74e)"
+#define PCTV_74E_USB_VID		0x2013
+#define PCTV_74E_USB_PID		0x0246
+
+/* Elgato: EyeTV DTT Deluxe */
+#define AS102_ELGATO_EYETV_DTT_NAME	"Elgato EyeTV DTT Deluxe"
+#define ELGATO_EYETV_DTT_USB_VID	0x0fd9
+#define ELGATO_EYETV_DTT_USB_PID	0x002c
+
+/* nBox: nBox DVB-T Dongle */
+#define AS102_NBOX_DVBT_DONGLE_NAME	"nBox DVB-T Dongle"
+#define NBOX_DVBT_DONGLE_USB_VID	0x0b89
+#define NBOX_DVBT_DONGLE_USB_PID	0x0007
+
+void as102_urb_stream_irq(struct urb *urb);
+
+struct as10x_usb_token_cmd_t {
+	/* token cmd */
+	struct as10x_cmd_t c;
+	/* token response */
+	struct as10x_cmd_t r;
+};
+#endif
+/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */
diff --git a/drivers/staging/media/as102/as10x_cmd.c b/drivers/staging/media/as102/as10x_cmd.c
new file mode 100644
index 000000000000..0dcba8065780
--- /dev/null
+++ b/drivers/staging/media/as102/as10x_cmd.c
@@ -0,0 +1,452 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include "as102_drv.h"
+#include "as10x_types.h"
+#include "as10x_cmd.h"
+
+/**
+ * as10x_cmd_turn_on - send turn on command to AS10x
+ * @phandle:   pointer to AS10x handle
+ *
+ * Return 0 when no error, < 0 in case of error.
+ */
+int as10x_cmd_turn_on(as10x_handle_t *phandle)
+{
+	int error;
+	struct as10x_cmd_t *pcmd, *prsp;
+
+	ENTER();
+
+	pcmd = phandle->cmd;
+	prsp = phandle->rsp;
+
+	/* prepare command */
+	as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+			sizeof(pcmd->body.turn_on.req));
+
+	/* fill command */
+	pcmd->body.turn_on.req.proc_id = cpu_to_le16(CONTROL_PROC_TURNON);
+
+	/* send command */
+	if (phandle->ops->xfer_cmd) {
+		error = phandle->ops->xfer_cmd(phandle, (uint8_t *) pcmd,
+					       sizeof(pcmd->body.turn_on.req) +
+					       HEADER_SIZE,
+					       (uint8_t *) prsp,
+					       sizeof(prsp->body.turn_on.rsp) +
+					       HEADER_SIZE);
+	} else {
+		error = AS10X_CMD_ERROR;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* parse response */
+	error = as10x_rsp_parse(prsp, CONTROL_PROC_TURNON_RSP);
+
+out:
+	LEAVE();
+	return error;
+}
+
+/**
+ * as10x_cmd_turn_off - send turn off command to AS10x
+ * @phandle:   pointer to AS10x handle
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_turn_off(as10x_handle_t *phandle)
+{
+	int error;
+	struct as10x_cmd_t *pcmd, *prsp;
+
+	ENTER();
+
+	pcmd = phandle->cmd;
+	prsp = phandle->rsp;
+
+	/* prepare command */
+	as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+			sizeof(pcmd->body.turn_off.req));
+
+	/* fill command */
+	pcmd->body.turn_off.req.proc_id = cpu_to_le16(CONTROL_PROC_TURNOFF);
+
+	/* send command */
+	if (phandle->ops->xfer_cmd) {
+		error = phandle->ops->xfer_cmd(
+			phandle, (uint8_t *) pcmd,
+			sizeof(pcmd->body.turn_off.req) + HEADER_SIZE,
+			(uint8_t *) prsp,
+			sizeof(prsp->body.turn_off.rsp) + HEADER_SIZE);
+	} else {
+		error = AS10X_CMD_ERROR;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* parse response */
+	error = as10x_rsp_parse(prsp, CONTROL_PROC_TURNOFF_RSP);
+
+out:
+	LEAVE();
+	return error;
+}
+
+/**
+ * as10x_cmd_set_tune - send set tune command to AS10x
+ * @phandle: pointer to AS10x handle
+ * @ptune:   tune parameters
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_set_tune(as10x_handle_t *phandle, struct as10x_tune_args *ptune)
+{
+	int error;
+	struct as10x_cmd_t *preq, *prsp;
+
+	ENTER();
+
+	preq = phandle->cmd;
+	prsp = phandle->rsp;
+
+	/* prepare command */
+	as10x_cmd_build(preq, (++phandle->cmd_xid),
+			sizeof(preq->body.set_tune.req));
+
+	/* fill command */
+	preq->body.set_tune.req.proc_id = cpu_to_le16(CONTROL_PROC_SETTUNE);
+	preq->body.set_tune.req.args.freq = cpu_to_le32(ptune->freq);
+	preq->body.set_tune.req.args.bandwidth = ptune->bandwidth;
+	preq->body.set_tune.req.args.hier_select = ptune->hier_select;
+	preq->body.set_tune.req.args.constellation = ptune->constellation;
+	preq->body.set_tune.req.args.hierarchy = ptune->hierarchy;
+	preq->body.set_tune.req.args.interleaving_mode  =
+		ptune->interleaving_mode;
+	preq->body.set_tune.req.args.code_rate  = ptune->code_rate;
+	preq->body.set_tune.req.args.guard_interval = ptune->guard_interval;
+	preq->body.set_tune.req.args.transmission_mode  =
+		ptune->transmission_mode;
+
+	/* send command */
+	if (phandle->ops->xfer_cmd) {
+		error = phandle->ops->xfer_cmd(phandle,
+					       (uint8_t *) preq,
+					       sizeof(preq->body.set_tune.req)
+					       + HEADER_SIZE,
+					       (uint8_t *) prsp,
+					       sizeof(prsp->body.set_tune.rsp)
+					       + HEADER_SIZE);
+	} else {
+		error = AS10X_CMD_ERROR;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* parse response */
+	error = as10x_rsp_parse(prsp, CONTROL_PROC_SETTUNE_RSP);
+
+out:
+	LEAVE();
+	return error;
+}
+
+/**
+ * as10x_cmd_get_tune_status - send get tune status command to AS10x
+ * @phandle: pointer to AS10x handle
+ * @pstatus: pointer to updated status structure of the current tune
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_get_tune_status(as10x_handle_t *phandle,
+			      struct as10x_tune_status *pstatus)
+{
+	int error;
+	struct as10x_cmd_t  *preq, *prsp;
+
+	ENTER();
+
+	preq = phandle->cmd;
+	prsp = phandle->rsp;
+
+	/* prepare command */
+	as10x_cmd_build(preq, (++phandle->cmd_xid),
+			sizeof(preq->body.get_tune_status.req));
+
+	/* fill command */
+	preq->body.get_tune_status.req.proc_id =
+		cpu_to_le16(CONTROL_PROC_GETTUNESTAT);
+
+	/* send command */
+	if (phandle->ops->xfer_cmd) {
+		error = phandle->ops->xfer_cmd(
+			phandle,
+			(uint8_t *) preq,
+			sizeof(preq->body.get_tune_status.req) + HEADER_SIZE,
+			(uint8_t *) prsp,
+			sizeof(prsp->body.get_tune_status.rsp) + HEADER_SIZE);
+	} else {
+		error = AS10X_CMD_ERROR;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* parse response */
+	error = as10x_rsp_parse(prsp, CONTROL_PROC_GETTUNESTAT_RSP);
+	if (error < 0)
+		goto out;
+
+	/* Response OK -> get response data */
+	pstatus->tune_state = prsp->body.get_tune_status.rsp.sts.tune_state;
+	pstatus->signal_strength  =
+		le16_to_cpu(prsp->body.get_tune_status.rsp.sts.signal_strength);
+	pstatus->PER = le16_to_cpu(prsp->body.get_tune_status.rsp.sts.PER);
+	pstatus->BER = le16_to_cpu(prsp->body.get_tune_status.rsp.sts.BER);
+
+out:
+	LEAVE();
+	return error;
+}
+
+/**
+ * send get TPS command to AS10x
+ * @phandle:   pointer to AS10x handle
+ * @ptps:      pointer to TPS parameters structure
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_get_tps(as10x_handle_t *phandle, struct as10x_tps *ptps)
+{
+	int error;
+	struct as10x_cmd_t *pcmd, *prsp;
+
+	ENTER();
+
+	pcmd = phandle->cmd;
+	prsp = phandle->rsp;
+
+	/* prepare command */
+	as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+			sizeof(pcmd->body.get_tps.req));
+
+	/* fill command */
+	pcmd->body.get_tune_status.req.proc_id =
+		cpu_to_le16(CONTROL_PROC_GETTPS);
+
+	/* send command */
+	if (phandle->ops->xfer_cmd) {
+		error = phandle->ops->xfer_cmd(phandle,
+					       (uint8_t *) pcmd,
+					       sizeof(pcmd->body.get_tps.req) +
+					       HEADER_SIZE,
+					       (uint8_t *) prsp,
+					       sizeof(prsp->body.get_tps.rsp) +
+					       HEADER_SIZE);
+	} else {
+		error = AS10X_CMD_ERROR;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* parse response */
+	error = as10x_rsp_parse(prsp, CONTROL_PROC_GETTPS_RSP);
+	if (error < 0)
+		goto out;
+
+	/* Response OK -> get response data */
+	ptps->constellation = prsp->body.get_tps.rsp.tps.constellation;
+	ptps->hierarchy = prsp->body.get_tps.rsp.tps.hierarchy;
+	ptps->interleaving_mode = prsp->body.get_tps.rsp.tps.interleaving_mode;
+	ptps->code_rate_HP = prsp->body.get_tps.rsp.tps.code_rate_HP;
+	ptps->code_rate_LP = prsp->body.get_tps.rsp.tps.code_rate_LP;
+	ptps->guard_interval = prsp->body.get_tps.rsp.tps.guard_interval;
+	ptps->transmission_mode  = prsp->body.get_tps.rsp.tps.transmission_mode;
+	ptps->DVBH_mask_HP = prsp->body.get_tps.rsp.tps.DVBH_mask_HP;
+	ptps->DVBH_mask_LP = prsp->body.get_tps.rsp.tps.DVBH_mask_LP;
+	ptps->cell_ID = le16_to_cpu(prsp->body.get_tps.rsp.tps.cell_ID);
+
+out:
+	LEAVE();
+	return error;
+}
+
+/**
+ * as10x_cmd_get_demod_stats - send get demod stats command to AS10x
+ * @phandle:       pointer to AS10x handle
+ * @pdemod_stats:  pointer to demod stats parameters structure
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_get_demod_stats(as10x_handle_t  *phandle,
+			      struct as10x_demod_stats *pdemod_stats)
+{
+	int error;
+	struct as10x_cmd_t *pcmd, *prsp;
+
+	ENTER();
+
+	pcmd = phandle->cmd;
+	prsp = phandle->rsp;
+
+	/* prepare command */
+	as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+			sizeof(pcmd->body.get_demod_stats.req));
+
+	/* fill command */
+	pcmd->body.get_demod_stats.req.proc_id =
+		cpu_to_le16(CONTROL_PROC_GET_DEMOD_STATS);
+
+	/* send command */
+	if (phandle->ops->xfer_cmd) {
+		error = phandle->ops->xfer_cmd(phandle,
+				(uint8_t *) pcmd,
+				sizeof(pcmd->body.get_demod_stats.req)
+				+ HEADER_SIZE,
+				(uint8_t *) prsp,
+				sizeof(prsp->body.get_demod_stats.rsp)
+				+ HEADER_SIZE);
+	} else {
+		error = AS10X_CMD_ERROR;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* parse response */
+	error = as10x_rsp_parse(prsp, CONTROL_PROC_GET_DEMOD_STATS_RSP);
+	if (error < 0)
+		goto out;
+
+	/* Response OK -> get response data */
+	pdemod_stats->frame_count =
+		le32_to_cpu(prsp->body.get_demod_stats.rsp.stats.frame_count);
+	pdemod_stats->bad_frame_count =
+		le32_to_cpu(prsp->body.get_demod_stats.rsp.stats.bad_frame_count);
+	pdemod_stats->bytes_fixed_by_rs =
+		le32_to_cpu(prsp->body.get_demod_stats.rsp.stats.bytes_fixed_by_rs);
+	pdemod_stats->mer =
+		le16_to_cpu(prsp->body.get_demod_stats.rsp.stats.mer);
+	pdemod_stats->has_started =
+		prsp->body.get_demod_stats.rsp.stats.has_started;
+
+out:
+	LEAVE();
+	return error;
+}
+
+/**
+ * as10x_cmd_get_impulse_resp - send get impulse response command to AS10x
+ * @phandle:  pointer to AS10x handle
+ * @is_ready: pointer to value indicating when impulse
+ *	      response data is ready
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_get_impulse_resp(as10x_handle_t     *phandle,
+			       uint8_t *is_ready)
+{
+	int error;
+	struct as10x_cmd_t *pcmd, *prsp;
+
+	ENTER();
+
+	pcmd = phandle->cmd;
+	prsp = phandle->rsp;
+
+	/* prepare command */
+	as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+			sizeof(pcmd->body.get_impulse_rsp.req));
+
+	/* fill command */
+	pcmd->body.get_impulse_rsp.req.proc_id =
+		cpu_to_le16(CONTROL_PROC_GET_IMPULSE_RESP);
+
+	/* send command */
+	if (phandle->ops->xfer_cmd) {
+		error = phandle->ops->xfer_cmd(phandle,
+					(uint8_t *) pcmd,
+					sizeof(pcmd->body.get_impulse_rsp.req)
+					+ HEADER_SIZE,
+					(uint8_t *) prsp,
+					sizeof(prsp->body.get_impulse_rsp.rsp)
+					+ HEADER_SIZE);
+	} else {
+		error = AS10X_CMD_ERROR;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* parse response */
+	error = as10x_rsp_parse(prsp, CONTROL_PROC_GET_IMPULSE_RESP_RSP);
+	if (error < 0)
+		goto out;
+
+	/* Response OK -> get response data */
+	*is_ready = prsp->body.get_impulse_rsp.rsp.is_ready;
+
+out:
+	LEAVE();
+	return error;
+}
+
+/**
+ * as10x_cmd_build - build AS10x command header
+ * @pcmd:     pointer to AS10x command buffer
+ * @xid:      sequence id of the command
+ * @cmd_len:  length of the command
+ */
+void as10x_cmd_build(struct as10x_cmd_t *pcmd,
+		     uint16_t xid, uint16_t cmd_len)
+{
+	pcmd->header.req_id = cpu_to_le16(xid);
+	pcmd->header.prog = cpu_to_le16(SERVICE_PROG_ID);
+	pcmd->header.version = cpu_to_le16(SERVICE_PROG_VERSION);
+	pcmd->header.data_len = cpu_to_le16(cmd_len);
+}
+
+/**
+ * as10x_rsp_parse - Parse command response
+ * @prsp:       pointer to AS10x command buffer
+ * @proc_id:    id of the command
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_rsp_parse(struct as10x_cmd_t *prsp, uint16_t proc_id)
+{
+	int error;
+
+	/* extract command error code */
+	error = prsp->body.common.rsp.error;
+
+	if ((error == 0) &&
+	    (le16_to_cpu(prsp->body.common.rsp.proc_id) == proc_id)) {
+		return 0;
+	}
+
+	return AS10X_CMD_ERROR;
+}
diff --git a/drivers/staging/media/as102/as10x_cmd.h b/drivers/staging/media/as102/as10x_cmd.h
new file mode 100644
index 000000000000..01a716380e0a
--- /dev/null
+++ b/drivers/staging/media/as102/as10x_cmd.h
@@ -0,0 +1,540 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _AS10X_CMD_H_
+#define _AS10X_CMD_H_
+
+#ifdef __KERNEL__
+#include <linux/kernel.h>
+#endif
+
+#include "as10x_types.h"
+
+/*********************************/
+/*       MACRO DEFINITIONS       */
+/*********************************/
+#define AS10X_CMD_ERROR -1
+
+#define SERVICE_PROG_ID        0x0002
+#define SERVICE_PROG_VERSION   0x0001
+
+#define HIER_NONE              0x00
+#define HIER_LOW_PRIORITY      0x01
+
+#define HEADER_SIZE (sizeof(struct as10x_cmd_header_t))
+
+/* context request types */
+#define GET_CONTEXT_DATA        1
+#define SET_CONTEXT_DATA        2
+
+/* ODSP suspend modes */
+#define CFG_MODE_ODSP_RESUME  0
+#define CFG_MODE_ODSP_SUSPEND 1
+
+/* Dump memory size */
+#define DUMP_BLOCK_SIZE_MAX   0x20
+
+/*********************************/
+/*     TYPE DEFINITION           */
+/*********************************/
+typedef enum {
+   CONTROL_PROC_TURNON               = 0x0001,
+   CONTROL_PROC_TURNON_RSP           = 0x0100,
+   CONTROL_PROC_SET_REGISTER         = 0x0002,
+   CONTROL_PROC_SET_REGISTER_RSP     = 0x0200,
+   CONTROL_PROC_GET_REGISTER         = 0x0003,
+   CONTROL_PROC_GET_REGISTER_RSP     = 0x0300,
+   CONTROL_PROC_SETTUNE              = 0x000A,
+   CONTROL_PROC_SETTUNE_RSP          = 0x0A00,
+   CONTROL_PROC_GETTUNESTAT          = 0x000B,
+   CONTROL_PROC_GETTUNESTAT_RSP      = 0x0B00,
+   CONTROL_PROC_GETTPS               = 0x000D,
+   CONTROL_PROC_GETTPS_RSP           = 0x0D00,
+   CONTROL_PROC_SETFILTER            = 0x000E,
+   CONTROL_PROC_SETFILTER_RSP        = 0x0E00,
+   CONTROL_PROC_REMOVEFILTER         = 0x000F,
+   CONTROL_PROC_REMOVEFILTER_RSP     = 0x0F00,
+   CONTROL_PROC_GET_IMPULSE_RESP     = 0x0012,
+   CONTROL_PROC_GET_IMPULSE_RESP_RSP = 0x1200,
+   CONTROL_PROC_START_STREAMING      = 0x0013,
+   CONTROL_PROC_START_STREAMING_RSP  = 0x1300,
+   CONTROL_PROC_STOP_STREAMING       = 0x0014,
+   CONTROL_PROC_STOP_STREAMING_RSP   = 0x1400,
+   CONTROL_PROC_GET_DEMOD_STATS      = 0x0015,
+   CONTROL_PROC_GET_DEMOD_STATS_RSP  = 0x1500,
+   CONTROL_PROC_ELNA_CHANGE_MODE     = 0x0016,
+   CONTROL_PROC_ELNA_CHANGE_MODE_RSP = 0x1600,
+   CONTROL_PROC_ODSP_CHANGE_MODE     = 0x0017,
+   CONTROL_PROC_ODSP_CHANGE_MODE_RSP = 0x1700,
+   CONTROL_PROC_AGC_CHANGE_MODE      = 0x0018,
+   CONTROL_PROC_AGC_CHANGE_MODE_RSP  = 0x1800,
+
+   CONTROL_PROC_CONTEXT              = 0x00FC,
+   CONTROL_PROC_CONTEXT_RSP          = 0xFC00,
+   CONTROL_PROC_DUMP_MEMORY          = 0x00FD,
+   CONTROL_PROC_DUMP_MEMORY_RSP      = 0xFD00,
+   CONTROL_PROC_DUMPLOG_MEMORY       = 0x00FE,
+   CONTROL_PROC_DUMPLOG_MEMORY_RSP   = 0xFE00,
+   CONTROL_PROC_TURNOFF              = 0x00FF,
+   CONTROL_PROC_TURNOFF_RSP          = 0xFF00
+} control_proc;
+
+
+#pragma pack(1)
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t proc_id;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* error */
+      uint8_t error;
+   } rsp;
+} TURN_ON;
+
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t proc_id;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* error */
+      uint8_t err;
+   } rsp;
+} TURN_OFF;
+
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t proc_id;
+      /* tune params */
+      struct as10x_tune_args args;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* response error */
+      uint8_t error;
+   } rsp;
+} SET_TUNE;
+
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t proc_id;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* response error */
+      uint8_t error;
+      /* tune status */
+      struct as10x_tune_status sts;
+   } rsp;
+} GET_TUNE_STATUS;
+
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t proc_id;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* response error */
+      uint8_t error;
+      /* tps details */
+      struct as10x_tps tps;
+   } rsp;
+} GET_TPS;
+
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t  proc_id;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* response error */
+      uint8_t error;
+   } rsp;
+} COMMON;
+
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t  proc_id;
+      /* PID to filter */
+      uint16_t  pid;
+      /* stream type (MPE, PSI/SI or PES )*/
+      uint8_t stream_type;
+      /* PID index in filter table */
+      uint8_t idx;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* response error */
+      uint8_t error;
+      /* Filter id */
+      uint8_t filter_id;
+   } rsp;
+} ADD_PID_FILTER;
+
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t  proc_id;
+      /* PID to remove */
+      uint16_t  pid;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* response error */
+      uint8_t error;
+   } rsp;
+} DEL_PID_FILTER;
+
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t proc_id;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* error */
+      uint8_t error;
+   } rsp;
+} START_STREAMING;
+
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t proc_id;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* error */
+      uint8_t error;
+   } rsp;
+} STOP_STREAMING;
+
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t proc_id;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* error */
+      uint8_t error;
+      /* demod stats */
+      struct as10x_demod_stats stats;
+   } rsp;
+} GET_DEMOD_STATS;
+
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t proc_id;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* error */
+      uint8_t error;
+      /* impulse response ready */
+      uint8_t is_ready;
+   } rsp;
+} GET_IMPULSE_RESP;
+
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t proc_id;
+      /* value to write (for set context)*/
+      struct as10x_register_value reg_val;
+      /* context tag */
+      uint16_t tag;
+      /* context request type */
+      uint16_t type;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* value read (for get context) */
+      struct as10x_register_value reg_val;
+      /* context request type */
+      uint16_t type;
+      /* error */
+      uint8_t error;
+   } rsp;
+} FW_CONTEXT;
+
+typedef union {
+   /* request */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* register description */
+      struct as10x_register_addr reg_addr;
+      /* register content */
+      struct as10x_register_value reg_val;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* error */
+      uint8_t error;
+   } rsp;
+} SET_REGISTER;
+
+typedef union {
+   /* request */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* register description */
+      struct as10x_register_addr reg_addr;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* error */
+      uint8_t error;
+      /* register content */
+      struct as10x_register_value reg_val;
+   } rsp;
+} GET_REGISTER;
+
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t proc_id;
+      /* mode */
+      uint8_t mode;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* error */
+      uint8_t error;
+   } rsp;
+} CFG_CHANGE_MODE;
+
+struct as10x_cmd_header_t {
+   uint16_t req_id;
+   uint16_t prog;
+   uint16_t version;
+   uint16_t data_len;
+};
+
+#define DUMP_BLOCK_SIZE 16
+typedef union {
+   /* request */
+   struct {
+      /* request identifier */
+      uint16_t proc_id;
+      /* dump memory type request */
+      uint8_t dump_req;
+      /* register description */
+      struct as10x_register_addr reg_addr;
+      /* nb blocks to read */
+      uint16_t num_blocks;
+   } req;
+   /* response */
+   struct {
+      /* response identifier */
+      uint16_t proc_id;
+      /* error */
+      uint8_t error;
+      /* dump response */
+      uint8_t dump_rsp;
+      /* data */
+      union {
+	 uint8_t  data8[DUMP_BLOCK_SIZE];
+	 uint16_t data16[DUMP_BLOCK_SIZE / sizeof(uint16_t)];
+	 uint32_t data32[DUMP_BLOCK_SIZE / sizeof(uint32_t)];
+      } u;
+   } rsp;
+} DUMP_MEMORY;
+
+typedef union {
+   struct {
+      /* request identifier */
+      uint16_t proc_id;
+      /* dump memory type request */
+      uint8_t dump_req;
+   } req;
+   struct {
+      /* request identifier */
+      uint16_t proc_id;
+      /* error */
+      uint8_t error;
+      /* dump response */
+      uint8_t dump_rsp;
+      /* dump data */
+      uint8_t data[DUMP_BLOCK_SIZE];
+   } rsp;
+} DUMPLOG_MEMORY;
+
+typedef union {
+   /* request */
+   struct {
+      uint16_t proc_id;
+      uint8_t data[64 - sizeof(struct as10x_cmd_header_t) -2 /* proc_id */];
+   } req;
+   /* response */
+   struct {
+      uint16_t proc_id;
+      uint8_t error;
+      uint8_t data[64 - sizeof(struct as10x_cmd_header_t) /* header */
+		      - 2 /* proc_id */ - 1 /* rc */];
+   } rsp;
+} RAW_DATA;
+
+struct as10x_cmd_t {
+   /* header */
+   struct as10x_cmd_header_t header;
+   /* body */
+   union {
+      TURN_ON           turn_on;
+      TURN_OFF          turn_off;
+      SET_TUNE          set_tune;
+      GET_TUNE_STATUS   get_tune_status;
+      GET_TPS           get_tps;
+      COMMON            common;
+      ADD_PID_FILTER    add_pid_filter;
+      DEL_PID_FILTER    del_pid_filter;
+      START_STREAMING   start_streaming;
+      STOP_STREAMING    stop_streaming;
+      GET_DEMOD_STATS   get_demod_stats;
+      GET_IMPULSE_RESP  get_impulse_rsp;
+      FW_CONTEXT        context;
+      SET_REGISTER      set_register;
+      GET_REGISTER      get_register;
+      CFG_CHANGE_MODE   cfg_change_mode;
+      DUMP_MEMORY       dump_memory;
+      DUMPLOG_MEMORY    dumplog_memory;
+      RAW_DATA          raw_data;
+   } body;
+};
+
+struct as10x_token_cmd_t {
+   /* token cmd */
+   struct as10x_cmd_t c;
+   /* token response */
+   struct as10x_cmd_t r;
+};
+#pragma pack()
+
+
+/**************************/
+/* FUNCTION DECLARATION   */
+/**************************/
+
+void as10x_cmd_build(struct as10x_cmd_t *pcmd, uint16_t proc_id,
+		      uint16_t cmd_len);
+int as10x_rsp_parse(struct as10x_cmd_t *r, uint16_t proc_id);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* as10x cmd */
+int as10x_cmd_turn_on(as10x_handle_t *phandle);
+int as10x_cmd_turn_off(as10x_handle_t *phandle);
+
+int as10x_cmd_set_tune(as10x_handle_t *phandle,
+		       struct as10x_tune_args *ptune);
+
+int as10x_cmd_get_tune_status(as10x_handle_t *phandle,
+			      struct as10x_tune_status *pstatus);
+
+int as10x_cmd_get_tps(as10x_handle_t *phandle,
+		      struct as10x_tps *ptps);
+
+int as10x_cmd_get_demod_stats(as10x_handle_t  *phandle,
+			      struct as10x_demod_stats *pdemod_stats);
+
+int as10x_cmd_get_impulse_resp(as10x_handle_t *phandle,
+			       uint8_t *is_ready);
+
+/* as10x cmd stream */
+int as10x_cmd_add_PID_filter(as10x_handle_t *phandle,
+			     struct as10x_ts_filter *filter);
+int as10x_cmd_del_PID_filter(as10x_handle_t *phandle,
+			     uint16_t pid_value);
+
+int as10x_cmd_start_streaming(as10x_handle_t *phandle);
+int as10x_cmd_stop_streaming(as10x_handle_t *phandle);
+
+/* as10x cmd cfg */
+int as10x_cmd_set_context(as10x_handle_t *phandle,
+			  uint16_t tag,
+			  uint32_t value);
+int as10x_cmd_get_context(as10x_handle_t *phandle,
+			  uint16_t tag,
+			  uint32_t *pvalue);
+
+int as10x_cmd_eLNA_change_mode(as10x_handle_t *phandle, uint8_t mode);
+int as10x_context_rsp_parse(struct as10x_cmd_t *prsp, uint16_t proc_id);
+#ifdef __cplusplus
+}
+#endif
+#endif
+/* EOF - vim: set textwidth=80 ts=3 sw=3 sts=3 et: */
diff --git a/drivers/staging/media/as102/as10x_cmd_cfg.c b/drivers/staging/media/as102/as10x_cmd_cfg.c
new file mode 100644
index 000000000000..ec6f69fcf399
--- /dev/null
+++ b/drivers/staging/media/as102/as10x_cmd_cfg.c
@@ -0,0 +1,215 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include "as102_drv.h"
+#include "as10x_types.h"
+#include "as10x_cmd.h"
+
+/***************************/
+/* FUNCTION DEFINITION     */
+/***************************/
+
+/**
+ * as10x_cmd_get_context - Send get context command to AS10x
+ * @phandle:   pointer to AS10x handle
+ * @tag:       context tag
+ * @pvalue:    pointer where to store context value read
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_get_context(as10x_handle_t *phandle, uint16_t tag,
+			  uint32_t *pvalue)
+{
+	int  error;
+	struct as10x_cmd_t *pcmd, *prsp;
+
+	ENTER();
+
+	pcmd = phandle->cmd;
+	prsp = phandle->rsp;
+
+	/* prepare command */
+	as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+			sizeof(pcmd->body.context.req));
+
+	/* fill command */
+	pcmd->body.context.req.proc_id = cpu_to_le16(CONTROL_PROC_CONTEXT);
+	pcmd->body.context.req.tag = cpu_to_le16(tag);
+	pcmd->body.context.req.type = cpu_to_le16(GET_CONTEXT_DATA);
+
+	/* send command */
+	if (phandle->ops->xfer_cmd) {
+		error  = phandle->ops->xfer_cmd(phandle,
+						(uint8_t *) pcmd,
+						sizeof(pcmd->body.context.req)
+						+ HEADER_SIZE,
+						(uint8_t *) prsp,
+						sizeof(prsp->body.context.rsp)
+						+ HEADER_SIZE);
+	} else {
+		error = AS10X_CMD_ERROR;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* parse response: context command do not follow the common response */
+	/* structure -> specific handling response parse required            */
+	error = as10x_context_rsp_parse(prsp, CONTROL_PROC_CONTEXT_RSP);
+
+	if (error == 0) {
+		/* Response OK -> get response data */
+		*pvalue = le32_to_cpu(prsp->body.context.rsp.reg_val.u.value32);
+		/* value returned is always a 32-bit value */
+	}
+
+out:
+	LEAVE();
+	return error;
+}
+
+/**
+ * as10x_cmd_set_context - send set context command to AS10x
+ * @phandle:   pointer to AS10x handle
+ * @tag:       context tag
+ * @value:     value to set in context
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_set_context(as10x_handle_t *phandle, uint16_t tag,
+			  uint32_t value)
+{
+	int error;
+	struct as10x_cmd_t *pcmd, *prsp;
+
+	ENTER();
+
+	pcmd = phandle->cmd;
+	prsp = phandle->rsp;
+
+	/* prepare command */
+	as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+			sizeof(pcmd->body.context.req));
+
+	/* fill command */
+	pcmd->body.context.req.proc_id = cpu_to_le16(CONTROL_PROC_CONTEXT);
+	/* pcmd->body.context.req.reg_val.mode initialization is not required */
+	pcmd->body.context.req.reg_val.u.value32 = cpu_to_le32(value);
+	pcmd->body.context.req.tag = cpu_to_le16(tag);
+	pcmd->body.context.req.type = cpu_to_le16(SET_CONTEXT_DATA);
+
+	/* send command */
+	if (phandle->ops->xfer_cmd) {
+		error  = phandle->ops->xfer_cmd(phandle,
+						(uint8_t *) pcmd,
+						sizeof(pcmd->body.context.req)
+						+ HEADER_SIZE,
+						(uint8_t *) prsp,
+						sizeof(prsp->body.context.rsp)
+						+ HEADER_SIZE);
+	} else {
+		error = AS10X_CMD_ERROR;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* parse response: context command do not follow the common response */
+	/* structure -> specific handling response parse required            */
+	error = as10x_context_rsp_parse(prsp, CONTROL_PROC_CONTEXT_RSP);
+
+out:
+	LEAVE();
+	return error;
+}
+
+/**
+ * as10x_cmd_eLNA_change_mode - send eLNA change mode command to AS10x
+ * @phandle:   pointer to AS10x handle
+ * @mode:      mode selected:
+ *	        - ON    : 0x0 => eLNA always ON
+ *	        - OFF   : 0x1 => eLNA always OFF
+ *	        - AUTO  : 0x2 => eLNA follow hysteresis parameters
+ *				 to be ON or OFF
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_eLNA_change_mode(as10x_handle_t *phandle, uint8_t mode)
+{
+	int error;
+	struct as10x_cmd_t *pcmd, *prsp;
+
+	ENTER();
+
+	pcmd = phandle->cmd;
+	prsp = phandle->rsp;
+
+	/* prepare command */
+	as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+			sizeof(pcmd->body.cfg_change_mode.req));
+
+	/* fill command */
+	pcmd->body.cfg_change_mode.req.proc_id =
+		cpu_to_le16(CONTROL_PROC_ELNA_CHANGE_MODE);
+	pcmd->body.cfg_change_mode.req.mode = mode;
+
+	/* send command */
+	if (phandle->ops->xfer_cmd) {
+		error  = phandle->ops->xfer_cmd(phandle, (uint8_t *) pcmd,
+				sizeof(pcmd->body.cfg_change_mode.req)
+				+ HEADER_SIZE, (uint8_t *) prsp,
+				sizeof(prsp->body.cfg_change_mode.rsp)
+				+ HEADER_SIZE);
+	} else {
+		error = AS10X_CMD_ERROR;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* parse response */
+	error = as10x_rsp_parse(prsp, CONTROL_PROC_ELNA_CHANGE_MODE_RSP);
+
+out:
+	LEAVE();
+	return error;
+}
+
+/**
+ * as10x_context_rsp_parse - Parse context command response
+ * @prsp:       pointer to AS10x command response buffer
+ * @proc_id:    id of the command
+ *
+ * Since the contex command reponse does not follow the common
+ * response, a specific parse function is required.
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_context_rsp_parse(struct as10x_cmd_t *prsp, uint16_t proc_id)
+{
+	int err;
+
+	err = prsp->body.context.rsp.error;
+
+	if ((err == 0) &&
+	    (le16_to_cpu(prsp->body.context.rsp.proc_id) == proc_id)) {
+		return 0;
+	}
+	return AS10X_CMD_ERROR;
+}
diff --git a/drivers/staging/media/as102/as10x_cmd_stream.c b/drivers/staging/media/as102/as10x_cmd_stream.c
new file mode 100644
index 000000000000..045c70683193
--- /dev/null
+++ b/drivers/staging/media/as102/as10x_cmd_stream.c
@@ -0,0 +1,223 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include "as102_drv.h"
+#include "as10x_cmd.h"
+
+/**
+ * as10x_cmd_add_PID_filter - send add filter command to AS10x
+ * @phandle:   pointer to AS10x handle
+ * @filter:    TSFilter filter for DVB-T
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_add_PID_filter(as10x_handle_t *phandle,
+			     struct as10x_ts_filter *filter)
+{
+	int error;
+	struct as10x_cmd_t *pcmd, *prsp;
+
+	ENTER();
+
+	pcmd = phandle->cmd;
+	prsp = phandle->rsp;
+
+	/* prepare command */
+	as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+			sizeof(pcmd->body.add_pid_filter.req));
+
+	/* fill command */
+	pcmd->body.add_pid_filter.req.proc_id =
+		cpu_to_le16(CONTROL_PROC_SETFILTER);
+	pcmd->body.add_pid_filter.req.pid = cpu_to_le16(filter->pid);
+	pcmd->body.add_pid_filter.req.stream_type = filter->type;
+
+	if (filter->idx < 16)
+		pcmd->body.add_pid_filter.req.idx = filter->idx;
+	else
+		pcmd->body.add_pid_filter.req.idx = 0xFF;
+
+	/* send command */
+	if (phandle->ops->xfer_cmd) {
+		error = phandle->ops->xfer_cmd(phandle, (uint8_t *) pcmd,
+				sizeof(pcmd->body.add_pid_filter.req)
+				+ HEADER_SIZE, (uint8_t *) prsp,
+				sizeof(prsp->body.add_pid_filter.rsp)
+				+ HEADER_SIZE);
+	} else {
+		error = AS10X_CMD_ERROR;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* parse response */
+	error = as10x_rsp_parse(prsp, CONTROL_PROC_SETFILTER_RSP);
+
+	if (error == 0) {
+		/* Response OK -> get response data */
+		filter->idx = prsp->body.add_pid_filter.rsp.filter_id;
+	}
+
+out:
+	LEAVE();
+	return error;
+}
+
+/**
+ * as10x_cmd_del_PID_filter - Send delete filter command to AS10x
+ * @phandle:      pointer to AS10x handle
+ * @pid_value:    PID to delete
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_del_PID_filter(as10x_handle_t *phandle,
+			     uint16_t pid_value)
+{
+	int error;
+	struct as10x_cmd_t *pcmd, *prsp;
+
+	ENTER();
+
+	pcmd = phandle->cmd;
+	prsp = phandle->rsp;
+
+	/* prepare command */
+	as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+			sizeof(pcmd->body.del_pid_filter.req));
+
+	/* fill command */
+	pcmd->body.del_pid_filter.req.proc_id =
+		cpu_to_le16(CONTROL_PROC_REMOVEFILTER);
+	pcmd->body.del_pid_filter.req.pid = cpu_to_le16(pid_value);
+
+	/* send command */
+	if (phandle->ops->xfer_cmd) {
+		error = phandle->ops->xfer_cmd(phandle, (uint8_t *) pcmd,
+				sizeof(pcmd->body.del_pid_filter.req)
+				+ HEADER_SIZE, (uint8_t *) prsp,
+				sizeof(prsp->body.del_pid_filter.rsp)
+				+ HEADER_SIZE);
+	} else {
+		error = AS10X_CMD_ERROR;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* parse response */
+	error = as10x_rsp_parse(prsp, CONTROL_PROC_REMOVEFILTER_RSP);
+
+out:
+	LEAVE();
+	return error;
+}
+
+/**
+ * as10x_cmd_start_streaming - Send start streaming command to AS10x
+ * @phandle:   pointer to AS10x handle
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_start_streaming(as10x_handle_t *phandle)
+{
+	int error;
+	struct as10x_cmd_t *pcmd, *prsp;
+
+	ENTER();
+
+	pcmd = phandle->cmd;
+	prsp = phandle->rsp;
+
+	/* prepare command */
+	as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+			sizeof(pcmd->body.start_streaming.req));
+
+	/* fill command */
+	pcmd->body.start_streaming.req.proc_id =
+		cpu_to_le16(CONTROL_PROC_START_STREAMING);
+
+	/* send command */
+	if (phandle->ops->xfer_cmd) {
+		error = phandle->ops->xfer_cmd(phandle, (uint8_t *) pcmd,
+				sizeof(pcmd->body.start_streaming.req)
+				+ HEADER_SIZE, (uint8_t *) prsp,
+				sizeof(prsp->body.start_streaming.rsp)
+				+ HEADER_SIZE);
+	} else {
+		error = AS10X_CMD_ERROR;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* parse response */
+	error = as10x_rsp_parse(prsp, CONTROL_PROC_START_STREAMING_RSP);
+
+out:
+	LEAVE();
+	return error;
+}
+
+/**
+ * as10x_cmd_stop_streaming - Send stop streaming command to AS10x
+ * @phandle:   pointer to AS10x handle
+ *
+ * Return 0 on success or negative value in case of error.
+ */
+int as10x_cmd_stop_streaming(as10x_handle_t *phandle)
+{
+	int8_t error;
+	struct as10x_cmd_t *pcmd, *prsp;
+
+	ENTER();
+
+	pcmd = phandle->cmd;
+	prsp = phandle->rsp;
+
+	/* prepare command */
+	as10x_cmd_build(pcmd, (++phandle->cmd_xid),
+			sizeof(pcmd->body.stop_streaming.req));
+
+	/* fill command */
+	pcmd->body.stop_streaming.req.proc_id =
+		cpu_to_le16(CONTROL_PROC_STOP_STREAMING);
+
+	/* send command */
+	if (phandle->ops->xfer_cmd) {
+		error = phandle->ops->xfer_cmd(phandle, (uint8_t *) pcmd,
+				sizeof(pcmd->body.stop_streaming.req)
+				+ HEADER_SIZE, (uint8_t *) prsp,
+				sizeof(prsp->body.stop_streaming.rsp)
+				+ HEADER_SIZE);
+	} else {
+		error = AS10X_CMD_ERROR;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* parse response */
+	error = as10x_rsp_parse(prsp, CONTROL_PROC_STOP_STREAMING_RSP);
+
+out:
+	LEAVE();
+	return error;
+}
diff --git a/drivers/staging/media/as102/as10x_handle.h b/drivers/staging/media/as102/as10x_handle.h
new file mode 100644
index 000000000000..4f01a76e9829
--- /dev/null
+++ b/drivers/staging/media/as102/as10x_handle.h
@@ -0,0 +1,58 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifdef __KERNEL__
+struct as102_bus_adapter_t;
+struct as102_dev_t;
+
+#define as10x_handle_t struct as102_bus_adapter_t
+#include "as10x_cmd.h"
+
+/* values for "mode" field */
+#define REGMODE8         8
+#define REGMODE16        16
+#define REGMODE32        32
+
+struct as102_priv_ops_t {
+	int (*upload_fw_pkt) (struct as102_bus_adapter_t *bus_adap,
+			      unsigned char *buf, int buflen, int swap32);
+
+	int (*send_cmd) (struct as102_bus_adapter_t *bus_adap,
+			 unsigned char *buf, int buflen);
+
+	int (*xfer_cmd) (struct as102_bus_adapter_t *bus_adap,
+			 unsigned char *send_buf, int send_buf_len,
+			 unsigned char *recv_buf, int recv_buf_len);
+/*
+	int (*pid_filter) (struct as102_bus_adapter_t *bus_adap,
+			   int index, u16 pid, int onoff);
+*/
+	int (*start_stream) (struct as102_dev_t *dev);
+	void (*stop_stream) (struct as102_dev_t *dev);
+
+	int (*reset_target) (struct as102_bus_adapter_t *bus_adap);
+
+	int (*read_write)(struct as102_bus_adapter_t *bus_adap, uint8_t mode,
+			  uint32_t rd_addr, uint16_t rd_len,
+			  uint32_t wr_addr, uint16_t wr_len);
+
+	int (*as102_read_ep2) (struct as102_bus_adapter_t *bus_adap,
+			       unsigned char *recv_buf,
+			       int recv_buf_len);
+};
+#endif
diff --git a/drivers/staging/media/as102/as10x_types.h b/drivers/staging/media/as102/as10x_types.h
new file mode 100644
index 000000000000..3dedb3c1420a
--- /dev/null
+++ b/drivers/staging/media/as102/as10x_types.h
@@ -0,0 +1,198 @@
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _AS10X_TYPES_H_
+#define _AS10X_TYPES_H_
+
+#include "as10x_handle.h"
+
+/*********************************/
+/*       MACRO DEFINITIONS       */
+/*********************************/
+
+/* bandwidth constant values */
+#define BW_5_MHZ           0x00
+#define BW_6_MHZ           0x01
+#define BW_7_MHZ           0x02
+#define BW_8_MHZ           0x03
+
+/* hierarchy priority selection values */
+#define HIER_NO_PRIORITY   0x00
+#define HIER_LOW_PRIORITY  0x01
+#define HIER_HIGH_PRIORITY 0x02
+
+/* constellation available values */
+#define CONST_QPSK         0x00
+#define CONST_QAM16        0x01
+#define CONST_QAM64        0x02
+#define CONST_UNKNOWN      0xFF
+
+/* hierarchy available values */
+#define HIER_NONE         0x00
+#define HIER_ALPHA_1      0x01
+#define HIER_ALPHA_2      0x02
+#define HIER_ALPHA_4      0x03
+#define HIER_UNKNOWN      0xFF
+
+/* interleaving available values */
+#define INTLV_NATIVE      0x00
+#define INTLV_IN_DEPTH    0x01
+#define INTLV_UNKNOWN     0xFF
+
+/* code rate available values */
+#define CODE_RATE_1_2     0x00
+#define CODE_RATE_2_3     0x01
+#define CODE_RATE_3_4     0x02
+#define CODE_RATE_5_6     0x03
+#define CODE_RATE_7_8     0x04
+#define CODE_RATE_UNKNOWN 0xFF
+
+/* guard interval available values */
+#define GUARD_INT_1_32    0x00
+#define GUARD_INT_1_16    0x01
+#define GUARD_INT_1_8     0x02
+#define GUARD_INT_1_4     0x03
+#define GUARD_UNKNOWN     0xFF
+
+/* transmission mode available values */
+#define TRANS_MODE_2K      0x00
+#define TRANS_MODE_8K      0x01
+#define TRANS_MODE_4K      0x02
+#define TRANS_MODE_UNKNOWN 0xFF
+
+/* DVBH signalling available values */
+#define TIMESLICING_PRESENT   0x01
+#define MPE_FEC_PRESENT       0x02
+
+/* tune state available */
+#define TUNE_STATUS_NOT_TUNED       0x00
+#define TUNE_STATUS_IDLE            0x01
+#define TUNE_STATUS_LOCKING         0x02
+#define TUNE_STATUS_SIGNAL_DVB_OK   0x03
+#define TUNE_STATUS_STREAM_DETECTED 0x04
+#define TUNE_STATUS_STREAM_TUNED    0x05
+#define TUNE_STATUS_ERROR           0xFF
+
+/* available TS FID filter types */
+#define TS_PID_TYPE_TS       0
+#define TS_PID_TYPE_PSI_SI   1
+#define TS_PID_TYPE_MPE      2
+
+/* number of echos available */
+#define MAX_ECHOS   15
+
+/* Context types */
+#define CONTEXT_LNA                   1010
+#define CONTEXT_ELNA_HYSTERESIS       4003
+#define CONTEXT_ELNA_GAIN             4004
+#define CONTEXT_MER_THRESHOLD         5005
+#define CONTEXT_MER_OFFSET            5006
+#define CONTEXT_IR_STATE              7000
+#define CONTEXT_TSOUT_MSB_FIRST       7004
+#define CONTEXT_TSOUT_FALLING_EDGE    7005
+
+/* Configuration modes */
+#define CFG_MODE_ON     0
+#define CFG_MODE_OFF    1
+#define CFG_MODE_AUTO   2
+
+#pragma pack(1)
+struct as10x_tps {
+   uint8_t constellation;
+   uint8_t hierarchy;
+   uint8_t interleaving_mode;
+   uint8_t code_rate_HP;
+   uint8_t code_rate_LP;
+   uint8_t guard_interval;
+   uint8_t transmission_mode;
+   uint8_t DVBH_mask_HP;
+   uint8_t DVBH_mask_LP;
+   uint16_t cell_ID;
+};
+
+struct as10x_tune_args {
+   /* frequency */
+   uint32_t freq;
+   /* bandwidth */
+   uint8_t bandwidth;
+   /* hierarchy selection */
+   uint8_t hier_select;
+   /* constellation */
+   uint8_t constellation;
+   /* hierarchy */
+   uint8_t hierarchy;
+   /* interleaving mode */
+   uint8_t interleaving_mode;
+   /* code rate */
+   uint8_t code_rate;
+   /* guard interval */
+   uint8_t guard_interval;
+   /* transmission mode */
+   uint8_t transmission_mode;
+};
+
+struct as10x_tune_status {
+   /* tune status */
+   uint8_t tune_state;
+   /* signal strength */
+   int16_t signal_strength;
+   /* packet error rate 10^-4 */
+   uint16_t PER;
+   /* bit error rate 10^-4 */
+   uint16_t BER;
+};
+
+struct as10x_demod_stats {
+   /* frame counter */
+   uint32_t frame_count;
+   /* Bad frame counter */
+   uint32_t bad_frame_count;
+   /* Number of wrong bytes fixed by Reed-Solomon */
+   uint32_t bytes_fixed_by_rs;
+   /* Averaged MER */
+   uint16_t mer;
+   /* statistics calculation state indicator (started or not) */
+   uint8_t has_started;
+};
+
+struct as10x_ts_filter {
+   uint16_t pid;  /** valid PID value 0x00 : 0x2000 */
+   uint8_t  type; /** Red TS_PID_TYPE_<N> values */
+   uint8_t  idx;  /** index in filtering table */
+};
+
+struct as10x_register_value {
+   uint8_t       mode;
+   union {
+      uint8_t    value8;    /* 8 bit value */
+      uint16_t   value16;   /* 16 bit value */
+      uint32_t   value32;   /* 32 bit value */
+   }u;
+};
+
+#pragma pack()
+
+struct as10x_register_addr {
+   /* register addr */
+   uint32_t addr;
+   /* register mode access */
+   uint8_t mode;
+};
+
+
+#endif
diff --git a/drivers/staging/cxd2099/Kconfig b/drivers/staging/media/cxd2099/Kconfig
index b48aefddc84c..b48aefddc84c 100644
--- a/drivers/staging/cxd2099/Kconfig
+++ b/drivers/staging/media/cxd2099/Kconfig
diff --git a/drivers/staging/cxd2099/Makefile b/drivers/staging/media/cxd2099/Makefile
index 64cfc77be357..64cfc77be357 100644
--- a/drivers/staging/cxd2099/Makefile
+++ b/drivers/staging/media/cxd2099/Makefile
diff --git a/drivers/staging/cxd2099/TODO b/drivers/staging/media/cxd2099/TODO
index 375bb6f8ee2c..375bb6f8ee2c 100644
--- a/drivers/staging/cxd2099/TODO
+++ b/drivers/staging/media/cxd2099/TODO
diff --git a/drivers/staging/cxd2099/cxd2099.c b/drivers/staging/media/cxd2099/cxd2099.c
index 1c04185bcfd7..1c04185bcfd7 100644
--- a/drivers/staging/cxd2099/cxd2099.c
+++ b/drivers/staging/media/cxd2099/cxd2099.c
diff --git a/drivers/staging/cxd2099/cxd2099.h b/drivers/staging/media/cxd2099/cxd2099.h
index 19c588a59588..19c588a59588 100644
--- a/drivers/staging/cxd2099/cxd2099.h
+++ b/drivers/staging/media/cxd2099/cxd2099.h
diff --git a/drivers/staging/dt3155v4l/Kconfig b/drivers/staging/media/dt3155v4l/Kconfig
index 226a1ca90b3c..226a1ca90b3c 100644
--- a/drivers/staging/dt3155v4l/Kconfig
+++ b/drivers/staging/media/dt3155v4l/Kconfig
diff --git a/drivers/staging/dt3155v4l/Makefile b/drivers/staging/media/dt3155v4l/Makefile
index ce7a3ec2faf3..ce7a3ec2faf3 100644
--- a/drivers/staging/dt3155v4l/Makefile
+++ b/drivers/staging/media/dt3155v4l/Makefile
diff --git a/drivers/staging/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c
index 04e93c49f03a..04e93c49f03a 100644
--- a/drivers/staging/dt3155v4l/dt3155v4l.c
+++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c
diff --git a/drivers/staging/dt3155v4l/dt3155v4l.h b/drivers/staging/media/dt3155v4l/dt3155v4l.h
index 2e4f89d402e4..2e4f89d402e4 100644
--- a/drivers/staging/dt3155v4l/dt3155v4l.h
+++ b/drivers/staging/media/dt3155v4l/dt3155v4l.h
diff --git a/drivers/staging/easycap/Kconfig b/drivers/staging/media/easycap/Kconfig
index a425a6f9cdca..a425a6f9cdca 100644
--- a/drivers/staging/easycap/Kconfig
+++ b/drivers/staging/media/easycap/Kconfig
diff --git a/drivers/staging/easycap/Makefile b/drivers/staging/media/easycap/Makefile
index a34e75f59c18..a34e75f59c18 100644
--- a/drivers/staging/easycap/Makefile
+++ b/drivers/staging/media/easycap/Makefile
diff --git a/drivers/staging/easycap/README b/drivers/staging/media/easycap/README
index 796b032384bd..796b032384bd 100644
--- a/drivers/staging/easycap/README
+++ b/drivers/staging/media/easycap/README
diff --git a/drivers/staging/easycap/easycap.h b/drivers/staging/media/easycap/easycap.h
index 7b256a948c27..7b256a948c27 100644
--- a/drivers/staging/easycap/easycap.h
+++ b/drivers/staging/media/easycap/easycap.h
diff --git a/drivers/staging/easycap/easycap_ioctl.c b/drivers/staging/media/easycap/easycap_ioctl.c
index c99addfb6242..c99addfb6242 100644
--- a/drivers/staging/easycap/easycap_ioctl.c
+++ b/drivers/staging/media/easycap/easycap_ioctl.c
diff --git a/drivers/staging/easycap/easycap_low.c b/drivers/staging/media/easycap/easycap_low.c
index 0385735ac6df..0385735ac6df 100644
--- a/drivers/staging/easycap/easycap_low.c
+++ b/drivers/staging/media/easycap/easycap_low.c
diff --git a/drivers/staging/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c
index a45c0b507067..a45c0b507067 100644
--- a/drivers/staging/easycap/easycap_main.c
+++ b/drivers/staging/media/easycap/easycap_main.c
diff --git a/drivers/staging/easycap/easycap_settings.c b/drivers/staging/media/easycap/easycap_settings.c
index 70f59b13c34d..70f59b13c34d 100644
--- a/drivers/staging/easycap/easycap_settings.c
+++ b/drivers/staging/media/easycap/easycap_settings.c
diff --git a/drivers/staging/easycap/easycap_sound.c b/drivers/staging/media/easycap/easycap_sound.c
index b22bb39b5f69..b22bb39b5f69 100644
--- a/drivers/staging/easycap/easycap_sound.c
+++ b/drivers/staging/media/easycap/easycap_sound.c
diff --git a/drivers/staging/easycap/easycap_testcard.c b/drivers/staging/media/easycap/easycap_testcard.c
index 0f71470ace39..0f71470ace39 100644
--- a/drivers/staging/easycap/easycap_testcard.c
+++ b/drivers/staging/media/easycap/easycap_testcard.c
diff --git a/drivers/staging/go7007/Kconfig b/drivers/staging/media/go7007/Kconfig
index 7dfb2815b9ec..7dfb2815b9ec 100644
--- a/drivers/staging/go7007/Kconfig
+++ b/drivers/staging/media/go7007/Kconfig
diff --git a/drivers/staging/go7007/Makefile b/drivers/staging/media/go7007/Makefile
index 6ee837c56706..6ee837c56706 100644
--- a/drivers/staging/go7007/Makefile
+++ b/drivers/staging/media/go7007/Makefile
diff --git a/drivers/staging/go7007/README b/drivers/staging/media/go7007/README
index 48f447637817..48f447637817 100644
--- a/drivers/staging/go7007/README
+++ b/drivers/staging/media/go7007/README
diff --git a/drivers/staging/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c
index 6c9279a6d606..6c9279a6d606 100644
--- a/drivers/staging/go7007/go7007-driver.c
+++ b/drivers/staging/media/go7007/go7007-driver.c
diff --git a/drivers/staging/go7007/go7007-fw.c b/drivers/staging/media/go7007/go7007-fw.c
index c9a6409edfe3..c9a6409edfe3 100644
--- a/drivers/staging/go7007/go7007-fw.c
+++ b/drivers/staging/media/go7007/go7007-fw.c
diff --git a/drivers/staging/go7007/go7007-i2c.c b/drivers/staging/media/go7007/go7007-i2c.c
index b8cfa1a6eaeb..b8cfa1a6eaeb 100644
--- a/drivers/staging/go7007/go7007-i2c.c
+++ b/drivers/staging/media/go7007/go7007-i2c.c
diff --git a/drivers/staging/go7007/go7007-priv.h b/drivers/staging/media/go7007/go7007-priv.h
index b58c394c6555..b58c394c6555 100644
--- a/drivers/staging/go7007/go7007-priv.h
+++ b/drivers/staging/media/go7007/go7007-priv.h
diff --git a/drivers/staging/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c
index 3db3b0a91cc1..3db3b0a91cc1 100644
--- a/drivers/staging/go7007/go7007-usb.c
+++ b/drivers/staging/media/go7007/go7007-usb.c
diff --git a/drivers/staging/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c
index 2b27d8da70a2..2b27d8da70a2 100644
--- a/drivers/staging/go7007/go7007-v4l2.c
+++ b/drivers/staging/media/go7007/go7007-v4l2.c
diff --git a/drivers/staging/go7007/go7007.h b/drivers/staging/media/go7007/go7007.h
index 7399c915a934..7399c915a934 100644
--- a/drivers/staging/go7007/go7007.h
+++ b/drivers/staging/media/go7007/go7007.h
diff --git a/drivers/staging/go7007/go7007.txt b/drivers/staging/media/go7007/go7007.txt
index 9db1f3952fd2..9db1f3952fd2 100644
--- a/drivers/staging/go7007/go7007.txt
+++ b/drivers/staging/media/go7007/go7007.txt
diff --git a/drivers/staging/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c
index e7736a915530..e7736a915530 100644
--- a/drivers/staging/go7007/s2250-board.c
+++ b/drivers/staging/media/go7007/s2250-board.c
diff --git a/drivers/staging/go7007/s2250-loader.c b/drivers/staging/media/go7007/s2250-loader.c
index 4e132519e253..4e132519e253 100644
--- a/drivers/staging/go7007/s2250-loader.c
+++ b/drivers/staging/media/go7007/s2250-loader.c
diff --git a/drivers/staging/go7007/s2250-loader.h b/drivers/staging/media/go7007/s2250-loader.h
index b7c301af16cc..b7c301af16cc 100644
--- a/drivers/staging/go7007/s2250-loader.h
+++ b/drivers/staging/media/go7007/s2250-loader.h
diff --git a/drivers/staging/go7007/saa7134-go7007.c b/drivers/staging/media/go7007/saa7134-go7007.c
index cf7c34a99459..cf7c34a99459 100644
--- a/drivers/staging/go7007/saa7134-go7007.c
+++ b/drivers/staging/media/go7007/saa7134-go7007.c
diff --git a/drivers/staging/go7007/snd-go7007.c b/drivers/staging/media/go7007/snd-go7007.c
index deac938d8505..deac938d8505 100644
--- a/drivers/staging/go7007/snd-go7007.c
+++ b/drivers/staging/media/go7007/snd-go7007.c
diff --git a/drivers/staging/go7007/wis-i2c.h b/drivers/staging/media/go7007/wis-i2c.h
index 3c2b9be455df..3c2b9be455df 100644
--- a/drivers/staging/go7007/wis-i2c.h
+++ b/drivers/staging/media/go7007/wis-i2c.h
diff --git a/drivers/staging/go7007/wis-ov7640.c b/drivers/staging/media/go7007/wis-ov7640.c
index 6bc9470fecb6..6bc9470fecb6 100644
--- a/drivers/staging/go7007/wis-ov7640.c
+++ b/drivers/staging/media/go7007/wis-ov7640.c
diff --git a/drivers/staging/go7007/wis-saa7113.c b/drivers/staging/media/go7007/wis-saa7113.c
index 05e0e1083864..05e0e1083864 100644
--- a/drivers/staging/go7007/wis-saa7113.c
+++ b/drivers/staging/media/go7007/wis-saa7113.c
diff --git a/drivers/staging/go7007/wis-saa7115.c b/drivers/staging/media/go7007/wis-saa7115.c
index 46cff59e28b7..46cff59e28b7 100644
--- a/drivers/staging/go7007/wis-saa7115.c
+++ b/drivers/staging/media/go7007/wis-saa7115.c
diff --git a/drivers/staging/go7007/wis-sony-tuner.c b/drivers/staging/media/go7007/wis-sony-tuner.c
index 8f1b7d4f6a2e..8f1b7d4f6a2e 100644
--- a/drivers/staging/go7007/wis-sony-tuner.c
+++ b/drivers/staging/media/go7007/wis-sony-tuner.c
diff --git a/drivers/staging/go7007/wis-tw2804.c b/drivers/staging/media/go7007/wis-tw2804.c
index 9134f03e3cf0..9134f03e3cf0 100644
--- a/drivers/staging/go7007/wis-tw2804.c
+++ b/drivers/staging/media/go7007/wis-tw2804.c
diff --git a/drivers/staging/go7007/wis-tw9903.c b/drivers/staging/media/go7007/wis-tw9903.c
index 9230f4a80529..9230f4a80529 100644
--- a/drivers/staging/go7007/wis-tw9903.c
+++ b/drivers/staging/media/go7007/wis-tw9903.c
diff --git a/drivers/staging/go7007/wis-uda1342.c b/drivers/staging/media/go7007/wis-uda1342.c
index 0127be2f3be0..0127be2f3be0 100644
--- a/drivers/staging/go7007/wis-uda1342.c
+++ b/drivers/staging/media/go7007/wis-uda1342.c
diff --git a/drivers/staging/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig
index 526ec0fc2f04..526ec0fc2f04 100644
--- a/drivers/staging/lirc/Kconfig
+++ b/drivers/staging/media/lirc/Kconfig
diff --git a/drivers/staging/lirc/Makefile b/drivers/staging/media/lirc/Makefile
index d76b0fa2af53..d76b0fa2af53 100644
--- a/drivers/staging/lirc/Makefile
+++ b/drivers/staging/media/lirc/Makefile
diff --git a/drivers/staging/lirc/TODO b/drivers/staging/media/lirc/TODO
index b6cb593f55c6..b6cb593f55c6 100644
--- a/drivers/staging/lirc/TODO
+++ b/drivers/staging/media/lirc/TODO
diff --git a/drivers/staging/lirc/TODO.lirc_zilog b/drivers/staging/media/lirc/TODO.lirc_zilog
index a97800a8e127..a97800a8e127 100644
--- a/drivers/staging/lirc/TODO.lirc_zilog
+++ b/drivers/staging/media/lirc/TODO.lirc_zilog
diff --git a/drivers/staging/lirc/lirc_bt829.c b/drivers/staging/media/lirc/lirc_bt829.c
index c5a0d27a02dc..c5a0d27a02dc 100644
--- a/drivers/staging/lirc/lirc_bt829.c
+++ b/drivers/staging/media/lirc/lirc_bt829.c
diff --git a/drivers/staging/lirc/lirc_ene0100.h b/drivers/staging/media/lirc/lirc_ene0100.h
index 06bebd6acc46..06bebd6acc46 100644
--- a/drivers/staging/lirc/lirc_ene0100.h
+++ b/drivers/staging/media/lirc/lirc_ene0100.h
diff --git a/drivers/staging/lirc/lirc_igorplugusb.c b/drivers/staging/media/lirc/lirc_igorplugusb.c
index 0dc2c2b22c2b..0dc2c2b22c2b 100644
--- a/drivers/staging/lirc/lirc_igorplugusb.c
+++ b/drivers/staging/media/lirc/lirc_igorplugusb.c
diff --git a/drivers/staging/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c
index f5308d5929c6..f5308d5929c6 100644
--- a/drivers/staging/lirc/lirc_imon.c
+++ b/drivers/staging/media/lirc/lirc_imon.c
diff --git a/drivers/staging/lirc/lirc_parallel.c b/drivers/staging/media/lirc/lirc_parallel.c
index 792aac0a8e7b..792aac0a8e7b 100644
--- a/drivers/staging/lirc/lirc_parallel.c
+++ b/drivers/staging/media/lirc/lirc_parallel.c
diff --git a/drivers/staging/lirc/lirc_parallel.h b/drivers/staging/media/lirc/lirc_parallel.h
index 4bed6afe0632..4bed6afe0632 100644
--- a/drivers/staging/lirc/lirc_parallel.h
+++ b/drivers/staging/media/lirc/lirc_parallel.h
diff --git a/drivers/staging/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c
index a2d18b0aa048..a2d18b0aa048 100644
--- a/drivers/staging/lirc/lirc_sasem.c
+++ b/drivers/staging/media/lirc/lirc_sasem.c
diff --git a/drivers/staging/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c
index 8a060a8a7224..8a060a8a7224 100644
--- a/drivers/staging/lirc/lirc_serial.c
+++ b/drivers/staging/media/lirc/lirc_serial.c
diff --git a/drivers/staging/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c
index 6903d3992eca..6903d3992eca 100644
--- a/drivers/staging/lirc/lirc_sir.c
+++ b/drivers/staging/media/lirc/lirc_sir.c
diff --git a/drivers/staging/lirc/lirc_ttusbir.c b/drivers/staging/media/lirc/lirc_ttusbir.c
index e4b329b8cafd..e4b329b8cafd 100644
--- a/drivers/staging/lirc/lirc_ttusbir.c
+++ b/drivers/staging/media/lirc/lirc_ttusbir.c
diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c
index 0302d82a12f7..0302d82a12f7 100644
--- a/drivers/staging/lirc/lirc_zilog.c
+++ b/drivers/staging/media/lirc/lirc_zilog.c
diff --git a/drivers/staging/solo6x10/Kconfig b/drivers/staging/media/solo6x10/Kconfig
index 03dcac4ea4d0..03dcac4ea4d0 100644
--- a/drivers/staging/solo6x10/Kconfig
+++ b/drivers/staging/media/solo6x10/Kconfig
diff --git a/drivers/staging/solo6x10/Makefile b/drivers/staging/media/solo6x10/Makefile
index 72816cf16704..72816cf16704 100644
--- a/drivers/staging/solo6x10/Makefile
+++ b/drivers/staging/media/solo6x10/Makefile
diff --git a/drivers/staging/solo6x10/TODO b/drivers/staging/media/solo6x10/TODO
index 7e6c4fa130df..7e6c4fa130df 100644
--- a/drivers/staging/solo6x10/TODO
+++ b/drivers/staging/media/solo6x10/TODO
diff --git a/drivers/staging/solo6x10/core.c b/drivers/staging/media/solo6x10/core.c
index f974f6412ad7..f974f6412ad7 100644
--- a/drivers/staging/solo6x10/core.c
+++ b/drivers/staging/media/solo6x10/core.c
diff --git a/drivers/staging/solo6x10/disp.c b/drivers/staging/media/solo6x10/disp.c
index 884c0eb757c4..884c0eb757c4 100644
--- a/drivers/staging/solo6x10/disp.c
+++ b/drivers/staging/media/solo6x10/disp.c
diff --git a/drivers/staging/solo6x10/enc.c b/drivers/staging/media/solo6x10/enc.c
index de502599bb19..de502599bb19 100644
--- a/drivers/staging/solo6x10/enc.c
+++ b/drivers/staging/media/solo6x10/enc.c
diff --git a/drivers/staging/solo6x10/g723.c b/drivers/staging/media/solo6x10/g723.c
index 59274bfca95b..59274bfca95b 100644
--- a/drivers/staging/solo6x10/g723.c
+++ b/drivers/staging/media/solo6x10/g723.c
diff --git a/drivers/staging/solo6x10/gpio.c b/drivers/staging/media/solo6x10/gpio.c
index 0925e6f33a99..0925e6f33a99 100644
--- a/drivers/staging/solo6x10/gpio.c
+++ b/drivers/staging/media/solo6x10/gpio.c
diff --git a/drivers/staging/solo6x10/i2c.c b/drivers/staging/media/solo6x10/i2c.c
index ef95a500b4da..ef95a500b4da 100644
--- a/drivers/staging/solo6x10/i2c.c
+++ b/drivers/staging/media/solo6x10/i2c.c
diff --git a/drivers/staging/solo6x10/jpeg.h b/drivers/staging/media/solo6x10/jpeg.h
index 50defec318cc..50defec318cc 100644
--- a/drivers/staging/solo6x10/jpeg.h
+++ b/drivers/staging/media/solo6x10/jpeg.h
diff --git a/drivers/staging/solo6x10/offsets.h b/drivers/staging/media/solo6x10/offsets.h
index 3d7e569f1cf8..3d7e569f1cf8 100644
--- a/drivers/staging/solo6x10/offsets.h
+++ b/drivers/staging/media/solo6x10/offsets.h
diff --git a/drivers/staging/solo6x10/osd-font.h b/drivers/staging/media/solo6x10/osd-font.h
index 591e0e82e0e8..591e0e82e0e8 100644
--- a/drivers/staging/solo6x10/osd-font.h
+++ b/drivers/staging/media/solo6x10/osd-font.h
diff --git a/drivers/staging/solo6x10/p2m.c b/drivers/staging/media/solo6x10/p2m.c
index 56210f0fc5ec..56210f0fc5ec 100644
--- a/drivers/staging/solo6x10/p2m.c
+++ b/drivers/staging/media/solo6x10/p2m.c
diff --git a/drivers/staging/solo6x10/registers.h b/drivers/staging/media/solo6x10/registers.h
index aca544472c93..aca544472c93 100644
--- a/drivers/staging/solo6x10/registers.h
+++ b/drivers/staging/media/solo6x10/registers.h
diff --git a/drivers/staging/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h
index abee7213202f..abee7213202f 100644
--- a/drivers/staging/solo6x10/solo6x10.h
+++ b/drivers/staging/media/solo6x10/solo6x10.h
diff --git a/drivers/staging/solo6x10/tw28.c b/drivers/staging/media/solo6x10/tw28.c
index db56b42c56c6..db56b42c56c6 100644
--- a/drivers/staging/solo6x10/tw28.c
+++ b/drivers/staging/media/solo6x10/tw28.c
diff --git a/drivers/staging/solo6x10/tw28.h b/drivers/staging/media/solo6x10/tw28.h
index a44a03afbd30..a44a03afbd30 100644
--- a/drivers/staging/solo6x10/tw28.h
+++ b/drivers/staging/media/solo6x10/tw28.h
diff --git a/drivers/staging/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c
index bee7280bbed9..bee7280bbed9 100644
--- a/drivers/staging/solo6x10/v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/v4l2-enc.c
diff --git a/drivers/staging/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c
index 571c3a348d30..571c3a348d30 100644
--- a/drivers/staging/solo6x10/v4l2.c
+++ b/drivers/staging/media/solo6x10/v4l2.c
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 225560c1a10f..4b752d5ee80e 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -653,6 +653,10 @@ struct v4l2_buffer {
 #define V4L2_BUF_FLAG_ERROR	0x0040
 #define V4L2_BUF_FLAG_TIMECODE	0x0100	/* timecode field is valid */
 #define V4L2_BUF_FLAG_INPUT     0x0200  /* input field is valid */
+#define V4L2_BUF_FLAG_PREPARED	0x0400	/* Buffer is prepared for queuing */
+/* Cache handling flags */
+#define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE	0x0800
+#define V4L2_BUF_FLAG_NO_CACHE_CLEAN		0x1000
 
 /*
  *	O V E R L A Y   P R E V I E W
@@ -1165,6 +1169,7 @@ enum v4l2_power_line_frequency {
 	V4L2_CID_POWER_LINE_FREQUENCY_DISABLED	= 0,
 	V4L2_CID_POWER_LINE_FREQUENCY_50HZ	= 1,
 	V4L2_CID_POWER_LINE_FREQUENCY_60HZ	= 2,
+	V4L2_CID_POWER_LINE_FREQUENCY_AUTO	= 3,
 };
 #define V4L2_CID_HUE_AUTO			(V4L2_CID_BASE+25)
 #define V4L2_CID_WHITE_BALANCE_TEMPERATURE	(V4L2_CID_BASE+26)
@@ -2138,6 +2143,23 @@ struct v4l2_dbg_chip_ident {
 	__u32 revision;    /* chip revision, chip specific */
 } __attribute__ ((packed));
 
+/**
+ * struct v4l2_create_buffers - VIDIOC_CREATE_BUFS argument
+ * @index:	on return, index of the first created buffer
+ * @count:	entry: number of requested buffers,
+ *		return: number of created buffers
+ * @memory:	buffer memory type
+ * @format:	frame format, for which buffers are requested
+ * @reserved:	future extensions
+ */
+struct v4l2_create_buffers {
+	__u32			index;
+	__u32			count;
+	enum v4l2_memory        memory;
+	struct v4l2_format	format;
+	__u32			reserved[8];
+};
+
 /*
  *	I O C T L   C O D E S   F O R   V I D E O   D E V I C E S
  *
@@ -2228,6 +2250,11 @@ struct v4l2_dbg_chip_ident {
 #define	VIDIOC_SUBSCRIBE_EVENT	 _IOW('V', 90, struct v4l2_event_subscription)
 #define	VIDIOC_UNSUBSCRIBE_EVENT _IOW('V', 91, struct v4l2_event_subscription)
 
+/* Experimental, the below two ioctls may change over the next couple of kernel
+   versions */
+#define VIDIOC_CREATE_BUFS	_IOWR('V', 92, struct v4l2_create_buffers)
+#define VIDIOC_PREPARE_BUF	_IOWR('V', 93, struct v4l2_buffer)
+
 /* Reminder: when adding new ioctls please add support for them to
    drivers/media/video/v4l2-compat-ioctl32.c as well! */
 
diff --git a/include/media/ov772x.h b/include/media/ov772x.h
index 548bf1155c83..00dbb7c4feae 100644
--- a/include/media/ov772x.h
+++ b/include/media/ov772x.h
@@ -12,12 +12,9 @@
 #ifndef __OV772X_H__
 #define __OV772X_H__
 
-#include <media/soc_camera.h>
-
 /* for flags */
 #define OV772X_FLAG_VFLIP	(1 << 0) /* Vertical flip image */
 #define OV772X_FLAG_HFLIP	(1 << 1) /* Horizontal flip image */
-#define OV772X_FLAG_8BIT	(1 << 2) /* default 10 bit */
 
 /*
  * for Edge ctrl
@@ -32,22 +29,23 @@ struct ov772x_edge_ctrl {
 	unsigned char lower;
 };
 
-#define OV772X_MANUAL_EDGE_CTRL	0x80 /* un-used bit of strength */
-#define EDGE_STRENGTH_MASK	0x1F
-#define EDGE_THRESHOLD_MASK	0x0F
-#define EDGE_UPPER_MASK		0xFF
-#define EDGE_LOWER_MASK		0xFF
+#define OV772X_MANUAL_EDGE_CTRL		0x80 /* un-used bit of strength */
+#define OV772X_EDGE_STRENGTH_MASK	0x1F
+#define OV772X_EDGE_THRESHOLD_MASK	0x0F
+#define OV772X_EDGE_UPPER_MASK		0xFF
+#define OV772X_EDGE_LOWER_MASK		0xFF
 
 #define OV772X_AUTO_EDGECTRL(u, l)	\
 {					\
-	.upper = (u & EDGE_UPPER_MASK),	\
-	.lower = (l & EDGE_LOWER_MASK),	\
+	.upper = (u & OV772X_EDGE_UPPER_MASK),	\
+	.lower = (l & OV772X_EDGE_LOWER_MASK),	\
 }
 
-#define OV772X_MANUAL_EDGECTRL(s, t)					\
-{									\
-	.strength  = (s & EDGE_STRENGTH_MASK) | OV772X_MANUAL_EDGE_CTRL,\
-	.threshold = (t & EDGE_THRESHOLD_MASK),				\
+#define OV772X_MANUAL_EDGECTRL(s, t)			\
+{							\
+	.strength  = (s & OV772X_EDGE_STRENGTH_MASK) |	\
+			OV772X_MANUAL_EDGE_CTRL,	\
+	.threshold = (t & OV772X_EDGE_THRESHOLD_MASK),	\
 }
 
 /*
diff --git a/include/media/s5k6aa.h b/include/media/s5k6aa.h
new file mode 100644
index 000000000000..ba34f7055e55
--- /dev/null
+++ b/include/media/s5k6aa.h
@@ -0,0 +1,51 @@
+/*
+ * S5K6AAFX camera sensor driver header
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef S5K6AA_H
+#define S5K6AA_H
+
+#include <media/v4l2-mediabus.h>
+
+/**
+ * struct s5k6aa_gpio - data structure describing a GPIO
+ * @gpio:  GPIO number
+ * @level: indicates active state of the @gpio
+ */
+struct s5k6aa_gpio {
+	int gpio;
+	int level;
+};
+
+/**
+ * struct s5k6aa_platform_data - s5k6aa driver platform data
+ * @set_power:   an additional callback to the board code, called
+ *               after enabling the regulators and before switching
+ *               the sensor off
+ * @mclk_frequency: sensor's master clock frequency in Hz
+ * @gpio_reset:  GPIO driving RESET pin
+ * @gpio_stby:   GPIO driving STBY pin
+ * @nlanes:      maximum number of MIPI-CSI lanes used
+ * @horiz_flip:  default horizontal image flip value, non zero to enable
+ * @vert_flip:   default vertical image flip value, non zero to enable
+ */
+
+struct s5k6aa_platform_data {
+	int (*set_power)(int enable);
+	unsigned long mclk_frequency;
+	struct s5k6aa_gpio gpio_reset;
+	struct s5k6aa_gpio gpio_stby;
+	enum v4l2_mbus_type bus_type;
+	u8 nlanes;
+	u8 horiz_flip;
+	u8 vert_flip;
+};
+
+#endif /* S5K6AA_H */
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index 7582952dceae..b1377b931eb7 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -12,12 +12,14 @@
 #ifndef SOC_CAMERA_H
 #define SOC_CAMERA_H
 
+#include <linux/bitops.h>
 #include <linux/device.h>
 #include <linux/mutex.h>
 #include <linux/pm.h>
 #include <linux/videodev2.h>
 #include <media/videobuf-core.h>
 #include <media/videobuf2-core.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
 
 struct file;
@@ -37,8 +39,8 @@ struct soc_camera_device {
 	unsigned char iface;		/* Host number */
 	unsigned char devnum;		/* Device number per host */
 	struct soc_camera_sense *sense;	/* See comment in struct definition */
-	struct soc_camera_ops *ops;
 	struct video_device *vdev;
+	struct v4l2_ctrl_handler ctrl_handler;
 	const struct soc_camera_format_xlate *current_fmt;
 	struct soc_camera_format_xlate *user_formats;
 	int num_user_formats;
@@ -93,14 +95,10 @@ struct soc_camera_host_ops {
 	int (*reqbufs)(struct soc_camera_device *, struct v4l2_requestbuffers *);
 	int (*querycap)(struct soc_camera_host *, struct v4l2_capability *);
 	int (*set_bus_param)(struct soc_camera_device *, __u32);
-	int (*get_ctrl)(struct soc_camera_device *, struct v4l2_control *);
-	int (*set_ctrl)(struct soc_camera_device *, struct v4l2_control *);
 	int (*get_parm)(struct soc_camera_device *, struct v4l2_streamparm *);
 	int (*set_parm)(struct soc_camera_device *, struct v4l2_streamparm *);
 	int (*enum_fsizes)(struct soc_camera_device *, struct v4l2_frmsizeenum *);
 	unsigned int (*poll)(struct file *, poll_table *);
-	const struct v4l2_queryctrl *controls;
-	int num_controls;
 };
 
 #define SOCAM_SENSOR_INVERT_PCLK	(1 << 0)
@@ -193,13 +191,6 @@ struct soc_camera_format_xlate {
 	const struct soc_mbus_pixelfmt *host_fmt;
 };
 
-struct soc_camera_ops {
-	unsigned long (*query_bus_param)(struct soc_camera_device *);
-	int (*set_bus_param)(struct soc_camera_device *, unsigned long);
-	const struct v4l2_queryctrl *controls;
-	int num_controls;
-};
-
 #define SOCAM_SENSE_PCLK_CHANGED	(1 << 0)
 
 /**
@@ -226,65 +217,18 @@ struct soc_camera_sense {
 	unsigned long pixel_clock;
 };
 
-static inline struct v4l2_queryctrl const *soc_camera_find_qctrl(
-	struct soc_camera_ops *ops, int id)
-{
-	int i;
-
-	for (i = 0; i < ops->num_controls; i++)
-		if (ops->controls[i].id == id)
-			return &ops->controls[i];
-
-	return NULL;
-}
-
-#define SOCAM_MASTER			(1 << 0)
-#define SOCAM_SLAVE			(1 << 1)
-#define SOCAM_HSYNC_ACTIVE_HIGH		(1 << 2)
-#define SOCAM_HSYNC_ACTIVE_LOW		(1 << 3)
-#define SOCAM_VSYNC_ACTIVE_HIGH		(1 << 4)
-#define SOCAM_VSYNC_ACTIVE_LOW		(1 << 5)
-#define SOCAM_DATAWIDTH_4		(1 << 6)
-#define SOCAM_DATAWIDTH_8		(1 << 7)
-#define SOCAM_DATAWIDTH_9		(1 << 8)
-#define SOCAM_DATAWIDTH_10		(1 << 9)
-#define SOCAM_DATAWIDTH_15		(1 << 10)
-#define SOCAM_DATAWIDTH_16		(1 << 11)
-#define SOCAM_PCLK_SAMPLE_RISING	(1 << 12)
-#define SOCAM_PCLK_SAMPLE_FALLING	(1 << 13)
-#define SOCAM_DATA_ACTIVE_HIGH		(1 << 14)
-#define SOCAM_DATA_ACTIVE_LOW		(1 << 15)
-#define SOCAM_MIPI_1LANE		(1 << 16)
-#define SOCAM_MIPI_2LANE		(1 << 17)
-#define SOCAM_MIPI_3LANE		(1 << 18)
-#define SOCAM_MIPI_4LANE		(1 << 19)
-#define SOCAM_MIPI	(SOCAM_MIPI_1LANE | SOCAM_MIPI_2LANE | \
-			SOCAM_MIPI_3LANE | SOCAM_MIPI_4LANE)
+#define SOCAM_DATAWIDTH(x)	BIT((x) - 1)
+#define SOCAM_DATAWIDTH_4	SOCAM_DATAWIDTH(4)
+#define SOCAM_DATAWIDTH_8	SOCAM_DATAWIDTH(8)
+#define SOCAM_DATAWIDTH_9	SOCAM_DATAWIDTH(9)
+#define SOCAM_DATAWIDTH_10	SOCAM_DATAWIDTH(10)
+#define SOCAM_DATAWIDTH_15	SOCAM_DATAWIDTH(15)
+#define SOCAM_DATAWIDTH_16	SOCAM_DATAWIDTH(16)
 
 #define SOCAM_DATAWIDTH_MASK (SOCAM_DATAWIDTH_4 | SOCAM_DATAWIDTH_8 | \
 			      SOCAM_DATAWIDTH_9 | SOCAM_DATAWIDTH_10 | \
 			      SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_16)
 
-static inline unsigned long soc_camera_bus_param_compatible(
-			unsigned long camera_flags, unsigned long bus_flags)
-{
-	unsigned long common_flags, hsync, vsync, pclk, data, buswidth, mode;
-	unsigned long mipi;
-
-	common_flags = camera_flags & bus_flags;
-
-	hsync = common_flags & (SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW);
-	vsync = common_flags & (SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW);
-	pclk = common_flags & (SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING);
-	data = common_flags & (SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_LOW);
-	mode = common_flags & (SOCAM_MASTER | SOCAM_SLAVE);
-	buswidth = common_flags & SOCAM_DATAWIDTH_MASK;
-	mipi = common_flags & SOCAM_MIPI;
-
-	return ((!hsync || !vsync || !pclk || !data || !mode || !buswidth) && !mipi) ? 0 :
-		common_flags;
-}
-
 static inline void soc_camera_limit_side(int *start, int *length,
 		unsigned int start_min,
 		unsigned int length_min, unsigned int length_max)
@@ -300,23 +244,37 @@ static inline void soc_camera_limit_side(int *start, int *length,
 		*start = start_min + length_max - *length;
 }
 
-extern unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl,
-						   unsigned long flags);
+unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl,
+					    unsigned long flags);
+unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl,
+					   const struct v4l2_mbus_config *cfg);
 
 /* This is only temporary here - until v4l2-subdev begins to link to video_device */
 #include <linux/i2c.h>
-static inline struct video_device *soc_camera_i2c_to_vdev(struct i2c_client *client)
+static inline struct video_device *soc_camera_i2c_to_vdev(const struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct soc_camera_device *icd = (struct soc_camera_device *)sd->grp_id;
+	return icd ? icd->vdev : NULL;
+}
+
+static inline struct soc_camera_link *soc_camera_i2c_to_link(const struct i2c_client *client)
+{
+	return client->dev.platform_data;
+}
+
+static inline struct v4l2_subdev *soc_camera_vdev_to_subdev(const struct video_device *vdev)
 {
-	struct soc_camera_device *icd = client->dev.platform_data;
-	return icd->vdev;
+	struct soc_camera_device *icd = dev_get_drvdata(vdev->parent);
+	return soc_camera_to_subdev(icd);
 }
 
-static inline struct soc_camera_device *soc_camera_from_vb2q(struct vb2_queue *vq)
+static inline struct soc_camera_device *soc_camera_from_vb2q(const struct vb2_queue *vq)
 {
 	return container_of(vq, struct soc_camera_device, vb2_vidq);
 }
 
-static inline struct soc_camera_device *soc_camera_from_vbq(struct videobuf_queue *vq)
+static inline struct soc_camera_device *soc_camera_from_vbq(const struct videobuf_queue *vq)
 {
 	return container_of(vq, struct soc_camera_device, vb_vidq);
 }
diff --git a/include/media/soc_camera_platform.h b/include/media/soc_camera_platform.h
index 74f0fa15ca47..8aa4200a0b1d 100644
--- a/include/media/soc_camera_platform.h
+++ b/include/media/soc_camera_platform.h
@@ -13,6 +13,7 @@
 
 #include <linux/videodev2.h>
 #include <media/soc_camera.h>
+#include <media/v4l2-mediabus.h>
 
 struct device;
 
@@ -20,7 +21,8 @@ struct soc_camera_platform_info {
 	const char *format_name;
 	unsigned long format_depth;
 	struct v4l2_mbus_framefmt format;
-	unsigned long bus_param;
+	unsigned long mbus_param;
+	enum v4l2_mbus_type mbus_type;
 	struct soc_camera_device *icd;
 	int (*set_capture)(struct soc_camera_platform_info *info, int enable);
 };
diff --git a/include/media/soc_mediabus.h b/include/media/soc_mediabus.h
index fae432544b41..73f1e7eb60f3 100644
--- a/include/media/soc_mediabus.h
+++ b/include/media/soc_mediabus.h
@@ -82,5 +82,7 @@ const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc(
 s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf);
 int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf,
 			unsigned int *numerator, unsigned int *denominator);
+unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg,
+					unsigned int flags);
 
 #endif
diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
index dd9f1e7b8ff7..4d1c74ad4c84 100644
--- a/include/media/v4l2-ioctl.h
+++ b/include/media/v4l2-ioctl.h
@@ -122,6 +122,8 @@ struct v4l2_ioctl_ops {
 	int (*vidioc_qbuf)    (struct file *file, void *fh, struct v4l2_buffer *b);
 	int (*vidioc_dqbuf)   (struct file *file, void *fh, struct v4l2_buffer *b);
 
+	int (*vidioc_create_bufs)(struct file *file, void *fh, struct v4l2_create_buffers *b);
+	int (*vidioc_prepare_buf)(struct file *file, void *fh, struct v4l2_buffer *b);
 
 	int (*vidioc_overlay) (struct file *file, void *fh, unsigned int i);
 	int (*vidioc_g_fbuf)   (struct file *file, void *fh,
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index 257da1a30f66..f0f3358d1b1b 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -158,6 +158,7 @@ struct v4l2_subdev_core_ops {
 	int (*s_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
 	int (*try_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
 	int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm);
+	int (*g_std)(struct v4l2_subdev *sd, v4l2_std_id *norm);
 	int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm);
 	long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
 #ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -534,13 +535,13 @@ struct v4l2_subdev {
 	void *dev_priv;
 	void *host_priv;
 	/* subdev device node */
-	struct video_device devnode;
+	struct video_device *devnode;
 };
 
 #define media_entity_to_v4l2_subdev(ent) \
 	container_of(ent, struct v4l2_subdev, entity)
 #define vdev_to_v4l2_subdev(vdev) \
-	container_of(vdev, struct v4l2_subdev, devnode)
+	video_get_drvdata(vdev)
 
 /*
  * Used for storing subdev information per file handle
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index ea55c08eddfb..a15d1f1b319e 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -105,6 +105,7 @@ enum vb2_fileio_flags {
 /**
  * enum vb2_buffer_state - current video buffer state
  * @VB2_BUF_STATE_DEQUEUED:	buffer under userspace control
+ * @VB2_BUF_STATE_PREPARED:	buffer prepared in videobuf and by the driver
  * @VB2_BUF_STATE_QUEUED:	buffer queued in videobuf, but not in driver
  * @VB2_BUF_STATE_ACTIVE:	buffer queued in driver and possibly used
  *				in a hardware operation
@@ -116,6 +117,7 @@ enum vb2_fileio_flags {
  */
 enum vb2_buffer_state {
 	VB2_BUF_STATE_DEQUEUED,
+	VB2_BUF_STATE_PREPARED,
 	VB2_BUF_STATE_QUEUED,
 	VB2_BUF_STATE_ACTIVE,
 	VB2_BUF_STATE_DONE,
@@ -167,13 +169,21 @@ struct vb2_buffer {
 /**
  * struct vb2_ops - driver-specific callbacks
  *
- * @queue_setup:	called from a VIDIOC_REQBUFS handler, before
- *			memory allocation; driver should return the required
- *			number of buffers in num_buffers, the required number
- *			of planes per buffer in num_planes; the size of each
- *			plane should be set in the sizes[] array and optional
- *			per-plane allocator specific context in alloc_ctxs[]
- *			array
+ * @queue_setup:	called from VIDIOC_REQBUFS and VIDIOC_CREATE_BUFS
+ *			handlers before memory allocation, or, if
+ *			*num_planes != 0, after the allocation to verify a
+ *			smaller number of buffers. Driver should return
+ *			the required number of buffers in *num_buffers, the
+ *			required number of planes per buffer in *num_planes; the
+ *			size of each plane should be set in the sizes[] array
+ *			and optional per-plane allocator specific context in the
+ *			alloc_ctxs[] array. When called from VIDIOC_REQBUFS,
+ *			fmt == NULL, the driver has to use the currently
+ *			configured format and *num_buffers is the total number
+ *			of buffers, that are being allocated. When called from
+ *			VIDIOC_CREATE_BUFS, fmt != NULL and it describes the
+ *			target frame format. In this case *num_buffers are being
+ *			allocated additionally to q->num_buffers.
  * @wait_prepare:	release any locks taken while calling vb2 functions;
  *			it is called before an ioctl needs to wait for a new
  *			buffer to arrive; required to avoid a deadlock in
@@ -186,11 +196,11 @@ struct vb2_buffer {
  *			perform additional buffer-related initialization;
  *			initialization failure (return != 0) will prevent
  *			queue setup from completing successfully; optional
- * @buf_prepare:	called every time the buffer is queued from userspace;
- *			drivers may perform any initialization required before
- *			each hardware operation in this callback;
- *			if an error is returned, the buffer will not be queued
- *			in driver; optional
+ * @buf_prepare:	called every time the buffer is queued from userspace
+ *			and from the VIDIOC_PREPARE_BUF ioctl; drivers may
+ *			perform any initialization required before each hardware
+ *			operation in this callback; if an error is returned, the
+ *			buffer will not be queued in driver; optional
  * @buf_finish:		called before every dequeue of the buffer back to
  *			userspace; drivers may perform any operations required
  *			before userspace accesses the buffer; optional
@@ -216,9 +226,9 @@ struct vb2_buffer {
  *			pre-queued buffers before calling STREAMON
  */
 struct vb2_ops {
-	int (*queue_setup)(struct vb2_queue *q, unsigned int *num_buffers,
-			   unsigned int *num_planes, unsigned int sizes[],
-			   void *alloc_ctxs[]);
+	int (*queue_setup)(struct vb2_queue *q, const struct v4l2_format *fmt,
+			   unsigned int *num_buffers, unsigned int *num_planes,
+			   unsigned int sizes[], void *alloc_ctxs[]);
 
 	void (*wait_prepare)(struct vb2_queue *q);
 	void (*wait_finish)(struct vb2_queue *q);
@@ -298,6 +308,9 @@ int vb2_wait_for_all_buffers(struct vb2_queue *q);
 int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b);
 int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req);
 
+int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create);
+int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b);
+
 int vb2_queue_init(struct vb2_queue *q);
 
 void vb2_queue_release(struct vb2_queue *q);
@@ -309,6 +322,13 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type);
 int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type);
 
 int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma);
+#ifndef CONFIG_MMU
+unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
+				    unsigned long addr,
+				    unsigned long len,
+				    unsigned long pgoff,
+				    unsigned long flags);
+#endif
 unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait);
 size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
 		loff_t *ppos, int nonblock);