Selaa lähdekoodia

Merge branch 'audio_display-ti-linux-4.19.y' of git.ti.com:~jyrisarha/ti-linux-kernel/jyrisarhas-audio-video-linux-feature-tree into ti-linux-4.19.y

TI-Feature: audio-display
TI-Tree: git@git.ti.com:~jyrisarha/ti-linux-kernel/jyrisarhas-audio-video-linux-feature-tree.git
TI-Branch: audio_display-ti-linux-4.19.y

* 'audio_display-ti-linux-4.19.y' of git.ti.com:~jyrisarha/ti-linux-kernel/jyrisarhas-audio-video-linux-feature-tree: (87 commits)
  ti_config_fragments: Add V4L2 to audio_display.cfg
  ARM: dts: am57xx-evm: add dtb-merge support for camera overlays
  ARM: dts: dt-overlays: add camera support for am57xx-evm
  ARM64: dts: ti: Add overlay for CSI2 OV490 camera
  HACK: Input: goodix: fix driver auto-loading
  arm: dts: dra76-evm: Add VIP and OV10633 dts nodes
  arm: dts: dra76-evm: Add CAL and OV490 nodes
  HACK: UAPI: Increasing VIDEO_MAX_FRAME/VB2_MAX_FRAME to 128
  ARM: dts: am572x-idk-common: Add OV2659 camera sensor support for AM572/4x IDK
  ARM: dts: am571x-idk: Add OV2659 camera sensor support for AM571x IDK
  ARM: dts: am57xx-idk-common: Add OV2659 camera sensor support
  ARM: dts: dra71-evm: Fix number of data lanes for OV490
  arm: dts: dra72-evm-common: Add entries for the CSI2 cameras
  arm: dts: dra72-evm-common: Add VIP and OV10633 dts nodes
  arm: dts: dra7-evm: Add VIP and OV10633 dts nodes
  arm: dts: dra72: Add VIP mux pinctrl node and bindings
  ARM: DRA7: hwmod: Add VIP nodes
  ARM: dts: DRA74x: Add VIP2 and VIP3 dtsi entries
  ARM: dts: DRA7: Add VIP1 dtsi entry
  media: ti-vpe: Add the VIP driver
  ...

Signed-off-by: LCPD Auto Merger <lcpd_integration@list.ti.com>
LCPD Auto Merger 7 vuotta sitten
vanhempi
commit
c5e03e558e
59 muutettua tiedostoa jossa 12169 lisäystä ja 728 poistoa
  1. 49 0
      Documentation/devicetree/bindings/media/i2c/mt9t11x.txt
  2. 50 0
      Documentation/devicetree/bindings/media/i2c/ov1063x.txt
  3. 46 0
      Documentation/devicetree/bindings/media/i2c/ov490.txt
  4. 14 6
      Documentation/devicetree/bindings/media/ti-cal.txt
  5. 94 0
      Documentation/devicetree/bindings/media/ti-vip.txt
  6. 33 0
      Documentation/devicetree/bindings/media/ti-vpe.txt
  7. 27 0
      MAINTAINERS
  8. 5 1
      arch/arm/boot/dts/Makefile
  9. 8 2
      arch/arm/boot/dts/am437x-gp-evm.dts
  10. 26 1
      arch/arm/boot/dts/am437x-sk-evm.dts
  11. 22 1
      arch/arm/boot/dts/am43x-epos-evm.dts
  12. 54 0
      arch/arm/boot/dts/am43xx-clocks.dtsi
  13. 18 0
      arch/arm/boot/dts/am571x-idk.dts
  14. 17 0
      arch/arm/boot/dts/am572x-idk-common.dtsi
  15. 25 0
      arch/arm/boot/dts/am57xx-idk-common.dtsi
  16. 11 0
      arch/arm/boot/dts/dra7-evm-common.dtsi
  17. 26 0
      arch/arm/boot/dts/dra7-evm.dts
  18. 69 0
      arch/arm/boot/dts/dra7.dtsi
  19. 6 0
      arch/arm/boot/dts/dra71-evm.dts
  20. 77 0
      arch/arm/boot/dts/dra72-evm-common.dtsi
  21. 41 0
      arch/arm/boot/dts/dra72x.dtsi
  22. 92 0
      arch/arm/boot/dts/dra74x.dtsi
  23. 73 0
      arch/arm/boot/dts/dra76-evm.dts
  24. 28 0
      arch/arm/boot/dts/dra76x.dtsi
  25. 24 0
      arch/arm/boot/dts/ti/am57xx-evm.its
  26. 45 0
      arch/arm/boot/dts/ti/mt9t111.dtso
  27. 72 0
      arch/arm/boot/dts/ti/ov10635.dtso
  28. 1 1
      arch/arm/mach-omap2/clockdomains7xx_data.c
  29. 209 0
      arch/arm/mach-omap2/omap_hwmod_7xx_data.c
  30. 25 0
      arch/arm64/boot/dts/ti/k3-am65-main.dtsi
  31. 52 0
      arch/arm64/boot/dts/ti/k3-am654-evm-csi2-ov490.dtso
  32. 7 0
      drivers/input/touchscreen/goodix.c
  33. 34 1
      drivers/media/i2c/Kconfig
  34. 3 0
      drivers/media/i2c/Makefile
  35. 2291 0
      drivers/media/i2c/mt9t11x.c
  36. 1069 0
      drivers/media/i2c/ov1063x.c
  37. 699 0
      drivers/media/i2c/ov1063x_regs.h
  38. 21 4
      drivers/media/i2c/ov2659.c
  39. 675 0
      drivers/media/i2c/ov490.c
  40. 14 1
      drivers/media/platform/Kconfig
  41. 1 3
      drivers/media/platform/Makefile
  42. 390 474
      drivers/media/platform/am437x/am437x-vpfe.c
  43. 27 3
      drivers/media/platform/am437x/am437x-vpfe.h
  44. 2 0
      drivers/media/platform/ti-vpe/Makefile
  45. 595 160
      drivers/media/platform/ti-vpe/cal.c
  46. 27 0
      drivers/media/platform/ti-vpe/cal_regs.h
  47. 4032 0
      drivers/media/platform/ti-vpe/vip.c
  48. 727 0
      drivers/media/platform/ti-vpe/vip.h
  49. 9 2
      drivers/media/platform/ti-vpe/vpdma.c
  50. 2 0
      drivers/media/platform/ti-vpe/vpdma.h
  51. 3 2
      drivers/media/platform/ti-vpe/vpdma_priv.h
  52. 135 64
      drivers/media/platform/ti-vpe/vpe.c
  53. 21 0
      drivers/media/v4l2-core/v4l2-fwnode.c
  54. 2 0
      drivers/staging/media/zoran/zoran.h
  55. 14 0
      include/dt-bindings/pinctrl/dra.h
  56. 3 0
      include/media/v4l2-fwnode.h
  57. 1 1
      include/media/videobuf2-core.h
  58. 1 1
      include/uapi/linux/videodev2.h
  59. 25 0
      ti_config_fragments/audio_display.cfg

+ 49 - 0
Documentation/devicetree/bindings/media/i2c/mt9t11x.txt

@@ -0,0 +1,49 @@
+* Aptina 1/4-Inch QXGA CMOS Digital Image Sensor
+
+The Aptina MT9T111 is a 1/4-inch CMOS active pixel digital image sensor with
+an active array size of 2048H x 1536V. It is programmable through a simple
+two-wire serial interface.
+
+Required Properties:
+
+- compatible: value should be either one among the following
+	(a) "aptina,mt9t111" for MT9T111 color sensor
+	(b) "aptina,mt9t112" for MT9T112 color sensor
+
+- input-clock-freq: Source clock freqquency (Hz)
+- pixel-clock-freq: Target pixel clock freqquency (Hz)
+
+Optional Properties:
+
+- reset-gpios: Toggle when the module is enabled or disabled.
+- powerdown-gpios: Toggle when the module is enabled or disabled.
+- oscen-gpios: Enabled once at probe time.
+- bufen-gpios: Enabled once at probe time.
+- camen-gpios: Enabled once at probe time.
+
+For further reading on port node refer to
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+
+	mt9t111@3C {
+		compatible = "aptina,mt9t111";
+		reg = <0x3C>;
+
+		reset-gpios = <&gpio4 17 GPIO_ACTIVE_LOW>;
+		oscen-gpios = <&gpio5 10 GPIO_ACTIVE_HIGH>;
+		powerdown-gpios = <&gpio5 11 GPIO_ACTIVE_LOW>;
+		bufen-gpios = <&gpio5 12 GPIO_ACTIVE_LOW>;
+		camen-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>;
+
+		port {
+			cam: endpoint {
+				remote-endpoint = <&vin3a>;
+				hsync-active = <1>;
+				vsync-active = <1>;
+				pclk-sample = <1>;
+				input-clock-freq = <32000000>;
+				pixel-clock-freq = <96000000>;
+			};
+		};
+	};

+ 50 - 0
Documentation/devicetree/bindings/media/i2c/ov1063x.txt

@@ -0,0 +1,50 @@
+* OmniVision 8/10 bit digital camera
+
+The Omnivision digital camera is a 720p camera which is configurable at 8/10bit
+YUYV output and can be configured for various resolutions.
+
+Required Properties:
+- compatible : Must be one of the following
+  - "ovti,ov10633": For OV10633 camera
+  - "ovti,ov10635": For OV10635 camera
+- clocks: reference to the xvclk input clock.
+- clock-names: should be "xvclk".
+
+Optional Properties:
+- reset-gpios: reference to the GPIO connected to the reset pin, if any.
+- powerdown-gpios: reference to the GPIO connected to the pwdn pin, if any.
+- mux-gpios : A list of gpios with active high/low flags for enabling the
+              sensor. This may contain gpios for power, board muxes, etc.
+              Driver would set all of them as specified by the active
+              high/low flag
+
+  There are no custom optional properties supported for this device.
+  Although all the endpoint properties documented in
+  Documentation/devicetree/bindings/media/video-interfaces.txt are supported.
+
+Example:
+
+	&i2c2 {
+		...
+		...
+		ovcamera@30 {
+			compatible = "ovti,ov10635";
+			reg = <0x30>;
+
+			clocks = <&clk_ov10635>;
+			clock-names = "xvclk";
+
+			mux-gpios = <&pcf_hdmi 3 GPIO_ACTIVE_LOW>;
+			reset-gpios = <&gpio4 17 GPIO_ACTIVE_HIGH>;
+			powerdown-gpios = <&gpio5 11 GPIO_ACTIVE_HIGH>;
+
+			port {
+				onboard_cam: endpoint {
+					hsync-active = <1>;
+					vsync-active = <1>;
+					pclk-sample = <1>;
+				};
+			};
+		};
+		...
+	};

+ 46 - 0
Documentation/devicetree/bindings/media/i2c/ov490.txt

@@ -0,0 +1,46 @@
+* OmniVision OV490 CSI2 Image Signal Processor (ISP)
+
+The Omnivision OV490 is a Image Signal Processor which is a companion
+processor for the Raw image sensors. This device can process the raw images
+and apply multiple algorithms to correct/transform/enhance the image quality.
+
+It needs programming lot of registers are most of them are written from
+a flash memory. The output is a CSI2 MIPI interface which can work with
+configurable number of lanes.
+
+Required Properties:
+- compatible : "ovti,ov490"
+
+Optional Properties:
+- mux-gpios : A list of gpios with active high/low flags for enabling the sensor.
+         This may contain gpios for power, board muxes, etc. Driver would
+         set all of them as specified by the active high/low flag.
+
+  There are no custom optional properties supported for this device.
+  Although all the CSI2 endpoint properties documented in
+  Documentation/devicetree/bindings/media/video-interfaces.txt are supported.
+
+Example:
+
+	&i2c2 {
+		...
+		...
+		ov490@cam0 {
+			compatible = "ovti,ov490";
+			reg = <0x24>;
+
+			mux-gpios = <&gpio_csi2_adap 0  GPIO_ACTIVE_LOW>,
+				    <&gpio_csi2_adap 1  GPIO_ACTIVE_HIGH>,
+				    <&gpio_csi2_adap 3  GPIO_ACTIVE_HIGH>,
+				    <&gpio_csi2_adap 4  GPIO_ACTIVE_LOW>;
+
+			port {
+				csi2_cam0: endpoint@0 {
+					clock-lanes = <0>;
+					data-lanes = <1 2 3 4>;
+					remote-endpoint = <&csi2_phy0>;
+				};
+			};
+		};
+		...
+	};

+ 14 - 6
Documentation/devicetree/bindings/media/ti-cal.txt

@@ -7,12 +7,21 @@ processing capability to connect CSI2 image-sensor modules to the
 DRA72x device.
 
 Required properties:
-- compatible: must be "ti,dra72-cal"
+- compatible:
+ Should be "ti,dra72-cal", for DRA72 controllers
+ Should be "ti,dra72-pre-es2-cal", for DRA72 controllers pre ES2.0
+ Should be "ti,dra76-cal", for DRA76 controllers
+ Should be "ti,am654-cal", for AM654 controllers
 - reg:	CAL Top level, Receiver Core #0, Receiver Core #1 and Camera RX
 	control address space
-- reg-names: cal_top, cal_rx_core0, cal_rx_core1, and camerrx_control
+- reg-names: cal_top, cal_rx_core0, cal_rx_core1 and camerrx_control
 	     registers
 - interrupts: should contain IRQ line for the CAL;
+- syscon-camerrx: phandle to the device control module and offset to the
+		  control_camerarx_core register
+		  This node is meant to replace the "camerrx_control" reg
+		  entry above but "camerrx_control" is still handled
+		  for backward compatibility.
 
 CAL supports 2 camera port nodes on MIPI bus. Each CSI2 camera port nodes
 should contain a 'port' child node with child 'endpoint' node. Please
@@ -25,13 +34,12 @@ Example:
 		ti,hwmods = "cal";
 		reg = <0x4845B000 0x400>,
 		      <0x4845B800 0x40>,
-		      <0x4845B900 0x40>,
-		      <0x4A002e94 0x4>;
+		      <0x4845B900 0x40>;
 		reg-names = "cal_top",
 			    "cal_rx_core0",
-			    "cal_rx_core1",
-			    "camerrx_control";
+			    "cal_rx_core1";
 		interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
+		syscon-camerrx = <&scm_conf 0xE94>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 

+ 94 - 0
Documentation/devicetree/bindings/media/ti-vip.txt

@@ -0,0 +1,94 @@
+Texas Instruments DRA7x VIDEO INPUT PORT (VIP)
+----------------------------------------------
+
+The Video Input Port (VIP) is a key component for image capture
+applications. The capture module provides the system interface and the
+processing capability to connect parallel image-sensor as well as
+BT.656/1120 capable encoder chip.
+
+Required properties:
+- compatible: must be "ti,vip1", "ti,vip2" or "ti,vip3".
+- reg:	VIP top level, parser, colorspace converter, scaler for slice 0 and 1
+	and vpdma memory address space;
+- reg-names: vip, parser0, csc0, sc0, parser1, csc1, sc1 and vpdma registers;
+- interrupts: should contain IRQ line for VIP;
+- syscon-pol: phandle to the device control module;
+
+VIP supports 2 slices. Each slice can handle up to 2 camera port nodes.
+Each port nodes should contain a 'port' child node with child 'endpoint'
+node. Please refer to the bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+	vip1: vip@0x48970000 {
+		compatible = "ti,vip1";
+		reg = <0x48970000 0x114>,
+		      <0x48975500 0xD8>,
+		      <0x48975700 0x18>,
+		      <0x48975800 0x80>,
+		      <0x48975a00 0xD8>,
+		      <0x48975c00 0x18>,
+		      <0x48975d00 0x80>,
+		      <0x4897d000 0x400>;
+		reg-names = "vip",
+			    "parser0",
+			    "csc0",
+			    "sc0",
+			    "parser1",
+			    "csc1",
+			    "sc1",
+			    "vpdma";
+		ti,hwmods = "vip1";
+		interrupts = <GIC_SPI 351 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 392 IRQ_TYPE_LEVEL_HIGH>;
+		/* CTRL_CORE_SMA_SW_1 */
+		syscon-pol = <&scm_conf 0x534>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+		vin1a: port@0 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0>;
+			status = "disabled";
+			endpoint@0 {
+				slave-mode;
+				remote-endpoint = <&camera1>;
+			};
+		};
+		vin2a: port@1 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <1>;
+			status = "disabled";
+		};
+		vin1b: port@2 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <2>;
+			status = "disabled";
+		};
+		vin2b: port@3 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <3>;
+			status = "disabled";
+		};
+	};
+
+	i2c5: i2c@4807c000 {
+		ov10633@37 {
+			compatible = "ovti,ov10633";
+			reg = <0x37>;
+
+			mux-gpios = <&pcf_hdmi 3 GPIO_ACTIVE_LOW>;
+			port {
+				camera1: endpoint {
+					remote-endpoint = <&vin1a>;
+					hsync-active = <1>;
+					vsync-active = <1>;
+					pclk-sample = <0>;
+				};
+			};
+		};
+	};

+ 33 - 0
Documentation/devicetree/bindings/media/ti-vpe.txt

@@ -0,0 +1,33 @@
+Texas Instruments DRA7x VIDEO PROCESSING ENGINE (VPE)
+------------------------------------------------------
+
+The Video Processing Engine (VPE) is a key component for image post
+processing applications. VPE consist of a single memory to memory
+path which can perform chroma up/down sampling, deinterlacing,
+scaling and color space conversion.
+
+Required properties:
+- compatible: must be "ti,vpe"
+- reg:	physical base address and length of the registers set for the 8
+	memory regions required;
+- reg-names: name associated with the memory regions described is <reg>;
+- interrupts: should contain IRQ line for VPE;
+
+Example:
+	vpe {
+		compatible = "ti,vpe";
+		ti,hwmods = "vpe";
+		clocks = <&dpll_core_h23x2_ck>;
+		clock-names = "fck";
+		reg =	<0x489d0000 0x120>,
+			<0x489d0700 0x80>,
+			<0x489d5700 0x18>,
+			<0x489dd000 0x400>;
+		reg-names =	"vpe_top",
+				"sc",
+				"csc",
+				"vpdma";
+		interrupts = <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+	};

+ 27 - 0
MAINTAINERS

@@ -9843,6 +9843,14 @@ S:	Odd Fixes
 F:	drivers/media/i2c/mt9t112.c
 F:	include/media/i2c/mt9t112.h
 
+MT9T11x APTINA CAMERA SENSOR
+M:	Benoit Parrot <bparrot@ti.com>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Odd Fixes
+F:	Documentation/devicetree/bindings/media/i2c/mt9t11x.txt
+F:	drivers/media/i2c/mt9t11x.c
+
 MT9V032 APTINA CAMERA SENSOR
 M:	Laurent Pinchart <laurent.pinchart@ideasonboard.com>
 L:	linux-media@vger.kernel.org
@@ -10682,6 +10690,15 @@ M:	Harald Welte <laforge@gnumonks.org>
 S:	Maintained
 F:	drivers/char/pcmcia/cm4040_cs.*
 
+OMNIVISION OV1063x SENSOR DRIVER
+M:	Benoit Parrot <bparrot@ti.com>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Maintained
+F:	Documentation/devicetree/bindings/media/i2c/ov1063x.txt
+F:	drivers/media/i2c/ov1063x.c
+F:	drivers/media/i2c/ov1063x_regs.h
+
 OMNIVISION OV13858 SENSOR DRIVER
 M:	Sakari Ailus <sakari.ailus@linux.intel.com>
 L:	linux-media@vger.kernel.org
@@ -10704,6 +10721,14 @@ T:	git git://linuxtv.org/media_tree.git
 S:	Maintained
 F:	drivers/media/i2c/ov2685.c
 
+OMNIVISION OV490 SENSOR DRIVER
+M:	Benoit Parrot <bparrot@ti.com>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Maintained
+F:	Documentation/devicetree/bindings/media/i2c/ov490.txt
+F:	drivers/media/i2c/ov490.c
+
 OMNIVISION OV5640 SENSOR DRIVER
 M:	Steve Longerbeam <slongerbeam@gmail.com>
 L:	linux-media@vger.kernel.org
@@ -14682,6 +14707,8 @@ W:	http://linuxtv.org/
 Q:	http://patchwork.linuxtv.org/project/linux-media/list/
 S:	Maintained
 F:	drivers/media/platform/ti-vpe/
+F:	Documentation/devicetree/bindings/media/ti-vpe.txt
+F:	Documentation/devicetree/bindings/media/ti-vip.txt
 
 TI WILINK WIRELESS DRIVERS
 L:	linux-wireless@vger.kernel.org

+ 5 - 1
arch/arm/boot/dts/Makefile

@@ -757,7 +757,11 @@ dtb-$(CONFIG_SOC_DRA7XX) += \
 	dra76-evm.dtb
 dtb-merge-$(CONFIG_SOC_DRA7XX) += \
 	am57xx-evm.dtb \
-	am57xx-evm-reva3.dtb
+	am57xx-evm-cam-mt9t111.dtb \
+	am57xx-evm-cam-ov10635.dtb \
+	am57xx-evm-reva3.dtb \
+	am57xx-evm-reva3-cam-mt9t111.dtb \
+	am57xx-evm-reva3-cam-ov10635.dtb
 ifeq ($(CONFIG_SOC_DRA7XX),y)
 dts-dirs += ti
 endif

+ 8 - 2
arch/arm/boot/dts/am437x-gp-evm.dts

@@ -165,6 +165,12 @@
 		pinctrl-1 = <&beeper_pins_sleep>;
 		gpios = <&gpio4 12 GPIO_ACTIVE_HIGH>;
 	};
+
+	audio_mstrclk: mclk_osc {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <12000000>;
+	};
 };
 
 &am43xx_pinmux {
@@ -715,7 +721,7 @@
 		compatible = "ovti,ov2659";
 		reg = <0x30>;
 
-		clocks = <&refclk 0>;
+		clocks = <&audio_mstrclk>;
 		clock-names = "xvclk";
 
 		port {
@@ -756,7 +762,7 @@
 		compatible = "ovti,ov2659";
 		reg = <0x30>;
 
-		clocks = <&refclk 0>;
+		clocks = <&audio_mstrclk>;
 		clock-names = "xvclk";
 
 		port {

+ 26 - 1
arch/arm/boot/dts/am437x-sk-evm.dts

@@ -275,6 +275,12 @@
 		>;
 	};
 
+	clkout1_pin: pinmux_clkout1_pin {
+		pinctrl-single,pins = <
+			0x270 (PIN_OUTPUT_PULLDOWN | MUX_MODE3)	/* XDMA_EVENT_INTR0/CLKOUT1 */
+		>;
+	};
+
 	cpsw_default: cpsw_default {
 		pinctrl-single,pins = <
 			/* Slave 1 */
@@ -596,6 +602,25 @@
 	pinctrl-0 = <&i2c1_pins>;
 	clock-frequency = <400000>;
 
+	ov2659@30 {
+		compatible = "ovti,ov2659";
+		reg = <0x30>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&clkout1_pin>;
+
+		clocks = <&clkout1_mux_ck>;
+		clock-names = "xvclk";
+		assigned-clocks = <&clkout1_mux_ck>;
+		assigned-clock-parents = <&clkout1_osc_div_ck>;
+
+		port {
+			ov2659_1: endpoint {
+				remote-endpoint = <&vpfe0_ep>;
+				link-frequencies = /bits/ 64 <70000000>;
+			};
+		};
+	};
+
 	edt-ft5306@38 {
 		status = "okay";
 		compatible = "edt,edt-ft5306", "edt,edt-ft5x06";
@@ -872,7 +897,7 @@
 	/* Camera port */
 	port {
 		vpfe0_ep: endpoint {
-			/* remote-endpoint = <&sensor>; add once we have it */
+			remote-endpoint = <&ov2659_1>;
 			ti,am437x-vpfe-interface = <0>;
 			bus-width = <8>;
 			hsync-active = <0>;

+ 22 - 1
arch/arm/boot/dts/am43x-epos-evm.dts

@@ -147,6 +147,12 @@
 			system-clock-frequency = <12000000>;
 		};
 	};
+
+	audio_mstrclk: mclk_osc {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <12000000>;
+	};
 };
 
 &am43xx_pinmux {
@@ -687,6 +693,21 @@
 		IOVDD-supply = <&dcdc4>; /* V3_3D -> DCDC4 */
 		DVDD-supply = <&ldo1>; /* V1_8AUD -> V1_8D -> LDO1 */
 	};
+
+	ov2659@30 {
+		compatible = "ovti,ov2659";
+		reg = <0x30>;
+
+		clocks = <&audio_mstrclk>;
+		clock-names = "xvclk";
+
+		port {
+			ov2659_1: endpoint {
+				remote-endpoint = <&vpfe1_ep>;
+				link-frequencies = /bits/ 64 <70000000>;
+			};
+		};
+	};
 };
 
 &i2c2 {
@@ -953,7 +974,7 @@
 
 	port {
 		vpfe1_ep: endpoint {
-			/* remote-endpoint = <&sensor>; add once we have it */
+			remote-endpoint = <&ov2659_1>;
 			ti,am437x-vpfe-interface = <0>;
 			bus-width = <8>;
 			hsync-active = <0>;

+ 54 - 0
arch/arm/boot/dts/am43xx-clocks.dtsi

@@ -707,6 +707,60 @@
 		ti,bit-shift = <8>;
 		reg = <0x2a48>;
 	};
+
+	clkout1_osc_div_ck: clkout1_osc_div_ck {
+		#clock-cells = <0>;
+		compatible = "ti,divider-clock";
+		clocks = <&sys_clkin_ck>;
+		ti,bit-shift = <20>;
+		ti,max-div = <4>;
+		reg = <0x4100>;
+	};
+
+	clkout1_src2_mux_ck: clkout1_src2_mux_ck {
+		#clock-cells = <0>;
+		compatible = "ti,mux-clock";
+		clocks = <&clk_rc32k_ck>, <&sysclk_div>, <&dpll_ddr_m2_ck>,
+			 <&dpll_per_m2_ck>, <&dpll_disp_m2_ck>,
+			 <&dpll_mpu_m2_ck>;
+		reg = <0x4100>;
+	};
+
+	clkout1_src2_pre_div_ck: clkout1_src2_pre_div_ck {
+		#clock-cells = <0>;
+		compatible = "ti,divider-clock";
+		clocks = <&clkout1_src2_mux_ck>;
+		ti,bit-shift = <4>;
+		ti,max-div = <8>;
+		reg = <0x4100>;
+	};
+
+	clkout1_src2_post_div_ck: clkout1_src2_post_div_ck {
+		#clock-cells = <0>;
+		compatible = "ti,divider-clock";
+		clocks = <&clkout1_src2_pre_div_ck>;
+		ti,bit-shift = <8>;
+		ti,max-div = <32>;
+		ti,index-power-of-two;
+		reg = <0x4100>;
+	};
+
+	clkout1_mux_ck: clkout1_mux_ck {
+		#clock-cells = <0>;
+		compatible = "ti,mux-clock";
+		clocks = <&clkout1_osc_div_ck>, <&clk_rc32k_ck>,
+			 <&clkout1_src2_post_div_ck>, <&dpll_extdev_m2_ck>;
+		ti,bit-shift = <16>;
+		reg = <0x4100>;
+	};
+
+	clkout1_ck: clkout1_ck {
+		#clock-cells = <0>;
+		compatible = "ti,gate-clock";
+		clocks = <&clkout1_mux_ck>;
+		ti,bit-shift = <23>;
+		reg = <0x4100>;
+	};
 };
 
 &prcm {

+ 18 - 0
arch/arm/boot/dts/am571x-idk.dts

@@ -114,3 +114,21 @@
 	pinctrl-1 = <&mmc2_pins_hs>;
 	pinctrl-2 = <&mmc2_pins_ddr_rev20 &mmc2_iodelay_ddr_conf>;
 };
+
+&ov2659_1 {
+	remote-endpoint = <&vin1b>;
+};
+
+&vin1b {
+	status = "okay";
+
+	endpoint@2 {
+		slave-mode;
+		remote-endpoint = <&ov2659_1>;
+	};
+};
+
+&vip1 {
+	status = "okay";
+};
+

+ 17 - 0
arch/arm/boot/dts/am572x-idk-common.dtsi

@@ -94,3 +94,20 @@
 		status = "okay";
 	};
 };
+
+&ov2659_1 {
+	remote-endpoint = <&vin4b>;
+};
+
+&vin4b {
+	status = "okay";
+
+	endpoint@3 {
+		slave-mode;
+		remote-endpoint = <&ov2659_1>;
+	};
+};
+
+&vip2 {
+	status = "okay";
+};

+ 25 - 0
arch/arm/boot/dts/am57xx-idk-common.dtsi

@@ -48,6 +48,12 @@
 		regulator-boot-on;
 	};
 
+	src_clk_x1: src_clk_x1 {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <20000000>;
+	};
+
 	leds-iio {
 		status = "disabled";
 		compatible = "gpio-leds";
@@ -338,6 +344,25 @@
 		gpio-controller;
 		#gpio-cells = <2>;
 	};
+
+	ov2659: ov2659@30 {
+		compatible = "ovti,ov2659";
+		reg = <0x30>;
+
+		clocks = <&src_clk_x1>;
+		clock-names = "xvclk";
+
+		pwrdn-gpios = <&gpio6 14 GPIO_ACTIVE_LOW>;
+
+		port {
+			ov2659_1: endpoint {
+				hsync-active = <1>;
+				vsync-active = <1>;
+				pclk-sample = <1>;
+				link-frequencies = /bits/ 64 <70000000>;
+			};
+		};
+	};
 };
 
 &mcspi3 {

+ 11 - 0
arch/arm/boot/dts/dra7-evm-common.dtsi

@@ -104,6 +104,12 @@
 			gpios = <&pcf_lcd 3 GPIO_ACTIVE_LOW>;
 		};
 	};
+
+	clk_ov10633_fixed: clk_ov10633_fixed {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <24000000>;
+	};
 };
 
 &i2c3 {
@@ -285,3 +291,8 @@
 		interrupts = <7 IRQ_TYPE_EDGE_RISING>;
 	};
 };
+
+&vip1 {
+	status = "okay";
+};
+

+ 26 - 0
arch/arm/boot/dts/dra7-evm.dts

@@ -343,6 +343,24 @@
 			line-name = "vin6_sel_s0";
 		};
 	};
+
+	ov10633@37 {
+		compatible = "ovti,ov10633";
+		reg = <0x37>;
+
+		clocks = <&clk_ov10633_fixed>;
+		clock-names = "xvclk";
+
+		mux-gpios = <&pcf_hdmi 3 GPIO_ACTIVE_LOW>; /* CAM_FPD_MUX_S0 */
+		port {
+			onboardLI: endpoint {
+				remote-endpoint = <&vin1a>;
+				hsync-active = <1>;
+				vsync-active = <1>;
+				pclk-sample = <0>;
+			};
+		};
+	};
 };
 
 &mmc1 {
@@ -530,3 +548,11 @@
 	pinctrl-1 = <&dcan1_pins_sleep>;
 	pinctrl-2 = <&dcan1_pins_default>;
 };
+
+&vin1a {
+	status = "okay";
+	endpoint@0 {
+		slave-mode;
+		remote-endpoint = <&onboardLI>;
+	};
+};

+ 69 - 0
arch/arm/boot/dts/dra7.dtsi

@@ -2109,6 +2109,75 @@
 			ti,absolute-max-voltage-uv = <1500000>;
 		};
 
+		vpe {
+			compatible = "ti,vpe";
+			ti,hwmods = "vpe";
+			clocks = <&dpll_core_h23x2_ck>;
+			clock-names = "fck";
+			reg = <0x489d0000 0x120>,
+			      <0x489d0700 0x80>,
+			      <0x489d5700 0x18>,
+			      <0x489dd000 0x400>;
+			reg-names = "vpe_top",
+				    "sc",
+				    "csc",
+				    "vpdma";
+			interrupts = <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		vip1: vip@0x48970000 {
+			compatible = "ti,vip1";
+			reg = <0x48970000 0x114>,
+			      <0x48975500 0xD8>,
+			      <0x48975700 0x18>,
+			      <0x48975800 0x80>,
+			      <0x48975a00 0xD8>,
+			      <0x48975c00 0x18>,
+			      <0x48975d00 0x80>,
+			      <0x4897d000 0x400>;
+			reg-names = "vip",
+				    "parser0",
+				    "csc0",
+				    "sc0",
+				    "parser1",
+				    "csc1",
+				    "sc1",
+				    "vpdma";
+			ti,hwmods = "vip1";
+			interrupts = <GIC_SPI 351 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 392 IRQ_TYPE_LEVEL_HIGH>;
+			/* CTRL_CORE_SMA_SW_1 */
+			syscon-pol = <&scm_conf 0x534>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+			vin1a: port@0 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <0>;
+				status = "disabled";
+			};
+			vin2a: port@1 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <1>;
+				status = "disabled";
+			};
+			vin1b: port@2 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <2>;
+				status = "disabled";
+			};
+			vin2b: port@3 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <3>;
+				status = "disabled";
+			};
+		};
 	};
 
 	thermal_zones: thermal-zones {

+ 6 - 0
arch/arm/boot/dts/dra71-evm.dts

@@ -150,6 +150,12 @@
 	};
 };
 
+&csi2_cam0 {
+	clock-lanes = <0>;
+	data-lanes = <1 2>;
+	remote-endpoint = <&csi2_phy0>;
+};
+
 &pcf_lcd {
 	interrupt-parent = <&gpio7>;
 	interrupts = <31 IRQ_TYPE_EDGE_FALLING>;

+ 77 - 0
arch/arm/boot/dts/dra72-evm-common.dtsi

@@ -185,6 +185,12 @@
 		};
 	};
 
+	clk_ov10633_fixed: clk_ov10633_fixed {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <24000000>;
+	};
+
 	vmmcwl_fixed: fixedregulator-mmcwl {
 		compatible = "regulator-fixed";
 		regulator-name = "vmmcwl_fixed";
@@ -275,6 +281,54 @@
 			line-name = "vin6_sel_s0";
 		};
 	};
+
+	ov10633@37 {
+		compatible = "ovti,ov10633";
+		reg = <0x37>;
+
+		clocks = <&clk_ov10633_fixed>;
+		clock-names = "xvclk";
+
+		mux-gpios = <&pcf_hdmi 2	GPIO_ACTIVE_HIGH>, /* VIN2_S0 */
+			    <&pcf_hdmi 6	GPIO_ACTIVE_LOW>; /* VIN2_S2 */
+		port {
+			onboardLI: endpoint {
+				remote-endpoint = <&vin2a>;
+				hsync-active = <1>;
+				vsync-active = <1>;
+				pclk-sample = <0>;
+			};
+		};
+	};
+
+	gpio_csi2_adap: tca6416@20 {
+		status = "okay";
+		compatible = "ti,tca6416";
+		reg = <0x20>;
+		gpio-controller;
+		#gpio-cells = <2>;
+	};
+
+	ov490@24 {
+		compatible = "ovti,ov490";
+		reg = <0x24>;
+
+		mux-gpios = <&gpio_csi2_adap 0	GPIO_ACTIVE_LOW>,
+						/* CSI2_SEL_I2C_CLK */
+			    <&gpio_csi2_adap 1	GPIO_ACTIVE_HIGH>,
+						/* CSI2_SEL_REF_CLK */
+			    <&gpio_csi2_adap 3	GPIO_ACTIVE_HIGH>,
+						/* CSI2_CAM0_RESETn */
+			    <&gpio_csi2_adap 4	GPIO_ACTIVE_LOW>;
+						/* CSI2_CAM0_PWR_DWN */
+		port {
+			csi2_cam0: endpoint@0 {
+				clock-lanes = <0>;
+				data-lanes = <1 2 3 4>;
+				remote-endpoint = <&csi2_phy0>;
+			};
+		};
+	};
 };
 
 &uart1 {
@@ -531,6 +585,29 @@
 	};
 };
 
+&vip1 {
+	status = "okay";
+};
+
+&vin2a {
+	status = "okay";
+	endpoint@0 {
+		slave-mode;
+		remote-endpoint = <&onboardLI>;
+	};
+};
+
+&cal {
+	status = "okay";
+};
+
+&csi2_0 {
+	csi2_phy0: endpoint@0 {
+		slave-mode;
+		remote-endpoint = <&csi2_cam0>;
+	};
+};
+
 &atl {
 	assigned-clocks = <&abe_dpll_sys_clk_mux>,
 			  <&atl_clkctrl DRA7_ATL_CLKCTRL 26>,

+ 41 - 0
arch/arm/boot/dts/dra72x.dtsi

@@ -17,6 +17,47 @@
 		interrupt-parent = <&wakeupgen>;
 		interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
 	};
+
+	ocp {
+		cal: cal@4845b000 {
+			compatible = "ti,dra72-cal";
+			ti,hwmods = "cal";
+			reg = <0x4845B000 0x400>,
+			      <0x4845B800 0x40>,
+			      <0x4845B900 0x40>;
+			reg-names = "cal_top",
+				    "cal_rx_core0",
+				    "cal_rx_core1";
+			interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
+			syscon-camerrx = <&scm_conf 0xE94>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				csi2_0: port@0 {
+					reg = <0>;
+				};
+				csi2_1: port@1 {
+					reg = <1>;
+				};
+			};
+		};
+	};
+};
+
+&scm {
+	dra72_vip_mux: pinmux@4a002e8c {
+		compatible = "pinctrl-single";
+		reg = <0xe8c 0x4>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-single,register-width = <32>;
+		pinctrl-single,function-mask = <0x7f>;
+	};
 };
 
 &dss {

+ 92 - 0
arch/arm/boot/dts/dra74x.dtsi

@@ -87,6 +87,98 @@
 			ti,syscon-mmuconfig = <&dsp2_system 0x1>;
 			status = "disabled";
 		};
+
+		vip2: vip@0x48990000 {
+			compatible = "ti,vip2";
+			reg = <0x48990000 0x114>,
+			      <0x48995500 0xD8>,
+			      <0x48995700 0x18>,
+			      <0x48995800 0x80>,
+			      <0x48995a00 0xD8>,
+			      <0x48995c00 0x18>,
+			      <0x48995d00 0x80>,
+			      <0x4899d000 0x400>;
+			reg-names = "vip",
+				    "parser0",
+				    "csc0",
+				    "sc0",
+				    "parser1",
+				    "csc1",
+				    "sc1",
+				    "vpdma";
+			ti,hwmods = "vip2";
+			interrupts = <GIC_SPI 352 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 393 IRQ_TYPE_LEVEL_HIGH>;
+			/* CTRL_CORE_SMA_SW_1 */
+			syscon-pol = <&scm_conf 0x534>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+			vin3a: port@0 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <0>;
+				status = "disabled";
+			};
+			vin4a: port@1 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <1>;
+				status = "disabled";
+			};
+			vin3b: port@2 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <2>;
+				status = "disabled";
+			};
+			vin4b: port@3 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <3>;
+				status = "disabled";
+			};
+		};
+
+		vip3: vip@0x489b0000 {
+			compatible = "ti,vip3";
+			reg = <0x489b0000 0x114>,
+			      <0x489b5500 0xD8>,
+			      <0x489b5700 0x18>,
+			      <0x489b5800 0x80>,
+			      <0x489b5a00 0xD8>,
+			      <0x489b5c00 0x18>,
+			      <0x489b5d00 0x80>,
+			      <0x489bd000 0x400>;
+			reg-names = "vip",
+				    "parser0",
+				    "csc0",
+				    "sc0",
+				    "parser1",
+				    "csc1",
+				    "sc1",
+				    "vpdma";
+			ti,hwmods = "vip3";
+			interrupts = <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 394 IRQ_TYPE_LEVEL_HIGH>;
+			/* CTRL_CORE_SMA_SW_1 */
+			syscon-pol = <&scm_conf 0x534>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+			vin5a: port@0 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <0>;
+				status = "disabled";
+			};
+			vin6a: port@1 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <1>;
+				status = "disabled";
+			};
+		};
 	};
 };
 

+ 73 - 0
arch/arm/boot/dts/dra76-evm.dts

@@ -320,6 +320,59 @@
 	};
 };
 
+&i2c5 {
+	status = "okay";
+	clock-frequency = <400000>;
+
+	ov10633@37 {
+		compatible = "ovti,ov10633";
+		reg = <0x37>;
+
+		clocks = <&clk_ov10633_fixed>;
+		clock-names = "xvclk";
+
+		mux-gpios = <&pcf_hdmi 2	GPIO_ACTIVE_HIGH>, /* VIN2_S0 */
+			    <&pcf_hdmi 6	GPIO_ACTIVE_LOW>; /* VIN2_S2 */
+		port {
+			onboardLI: endpoint {
+				remote-endpoint = <&vin2a>;
+				hsync-active = <1>;
+				vsync-active = <1>;
+				pclk-sample = <0>;
+			};
+		};
+	};
+
+	gpio_csi2_adap: tca6416@20 {
+		status = "okay";
+		compatible = "ti,tca6416";
+		reg = <0x20>;
+		gpio-controller;
+		#gpio-cells = <2>;
+	};
+
+	ov490@24 {
+		compatible = "ovti,ov490";
+		reg = <0x24>;
+
+		mux-gpios = <&gpio_csi2_adap 0	GPIO_ACTIVE_LOW>,
+						/* CSI2_SEL_I2C_CLK */
+			    <&gpio_csi2_adap 1	GPIO_ACTIVE_HIGH>,
+						/* CSI2_SEL_REF_CLK */
+			    <&gpio_csi2_adap 3	GPIO_ACTIVE_HIGH>,
+						/* CSI2_CAM0_RESETn */
+			    <&gpio_csi2_adap 4	GPIO_ACTIVE_LOW>;
+						/* CSI2_CAM0_PWR_DWN */
+		port {
+			csi2_cam0: endpoint@0 {
+				clock-lanes = <0>;
+				data-lanes = <1 2 3 4>;
+				remote-endpoint = <&csi2_phy0>;
+			};
+		};
+	};
+};
+
 &cpu0 {
 	vdd-supply = <&buck10_reg>;
 };
@@ -450,3 +503,23 @@
 		max-bitrate = <5000000>;
 	};
 };
+
+&cal {
+	status = "okay";
+};
+
+&csi2_0 {
+	csi2_phy0: endpoint@0 {
+		slave-mode;
+		remote-endpoint = <&csi2_cam0>;
+	};
+};
+
+&vin2a {
+	status = "okay";
+	endpoint@0 {
+		slave-mode;
+		remote-endpoint = <&onboardLI>;
+	};
+};
+

+ 28 - 0
arch/arm/boot/dts/dra76x.dtsi

@@ -47,6 +47,34 @@
 				bosch,mram-cfg = <0x0 0 0 32 0 0 1 1>;
 			};
 		};
+
+		cal: cal@489b0000 {
+			compatible = "ti,dra76-cal";
+			ti,hwmods = "cal";
+			reg = <0x489B0000 0x400>,
+			      <0x489B0800 0x40>,
+			      <0x489B0900 0x40>;
+				reg-names = "cal_top",
+					    "cal_rx_core0",
+					    "cal_rx_core1";
+			interrupts = <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>;
+			syscon-camerrx = <&scm_conf 0x6DC>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				csi2_0: port@0 {
+					reg = <0>;
+				};
+				csi2_1: port@1 {
+					reg = <1>;
+				};
+			};
+		};
 	};
 
 };

+ 24 - 0
arch/arm/boot/dts/ti/am57xx-evm.its

@@ -39,6 +39,22 @@
 			compression = "none";
 			load = <0x83080000>;
 		};
+		fdt@4 {
+			description = "MT9T111 camera support";
+			data = /incbin/("mt9t111.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83100000>;
+		};
+		fdt@5 {
+			description = "OV10635 camera support";
+			data = /incbin/("ov10635.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83100000>;
+		};
 	};
 
 	configurations {
@@ -63,5 +79,13 @@
 			kernel = "kernel@1";
 			fdt = "fdt@2";
 		};
+		cam-mt9t111 {
+			description = "MT9T111 camera";
+			fdt = "fdt@4";
+		};
+		cam-ov10635 {
+			description = "OV10635 camera";
+			fdt = "fdt@5";
+		};
 	};
 };

+ 45 - 0
arch/arm/boot/dts/ti/mt9t111.dtso

@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2018 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/gpio/gpio.h>
+
+&i2c5 {
+	mt9t11x@3C {
+		compatible = "aptina,mt9t111";
+		bufen-gpios = <&gpio5 12 GPIO_ACTIVE_LOW>;
+		camen-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>;
+		oscen-gpios = <&gpio5 10 GPIO_ACTIVE_HIGH>;
+		powerdown-gpios = <&gpio5 11 GPIO_ACTIVE_LOW>;
+		reg = <0x3C>;
+		reset-gpios = <&gpio4 17 GPIO_ACTIVE_LOW>;
+
+		port {
+			cam: endpoint {
+				hsync-active = <1>;
+				input-clock-freq = <32000000>;
+				pclk-sample = <0>;
+				pixel-clock-freq = <96000000>;
+				remote-endpoint = <&vin3a>;
+				vsync-active = <0>;
+			};
+		};
+	};
+};
+
+&vin3a {
+	status = "okay";
+
+	endpoint {
+		remote-endpoint = <&cam>;
+		slave-mode;
+	};
+};
+
+&vip2 {
+	status = "okay";
+};
+

+ 72 - 0
arch/arm/boot/dts/ti/ov10635.dtso

@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2018 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+  fragment@101 {
+	target-path = "/";
+
+	__overlay__ {
+		clk_ov10635: clk_ov10635 {
+			compatible = "gpio-gate-clock";
+			#clock-cells = <0>;
+			clocks = <&clk_ov10635_fixed>;
+			enable-gpios = <&gpio5 10 GPIO_ACTIVE_HIGH>;
+		};
+
+		clk_ov10635_fixed: clk_ov10635_fixed {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <24000000>;
+		};
+	};
+  };
+};
+
+&i2c5 {
+	ov10635@30 {
+		compatible = "ovti,ov10635";
+		clock-names = "xvclk";
+		clocks = <&clk_ov10635>;
+		powerdown-gpios = <&gpio5 11 GPIO_ACTIVE_HIGH>;
+		reg = <0x30>;
+		reset-gpios = <&gpio4 17 GPIO_ACTIVE_HIGH>;
+
+		port {
+			cam: endpoint {
+				hsync-active = <1>;
+				pclk-sample = <1>;
+				remote-endpoint = <&vin3a>;
+				vsync-active = <1>;
+			};
+		};
+	};
+};
+
+&vin3a {
+	status = "okay";
+
+	endpoint {
+		remote-endpoint = <&cam>;
+		slave-mode;
+	};
+};
+
+&vip2 {
+	status = "okay";
+};
+
+&gpio6 {
+	p11 {
+		gpio-hog;
+		gpios = <11 GPIO_ACTIVE_LOW>;
+		line-name = "cm-camen-gpio";
+		output-high;
+	};
+};
+

+ 1 - 1
arch/arm/mach-omap2/clockdomains7xx_data.c

@@ -609,7 +609,7 @@ static struct clockdomain cam_7xx_clkdm = {
 	.dep_bit	  = DRA7XX_CAM_STATDEP_SHIFT,
 	.wkdep_srcs	  = cam_wkup_sleep_deps,
 	.sleepdep_srcs	  = cam_wkup_sleep_deps,
-	.flags		  = CLKDM_CAN_HWSUP_SWSUP,
+	.flags		  = CLKDM_CAN_SWSUP,
 };
 
 static struct clockdomain l4per_7xx_clkdm = {

+ 209 - 0
arch/arm/mach-omap2/omap_hwmod_7xx_data.c

@@ -235,6 +235,160 @@ static struct omap_hwmod dra7xx_bb2d_hwmod = {
 	},
 };
 
+/*
+ * 'vpe' class
+ *
+ */
+
+static struct omap_hwmod_class_sysconfig dra7xx_vpe_sysc = {
+	.rev_offs	= -ENODEV,
+	.sysc_offs	= 0x0010,
+	.sysc_flags	= (SYSC_HAS_MIDLEMODE | SYSC_HAS_SIDLEMODE),
+	.idlemodes	= (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+			   MSTANDBY_FORCE | MSTANDBY_NO |
+			   MSTANDBY_SMART),
+	.sysc_fields	= &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class dra7xx_vpe_hwmod_class = {
+	.name	= "vpe",
+	.sysc	= &dra7xx_vpe_sysc,
+};
+
+/* vpe */
+static struct omap_hwmod dra7xx_vpe_hwmod = {
+	.name		= "vpe",
+	.class		= &dra7xx_vpe_hwmod_class,
+	.clkdm_name	= "vpe_clkdm",
+	.flags		= (HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY),
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = DRA7XX_CM_VPE_VPE_CLKCTRL_OFFSET,
+			.context_offs = DRA7XX_RM_VPE_VPE_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
+		},
+	},
+};
+
+/*
+ * 'vip' class
+ *
+ */
+
+static struct omap_hwmod_class_sysconfig dra7xx_vip_sysc = {
+	.rev_offs	= -ENODEV,
+	.sysc_offs	= 0x0010,
+	.sysc_flags	= (SYSC_HAS_MIDLEMODE | SYSC_HAS_SIDLEMODE),
+	.idlemodes	= (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+			   MSTANDBY_FORCE | MSTANDBY_NO |
+			   MSTANDBY_SMART),
+	.sysc_fields	= &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class dra7xx_vip_hwmod_class = {
+	.name	= "vip",
+	.sysc	= &dra7xx_vip_sysc,
+};
+
+/* vip1 */
+static struct omap_hwmod dra7xx_vip1_hwmod = {
+	.name		= "vip1",
+	.class		= &dra7xx_vip_hwmod_class,
+	.clkdm_name	= "cam_clkdm",
+	.main_clk	= "vip1_gclk_mux",
+	.flags		= (HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY),
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = DRA7XX_CM_CAM_VIP1_CLKCTRL_OFFSET,
+			.context_offs = DRA7XX_RM_CAM_VIP1_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
+		},
+	},
+};
+
+/* vip2 */
+static struct omap_hwmod dra7xx_vip2_hwmod = {
+	.name		= "vip2",
+	.class		= &dra7xx_vip_hwmod_class,
+	.clkdm_name	= "cam_clkdm",
+	.main_clk	= "vip2_gclk_mux",
+	.flags		= (HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY),
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = DRA7XX_CM_CAM_VIP2_CLKCTRL_OFFSET,
+			.context_offs = DRA7XX_RM_CAM_VIP2_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
+		},
+	},
+};
+
+/* vip3 */
+static struct omap_hwmod dra7xx_vip3_hwmod = {
+	.name		= "vip3",
+	.class		= &dra7xx_vip_hwmod_class,
+	.clkdm_name	= "cam_clkdm",
+	.main_clk	= "vip3_gclk_mux",
+	.flags		= (HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY),
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = DRA7XX_CM_CAM_VIP3_CLKCTRL_OFFSET,
+			.context_offs = DRA7XX_RM_CAM_VIP3_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
+		},
+	},
+};
+
+/*
+ * 'cal' class
+ *
+ */
+
+static struct omap_hwmod_class_sysconfig dra7xx_cal_sysc = {
+	.rev_offs	= 0x0000,
+	.sysc_offs	= 0x0010,
+	.sysc_flags	= (SYSC_HAS_SIDLEMODE | SYSC_HAS_RESET_STATUS |
+			   SYSC_HAS_SOFTRESET | SYSC_HAS_MIDLEMODE),
+	.idlemodes	= (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+			   MSTANDBY_FORCE | MSTANDBY_NO),
+	.sysc_fields	= &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class dra7xx_cal_hwmod_class = {
+	.name	= "cal",
+	.sysc	= &dra7xx_cal_sysc,
+};
+
+/* cal */
+static struct omap_hwmod dra7xx_cal_hwmod = {
+	.name		= "cal",
+	.class		= &dra7xx_cal_hwmod_class,
+	.clkdm_name	= "cam_clkdm",
+	.main_clk	= "vip2_gclk_mux",
+	.flags		= (HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY),
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = DRA7XX_CM_CAM_VIP2_CLKCTRL_OFFSET,
+			.context_offs = DRA7XX_RM_CAM_VIP2_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
+		},
+	},
+};
+
+static struct omap_hwmod dra76x_cal_hwmod = {
+	.name		= "cal",
+	.class		= &dra7xx_cal_hwmod_class,
+	.clkdm_name	= "cam_clkdm",
+	.main_clk	= "vip3_gclk_mux",
+	.flags		= (HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY),
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = DRA7XX_CM_CAM_VIP3_CLKCTRL_OFFSET,
+			.context_offs = DRA7XX_RM_CAM_VIP3_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
+		},
+	},
+};
+
 /*
  * 'counter' class
  *
@@ -3792,6 +3946,54 @@ static struct omap_hwmod_ocp_if dra7xx_l4_per2__vcp2 = {
 	.user		= OCP_USER_MPU | OCP_USER_SDMA,
 };
 
+/* l4_per3 -> vpe */
+static struct omap_hwmod_ocp_if dra7xx_l4_per3__vpe = {
+	.master		= &dra7xx_l4_per3_hwmod,
+	.slave		= &dra7xx_vpe_hwmod,
+	.clk		= "l3_iclk_div",
+	.user		= OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l4_per3 -> vip1 */
+static struct omap_hwmod_ocp_if dra7xx_l4_per3__vip1 = {
+	.master		= &dra7xx_l4_per3_hwmod,
+	.slave		= &dra7xx_vip1_hwmod,
+	.clk		= "l3_iclk_div",
+	.user		= OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l4_per3 -> vip2 */
+static struct omap_hwmod_ocp_if dra7xx_l4_per3__vip2 = {
+	.master		= &dra7xx_l4_per3_hwmod,
+	.slave		= &dra7xx_vip2_hwmod,
+	.clk		= "l3_iclk_div",
+	.user		= OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l4_per3 -> vip3 */
+static struct omap_hwmod_ocp_if dra7xx_l4_per3__vip3 = {
+	.master		= &dra7xx_l4_per3_hwmod,
+	.slave		= &dra7xx_vip3_hwmod,
+	.clk		= "l3_iclk_div",
+	.user		= OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l4_per2 -> cal */
+static struct omap_hwmod_ocp_if dra7xx_l4_per2__cal = {
+	.master		= &dra7xx_l4_per2_hwmod,
+	.slave		= &dra7xx_cal_hwmod,
+	.clk		= "l3_iclk_div",
+	.user		= OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l4_per3 -> dra76x_cal */
+static struct omap_hwmod_ocp_if dra76x_l4_per3__cal = {
+	.master		= &dra7xx_l4_per3_hwmod,
+	.slave		= &dra76x_cal_hwmod,
+	.clk		= "l3_iclk_div",
+	.user		= OCP_USER_MPU | OCP_USER_SDMA,
+};
+
 /* l4_wkup -> wd_timer2 */
 static struct omap_hwmod_ocp_if dra7xx_l4_wkup__wd_timer2 = {
 	.master		= &dra7xx_l4_wkup_hwmod,
@@ -3947,6 +4149,8 @@ static struct omap_hwmod_ocp_if *dra7xx_hwmod_ocp_ifs[] __initdata = {
 	&dra7xx_l4_per2__vcp1,
 	&dra7xx_l3_main_1__vcp2,
 	&dra7xx_l4_per2__vcp2,
+	&dra7xx_l4_per3__vpe,
+	&dra7xx_l4_per3__vip1,
 	&dra7xx_l4_wkup__wd_timer2,
 	&dra7xx_l4_per2__epwmss0,
 	&dra7xx_l4_per2__epwmss1,
@@ -3964,19 +4168,24 @@ static struct omap_hwmod_ocp_if *dra7xx_gp_hwmod_ocp_ifs[] __initdata = {
 /* SoC variant specific hwmod links */
 static struct omap_hwmod_ocp_if *dra76x_hwmod_ocp_ifs[] __initdata = {
 	&dra7xx_l4_per3__usb_otg_ss4,
+	&dra7xx_l4_per3__vip2,
 	NULL,
 };
 
 static struct omap_hwmod_ocp_if *acd_76x_hwmod_ocp_ifs[] __initdata = {
+	&dra76x_l4_per3__cal,
 	NULL,
 };
 
 static struct omap_hwmod_ocp_if *dra74x_hwmod_ocp_ifs[] __initdata = {
 	&dra7xx_l4_per3__usb_otg_ss4,
+	&dra7xx_l4_per3__vip2,
+	&dra7xx_l4_per3__vip3,
 	NULL,
 };
 
 static struct omap_hwmod_ocp_if *dra72x_hwmod_ocp_ifs[] __initdata = {
+	&dra7xx_l4_per2__cal,
 	NULL,
 };
 

+ 25 - 0
arch/arm64/boot/dts/ti/k3-am65-main.dtsi

@@ -485,4 +485,29 @@
 			#size-cells = <0>;
 		};
 	};
+
+	cal: cal@6f03000 {
+		compatible = "ti,am654-cal";
+		reg = <0x0 0x06f03000 0x0 0x400>,
+		      <0x0 0x06f03800 0x0 0x40>;
+		reg-names = "cal_top",
+			    "cal_rx_core0";
+		interrupts = <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>;
+		syscon-camerrx = <&scm_conf 0x40c0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		clock-names = "fck";
+		clocks = <&k3_clks 2 0>;
+		power-domains = <&k3_pds 2>;
+		status = "disabled";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			csi2_0: port@0 {
+				reg = <0>;
+			};
+		};
+	};
 };

+ 52 - 0
arch/arm64/boot/dts/ti/k3-am654-evm-csi2-ov490.dtso

@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/gpio/gpio.h>
+
+&main_i2c1 {
+	#address-cells = <1>;
+	#size-cells = <0>;
+	gpio_csi2_adap: tca6416@20 {
+		status = "okay";
+		compatible = "ti,tca6416";
+		reg = <0x20>;
+		gpio-controller;
+		#gpio-cells = <2>;
+	};
+
+	ov490@24 {
+		compatible = "ovti,ov490";
+		reg = <0x24>;
+
+		mux-gpios = <&gpio_csi2_adap 0  GPIO_ACTIVE_LOW>,
+						/* CSI2_SEL_I2C_CLK */
+			    <&gpio_csi2_adap 1  GPIO_ACTIVE_HIGH>,
+						/* CSI2_SEL_REF_CLK */
+			    <&gpio_csi2_adap 3  GPIO_ACTIVE_HIGH>,
+						/* CSI2_CAM0_RESETn */
+			    <&gpio_csi2_adap 4  GPIO_ACTIVE_LOW>;
+						/* CSI2_CAM0_PWR_DWN */
+		port {
+			csi2_cam0: endpoint@0 {
+				clock-lanes = <0>;
+				data-lanes = <1 2 3 4>;
+				remote-endpoint = <&csi2_phy0>;
+			};
+		};
+	};
+};
+
+&cal {
+	status = "okay";
+};
+
+&csi2_0 {
+	csi2_phy0: endpoint@0 {
+		slave-mode;
+		remote-endpoint = <&csi2_cam0>;
+	};
+};

+ 7 - 0
drivers/input/touchscreen/goodix.c

@@ -914,6 +914,13 @@ static SIMPLE_DEV_PM_OPS(goodix_pm_ops, goodix_suspend, goodix_resume);
 
 static const struct i2c_device_id goodix_ts_id[] = {
 	{ "GDIX1001:00", 0 },
+	{ "gt911", 0 },
+	{ "gt9110", 0 },
+	{ "gt912", 0 },
+	{ "gt927", 0 },
+	{ "gt9271", 0 },
+	{ "gt928", 0 },
+	{ "gt967", 0 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, goodix_ts_id);

+ 34 - 1
drivers/media/i2c/Kconfig

@@ -628,7 +628,7 @@ config VIDEO_OV2640
 config VIDEO_OV2659
 	tristate "OmniVision OV2659 sensor support"
 	depends on VIDEO_V4L2 && I2C
-	depends on MEDIA_CAMERA_SUPPORT
+	depends on MEDIA_CAMERA_SUPPORT && GPIOLIB
 	select V4L2_FWNODE
 	---help---
 	  This is a Video4Linux2 sensor driver for the OmniVision
@@ -799,6 +799,29 @@ config VIDEO_OV13858
 	  This is a Video4Linux2 sensor driver for the OmniVision
 	  OV13858 camera.
 
+config VIDEO_OV1063X
+	tristate "OmniVision OV1063X sensor support"
+	depends on I2C && VIDEO_V4L2
+	depends on MEDIA_CAMERA_SUPPORT && GPIOLIB
+	help
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV1063X Sensor.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ov1063x.
+
+config VIDEO_OV490
+	tristate "OmniVision OV490 sensor support"
+	depends on I2C && VIDEO_V4L2 && GPIOLIB
+	depends on MEDIA_CAMERA_SUPPORT
+	select V4L2_FWNODE
+	help
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV490 Sensor.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ov490.
+
 config VIDEO_VS6624
 	tristate "ST VS6624 sensor support"
 	depends on VIDEO_V4L2 && I2C
@@ -884,6 +907,16 @@ config VIDEO_MT9V111
 	  To compile this driver as a module, choose M here: the
 	  module will be called mt9v111.
 
+config VIDEO_MT9T11X
+	tristate "Aptina MT9T11x sensor support"
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && GPIOLIB
+	help
+	  This is a V4L2 sensor-level driver for the Aptina
+	  MT9T11x camera sensors.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mt9t11x.
+
 config VIDEO_SR030PC30
 	tristate "Siliconfile SR030PC30 sensor support"
 	depends on I2C && VIDEO_V4L2

+ 3 - 0
drivers/media/i2c/Makefile

@@ -80,6 +80,8 @@ obj-$(CONFIG_VIDEO_OV772X) += ov772x.o
 obj-$(CONFIG_VIDEO_OV7740) += ov7740.o
 obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
 obj-$(CONFIG_VIDEO_OV13858) += ov13858.o
+obj-$(CONFIG_VIDEO_OV1063X) += ov1063x.o
+obj-$(CONFIG_VIDEO_OV490) += ov490.o
 obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
 obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o
 obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
@@ -88,6 +90,7 @@ obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o
 obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
 obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
 obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o
+obj-$(CONFIG_VIDEO_MT9T11X)	+= mt9t11x.o
 obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
 obj-$(CONFIG_VIDEO_NOON010PC30)	+= noon010pc30.o
 obj-$(CONFIG_VIDEO_RJ54N1)	+= rj54n1cb0c.o

+ 2291 - 0
drivers/media/i2c/mt9t11x.c

@@ -0,0 +1,2291 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Aptina mt9t11x CMOS Image Sensor driver
+ *
+ * This an adaptation of the existing soc_camera/mt9t112.c
+ * to use the newer v4l2 subdevice framework.
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Benoit Parrot <bparrot@ti.com>
+ *
+ * Register definitions and initial settings based on
+ * soc_camera/mt9t112.c driver written by Kuninori Morimoto.
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/media.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/gpio/consumer.h>
+#include <linux/ratelimit.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/videodev2.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-common.h>
+#include <linux/of_graph.h>
+#include <media/v4l2-fwnode.h>
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+#define DRIVER_NAME "mt9t11x"
+
+#define MT9T111_ID	0
+#define MT9T112_ID	1
+
+/* you can check PLL/clock info */
+#define EXT_CLOCK 32000000
+#define MT9T11x_DEF_PIXEL_CLOCK 96000000
+#define MT9T11x_DEF_EXT_CLOCK 32000000
+
+#define MT9T11x_MAX_EXT_CLK 54000000
+#define MT9T11x_MAX_VCO_CLK 768000000
+#define MT9T11x_MAX_PIXEL_CLK 96000000
+#define MT9T11x_MAX_MIPI_CLK 768000000
+#define MT9T11x_MAX_MCU_CLK 96000000
+#define MT9T11x_MAX_SOC_CLK 54000000
+#define MT9T11x_MAX_SENSOR_CLK 70000000
+#define MT9T11x_MAX_PFD_CLK 24000000
+
+#define MT9T11x_FLAG_VFLIP	BIT(2)
+#define MT9T11x_FLAG_HFLIP	BIT(3)
+
+struct mt9t11x_pll_divider {
+	u8 m, n;
+	u8 p1, p2, p3, p4, p5, p6, p7;
+};
+
+/*
+ * mt9t11x camera info
+ */
+struct mt9t11x_camera_info {
+	u32 flags;
+	struct mt9t11x_pll_divider divider;
+	unsigned int mclk;
+	unsigned int pclk;
+	struct v4l2_fwnode_endpoint endpoint;
+};
+
+/************************************************************************
+ *			macro
+ ***********************************************************************/
+/*
+ * frame size
+ */
+#define MIN_WIDTH   32
+#define MIN_HEIGHT  32
+#define MAX_WIDTH   2048
+#define MAX_HEIGHT  1536
+
+#define VGA_WIDTH   640
+#define VGA_HEIGHT  480
+
+/*
+ * Logical address
+ */
+#define _VAR(id, offset, base) ((base) | ((id) & 0x1f) << 10 | \
+				((offset) & 0x3ff))
+#define VAR(id, offset)  _VAR((id), (offset), 0x0000)
+#define VAR8(id, offset) _VAR((id), (offset), 0x8000)
+
+/************************************************************************
+ *			struct
+ ***********************************************************************/
+struct mt9t11x_format {
+	u32 code;
+	u32 colorspace;
+	u16 fmt;
+	u16 order;
+};
+
+struct mt9t11x_framesize {
+	u16 width;
+	u16 height;
+};
+
+struct mt9t11x_resolution_param {
+	u16 col_strt;
+	u16 row_end;
+	u16 col_end;
+	u16 read_mode;
+	u16 fine_cor;
+	u16 fine_min;
+	u16 fine_max;
+	u16 base_lines;
+	u16 min_lin_len;
+	u16 line_len;
+	u16 con_width;
+	u16 con_height;
+	u16 s_f1_50;
+	u16 s_f2_50;
+	u16 s_f1_60;
+	u16 s_f2_60;
+	u16 per_50;
+	u16 per_50_M;
+	u16 per_60;
+	u16 fd_w_height;
+	u16 tx_water;
+	u16 max_fd_50;
+	u16 max_fd_60;
+	u16 targ_fd;
+};
+
+struct mt9t11x_priv {
+	struct v4l2_subdev		subdev;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	struct media_pad		pad;
+#endif
+	struct v4l2_ctrl_handler	handler;
+
+	struct mt9t11x_camera_info	*info;
+	struct i2c_client		*client;
+	struct v4l2_rect		frame;
+	struct v4l2_clk			*clk;
+	const struct mt9t11x_format	*format;
+	int				num_formats;
+	u32				flags;
+
+	/* GPIOs */
+	struct gpio_desc		*reset_gpio;
+	struct gpio_desc		*powerdown_gpio;
+	struct gpio_desc		*oscen_gpio;
+	struct gpio_desc		*bufen_gpio;
+	struct gpio_desc		*camen_gpio;
+
+	/* V4L2 Ctrl handle */
+	struct v4l2_ctrl		*hflip;
+	struct v4l2_ctrl		*vflip;
+
+	/* Protects the struct fields below */
+	struct mutex			lock;
+	int				streaming;
+	int				power;
+
+	struct mt9t11x_resolution_param	resolution;
+};
+
+/************************************************************************
+ *			supported frame sizes
+ ***********************************************************************/
+
+static const struct mt9t11x_framesize mt9t11x_framesizes[] = {
+	{
+		.width		= 2048,
+		.height		= 1536,
+	}, {
+		.width		= 1920,
+		.height		= 1200,
+	}, {
+		.width		= 1920,
+		.height		= 1080,
+	}, {
+		.width		= 1600,
+		.height		= 1200,
+	}, {
+		.width		= 1440,
+		.height		= 900,
+	}, {
+		.width		= 1280,
+		.height		= 800,
+	}, {
+		.width		= 1280,
+		.height		= 720,
+	}, {
+		.width		= 800,
+		.height		= 600,
+	}, {
+		.width		= 800,
+		.height		= 480,
+	}, {
+		.width		= 640,
+		.height		= 480,
+	}, {
+		.width		= 600,
+		.height		= 400,
+	}, {
+		.width		= 352,
+		.height		= 288,
+	}, {
+		.width		= 320,
+		.height		= 240,
+	}, {
+		.width		= 160,
+		.height		= 120,
+	},
+};
+
+/************************************************************************
+ *			supported format
+ ***********************************************************************/
+
+static const struct mt9t11x_format mt9t11x_cfmts[] = {
+	{
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+		.fmt		= 1,
+		.order		= 0,
+	}, {
+		.code		= MEDIA_BUS_FMT_VYUY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+		.fmt		= 1,
+		.order		= 1,
+	}, {
+		.code		= MEDIA_BUS_FMT_YUYV8_2X8,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+		.fmt		= 1,
+		.order		= 2,
+	}, {
+		.code		= MEDIA_BUS_FMT_YVYU8_2X8,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+		.fmt		= 1,
+		.order		= 3,
+	}, {
+		.code		= MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.fmt		= 8,
+		.order		= 2,
+	}, {
+		.code		= MEDIA_BUS_FMT_RGB565_2X8_LE,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.fmt		= 4,
+		.order		= 2,
+	},
+};
+
+/************************************************************************
+ *			general function
+ ***********************************************************************/
+static inline struct mt9t11x_priv *sd_to_mt9t11x(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct mt9t11x_priv, subdev);
+}
+
+static inline struct mt9t11x_priv *to_mt9t11x(const struct i2c_client *client)
+{
+	return sd_to_mt9t11x(i2c_get_clientdata(client));
+}
+
+static inline struct mt9t11x_priv *ctrl_to_mt9t11x(struct v4l2_ctrl *ctrl)
+{
+	return container_of(ctrl->handler, struct mt9t11x_priv, handler);
+}
+
+static int mt9t11x_reg_read(const struct i2c_client *client, u16 command)
+{
+	struct i2c_msg msg[2];
+	u8 buf[2];
+	int ret;
+
+	command = swab16(command);
+
+	msg[0].addr  = client->addr;
+	msg[0].flags = 0;
+	msg[0].len   = 2;
+	msg[0].buf   = (u8 *)&command;
+
+	msg[1].addr  = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len   = 2;
+	msg[1].buf   = buf;
+
+	/*
+	 * if return value of this function is < 0,
+	 * it mean error.
+	 * else, under 16bit is valid data.
+	 */
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret < 0)
+		return ret;
+
+	memcpy(&ret, buf, 2);
+	return swab16(ret);
+}
+
+static int mt9t11x_reg_write(const struct i2c_client *client,
+			     u16 command, u16 data)
+{
+	struct i2c_msg msg;
+	u8 buf[4];
+	int ret;
+
+	command = swab16(command);
+	data = swab16(data);
+
+	memcpy(buf + 0, &command, 2);
+	memcpy(buf + 2, &data,    2);
+
+	msg.addr  = client->addr;
+	msg.flags = 0;
+	msg.len   = 4;
+	msg.buf   = buf;
+
+	/*
+	 * i2c_transfer return message length,
+	 * but this function should return 0 if correct case
+	 */
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret >= 0)
+		ret = 0;
+
+	return ret;
+}
+
+static int mt9t11x_reg_mask_set(const struct i2c_client *client,
+				u16  command,
+				u16  mask,
+				u16  set)
+{
+	int val = mt9t11x_reg_read(client, command);
+
+	if (val < 0)
+		return val;
+
+	val &= ~mask;
+	val |= set & mask;
+
+	return mt9t11x_reg_write(client, command, val);
+}
+
+/* mcu access */
+static int mt9t11x_mcu_read(const struct i2c_client *client, u16 command)
+{
+	int ret;
+
+	ret = mt9t11x_reg_write(client, 0x098E, command);
+	if (ret < 0)
+		return ret;
+
+	return mt9t11x_reg_read(client, 0x0990);
+}
+
+static int mt9t11x_mcu_write(const struct i2c_client *client,
+			     u16 command, u16 data)
+{
+	int ret;
+
+	ret = mt9t11x_reg_write(client, 0x098E, command);
+	if (ret < 0)
+		return ret;
+
+	return mt9t11x_reg_write(client, 0x0990, data);
+}
+
+static int mt9t11x_mcu_mask_set(const struct i2c_client *client,
+				u16  command,
+				u16  mask,
+				u16  set)
+{
+	int val = mt9t11x_mcu_read(client, command);
+
+	if (val < 0)
+		return val;
+
+	val &= ~mask;
+	val |= set & mask;
+
+	return mt9t11x_mcu_write(client, command, val);
+}
+
+static int mt9t11x_reset(const struct i2c_client *client)
+{
+	int ret;
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	ret = mt9t11x_reg_mask_set(client, 0x001a, 0x0001, 0x0001);
+	if (ret < 0)
+		return ret;
+	usleep_range(1000, 2000);
+	ret = mt9t11x_reg_mask_set(client, 0x001a, 0x0001, 0x0000);
+	if (ret < 0)
+		return ret;
+
+	return ret;
+}
+
+static int mt9t11x_streaming(struct mt9t11x_priv *priv, int on)
+{
+	struct i2c_client *client = priv->client;
+	int ret, tmp;
+
+	dev_dbg(&client->dev, "%s: on: %d\n", __func__, on);
+	on = (on) ? 1 : 0;
+
+	ret = mt9t11x_reg_mask_set(client, 0x001a, 0x0200, on << 9);
+	if (ret < 0)
+		return ret;
+
+	tmp = mt9t11x_reg_read(client, 0x001A);
+	dev_dbg(&client->dev, "reset_and_misc_control: 0x%04x\n", tmp);
+
+	return 0;
+}
+
+#define CLOCK_INFO(a, b)				\
+	do {						\
+		if (debug > 1)				\
+			mt9t11x_clock_info(a, b);	\
+	} while (0)
+static int mt9t11x_clock_info(const struct i2c_client *client, u32 ext)
+{
+	int m, n, p1, p2, p3, p4, p5, p6, p7;
+	u32 vco, clk;
+	char *enable;
+
+	ext /= 1000; /* kbyte order */
+
+	n = mt9t11x_reg_read(client, 0x0012);
+	p1 = n & 0x000f;
+	n = n >> 4;
+	p2 = n & 0x000f;
+	n = n >> 4;
+	p3 = n & 0x000f;
+
+	n = mt9t11x_reg_read(client, 0x002a);
+	p4 = n & 0x000f;
+	n = n >> 4;
+	p5 = n & 0x000f;
+	n = n >> 4;
+	p6 = n & 0x000f;
+
+	n = mt9t11x_reg_read(client, 0x002c);
+	p7 = n & 0x000f;
+
+	n = mt9t11x_reg_read(client, 0x0010);
+	m = n & 0x00ff;
+	n = (n >> 8) & 0x003f;
+
+	enable = ((ext < 6000) || (ext > 54000)) ? "X" : "";
+	dev_dbg(&client->dev, "EXTCLK          : %10u K %s\n", ext, enable);
+
+	vco = 2 * m * ext / (n + 1);
+	enable = ((vco < 384000) || (vco > 768000)) ? "X" : "";
+	dev_dbg(&client->dev, "VCO             : %10u K %s\n", vco, enable);
+
+	clk = vco / (p1 + 1) / (p2 + 1);
+	enable = (clk > 96000) ? "X" : "";
+	dev_dbg(&client->dev, "PIXCLK          : %10u K %s\n", clk, enable);
+
+	clk = vco / (p3 + 1);
+	enable = (clk > 768000) ? "X" : "";
+	dev_dbg(&client->dev, "MIPICLK         : %10u K %s\n", clk, enable);
+
+	clk = vco / (p6 + 1);
+	enable = (clk > 96000) ? "X" : "";
+	dev_dbg(&client->dev, "MCU CLK         : %10u K %s\n", clk, enable);
+
+	clk = vco / (p5 + 1);
+	enable = (clk > 54000) ? "X" : "";
+	dev_dbg(&client->dev, "SOC CLK         : %10u K %s\n", clk, enable);
+
+	clk = vco / (p4 + 1);
+	enable = (clk > 70000) ? "X" : "";
+	dev_dbg(&client->dev, "Sensor CLK      : %10u K %s\n", clk, enable);
+
+	clk = vco / (p7 + 1);
+	dev_dbg(&client->dev, "External sensor : %10u K\n", clk);
+
+	clk = ext / (n + 1);
+	enable = ((clk < 2000) || (clk > 24000)) ? "X" : "";
+	dev_dbg(&client->dev, "PFD             : %10u K %s\n", clk, enable);
+
+	return 0;
+}
+
+static void mt9t11x_frame_check(u32 *width, u32 *height, u32 *left, u32 *top)
+{
+	v4l_bound_align_image(width, MIN_WIDTH, MAX_WIDTH, 1,
+			      height, MIN_HEIGHT, MAX_HEIGHT, 1, 0);
+	*left = 0;
+	*top = 0;
+}
+
+static int mt9t11x_set_a_frame_size(const struct i2c_client *client,
+				    u16 width,
+				    u16 height)
+{
+	int ret;
+	u16 wstart = (MAX_WIDTH - width) / 2;
+	u16 hstart = (MAX_HEIGHT - height) / 2;
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	/* (Context A) Image Width/Height */
+	ret = mt9t11x_mcu_write(client, VAR(26, 0), width);
+	if (ret < 0)
+		return ret;
+	ret = mt9t11x_mcu_write(client, VAR(26, 2), height);
+	if (ret < 0)
+		return ret;
+
+	/* (Context A) Output Width/Height */
+	ret = mt9t11x_mcu_write(client, VAR(18, 43), 8 + width);
+	if (ret < 0)
+		return ret;
+	ret = mt9t11x_mcu_write(client, VAR(18, 45), 8 + height);
+	if (ret < 0)
+		return ret;
+
+	/* (Context A) Start Row/Column */
+	ret = mt9t11x_mcu_write(client, VAR(18, 2), 4 + hstart);
+	if (ret < 0)
+		return ret;
+	ret = mt9t11x_mcu_write(client, VAR(18, 4), 4 + wstart);
+	if (ret < 0)
+		return ret;
+
+	/* (Context A) End Row/Column */
+	ret = mt9t11x_mcu_write(client, VAR(18, 6), 11 + height + hstart);
+	if (ret < 0)
+		return ret;
+	ret = mt9t11x_mcu_write(client, VAR(18, 8), 11 + width  + wstart);
+	if (ret < 0)
+		return ret;
+
+	ret = mt9t11x_mcu_write(client, VAR8(1, 0), 0x06);
+	if (ret < 0)
+		return ret;
+
+	return ret;
+}
+
+static int mt9t11x_set_pll_dividers(const struct i2c_client *client,
+				    u8 m, u8 n,
+				    u8 p1, u8 p2, u8 p3,
+				    u8 p4, u8 p5, u8 p6,
+				    u8 p7)
+{
+	int ret;
+	u16 val;
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	/* N/M */
+	val = (n << 8) |
+	      (m << 0);
+	ret = mt9t11x_reg_mask_set(client, 0x0010, 0x3fff, val);
+	if (ret < 0)
+		return ret;
+
+	/* P1/P2/P3 */
+	val = ((p3 & 0x0F) << 8) |
+	      ((p2 & 0x0F) << 4) |
+	      ((p1 & 0x0F) << 0);
+	ret = mt9t11x_reg_mask_set(client, 0x0012, 0x0fff, val);
+	if (ret < 0)
+		return ret;
+
+	/* P4/P5/P6 */
+	val = (0x7         << 12) |
+	      ((p6 & 0x0F) <<  8) |
+	      ((p5 & 0x0F) <<  4) |
+	      ((p4 & 0x0F) <<  0);
+	ret = mt9t11x_reg_mask_set(client, 0x002A, 0x7fff, val);
+	if (ret < 0)
+		return ret;
+
+	/* P7 */
+	val = (0x1         << 12) |
+	      ((p7 & 0x0F) <<  0);
+	ret = mt9t11x_reg_mask_set(client, 0x002C, 0x100f, val);
+	if (ret < 0)
+		return ret;
+
+	return ret;
+}
+
+static int mt9t11x_set_resolution_params(const struct i2c_client *client)
+{
+	int ret = 1;
+	struct mt9t11x_priv *priv = to_mt9t11x(client);
+	struct mt9t11x_resolution_param *resolution = &priv->resolution;
+
+	if (priv->frame.width == 1280 && priv->frame.height == 720) {
+		resolution->col_strt    = 0x0004;
+		resolution->row_end     = 0x05AD;
+		resolution->col_end     = 0x050B;
+		resolution->read_mode   = 0x002C;
+		resolution->fine_cor    = 0x008C;
+		resolution->fine_min    = 0x01F1;
+		resolution->fine_max    = 0x00FF;
+		resolution->base_lines  = 0x032D;
+		resolution->min_lin_len = 0x0378;
+		resolution->line_len    = 0x091C;
+		resolution->con_width   = 0x0508;
+		resolution->con_height  = 0x02D8;
+		resolution->s_f1_50     = 0x23;
+		resolution->s_f2_50     = 0x25;
+		resolution->s_f1_60     = 0x2B;
+		resolution->s_f2_60     = 0x2D;
+		resolution->per_50      = 0xDC;
+		resolution->per_50_M    = 0x00;
+		resolution->per_60      = 0xB7;
+		resolution->fd_w_height = 0x05;
+		resolution->tx_water    = 0x0210;
+		resolution->max_fd_50   = 0x0004;
+		resolution->max_fd_60   = 0x0004;
+		resolution->targ_fd     = 0x0004;
+	} else if (priv->frame.width <= 1024 &&
+		   priv->frame.height <= 768 &&
+		   priv->frame.width != priv->frame.height) {
+		resolution->col_strt    = 0x000;
+		resolution->row_end     = 0x60D;
+		resolution->col_end     = 0x80D;
+		resolution->read_mode   = 0x046C;
+		resolution->fine_cor    = 0x00CC;
+		resolution->fine_min    = 0x0381;
+		resolution->fine_max    = 0x024F;
+		resolution->base_lines  = 0x0364;
+		resolution->min_lin_len = 0x05D0;
+		resolution->line_len    = 0x07AC;
+		resolution->con_width   = 0x0408;
+		resolution->con_height  = 0x0308;
+		resolution->s_f1_50     = 0x23;
+		resolution->s_f2_50     = 0x25;
+		resolution->s_f1_60     = 0x2A;
+		resolution->s_f2_60     = 0x2C;
+		resolution->per_50      = 0x05;
+		resolution->per_50_M    = 0x01;
+		resolution->per_60      = 0xD9;
+		resolution->fd_w_height = 0x06;
+		resolution->max_fd_50   = 0x0003;
+		resolution->max_fd_60   = 0x0004;
+		resolution->targ_fd     = 0x0003;
+		if (priv->frame.width == 1024 &&
+		    priv->frame.height == 768) {
+			resolution->tx_water = 0x0218;
+		} else if (priv->frame.width == 800 &&
+			   priv->frame.height == 480) {
+			resolution->tx_water = 0x02DA;
+		} else {
+			/*
+			 * 640 x 480 but use it with everything else until
+			 * we figure out how to calc it
+			 */
+			resolution->tx_water = 0x0352;
+		}
+	} else {
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int mt9t11x_pll_setup_pll(const struct i2c_client *client)
+{
+	struct mt9t11x_priv *priv = to_mt9t11x(client);
+	int data, i, ret;
+
+	/* Bypass PLL */
+	ret = mt9t11x_reg_mask_set(client, 0x14, 1, 1);
+	if (ret < 0)
+		return ret;
+	/* Power-down PLL */
+	ret = mt9t11x_reg_mask_set(client, 0X14, 2, 0);
+	if (ret < 0)
+		return ret;
+	/* PLL control: BYPASS PLL = 8517 */
+	ret = mt9t11x_reg_write(client, 0x0014, 0x2145);
+	if (ret < 0)
+		return ret;
+
+	/* Replace these registers when new timing parameters are generated */
+	ret = mt9t11x_set_pll_dividers(client,
+				       priv->info->divider.m,
+				       priv->info->divider.n,
+				       priv->info->divider.p1,
+				       priv->info->divider.p2,
+				       priv->info->divider.p3,
+				       priv->info->divider.p4,
+				       priv->info->divider.p5,
+				       priv->info->divider.p6,
+				       priv->info->divider.p7);
+	if (ret < 0)
+		return ret;
+
+	/* Reset Misc. Control = 536 */
+	/* make sure parallel interface is not enable at first */
+	ret = mt9t11x_reg_write(client, 0x001A, 0x018);
+	if (ret < 0)
+		return ret;
+	/* PLL control: TEST_BYPASS on = 9541 */
+	ret = mt9t11x_reg_write(client, 0x0014, 0x2545);
+	if (ret < 0)
+		return ret;
+	/* PLL control: PLL_ENABLE on = 9543 */
+	ret = mt9t11x_reg_write(client, 0x0014, 0x2547);
+	if (ret < 0)
+		return ret;
+	/* PLL control: SEL_LOCK_DET on = 9287 */
+	ret = mt9t11x_reg_write(client, 0x0014, 0x2447);
+	if (ret < 0)
+		return ret;
+	/* PLL control: TEST_BYPASS off = 8263 */
+	ret = mt9t11x_reg_write(client, 0x0014, 0x2047);
+	if (ret < 0)
+		return ret;
+
+	/*  Wait for the PLL to lock */
+	for (i = 0; i < 1000; i++) {
+		data = mt9t11x_reg_read(client, 0x0014);
+		if (0x8000 & data)
+			break;
+
+		usleep_range(10000, 11000);
+	}
+
+	/* PLL control: PLL_BYPASS off = 8262 */
+	ret = mt9t11x_reg_write(client, 0x0014, 0x2046);
+	if (ret < 0)
+		return ret;
+	/* Reference clock count for 20 us = 640 */
+	ret = mt9t11x_reg_write(client, 0x0022, 0x0280);
+	if (ret < 0)
+		return ret;
+	/* Pad Slew Rate = 1911 */
+	ret = mt9t11x_reg_write(client, 0x001E, 0x0777);
+	if (ret < 0)
+		return ret;
+	/* JPEG Clock = 1024 */
+	ret = mt9t11x_reg_write(client, 0x0016, 0x0400);
+
+	return ret;
+}
+
+inline u32 calc_vco(u32 desired, u32 ext, u8 *_m, u8 *_n)
+{
+	u32 m, n;
+	u64 actual;
+	s64 delta, bestdelta = -1;
+
+	n = *_n + 1;
+	if (n == 0)
+		n = 1;
+
+	for (; n <= 64; n++)
+		for (m = 1; m <= 256; m++) {
+			actual = ext * 2;
+			actual *= m;
+			actual = div_s64(actual, n);
+			delta = (desired - actual);
+			if (delta < 0)
+				continue;
+			if (delta < bestdelta || bestdelta == -1) {
+				bestdelta = delta;
+				*_m = (u8)(m);
+				*_n = (u8)(n - 1);
+			}
+		}
+	actual = ext * 2 * (*_m);
+	actual = div_s64(actual, *_n + 1);
+
+	return actual;
+}
+
+static inline u32 calc_pixclk(u32 desired, u32 vco, u8 *_p1, u8 *_p2)
+{
+	u32 p1, p2;
+	u32 actual;
+	s32 delta, bestdelta = -1;
+
+	for (p1 = 1; p1 <= 16; p1++)
+		for (p2 = 1; p2 <= 16; p2++) {
+			actual = vco;
+			actual /= p1;
+			actual /= p2;
+			delta = (desired - actual);
+			if (delta < 0)
+				continue;
+			if (delta < bestdelta || bestdelta == -1) {
+				bestdelta = delta;
+				*_p1 = (u8)(p1 - 1);
+				*_p2 = (u8)(p2 - 1);
+			}
+		}
+	actual = vco / (*_p1 + 1);
+	actual /= (*_p2 + 1);
+
+	return actual;
+}
+
+static inline u32 calc_div(u32 desired, u32 src, u8 *_div)
+{
+	u32 div;
+
+	if (src > desired) {
+		div = src / desired;
+		if ((src % desired) > 0)
+			div++;
+	} else {
+		div = 1;
+	}
+
+	*_div = (u8)(div - 1);
+
+	return 0;
+}
+
+static unsigned int mt9t11x_pll_calc_params(struct mt9t11x_priv *priv)
+{
+	struct i2c_client *client = priv->client;
+	struct mt9t11x_camera_info *info = priv->info;
+	u32 vco;
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	calc_div(MT9T11x_MAX_PFD_CLK, info->mclk, &info->divider.n);
+	vco = calc_vco(MT9T11x_MAX_VCO_CLK, info->mclk, &info->divider.m,
+		       &info->divider.n);
+	calc_pixclk(info->pclk, vco, &info->divider.p1, &info->divider.p2);
+	calc_div(MT9T11x_MAX_MIPI_CLK, vco, &info->divider.p3);
+	calc_div(MT9T11x_MAX_MCU_CLK, vco, &info->divider.p6);
+	calc_div(MT9T11x_MAX_SOC_CLK, vco, &info->divider.p5);
+	calc_div(MT9T11x_MAX_SENSOR_CLK, vco, &info->divider.p4);
+
+	return 0;
+}
+
+static int mt9t11x_sysctl_startup(const struct i2c_client *client)
+{
+	int ret = 0;
+
+	/* reset */
+	mt9t11x_reset(client);
+
+	/* Setup PLL */
+	mt9t11x_pll_setup_pll(client);
+
+	return ret;
+}
+
+static int mt9t11x_high_speed_overrides(const struct i2c_client *client)
+{
+	int ret;
+
+	/*
+	 * Use this section to apply settings that are specific to this
+	 * revision of SOC or for any other specialized settings
+	 * clear the "Output Buffer Enable Adaptive Clock" bit to enable
+	 * the SYSCTL slew rate settings, change this in the variables
+	 * and register
+	 */
+
+	/* PRI_A_CONFIG_JPEG_OB_TX_CONTROL_VAR */
+	ret = mt9t11x_mcu_write(client, VAR(26, 160), 0x082E);
+	if (ret < 0)
+		return ret;
+	/* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */
+	ret = mt9t11x_mcu_write(client, VAR(27, 160), 0x082E);
+	if (ret < 0)
+		return ret;
+	/* SEC_A_CONFIG_JPEG_OB_TX_CONTROL_VAR */
+	ret = mt9t11x_mcu_write(client, VAR(28, 160), 0x082E);
+	if (ret < 0)
+		return ret;
+	/* SEC_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */
+	ret = mt9t11x_mcu_write(client, VAR(29, 160), 0x082E);
+	if (ret < 0)
+		return ret;
+	ret = mt9t11x_reg_mask_set(client, 0x3C52, 0x0040, 0);
+	if (ret < 0)
+		return ret;
+
+	/* Set correct values for Context B FIFO control */
+	/* CAM1_CTX_B_RX_FIFO_TRIGGER_MARK */
+	ret = mt9t11x_mcu_write(client, VAR(18, 142), 32);
+	if (ret < 0)
+		return ret;
+	/* PRI_B_CONFIG_IO_OB_MANUAL_FLAG */
+	ret = mt9t11x_mcu_write(client, VAR(27, 172), 0);
+
+	return ret;
+}
+
+static int mt9t11x_go(const struct i2c_client *client)
+{
+	int data, i, ret;
+
+	/* release MCU from standby */
+	ret = mt9t11x_reg_mask_set(client, 0x0018, 0x0001, 0);
+	if (ret < 0)
+		return ret;
+
+	/* wait for K26A to come out of standby */
+	for (i = 0; i < 100; i++) {
+		data = mt9t11x_reg_read(client, 0x0018);
+		if (!(0x4000 & data))
+			break;
+
+		usleep_range(10000, 11000);
+	}
+
+	return ret;
+}
+
+static int mt9t11x_continue(const struct i2c_client *client)
+{
+	int data, i, ret;
+
+	/* clear powerup stop bit */
+	ret = mt9t11x_reg_mask_set(client, 0x0018, 0x0004, 0);
+	if (ret < 0)
+		return ret;
+
+	/* wait for sequencer to enter preview state */
+	for (i = 0; i < 100; i++) {
+		data = mt9t11x_mcu_read(client, VAR8(1, 1));
+		if (data == 3)
+			break;
+
+		usleep_range(10000, 11000);
+	}
+
+	return ret;
+}
+
+static int mt9t11x_mcu_powerup_stop_enable(const struct i2c_client *client)
+{
+	int ret;
+
+	/* set powerup stop bit */
+	ret = mt9t11x_reg_mask_set(client, 0x0018, 0x0004, 0x0004);
+
+	return ret;
+}
+
+static int mt9t11x_custom_setup(const struct i2c_client *client)
+{
+	struct mt9t11x_priv *priv = to_mt9t11x(client);
+	struct mt9t11x_resolution_param *resolution = &priv->resolution;
+	int ret;
+
+	/* I2C Master Clock Divider */
+	ret = mt9t11x_mcu_write(client, VAR(24, 6), 0x0100);
+	if (ret < 0)
+		return ret;
+	/* Output Width (A) */
+	ret = mt9t11x_mcu_write(client, VAR(26, 0), priv->frame.width);
+	if (ret < 0)
+		return ret;
+	/* Output Height (A) */
+	ret = mt9t11x_mcu_write(client, VAR(26, 2), priv->frame.height);
+	if (ret < 0)
+		return ret;
+	/* JPEG (A) */
+	ret = mt9t11x_mcu_write(client, VAR8(26, 142), 0x00);
+	if (ret < 0)
+		return ret;
+	/* Adaptive Output Clock (A) */
+	ret = mt9t11x_mcu_mask_set(client, VAR(26, 160), 0x0040, 0x0000);
+	if (ret < 0)
+		return ret;
+	/* Row Start (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 2), 0x000);
+	if (ret < 0)
+		return ret;
+	/* Column Start (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 4), resolution->col_strt);
+	if (ret < 0)
+		return ret;
+	/* Row End (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 6), resolution->row_end);
+	if (ret < 0)
+		return ret;
+	/* Column End (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 8), resolution->col_end);
+	if (ret < 0)
+		return ret;
+	/* Row Speed (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 10), 0x0111);
+	if (ret < 0)
+		return ret;
+	/* Read Mode (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 12), resolution->read_mode);
+	if (ret < 0)
+		return ret;
+	/* Fine Correction (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 15), resolution->fine_cor);
+	if (ret < 0)
+		return ret;
+	/* Fine IT Min (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 17), resolution->fine_min);
+	if (ret < 0)
+		return ret;
+	/* Fine IT Max Margin (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 19), resolution->fine_max);
+	if (ret < 0)
+		return ret;
+	/* Base Frame Lines (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 29), resolution->base_lines);
+	if (ret < 0)
+		return ret;
+	/* Min Line Length (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 31), resolution->min_lin_len);
+	if (ret < 0)
+		return ret;
+	/* Line Length (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 37), resolution->line_len);
+	if (ret < 0)
+		return ret;
+	/* Contex Width (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 43), resolution->con_width);
+	if (ret < 0)
+		return ret;
+	/* Context Height (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 45), resolution->con_height);
+	if (ret < 0)
+		return ret;
+	/* Output Width (B) */
+	ret = mt9t11x_mcu_write(client, VAR(27, 0), 0x0800);
+	if (ret < 0)
+		return ret;
+	/* Output Height (B) */
+	ret = mt9t11x_mcu_write(client, VAR(27, 2), 0x0600);
+	if (ret < 0)
+		return ret;
+	/* JPEG (B) */
+	ret = mt9t11x_mcu_write(client, VAR8(27, 142), 0x01);
+	if (ret < 0)
+		return ret;
+	/* Adaptive Output Clock (B) */
+	ret = mt9t11x_mcu_mask_set(client, VAR(27, 160), 0x0040, 0x0000);
+	if (ret < 0)
+		return ret;
+	/* Row Start (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 74), 0x004);
+	if (ret < 0)
+		return ret;
+	/* Column Start (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 76), 0x004);
+	if (ret < 0)
+		return ret;
+	/* Row End (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 78), 0x60B);
+	if (ret < 0)
+		return ret;
+	/* Column End (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 80), 0x80B);
+	if (ret < 0)
+		return ret;
+	/* Row Speed (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 82), 0x0111);
+	if (ret < 0)
+		return ret;
+	/* Read Mode (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 84), 0x0024);
+	if (ret < 0)
+		return ret;
+	/* Fine Correction (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 87), 0x008C);
+	if (ret < 0)
+		return ret;
+	/* Fine IT Min (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 89), 0x01F1);
+	if (ret < 0)
+		return ret;
+	/* Fine IT Max Margin (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 91), 0x00FF);
+	if (ret < 0)
+		return ret;
+	/* Base Frame Lines (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 101), 0x06AE);
+	if (ret < 0)
+		return ret;
+	/* Min Line Length (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 103), 0x0378);
+	if (ret < 0)
+		return ret;
+	/* Line Length (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 109), 0x0A3A);
+	if (ret < 0)
+		return ret;
+	/* Contex Width (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 115), 0x0808);
+	if (ret < 0)
+		return ret;
+	/* Context Height (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 117), 0x0608);
+	if (ret < 0)
+		return ret;
+	/* search_f1_50 */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 165), resolution->s_f1_50);
+	if (ret < 0)
+		return ret;
+	/* search_f2_50 */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 166), resolution->s_f2_50);
+	if (ret < 0)
+		return ret;
+	/* search_f1_60 */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 167), resolution->s_f1_60);
+	if (ret < 0)
+		return ret;
+	/* search_f2_60 */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 168), resolution->s_f2_60);
+	if (ret < 0)
+		return ret;
+	/* period_50Hz (A) */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 68), resolution->per_50);
+	if (ret < 0)
+		return ret;
+	/* period_50Hz (A MSB) */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 303), resolution->per_50_M);
+	if (ret < 0)
+		return ret;
+	/* period_60Hz (A) */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 69), resolution->per_60);
+	if (ret < 0)
+		return ret;
+	/* period_60Hz (A MSB) */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 301), 0x00);
+	if (ret < 0)
+		return ret;
+	/* period_50Hz (B) */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 140), 0xD2);
+	if (ret < 0)
+		return ret;
+	/* period_50Hz (B) MSB */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 304), 0x00);
+	if (ret < 0)
+		return ret;
+	/* period_60Hz (B) */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 141), 0xAF);
+	if (ret < 0)
+		return ret;
+	/* period_60Hz (B) MSB */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 302), 0x00);
+	if (ret < 0)
+		return ret;
+	/* FD Window Height */
+	ret = mt9t11x_mcu_write(client, VAR8(14, 37), resolution->fd_w_height);
+	if (ret < 0)
+		return ret;
+	/* Stat_min */
+	ret = mt9t11x_mcu_write(client, VAR8(8, 9), 0x02);
+	if (ret < 0)
+		return ret;
+	/* Stat_max */
+	ret = mt9t11x_mcu_write(client, VAR8(8, 10), 0x03);
+	if (ret < 0)
+		return ret;
+	/* Min_amplitude */
+	ret = mt9t11x_mcu_write(client, VAR8(8, 12), 0x0A);
+	if (ret < 0)
+		return ret;
+	/* RX FIFO Watermark (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 70), 0x0080);
+	if (ret < 0)
+		return ret;
+	/* TX FIFO Watermark (A) */
+	ret = mt9t11x_mcu_write(client, VAR(26, 170), resolution->tx_water);
+	if (ret < 0)
+		return ret;
+	/* Max FD Zone 50 Hz */
+	ret = mt9t11x_mcu_write(client, VAR(26, 21), resolution->max_fd_50);
+	if (ret < 0)
+		return ret;
+	/* Max FD Zone 60 Hz */
+	ret = mt9t11x_mcu_write(client, VAR(26, 23), resolution->max_fd_60);
+	if (ret < 0)
+		return ret;
+	/* AE Target FD Zone */
+	ret = mt9t11x_mcu_write(client, VAR(26, 45), resolution->targ_fd);
+	if (ret < 0)
+		return ret;
+	/* RX FIFO Watermark (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 142), 0x0080);
+	if (ret < 0)
+		return ret;
+	/* TX FIFO Watermark (B) */
+	ret = mt9t11x_mcu_write(client, VAR(27, 170), 0x01D0);
+	if (ret < 0)
+		return ret;
+	/* Refresh Sequencer Mode */
+	ret = mt9t11x_mcu_write(client, VAR8(1, 0), 0x06);
+	if (ret < 0)
+		return ret;
+	/* Refresh Sequencer */
+	ret = mt9t11x_mcu_write(client, VAR8(1, 0), 0x05);
+	if (ret < 0)
+		return ret;
+
+#ifdef TEST_PATTERN
+	ret = mt9t11x_mcu_write(client, VAR(24, 3), 0x100);
+	if (ret < 0)
+		return ret;
+	ret = mt9t11x_mcu_write(client, VAR(24, 37), 0x0B);
+#endif
+
+	return ret;
+}
+
+static int mt9t11x_optimal_power_consumption(const struct i2c_client *client)
+{
+	int ret;
+
+	/* Analog setting B */
+	ret = mt9t11x_reg_write(client, 0x3084, 0x2409);
+	if (ret < 0)
+		return ret;
+	ret = mt9t11x_reg_write(client, 0x3092, 0x0A49);
+	if (ret < 0)
+		return ret;
+	ret = mt9t11x_reg_write(client, 0x3094, 0x4949);
+	if (ret < 0)
+		return ret;
+	ret = mt9t11x_reg_write(client, 0x3096, 0x4950);
+
+	return ret;
+}
+
+static int mt9t11x_blooming_row_pattern(const struct i2c_client *client)
+{
+	int ret;
+
+	/* Improve high light image quality */
+
+	/* [CAM1_CTX_A_COARSE_ITMIN] */
+	ret = mt9t11x_mcu_write(client, VAR(18, 21), 0x0004);
+	if (ret < 0)
+		return ret;
+	/* [CAM1_CTX_B_COARSE_ITMIN] */
+	ret = mt9t11x_mcu_write(client, VAR(18, 93), 0x0004);
+
+	return ret;
+}
+
+static int mt9t11x_set_orientation(const struct i2c_client *client,
+				   u32 mask, u32 flip)
+{
+	int ret;
+
+	flip &= mask;
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	/* [CAM1_CTX_A_READ_MODE] */
+	ret = mt9t11x_mcu_mask_set(client, VAR(18, 12), mask, flip);
+	if (ret < 0)
+		return ret;
+	/* [CAM1_CTX_A_PIXEL_ORDER] */
+	ret = mt9t11x_mcu_mask_set(client, VAR8(18, 14), mask, flip);
+	if (ret < 0)
+		return ret;
+
+	/* [CAM1_CTX_B_READ_MODE] */
+	ret = mt9t11x_mcu_mask_set(client, VAR(18, 84), mask, flip);
+	if (ret < 0)
+		return ret;
+	/* [CAM1_CTX_B_PIXEL_ORDER] */
+	ret = mt9t11x_mcu_mask_set(client, VAR8(18, 86), mask, flip);
+	if (ret < 0)
+		return ret;
+
+	/* [SEQ_CMD] */
+	ret = mt9t11x_mcu_write(client, VAR8(1, 0), 0x06);
+
+	return ret;
+}
+
+static int mt9t11x_init_camera_optimized(const struct i2c_client *client)
+{
+	int ret;
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	ret = mt9t11x_sysctl_startup(client);
+	if (ret < 0)
+		return ret;
+	ret = mt9t11x_mcu_powerup_stop_enable(client);
+	if (ret < 0)
+		return ret;
+	ret = mt9t11x_go(client);
+	if (ret < 0)
+		return ret;
+
+	ret = mt9t11x_custom_setup(client);
+	if (ret < 0)
+		return ret;
+
+	ret = mt9t11x_high_speed_overrides(client);
+	if (ret < 0)
+		return ret;
+
+	ret = mt9t11x_optimal_power_consumption(client);
+	if (ret < 0)
+		return ret;
+
+	ret = mt9t11x_blooming_row_pattern(client);
+	if (ret < 0)
+		return ret;
+
+	ret = mt9t11x_continue(client);
+
+	return ret;
+}
+
+static int mt9t11x_init_setting(const struct i2c_client *client)
+{
+	struct mt9t11x_priv *priv = to_mt9t11x(client);
+	int ret;
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	/* Adaptive Output Clock (A) */
+	ret = mt9t11x_mcu_mask_set(client, VAR(26, 160), 0x0040, 0x0000);
+	if (ret < 0)
+		return ret;
+	/* Read Mode (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 12), 0x0024);
+	if (ret < 0)
+		return ret;
+	/* Fine Correction (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 15), 0x00CC);
+	if (ret < 0)
+		return ret;
+	/* Fine IT Min (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 17), 0x01f1);
+	if (ret < 0)
+		return ret;
+	/* Fine IT Max Margin (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 19), 0x00fF);
+	if (ret < 0)
+		return ret;
+	/* Base Frame Lines (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 29), 0x032D);
+	if (ret < 0)
+		return ret;
+	/* Min Line Length (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 31), 0x073a);
+	if (ret < 0)
+		return ret;
+	/* Line Length (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 37), 0x07d0);
+	if (ret < 0)
+		return ret;
+	/* Adaptive Output Clock (B) */
+	ret = mt9t11x_mcu_mask_set(client, VAR(27, 160), 0x0040, 0x0000);
+	if (ret < 0)
+		return ret;
+	/* Row Start (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 74), 0x004);
+	if (ret < 0)
+		return ret;
+	/* Column Start (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 76), 0x004);
+	if (ret < 0)
+		return ret;
+	/* Row End (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 78), 0x60B);
+	if (ret < 0)
+		return ret;
+	/* Column End (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 80), 0x80B);
+	if (ret < 0)
+		return ret;
+	/* Fine Correction (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 87), 0x008C);
+	if (ret < 0)
+		return ret;
+	/* Fine IT Min (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 89), 0x01F1);
+	if (ret < 0)
+		return ret;
+	/* Fine IT Max Margin (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 91), 0x00FF);
+	if (ret < 0)
+		return ret;
+	/* Base Frame Lines (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 101), 0x0668);
+	if (ret < 0)
+		return ret;
+	/* Min Line Length (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 103), 0x0AF0);
+	if (ret < 0)
+		return ret;
+	/* Line Length (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 109), 0x0AF0);
+	if (ret < 0)
+		return ret;
+	/*
+	 * Flicker Dectection registers
+	 * This section should be replaced whenever new Timing file is
+	 * generated.
+	 * All the following registers need to be replaced
+	 * Following registers are generated from Register Wizard but user can
+	 * modify them. For detail see auto flicker detection tuning
+	 */
+	/* FD_FDPERIOD_SELECT */
+	ret = mt9t11x_mcu_write(client, VAR8(8, 5), 0x01);
+	if (ret < 0)
+		return ret;
+	/* PRI_B_CONFIG_FD_ALGO_RUN */
+	ret = mt9t11x_mcu_write(client, VAR(27, 17), 0x0003);
+	if (ret < 0)
+		return ret;
+	/* PRI_A_CONFIG_FD_ALGO_RUN */
+	ret = mt9t11x_mcu_write(client, VAR(26, 17), 0x0003);
+	if (ret < 0)
+		return ret;
+	/*
+	 * AFD range detection tuning registers
+	 */
+	/* search_f1_50 */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 165), 0x25);
+	if (ret < 0)
+		return ret;
+	/* search_f2_50 */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 166), 0x28);
+	if (ret < 0)
+		return ret;
+	/* search_f1_60 */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 167), 0x2C);
+	if (ret < 0)
+		return ret;
+	/* search_f2_60 */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 168), 0x2F);
+	if (ret < 0)
+		return ret;
+	/* period_50Hz (A) */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 68), 0xBA);
+	if (ret < 0)
+		return ret;
+	/* period_50Hz (A MSB) */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 303), 0x00);
+	if (ret < 0)
+		return ret;
+	/* period_60Hz (A) */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 69), 0x9B);
+	if (ret < 0)
+		return ret;
+	/* period_60Hz (A MSB) */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 301), 0x00);
+	if (ret < 0)
+		return ret;
+	/* period_50Hz (B) */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 140), 0x82);
+	if (ret < 0)
+		return ret;
+	/* period_50Hz (B) MSB */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 304), 0x00);
+	if (ret < 0)
+		return ret;
+	/* period_60Hz (B) */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 141), 0x6D);
+	if (ret < 0)
+		return ret;
+	/* period_60Hz (B) MSB */
+	ret = mt9t11x_mcu_write(client, VAR8(18, 302), 0x00);
+	if (ret < 0)
+		return ret;
+	/* FD Mode */
+	ret = mt9t11x_mcu_write(client, VAR8(8, 2), 0x10);
+	if (ret < 0)
+		return ret;
+	/* Stat_min */
+	ret = mt9t11x_mcu_write(client, VAR8(8, 9), 0x02);
+	if (ret < 0)
+		return ret;
+	/* Stat_max */
+	ret = mt9t11x_mcu_write(client, VAR8(8, 10), 0x03);
+	if (ret < 0)
+		return ret;
+	/* Min_amplitude */
+	ret = mt9t11x_mcu_write(client, VAR8(8, 12), 0x0A);
+	if (ret < 0)
+		return ret;
+	/* RX FIFO Watermark (A) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 70), 0x0014);
+	if (ret < 0)
+		return ret;
+	/* RX FIFO Watermark (B) */
+	ret = mt9t11x_mcu_write(client, VAR(18, 142), 0x0014);
+	if (ret < 0)
+		return ret;
+
+	ret = mt9t11x_set_a_frame_size(client,
+				       priv->frame.width,
+				       priv->frame.height);
+
+	return ret;
+}
+
+static int mt9t11x_init_camera(const struct i2c_client *client)
+{
+	int ret;
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	ret = mt9t11x_sysctl_startup(client);
+	if (ret < 0)
+		return ret;
+	ret = mt9t11x_mcu_powerup_stop_enable(client);
+	if (ret < 0)
+		return ret;
+	ret = mt9t11x_go(client);
+	if (ret < 0)
+		return ret;
+
+	ret = mt9t11x_init_setting(client);
+	if (ret < 0)
+		return ret;
+
+	ret = mt9t11x_high_speed_overrides(client);
+	if (ret < 0)
+		return ret;
+
+	ret = mt9t11x_optimal_power_consumption(client);
+	if (ret < 0)
+		return ret;
+
+	ret = mt9t11x_blooming_row_pattern(client);
+	if (ret < 0)
+		return ret;
+
+	ret = mt9t11x_continue(client);
+	if (ret < 0)
+		return ret;
+
+	return ret;
+}
+
+/************************************************************************
+ *			v4l2_subdev_core_ops
+ ***********************************************************************/
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9t11x_g_register(struct v4l2_subdev *sd,
+			      struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int                ret;
+
+	reg->size = 2;
+	ret = mt9t11x_reg_read(client, reg->reg);
+
+	reg->val = (__u64)ret;
+
+	return 0;
+}
+
+static int mt9t11x_s_register(struct v4l2_subdev *sd,
+			      const struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+
+	ret = mt9t11x_reg_write(client, reg->reg, reg->val);
+
+	return ret;
+}
+#endif
+
+static void __mt9t11x_set_power(struct mt9t11x_priv *priv, int on)
+{
+	struct i2c_client *client = priv->client;
+
+	dev_dbg(&client->dev, "%s: on: %d\n", __func__, on);
+	on = (on) ? 1 : 0;
+
+	if (priv->power == on)
+		return;
+
+	if (on) {
+		if (priv->powerdown_gpio)
+			gpiod_set_value_cansleep(priv->powerdown_gpio, 0);
+		if (priv->reset_gpio)
+			gpiod_set_value_cansleep(priv->reset_gpio, 0);
+		usleep_range(25000, 26000);
+	} else {
+		if (priv->powerdown_gpio)
+			gpiod_set_value_cansleep(priv->powerdown_gpio, 1);
+		if (priv->reset_gpio)
+			gpiod_set_value_cansleep(priv->reset_gpio, 1);
+	}
+
+	priv->power = on;
+}
+
+static int mt9t11x_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct mt9t11x_priv *priv = sd_to_mt9t11x(sd);
+	struct i2c_client *client = priv->client;
+
+	dev_dbg(&client->dev, "%s: on: %d\n", __func__, on);
+
+	mutex_lock(&priv->lock);
+	__mt9t11x_set_power(priv, on);
+	mutex_unlock(&priv->lock);
+	return 0;
+}
+
+static struct v4l2_subdev_core_ops mt9t11x_subdev_core_ops = {
+	.log_status = v4l2_ctrl_subdev_log_status,
+	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+	.s_power	= mt9t11x_s_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register	= mt9t11x_g_register,
+	.s_register	= mt9t11x_s_register,
+#endif
+};
+
+/*
+ * V4L2 controls
+ */
+static int mt9t11x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct mt9t11x_priv *priv = ctrl_to_mt9t11x(ctrl);
+	struct i2c_client *client = priv->client;
+	int ret = -EINVAL;
+
+	dev_dbg(&client->dev, "%s: ctrl_id:0x%x (%s), value: %d\n",
+		__func__, ctrl->id, ctrl->name, ctrl->val);
+
+	mutex_lock(&priv->lock);
+	/*
+	 * If the device is not powered up now, postpone applying control's
+	 * value to the hardware, until it is ready to accept commands.
+	 */
+	if (priv->power == 0) {
+		mutex_unlock(&priv->lock);
+		return 0;
+	}
+
+	switch (ctrl->id) {
+	case V4L2_CID_VFLIP:
+		mt9t11x_set_orientation(client, 0x2, (ctrl->val) ? 2 : 0);
+		ret = 0;
+		break;
+	case V4L2_CID_HFLIP:
+		mt9t11x_set_orientation(client, 0x1, (ctrl->val) ? 1 : 0);
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops mt9t11x_ctrl_ops = {
+	.s_ctrl	= mt9t11x_s_ctrl,
+};
+
+static int mt9t11x_initialize_controls(struct mt9t11x_priv *priv)
+{
+	const struct v4l2_ctrl_ops *ops = &mt9t11x_ctrl_ops;
+	struct i2c_client *client = priv->client;
+	struct v4l2_subdev *sd = &priv->subdev;
+	struct v4l2_ctrl_handler *hdl = &priv->handler;
+	int ret;
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	ret = v4l2_ctrl_handler_init(hdl, 2);
+	if (ret < 0)
+		return ret;
+
+	priv->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+	priv->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+	sd->ctrl_handler = hdl;
+	if (priv->handler.error)
+		return priv->handler.error;
+
+	return 0;
+}
+
+/************************************************************************
+ *			v4l2_subdev_video_ops
+ ***********************************************************************/
+static int mt9t11x_set_params(struct mt9t11x_priv *priv,
+			      const struct v4l2_rect *rect,
+			      u32 code)
+{
+	int i;
+
+	/*
+	 * get color format
+	 */
+	for (i = 0; i < priv->num_formats; i++)
+		if (mt9t11x_cfmts[i].code == code)
+			break;
+
+	if (i == priv->num_formats)
+		return -EINVAL;
+
+	priv->frame  = *rect;
+
+	/*
+	 * frame size check
+	 */
+	mt9t11x_frame_check(&priv->frame.width, &priv->frame.height,
+			    &priv->frame.left, &priv->frame.top);
+
+	priv->format = mt9t11x_cfmts + i;
+
+	return 0;
+}
+
+static int mt9t11x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct mt9t11x_priv *priv = sd_to_mt9t11x(sd);
+	struct i2c_client *client = priv->client;
+	int ret = 0;
+	int optimize = 0;
+	struct v4l2_rect rect = {
+		.width = priv->frame.width,
+		.height = priv->frame.height,
+		.left = priv->frame.left,
+		.top = priv->frame.top,
+	};
+	u16 param;
+
+	dev_dbg(&client->dev, "%s: enable: %d\n", __func__, enable);
+
+	mutex_lock(&priv->lock);
+
+	if (priv->streaming == enable)
+		goto unlock;
+
+	if (!enable) {
+		/* Stop Streaming Sequence */
+		mt9t11x_streaming(priv, false);
+		__mt9t11x_set_power(priv, 0);
+		priv->streaming = enable;
+		goto unlock;
+	}
+
+	mt9t11x_set_params(priv, &rect, priv->format->code);
+
+	__mt9t11x_set_power(priv, 1);
+
+	/* fill the structure with new resolution parameters */
+	optimize = mt9t11x_set_resolution_params(client);
+
+	if (optimize) {
+		ret = mt9t11x_init_camera_optimized(client);
+		if (ret < 0)
+			goto unlock;
+	} else {
+		ret = mt9t11x_init_camera(client);
+		if (ret < 0)
+			goto unlock;
+	}
+	/*
+	 * By default data is sampled on falling edge of pixclk.
+	 * Change the default to be rising edge. i.e. Invert PCLK
+	 */
+	param = (priv->info->flags & V4L2_MBUS_PCLK_SAMPLE_RISING) ?
+		0x0001 : 0x0000;
+	ret = mt9t11x_reg_write(client, 0x3C20, param);
+	if (ret < 0)
+		goto unlock;
+	usleep_range(5000, 6000);
+
+	ret = mt9t11x_mcu_write(client, VAR(26, 7), priv->format->fmt);
+	if (ret < 0)
+		goto unlock;
+	ret = mt9t11x_mcu_write(client, VAR(26, 9), priv->format->order);
+	if (ret < 0)
+		goto unlock;
+
+	ret = mt9t11x_mcu_write(client, VAR8(1, 0), 0x06);
+	if (ret < 0)
+		goto unlock;
+
+	if (priv->flags & MT9T11x_FLAG_VFLIP)
+		v4l2_ctrl_s_ctrl(priv->vflip, 1);
+
+	/* Make sure H/W is consistent with current control settings */
+	ret = mt9t11x_set_orientation(client, 0x3,
+				      (v4l2_ctrl_g_ctrl(priv->vflip) << 1 |
+				       v4l2_ctrl_g_ctrl(priv->hflip)));
+	if (ret < 0)
+		goto unlock;
+
+	dev_dbg(&client->dev, "format : %04x\n", priv->format->code);
+	dev_dbg(&client->dev, "size   : %d x %d\n",
+		priv->frame.width,
+		priv->frame.height);
+
+	priv->streaming = enable;
+	mt9t11x_streaming(priv, true);
+
+	CLOCK_INFO(client, EXT_CLOCK);
+
+unlock:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static void mt9t11x_get_default_format(struct mt9t11x_priv *priv,
+				       struct v4l2_mbus_framefmt *mf)
+{
+	struct v4l2_rect rect = {
+		.width = VGA_WIDTH,
+		.height = VGA_HEIGHT,
+		.left = (MAX_WIDTH - VGA_WIDTH) / 2,
+		.top = (MAX_HEIGHT - VGA_HEIGHT) / 2,
+	};
+
+	mt9t11x_set_params(priv, &rect, MEDIA_BUS_FMT_UYVY8_2X8);
+
+	/* Need fixing */
+	mf->width = rect.width;
+	mf->height = rect.height;
+	mf->colorspace = mt9t11x_cfmts[0].colorspace;
+	mf->code = mt9t11x_cfmts[0].code;
+
+	mf->field = V4L2_FIELD_NONE;
+}
+
+static int mt9t11x_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct mt9t11x_priv *priv = sd_to_mt9t11x(sd);
+	struct v4l2_mbus_framefmt *mf;
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		mf = v4l2_subdev_get_try_format(sd, cfg, 0);
+		mutex_lock(&priv->lock);
+		fmt->format = *mf;
+		mutex_unlock(&priv->lock);
+		return 0;
+	}
+
+	mutex_lock(&priv->lock);
+	mf = &fmt->format;
+	mf->width	= priv->frame.width;
+	mf->height	= priv->frame.height;
+	mf->colorspace	= priv->format->colorspace;
+	mf->code	= priv->format->code;
+	mf->field	= V4L2_FIELD_NONE;
+	mutex_unlock(&priv->lock);
+
+	return 0;
+}
+
+static void __mt9t11x_try_frame_size(struct v4l2_mbus_framefmt *mf)
+{
+	const struct mt9t11x_framesize *fsize = &mt9t11x_framesizes[0];
+	const struct mt9t11x_framesize *match = NULL;
+	int i = ARRAY_SIZE(mt9t11x_framesizes);
+	unsigned int min_err = UINT_MAX;
+
+	while (i--) {
+		int err = abs(fsize->width - mf->width)
+				+ abs(fsize->height - mf->height);
+		if (err < min_err) {
+			min_err = err;
+			match = fsize;
+		}
+		fsize++;
+	}
+
+	if (!match)
+		match = &mt9t11x_framesizes[0];
+
+	mf->width  = match->width;
+	mf->height = match->height;
+}
+
+static int mt9t11x_set_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct mt9t11x_priv *priv = sd_to_mt9t11x(sd);
+	int index = priv->num_formats;
+	struct v4l2_mbus_framefmt *mf = &fmt->format;
+	int ret = 0;
+	struct v4l2_rect rect;
+
+	__mt9t11x_try_frame_size(mf);
+	rect.width = mf->width;
+	rect.height = mf->height;
+	rect.left = priv->frame.left;
+	rect.top = priv->frame.top;
+
+	while (--index >= 0)
+		if (mt9t11x_cfmts[index].code == mf->code)
+			break;
+
+	if (index < 0)
+		return -EINVAL;
+
+	mf->colorspace = mt9t11x_cfmts[index].colorspace;
+	mf->code = mt9t11x_cfmts[index].code;
+	mf->field = V4L2_FIELD_NONE;
+
+	mutex_lock(&priv->lock);
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+		*mf = fmt->format;
+	} else {
+		ret = mt9t11x_set_params(priv, &rect, mf->code);
+	}
+
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static int mt9t11x_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct mt9t11x_priv *priv = sd_to_mt9t11x(sd);
+
+	if (code->index >= priv->num_formats)
+		return -EINVAL;
+
+	code->code = mt9t11x_cfmts[code->index].code;
+
+	return 0;
+}
+
+static int mt9t11x_enum_frame_sizes(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_pad_config *cfg,
+				    struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct mt9t11x_priv *priv = sd_to_mt9t11x(sd);
+	int i = priv->num_formats;
+
+	if (fse->index >= ARRAY_SIZE(mt9t11x_framesizes))
+		return -EINVAL;
+
+	while (--i)
+		if (mt9t11x_cfmts[i].code == fse->code)
+			break;
+
+	fse->code = mt9t11x_cfmts[i].code;
+
+	fse->min_width  = mt9t11x_framesizes[fse->index].width;
+	fse->max_width  = fse->min_width;
+	fse->max_height = mt9t11x_framesizes[fse->index].height;
+	fse->min_height = fse->max_height;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops mt9t11x_subdev_video_ops = {
+	.s_stream	= mt9t11x_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops mt9t11x_subdev_pad_ops = {
+	.enum_mbus_code		= mt9t11x_enum_mbus_code,
+	.enum_frame_size	= mt9t11x_enum_frame_sizes,
+	.get_fmt		= mt9t11x_get_fmt,
+	.set_fmt		= mt9t11x_set_fmt,
+};
+
+/************************************************************************
+ *			i2c driver
+ ***********************************************************************/
+static struct v4l2_subdev_ops mt9t11x_subdev_ops = {
+	.core	= &mt9t11x_subdev_core_ops,
+	.video	= &mt9t11x_subdev_video_ops,
+	.pad   = &mt9t11x_subdev_pad_ops,
+};
+
+/*
+ * V4L2 subdev internal operations
+ */
+static int mt9t11x_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct mt9t11x_priv *priv = sd_to_mt9t11x(sd);
+	struct i2c_client *client = priv->client;
+	struct v4l2_mbus_framefmt *mf;
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	mf = v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	mt9t11x_get_default_format(priv, mf);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_internal_ops mt9t11x_subdev_internal_ops = {
+	.open = mt9t11x_open,
+};
+
+static int mt9t11x_camera_probe(struct i2c_client *client)
+{
+	struct mt9t11x_priv *priv = to_mt9t11x(client);
+	const char          *devname;
+	int                  chipid;
+	int                  custom_rev;
+	int		     ret = 0;
+
+	__mt9t11x_set_power(priv, 1);
+
+	/*
+	 * check and show chip ID
+	 */
+	chipid = mt9t11x_reg_read(client, 0x0000);
+
+	switch (chipid) {
+	case 0x2680:
+		devname = "mt9t111";
+		/*
+		 * Looks like only uyvy is supported
+		 * so limiting available formats.
+		 */
+		priv->num_formats = 1;
+		break;
+	case 0x2682:
+		devname = "mt9t112";
+		priv->num_formats = ARRAY_SIZE(mt9t11x_cfmts);
+		break;
+	default:
+		dev_err(&client->dev, "Product ID error %04x\n", chipid);
+		ret = -ENODEV;
+		goto done;
+	}
+
+	custom_rev = mt9t11x_reg_read(client, 0x31FE);
+
+	dev_info(&client->dev, "%s chip ID %04x rev %04x\n",
+		 devname, chipid, custom_rev);
+
+done:
+	__mt9t11x_set_power(priv, 0);
+	return ret;
+}
+
+static struct mt9t11x_camera_info *
+mt9t11x_get_pdata(struct i2c_client *client)
+{
+	struct mt9t11x_camera_info *pdata;
+	struct device_node *endpoint;
+	struct v4l2_fwnode_endpoint *v4l2_endpoint;
+
+	dev_dbg(&client->dev, "_get_pdata invoked\n");
+
+	if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
+		return client->dev.platform_data;
+
+	dev_dbg(&client->dev, "_get_pdata: DT Node found\n");
+
+	endpoint = of_graph_get_next_endpoint(client->dev.of_node, NULL);
+	if (!endpoint)
+		return NULL;
+
+	dev_dbg(&client->dev, "_get_pdata: endpoint found\n");
+
+	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		goto done;
+
+	v4l2_endpoint = &pdata->endpoint;
+	v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), v4l2_endpoint);
+
+	if (of_property_read_u32(endpoint, "input-clock-freq", &pdata->mclk)) {
+		dev_err(&client->dev, "input-clock-freq property not found\n");
+		goto err_out;
+	} else if (pdata->mclk > MT9T11x_MAX_EXT_CLK) {
+		dev_err(&client->dev, "input-clock-freq property exceed max value\n");
+		goto err_out;
+	}
+	dev_info(&client->dev, "input-clock-freq: %d\n", pdata->mclk);
+
+	if (of_property_read_u32(endpoint, "pixel-clock-freq", &pdata->pclk)) {
+		dev_err(&client->dev, "pixel-clock-freq property not found\n");
+		goto err_out;
+	} else if (pdata->pclk > MT9T11x_MAX_PIXEL_CLK) {
+		dev_err(&client->dev, "pixel-clock-freq property exceed max value\n");
+		goto err_out;
+	}
+	dev_info(&client->dev, "pixel-clock-freq: %d\n", pdata->pclk);
+
+	/* Just copy them for now */
+	if (pdata->endpoint.bus_type == V4L2_MBUS_PARALLEL)
+		pdata->flags = pdata->endpoint.bus.parallel.flags;
+	else
+		pdata->flags = 0;
+done:
+	of_node_put(endpoint);
+	return pdata;
+err_out:
+	of_node_put(endpoint);
+	return NULL;
+}
+
+static int mt9t11x_probe(struct i2c_client *client,
+			 const struct i2c_device_id *did)
+{
+	struct mt9t11x_priv *priv;
+	struct v4l2_subdev *sd;
+	struct mt9t11x_camera_info *info = mt9t11x_get_pdata(client);
+	struct v4l2_mbus_framefmt mf;
+
+	int ret;
+	struct gpio_desc *gpio;
+
+	if (!info) {
+		dev_err(&client->dev, "mt9t11x: missing platform data!\n");
+		return -EINVAL;
+	}
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->info = info;
+
+	mutex_init(&priv->lock);
+	priv->client = client;
+
+	gpio = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(gpio)) {
+		if (PTR_ERR(gpio) != -ENOENT)
+			return PTR_ERR(gpio);
+		gpio = NULL;
+	}
+	priv->reset_gpio = gpio;
+
+	gpio = devm_gpiod_get(&client->dev, "powerdown", GPIOD_OUT_LOW);
+	if (IS_ERR(gpio)) {
+		if (PTR_ERR(gpio) != -ENOENT)
+			return PTR_ERR(gpio);
+		gpio = NULL;
+	}
+	priv->powerdown_gpio = gpio;
+
+	gpio = devm_gpiod_get(&client->dev, "oscen", GPIOD_OUT_LOW);
+	if (IS_ERR(gpio)) {
+		if (PTR_ERR(gpio) != -ENOENT)
+			return PTR_ERR(gpio);
+		gpio = NULL;
+	}
+	priv->oscen_gpio = gpio;
+
+	gpio = devm_gpiod_get(&client->dev, "bufen", GPIOD_OUT_LOW);
+	if (IS_ERR(gpio)) {
+		if (PTR_ERR(gpio) != -ENOENT)
+			return PTR_ERR(gpio);
+		gpio = NULL;
+	}
+	priv->bufen_gpio = gpio;
+
+	gpio = devm_gpiod_get(&client->dev, "camen", GPIOD_OUT_LOW);
+	if (IS_ERR(gpio)) {
+		if (PTR_ERR(gpio) != -ENOENT)
+			return PTR_ERR(gpio);
+		gpio = NULL;
+	}
+	priv->camen_gpio = gpio;
+
+	/* Do these here for now */
+	if (priv->bufen_gpio)
+		gpiod_set_value_cansleep(priv->bufen_gpio, 1);
+
+	if (priv->oscen_gpio)
+		gpiod_set_value_cansleep(priv->oscen_gpio, 1);
+
+	if (priv->camen_gpio)
+		gpiod_set_value_cansleep(priv->camen_gpio, 1);
+
+	sd = &priv->subdev;
+	v4l2_i2c_subdev_init(sd, client, &mt9t11x_subdev_ops);
+	strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name));
+
+	sd->internal_ops = &mt9t11x_subdev_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+		     V4L2_SUBDEV_FL_HAS_EVENTS;
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	priv->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&sd->entity, 1, &priv->pad);
+	if (ret < 0)
+		return ret;
+#endif
+	ret = mt9t11x_initialize_controls(priv);
+	if (ret < 0)
+		goto err_me;
+
+	ret = mt9t11x_camera_probe(client);
+	if (ret < 0)
+		goto err_ctrls;
+
+	mt9t11x_get_default_format(priv, &mf);
+
+	/* Calculate the PLL register value needed */
+	mt9t11x_pll_calc_params(priv);
+
+	ret = v4l2_async_register_subdev(sd);
+	if (ret)
+		goto err_ctrls;
+
+	dev_info(&client->dev, "%s sensor driver registered !!\n", sd->name);
+
+	return 0;
+err_ctrls:
+	v4l2_ctrl_handler_free(sd->ctrl_handler);
+err_me:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+	return ret;
+}
+
+static int mt9t11x_remove(struct i2c_client *client)
+{
+	struct mt9t11x_priv *priv = to_mt9t11x(client);
+
+	v4l2_async_unregister_subdev(&priv->subdev);
+	v4l2_device_unregister_subdev(&priv->subdev);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&priv->subdev.entity);
+#endif
+	v4l2_ctrl_handler_free(priv->subdev.ctrl_handler);
+	return 0;
+}
+
+static const struct i2c_device_id mt9t11x_id[] = {
+	{ "mt9t111", 0 },
+	{ "mt9t112", 0 },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, mt9t11x_id);
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id mt9t11x_of_match[] = {
+	{ .compatible = "aptina,mt9t111", .data = (void *)MT9T111_ID},
+	{ .compatible = "aptina,mt9t112", .data = (void *)MT9T112_ID},
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mt9t11x_of_match);
+#endif
+
+static struct i2c_driver mt9t11x_i2c_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name	= DRIVER_NAME,
+		.of_match_table = of_match_ptr(mt9t11x_of_match),
+	},
+	.probe		= mt9t11x_probe,
+	.remove		= mt9t11x_remove,
+	.id_table	= mt9t11x_id,
+};
+
+module_i2c_driver(mt9t11x_i2c_driver);
+
+MODULE_AUTHOR("Benoit Parrot <bparrot@ti.com>");
+MODULE_DESCRIPTION("MT9T11x CMOS Image Sensor driver");
+MODULE_LICENSE("GPL v2");

+ 1069 - 0
drivers/media/i2c/ov1063x.c

@@ -0,0 +1,1069 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * OmniVision OV1063X Camera Driver
+ *
+ * Based on the original driver written by Phil Edworthy.
+ * Copyright (C) 2013 Phil Edworthy
+ * Copyright (C) 2013 Renesas Electronics
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This driver has been tested at QVGA, VGA and 720p, and 1280x800 at up to
+ * 30fps and it should work at any resolution in between and any frame rate
+ * up to 30fps.
+ *
+ * FIXME:
+ *  Horizontal flip (mirroring) does not work correctly. The image is flipped,
+ *  but the colors are wrong.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+
+#include <linux/v4l2-mediabus.h>
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-common.h>
+#include <linux/of_graph.h>
+#include <media/v4l2-fwnode.h>
+
+#include "ov1063x_regs.h"
+
+/* Register definitions */
+#define	OV1063X_VFLIP			0x381c
+#define	 OV1063X_VFLIP_ON		GENMASK(7, 6)
+#define	 OV1063X_VFLIP_SUBSAMPLE	BIT_MASK(0)
+#define	OV1063X_HMIRROR			0x381d
+#define	 OV1063X_HMIRROR_ON		GENMASK(1, 0)
+#define OV1063X_PID			0x300a
+#define OV1063X_VER			0x300b
+
+#define OV1063X_FORMAT_CTRL00		0x4300
+#define   OV1063X_FORMAT_YUYV		0x38
+#define   OV1063X_FORMAT_YYYU		0x39
+#define   OV1063X_FORMAT_UYVY		0x3A
+#define   OV1063X_FORMAT_VYUY		0x3B
+
+/* IDs */
+#define OV10633_VERSION_REG		0xa630
+#define OV10635_VERSION_REG		0xa635
+#define OV1063X_VERSION(pid, ver)	(((pid) << 8) | ((ver) & 0xff))
+
+enum ov1063x_model {
+	SENSOR_OV10633,
+	SENSOR_OV10635,
+};
+
+#define OV1063X_SENSOR_WIDTH		1312
+#define OV1063X_SENSOR_HEIGHT		814
+
+#define OV1063X_MAX_WIDTH		1280
+#define OV1063X_MAX_HEIGHT		800
+
+#define MAX_NUM_GPIOS			20
+
+struct ov1063x_color_format {
+	u32 code;
+	u32 colorspace;
+};
+
+struct ov1063x_framesize {
+	u16 width;
+	u16 height;
+};
+
+struct ov1063x_priv {
+	struct v4l2_subdev		subdev;
+	struct v4l2_async_subdev	asd;
+	struct v4l2_ctrl_handler	hdl;
+	int				model;
+	int				revision;
+	int				xvclk_rate;
+	/* Protects the struct fields below */
+	struct mutex lock;
+
+	int				fps_numerator;
+	int				fps_denominator;
+	struct v4l2_mbus_framefmt	format;
+	int				width;
+	int				height;
+
+	struct gpio			mux_gpios[MAX_NUM_GPIOS];
+	int				num_gpios;
+
+	struct regmap			*regmap;
+
+	/* Sensor reference clock */
+	struct clk			*xvclk;
+
+	bool				power;
+
+	/* GPIOs */
+	struct gpio_desc		*reset_gpio;
+	struct gpio_desc		*powerdown_gpio;
+
+	struct v4l2_ctrl		*colorbar;
+};
+
+static int ov1063x_init_gpios(struct i2c_client *client);
+
+static const struct ov1063x_framesize ov1063x_framesizes[] = {
+	{
+		.width		= 1280,
+		.height		= 800,
+	}, {
+		.width		= 1280,
+		.height		= 720,
+	}, {
+		.width		= 752,
+		.height		= 480,
+	}, {
+		.width		= 640,
+		.height		= 480,
+	}, {
+		.width		= 600,
+		.height		= 400,
+	}, {
+		.width		= 352,
+		.height		= 288,
+	}, {
+		.width		= 320,
+		.height		= 240,
+	},
+};
+
+/*
+ * supported color format list
+ */
+static const struct ov1063x_color_format ov1063x_cfmts[] = {
+	{
+		.code		= MEDIA_BUS_FMT_YUYV8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+	},
+	{
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+	},
+	{
+		.code		= MEDIA_BUS_FMT_VYUY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+	},
+	{
+		.code		= MEDIA_BUS_FMT_YVYU8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+	},
+	{
+		.code		= MEDIA_BUS_FMT_YUYV10_2X10,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+	},
+};
+
+static struct ov1063x_priv *to_ov1063x(const struct i2c_client *client)
+{
+	return container_of(i2c_get_clientdata(client), struct ov1063x_priv,
+			subdev);
+}
+
+/* Helper function to write consecutive 8 bit registers */
+static int ov1063x_regmap_write16(struct regmap *map, u16 reg, u16 val)
+{
+	int ret;
+
+	ret = regmap_write(map, reg, val >> 8);
+	if (ret)
+		return ret;
+
+	return regmap_write(map, reg + 1, val & 0xff);
+}
+
+/* Start/Stop streaming from the device */
+static int ov1063x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov1063x_priv *priv = to_ov1063x(client);
+	struct regmap *map = priv->regmap;
+	int ret;
+
+	ret = ov1063x_init_gpios(client);
+	if (ret) {
+		dev_err(&client->dev, "Failed to request gpios");
+		return ret;
+	}
+
+	ret = regmap_write(map, 0x0100, enable);
+	if (ret)
+		return ret;
+
+	return regmap_write(map, 0x301c, enable ? 0xf0 : 0x70);
+}
+
+static int ov1063x_set_regs(struct i2c_client *client,
+			    const struct ov1063x_reg *regs, int nr_regs);
+
+/* Set status of additional camera capabilities */
+static int ov1063x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov1063x_priv *priv = container_of(ctrl->handler,
+					struct ov1063x_priv, hdl);
+	struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
+	struct regmap *map = priv->regmap;
+	const struct ov1063x_reg *regs;
+	int n_regs;
+
+	switch (ctrl->id) {
+	case V4L2_CID_VFLIP:
+		return regmap_update_bits(map, OV1063X_VFLIP,
+					  OV1063X_VFLIP_ON,
+					  ctrl->val ? OV1063X_VFLIP_ON : 0);
+	case V4L2_CID_HFLIP:
+		return regmap_update_bits(map, OV1063X_HMIRROR,
+					  OV1063X_HMIRROR_ON,
+					  ctrl->val ? OV1063X_HMIRROR_ON : 0);
+	case V4L2_CID_TEST_PATTERN:
+		if (ctrl->val) {
+			n_regs = ARRAY_SIZE(ov1063x_regs_colorbar_enable);
+			regs = ov1063x_regs_colorbar_enable;
+		} else {
+			n_regs = ARRAY_SIZE(ov1063x_regs_colorbar_disable);
+			regs = ov1063x_regs_colorbar_disable;
+		}
+		return ov1063x_set_regs(client, regs, n_regs);
+	}
+
+	return -EINVAL;
+}
+
+/*
+ * Get the best pixel clock (pclk) that meets minimum hts/vts requirements.
+ * xvclk_rate => pre-divider => clk1 => multiplier => clk2 => post-divider
+ * => pclk
+ * We try all valid combinations of settings for the 3 blocks to get the pixel
+ * clock, and from that calculate the actual hts/vts to use. The vts is
+ * extended so as to achieve the required frame rate. The function also returns
+ * the PLL register contents needed to set the pixel clock.
+ */
+static int ov1063x_get_pclk(int xvclk_rate, int *htsmin, int *vtsmin,
+			    int fps_numerator, int fps_denominator,
+			    u8 *r3003, u8 *r3004)
+{
+	int pre_divs[] = { 2, 3, 4, 6, 8, 10, 12, 14 };
+	int pclk;
+	int best_pclk = INT_MAX;
+	int best_hts = 0;
+	int i, j, k;
+	int best_i = 0, best_j = 0, best_k = 0;
+	int clk1, clk2;
+	int hts;
+
+	/* Pre-div, reg 0x3004, bits 6:4 */
+	for (i = 0; i < ARRAY_SIZE(pre_divs); i++) {
+		clk1 = (xvclk_rate / pre_divs[i]) * 2;
+
+		if (clk1 < 3000000 || clk1 > 27000000)
+			continue;
+
+		/* Mult = reg 0x3003, bits 5:0 */
+		for (j = 1; j < 32; j++) {
+			clk2 = (clk1 * j);
+
+			if (clk2 < 200000000 || clk2 > 500000000)
+				continue;
+
+			/* Post-div, reg 0x3004, bits 2:0 */
+			for (k = 0; k < 8; k++) {
+				pclk = clk2 / (2 * (k + 1));
+
+				if (pclk > 96000000)
+					continue;
+
+				hts = *htsmin + 200 + pclk / 300000;
+
+				/* 2 clock cycles for every YUV422 pixel */
+				if (pclk < (((hts * *vtsmin) / fps_denominator)
+					* fps_numerator * 2))
+					continue;
+
+				if (pclk < best_pclk) {
+					best_pclk = pclk;
+					best_hts = hts;
+					best_i = i;
+					best_j = j;
+					best_k = k;
+				}
+			}
+		}
+	}
+
+	/* register contents */
+	*r3003 = (u8)best_j;
+	*r3004 = ((u8)best_i << 4) | (u8)best_k;
+
+	/* Did we get a valid PCLK? */
+	if (best_pclk == INT_MAX)
+		return -1;
+
+	*htsmin = best_hts;
+
+	/* Adjust vts to get as close to the desired frame rate as we can */
+	*vtsmin = best_pclk / ((best_hts / fps_denominator) *
+		  fps_numerator * 2);
+
+	return best_pclk;
+}
+
+static int ov1063x_set_regs(struct i2c_client *client,
+			    const struct ov1063x_reg *regs, int nr_regs)
+{
+	struct ov1063x_priv *priv = to_ov1063x(client);
+	struct regmap *map = priv->regmap;
+	int i, ret;
+	u8 val;
+
+	for (i = 0; i < nr_regs; i++) {
+		if (regs[i].reg == 0x300c) {
+			val = ((client->addr * 2) | 0x1);
+
+			ret = regmap_write(map, regs[i].reg, val);
+			if (ret)
+				return ret;
+		} else {
+			ret = regmap_write(map, regs[i].reg, regs[i].val);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+/* Setup registers according to resolution and color encoding */
+static int ov1063x_set_params(struct i2c_client *client, u32 width, u32 height)
+{
+	struct ov1063x_priv *priv = to_ov1063x(client);
+	struct regmap *map = priv->regmap;
+	int ret = -EINVAL;
+	int pclk;
+	int hts, vts;
+	u8 r3003, r3004, r4300;
+	int tmp;
+	u32 height_pre_subsample;
+	u32 width_pre_subsample;
+	u8 horiz_crop_mode;
+	int nr_isp_pixels;
+	int vert_sub_sample = 0;
+	int horiz_sub_sample = 0;
+	int sensor_width;
+	int n_regs;
+
+	if (width > OV1063X_MAX_WIDTH || height > OV1063X_MAX_HEIGHT)
+		return ret;
+
+	priv->width = width;
+	priv->height = height;
+
+	/* Vertical sub-sampling? */
+	height_pre_subsample = priv->height;
+	if (priv->height <= 400) {
+		vert_sub_sample = 1;
+		height_pre_subsample <<= 1;
+	}
+
+	/* Horizontal sub-sampling? */
+	width_pre_subsample = priv->width;
+	if (priv->width <= 640) {
+		horiz_sub_sample = 1;
+		width_pre_subsample <<= 1;
+	}
+
+	/* Horizontal cropping */
+	if (width_pre_subsample > 768) {
+		sensor_width = OV1063X_SENSOR_WIDTH;
+		horiz_crop_mode = 0x63;
+	} else if (width_pre_subsample > 656) {
+		sensor_width = 768;
+		horiz_crop_mode = 0x6b;
+	} else {
+		sensor_width = 656;
+		horiz_crop_mode = 0x73;
+	}
+
+	/* minimum values for hts and vts */
+	hts = sensor_width;
+	vts = height_pre_subsample + 50;
+	dev_dbg(&client->dev, "fps=(%d/%d), hts=%d, vts=%d\n",
+		priv->fps_numerator, priv->fps_denominator, hts, vts);
+
+	/* Get the best PCLK & adjust hts,vts accordingly */
+	pclk = ov1063x_get_pclk(priv->xvclk_rate, &hts, &vts,
+				priv->fps_numerator, priv->fps_denominator,
+				&r3003, &r3004);
+	if (pclk < 0)
+		return ret;
+	dev_dbg(&client->dev, "pclk=%d, hts=%d, vts=%d\n", pclk, hts, vts);
+	dev_dbg(&client->dev, "r3003=0x%X r3004=0x%X\n", r3003, r3004);
+
+	/* Disable ISP & program all registers that we might modify */
+	ret = ov1063x_set_regs(client, ov1063x_regs_change_mode,
+			       ARRAY_SIZE(ov1063x_regs_change_mode));
+	if (ret)
+		return ret;
+
+	/* Set to 1280x720 */
+	ret = regmap_write(map, 0x380f, 0x80);
+	if (ret)
+		return ret;
+
+	/* Set PLL */
+	ret = regmap_write(map, 0x3003, r3003);
+	if (ret)
+		return ret;
+	ret = regmap_write(map, 0x3004, r3004);
+	if (ret)
+		return ret;
+
+	/* Set HSYNC */
+	ret = regmap_write(map, 0x4700, 0x00);
+	if (ret)
+		return ret;
+
+	switch (priv->format.code) {
+	case MEDIA_BUS_FMT_UYVY8_2X8:
+		r4300 = OV1063X_FORMAT_UYVY;
+		break;
+	case MEDIA_BUS_FMT_VYUY8_2X8:
+		r4300 = OV1063X_FORMAT_VYUY;
+		break;
+	case MEDIA_BUS_FMT_YUYV8_2X8:
+		r4300 = OV1063X_FORMAT_YUYV;
+		break;
+	case MEDIA_BUS_FMT_YVYU8_2X8:
+		r4300 = OV1063X_FORMAT_YYYU;
+		break;
+	default:
+		r4300 = OV1063X_FORMAT_UYVY;
+		break;
+	}
+
+	/* Set format to UYVY */
+	ret = regmap_write(map, OV1063X_FORMAT_CTRL00, r4300);
+	if (ret)
+		return ret;
+
+	dev_dbg(&client->dev, "r4300=0x%X\n", r4300);
+
+	/* Set output to 8-bit yuv */
+	ret = regmap_write(map, 0x4605, 0x08);
+	if (ret)
+		return ret;
+
+	/* Horizontal cropping */
+	ret = regmap_write(map, 0x3621, horiz_crop_mode);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(map, 0x3702, (pclk + 1500000) / 3000000);
+	if (ret)
+		return ret;
+	ret = regmap_write(map, 0x3703, (pclk + 666666) / 1333333);
+	if (ret)
+		return ret;
+	ret = regmap_write(map, 0x3704, (pclk + 961500) / 1923000);
+	if (ret)
+		return ret;
+
+	/* Vertical cropping */
+	tmp = ((OV1063X_SENSOR_HEIGHT - height_pre_subsample) / 2) & ~0x1;
+	ret = ov1063x_regmap_write16(map, 0x3802, tmp);
+	if (ret)
+		return ret;
+	tmp = tmp + height_pre_subsample + 3;
+	ret = ov1063x_regmap_write16(map, 0x3806, tmp);
+	if (ret)
+		return ret;
+
+	dev_dbg(&client->dev, "width x height = %x x %x\n",
+		priv->width, priv->height);
+	/* Output size */
+	ret = ov1063x_regmap_write16(map, 0x3808, priv->width);
+	if (ret)
+		return ret;
+	ret = ov1063x_regmap_write16(map, 0x380a, priv->height);
+	if (ret)
+		return ret;
+
+	dev_dbg(&client->dev, "hts x vts = %x x %x\n", hts, vts);
+
+	ret = ov1063x_regmap_write16(map, 0x380c, hts);
+	if (ret)
+		return ret;
+
+	ret = ov1063x_regmap_write16(map, 0x380e, vts);
+	if (ret)
+		return ret;
+
+	if (vert_sub_sample) {
+		ret = regmap_update_bits(map, OV1063X_VFLIP,
+					 OV1063X_VFLIP_SUBSAMPLE,
+					 OV1063X_VFLIP_SUBSAMPLE);
+		if (ret)
+			return ret;
+		n_regs = ARRAY_SIZE(ov1063x_regs_vert_sub_sample);
+		ret = ov1063x_set_regs(client, ov1063x_regs_vert_sub_sample,
+				       n_regs);
+		if (ret)
+			return ret;
+	}
+
+	ret = ov1063x_regmap_write16(map, 0x4606, 2 * hts);
+	if (ret)
+		return ret;
+	ret = ov1063x_regmap_write16(map, 0x460a,
+				     2 * (hts - width_pre_subsample));
+	if (ret)
+		return ret;
+
+	tmp = (vts - 8) * 16;
+	ret = ov1063x_regmap_write16(map, 0xc488, tmp);
+	if (ret)
+		return ret;
+	ret = ov1063x_regmap_write16(map, 0xc48a, tmp);
+	if (ret)
+		return ret;
+
+	nr_isp_pixels = sensor_width * (priv->height + 4);
+	ret = ov1063x_regmap_write16(map, 0xc4cc, nr_isp_pixels / 256);
+	if (ret)
+		return ret;
+	ret = ov1063x_regmap_write16(map, 0xc4ce, nr_isp_pixels / 256);
+	if (ret)
+		return ret;
+	ret = ov1063x_regmap_write16(map, 0xc512, nr_isp_pixels / 16);
+	if (ret)
+		return ret;
+
+	/* Horizontal sub-sampling */
+	if (horiz_sub_sample) {
+		ret = regmap_write(map, 0x5005, 0x9);
+		if (ret)
+			return ret;
+
+		ret = regmap_write(map, 0x3007, 0x2);
+		if (ret)
+			return ret;
+	}
+
+	ret = ov1063x_regmap_write16(map, 0xc518, vts);
+	if (ret)
+		return ret;
+	ret = ov1063x_regmap_write16(map, 0xc51a, hts);
+	if (ret)
+		return ret;
+
+	/* Enable ISP blocks */
+	ret = ov1063x_set_regs(client, ov1063x_regs_enable,
+			       ARRAY_SIZE(ov1063x_regs_enable));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * V4L2 subdev video and pad level operations
+ */
+
+static void ov1063x_get_default_format(struct v4l2_mbus_framefmt *mf)
+{
+	mf->width = ov1063x_framesizes[0].width;
+	mf->height = ov1063x_framesizes[0].height;
+	mf->colorspace = ov1063x_cfmts[0].colorspace;
+	mf->code = ov1063x_cfmts[0].code;
+
+	mf->field = V4L2_FIELD_NONE;
+}
+
+static int ov1063x_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov1063x_priv *priv = to_ov1063x(client);
+	struct v4l2_mbus_framefmt *mf;
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		mf = v4l2_subdev_get_try_format(sd, cfg, 0);
+		mutex_lock(&priv->lock);
+		fmt->format = *mf;
+		mutex_unlock(&priv->lock);
+		return 0;
+	}
+
+	mutex_lock(&priv->lock);
+	fmt->format = priv->format;
+	mutex_unlock(&priv->lock);
+
+	return 0;
+}
+
+static void __ov1063x_try_frame_size(struct v4l2_mbus_framefmt *mf)
+{
+	const struct ov1063x_framesize *fsize = &ov1063x_framesizes[0];
+	const struct ov1063x_framesize *match = NULL;
+	int i = ARRAY_SIZE(ov1063x_framesizes);
+	unsigned int min_err = UINT_MAX;
+
+	while (i--) {
+		int err = abs(fsize->width - mf->width)
+				+ abs(fsize->height - mf->height);
+		if (err < min_err) {
+			min_err = err;
+			match = fsize;
+		}
+		fsize++;
+	}
+
+	if (!match)
+		match = &ov1063x_framesizes[0];
+
+	mf->width  = match->width;
+	mf->height = match->height;
+}
+
+static int ov1063x_set_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int index = ARRAY_SIZE(ov1063x_cfmts);
+	struct ov1063x_priv *priv = to_ov1063x(client);
+	struct v4l2_mbus_framefmt *mf = &fmt->format;
+	int ret = 0;
+
+	__ov1063x_try_frame_size(mf);
+
+	while (--index >= 0)
+		if (ov1063x_cfmts[index].code == mf->code)
+			break;
+
+	if (index < 0)
+		return -EINVAL;
+
+	mf->colorspace = ov1063x_cfmts[index].colorspace;
+	mf->code = ov1063x_cfmts[index].code;
+	mf->field = V4L2_FIELD_NONE;
+
+	mutex_lock(&priv->lock);
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+		*mf = fmt->format;
+	} else {
+		priv->format = fmt->format;
+		ret = ov1063x_set_params(client, mf->width, mf->height);
+	}
+
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static int ov1063x_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index >= ARRAY_SIZE(ov1063x_cfmts))
+		return -EINVAL;
+
+	code->code = ov1063x_cfmts[code->index].code;
+
+	return 0;
+}
+
+static int ov1063x_enum_frame_sizes(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_pad_config *cfg,
+				    struct v4l2_subdev_frame_size_enum *fse)
+{
+	int i = ARRAY_SIZE(ov1063x_cfmts);
+
+	if (fse->index >= ARRAY_SIZE(ov1063x_framesizes))
+		return -EINVAL;
+
+	while (--i)
+		if (ov1063x_cfmts[i].code == fse->code)
+			break;
+
+	fse->code = ov1063x_cfmts[i].code;
+
+	fse->min_width  = ov1063x_framesizes[fse->index].width;
+	fse->max_width  = fse->min_width;
+	fse->max_height = ov1063x_framesizes[fse->index].height;
+	fse->min_height = fse->max_height;
+
+	return 0;
+}
+
+static int ov1063x_init_gpios(struct i2c_client *client)
+{
+	struct ov1063x_priv *priv = to_ov1063x(client);
+	int ret = 0;
+
+	ret = gpio_request_array(priv->mux_gpios, priv->num_gpios);
+	if (ret)
+		goto done;
+	gpio_free_array(priv->mux_gpios, priv->num_gpios);
+
+done:
+	return ret;
+}
+
+static int ov1063x_init_cam_gpios(struct i2c_client *client)
+{
+	struct ov1063x_priv *priv = to_ov1063x(client);
+	struct gpio_desc *gpio;
+
+	gpio = devm_gpiod_get_optional(&client->dev, "reset",
+				       GPIOD_OUT_LOW);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+	priv->reset_gpio = gpio;
+
+	gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
+				       GPIOD_OUT_LOW);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+	priv->powerdown_gpio = gpio;
+
+	return 0;
+}
+
+static void ov1063x_set_power(struct i2c_client *client, bool on)
+{
+	struct ov1063x_priv *priv = to_ov1063x(client);
+
+	dev_dbg(&client->dev, "%s: on: %d\n", __func__, on);
+
+	if (priv->power == on)
+		return;
+
+	if (on) {
+		if (priv->powerdown_gpio) {
+			gpiod_set_value_cansleep(priv->powerdown_gpio, 1);
+			usleep_range(1000, 1200);
+		}
+		if (priv->reset_gpio) {
+			gpiod_set_value_cansleep(priv->reset_gpio, 1);
+			usleep_range(250000, 260000);
+		}
+	} else {
+		if (priv->powerdown_gpio)
+			gpiod_set_value_cansleep(priv->powerdown_gpio, 0);
+		if (priv->reset_gpio)
+			gpiod_set_value_cansleep(priv->reset_gpio, 0);
+	}
+
+	priv->power = on;
+}
+
+static int ov1063x_video_probe(struct i2c_client *client)
+{
+	struct ov1063x_priv *priv = to_ov1063x(client);
+	struct regmap *map = priv->regmap;
+	u32 pid, ver;
+	int ret;
+
+	ov1063x_set_power(client, true);
+
+	ret = ov1063x_set_regs(client, ov1063x_regs_default,
+			       ARRAY_SIZE(ov1063x_regs_default));
+	if (ret)
+		return ret;
+
+	usleep_range(500, 510);
+
+	/* check and show product ID and manufacturer ID */
+	ret = regmap_read(map, OV1063X_PID, &pid);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(map, OV1063X_VER, &ver);
+	if (ret)
+		return ret;
+
+	if (OV1063X_VERSION(pid, ver) == OV10635_VERSION_REG) {
+		priv->model = SENSOR_OV10635;
+		priv->revision = 1;
+	} else if (OV1063X_VERSION(pid, ver) == OV10633_VERSION_REG) {
+		priv->model = SENSOR_OV10633;
+		priv->revision = 1;
+	} else {
+		dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver);
+		return -ENODEV;
+	}
+
+	dev_info(&client->dev, "ov1063x Product ID %x Manufacturer ID %x\n",
+		 pid, ver);
+
+	/* Program all the 'standard' registers */
+
+	return v4l2_ctrl_handler_setup(&priv->hdl);
+}
+
+/*
+ * V4L2 subdev internal operations
+ */
+static int ov1063x_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *mf;
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	mf = v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	ov1063x_get_default_format(mf);
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops ov1063x_ctrl_ops = {
+	.s_ctrl = ov1063x_s_ctrl,
+};
+
+static const char * const ov1063x_test_pattern_menu[] = {
+	"Disabled",
+	"Vertical Color Bars",
+};
+
+static const struct v4l2_subdev_video_ops ov1063x_subdev_video_ops = {
+	.s_stream	= ov1063x_s_stream,
+};
+
+static const struct v4l2_subdev_internal_ops ov1063x_sd_internal_ops = {
+	.open		= ov1063x_open,
+};
+
+static const struct v4l2_subdev_core_ops ov1063x_subdev_core_ops = {
+	.log_status		= v4l2_ctrl_subdev_log_status,
+	.subscribe_event	= v4l2_ctrl_subdev_subscribe_event,
+	.unsubscribe_event	= v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_pad_ops ov1063x_subdev_pad_ops = {
+	.enum_mbus_code		= ov1063x_enum_mbus_code,
+	.enum_frame_size	= ov1063x_enum_frame_sizes,
+	.get_fmt		= ov1063x_get_fmt,
+	.set_fmt		= ov1063x_set_fmt,
+};
+
+static struct v4l2_subdev_ops ov1063x_subdev_ops = {
+	.core	= &ov1063x_subdev_core_ops,
+	.video	= &ov1063x_subdev_video_ops,
+	.pad	= &ov1063x_subdev_pad_ops,
+};
+
+static const struct regmap_config ov1063x_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 8,
+};
+
+/*
+ * i2c_driver function
+ */
+
+static int ov1063x_of_probe(struct i2c_client *client,
+			    struct device_node *node)
+{
+	struct ov1063x_priv *priv = to_ov1063x(client);
+	struct gpio *gpios = &priv->mux_gpios[0];
+	unsigned int flags;
+	int i, gpio;
+
+	/*
+	 * Iterate over all the gpios in the device tree
+	 * ENOENT is returned when trying to access last + 1 gpio
+	 */
+	for (i = 0; i < MAX_NUM_GPIOS; i++) {
+		gpio = of_get_named_gpio_flags(node, "mux-gpios", i, &flags);
+		if (gpio_is_valid(gpio)) {
+			gpios[i].gpio	= gpio;
+			gpios[i].flags	= (flags & OF_GPIO_ACTIVE_LOW) ?
+				GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH;
+			gpios[i].label	= client->name;
+		} else {
+			if (gpio == -ENOENT)
+				break;
+
+			return gpio;
+		}
+	}
+	priv->num_gpios = i;
+	return 0;
+}
+
+static int ov1063x_probe(struct i2c_client *client,
+			 const struct i2c_device_id *did)
+{
+	struct device_node *node = client->dev.of_node;
+	struct ov1063x_priv *priv;
+	struct v4l2_subdev *sd;
+	struct clk *clk;
+	unsigned int menu_size;
+	int ret = 0;
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, priv);
+
+	priv->regmap = devm_regmap_init_i2c(client, &ov1063x_regmap_config);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	clk = devm_clk_get(&client->dev, "xvclk");
+	if (IS_ERR(clk)) {
+		dev_err(&client->dev, "xvclk reference is missing!\n");
+		ret = PTR_ERR(clk);
+		goto err;
+	}
+	priv->xvclk = clk;
+
+	priv->xvclk_rate = clk_get_rate(clk);
+	dev_dbg(&client->dev, "xvclk_rate: %d (Hz)\n", priv->xvclk_rate);
+
+	if (priv->xvclk_rate < 6000000 ||
+	    priv->xvclk_rate > 27000000) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = clk_prepare_enable(priv->xvclk);
+	if (ret < 0)
+		goto err;
+
+	ret = ov1063x_of_probe(client, node);
+	if (ret)
+		goto err;
+
+	/* Default framerate */
+	priv->fps_numerator = 30;
+	priv->fps_denominator = 1;
+	ov1063x_get_default_format(&priv->format);
+	priv->width = priv->format.width;
+	priv->height = priv->format.height;
+
+	sd = &priv->subdev;
+	v4l2_i2c_subdev_init(sd, client, &ov1063x_subdev_ops);
+
+	sd->internal_ops = &ov1063x_sd_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+		     V4L2_SUBDEV_FL_HAS_EVENTS;
+
+	v4l2_ctrl_handler_init(&priv->hdl, 3);
+	v4l2_ctrl_new_std(&priv->hdl, &ov1063x_ctrl_ops,
+			  V4L2_CID_VFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ov1063x_ctrl_ops,
+			  V4L2_CID_HFLIP, 0, 1, 1, 0);
+	menu_size = ARRAY_SIZE(ov1063x_test_pattern_menu) - 1;
+	priv->colorbar =
+		v4l2_ctrl_new_std_menu_items(&priv->hdl, &ov1063x_ctrl_ops,
+					     V4L2_CID_TEST_PATTERN, menu_size,
+					     0, 0, ov1063x_test_pattern_menu);
+	priv->subdev.ctrl_handler = &priv->hdl;
+	if (priv->hdl.error) {
+		ret = priv->hdl.error;
+		goto err;
+	}
+
+	mutex_init(&priv->lock);
+
+	ret = ov1063x_init_cam_gpios(client);
+	if (ret) {
+		dev_err(&client->dev, "Failed to request cam gpios");
+		goto err;
+	}
+
+	ret = ov1063x_init_gpios(client);
+	if (ret) {
+		dev_err(&client->dev, "Failed to request mux gpios");
+		goto err;
+	}
+
+	ret = ov1063x_video_probe(client);
+	if (ret) {
+		v4l2_ctrl_handler_free(&priv->hdl);
+		goto err;
+	}
+
+	sd->dev = &client->dev;
+	ret = v4l2_async_register_subdev(sd);
+
+	dev_info(&client->dev, "%s sensor driver registered !!\n", sd->name);
+
+	return 0;
+err:
+	clk_disable_unprepare(priv->xvclk);
+	return ret;
+}
+
+static int ov1063x_remove(struct i2c_client *client)
+{
+	struct ov1063x_priv *priv = i2c_get_clientdata(client);
+
+	v4l2_device_unregister_subdev(&priv->subdev);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	ov1063x_set_power(client, false);
+	clk_disable_unprepare(priv->xvclk);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov1063x_id[] = {
+	{ "ov10635", 0 },
+	{ "ov10633", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov1063x_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id ov1063x_dt_id[] = {
+	{
+		.compatible = "ovti,ov10635", .data = "ov10635"
+	},
+	{
+		.compatible = "ovti,ov10633", .data = "ov10633"
+	},
+	{
+	}
+};
+#endif
+
+static struct i2c_driver ov1063x_i2c_driver = {
+	.driver = {
+		.name	= "ov1063x",
+		.of_match_table = of_match_ptr(ov1063x_dt_id),
+	},
+	.probe = ov1063x_probe,
+	.remove = ov1063x_remove,
+	.id_table = ov1063x_id,
+};
+
+module_i2c_driver(ov1063x_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV1063X");
+MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>");
+MODULE_LICENSE("GPL v2");

+ 699 - 0
drivers/media/i2c/ov1063x_regs.h

@@ -0,0 +1,699 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * OmniVision OV1063X Camera Driver
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated -  http://www.ti.com/
+ */
+
+struct ov1063x_reg {
+	u16	reg;
+	u8	val;
+};
+
+static const struct ov1063x_reg ov1063x_regs_default[] = {
+	/* Register configuration for full resolution : 1280x720 */
+		{0x103, 0x1},	 /** Software Reset */
+		{0x301b, 0xff},  /** System Control Clock Reset #1 */
+		{0x301c, 0xff},  /** System Control Clock Reset #2 */
+		{0x301a, 0xff},  /** System Control Clock Reset #0 */
+		{0x300c, 0x61},  /** Serial Camera Control Bus ID */
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x300c, 0x61},
+		{0x3021, 0x3},  /** System Control Misc */
+		{0x3011, 0x2},
+		{0x6900, 0xc},
+		{0x6901, 0x1},
+		{0x3033, 0x8},  /** System clock/4 */
+		{0x3503, 0x10}, /** AEC Delay enabled */
+		{0x302d, 0x2f}, /** Power Down Control */
+		{0x3025, 0x3},  /** Debug Control enabled */
+		/*
+		 * FPS is computed as:
+		 * XVCLK	   = 9MHz
+		 * preDivide   = {/1,/1.5,/2,/3,/4,/5,/6,/7}
+		 * decided based on (0x3004[6:4].
+		 * numerator   = (0x3003[5:0] * XVCLK)/preDivide)
+		 * denominator = (2 * (1+0x3004[2:0]))
+		 * FPS = numerator/denominator.
+		 */
+		/* {0x3003, 0x1B}, */ /* fps = 30fps. */
+		/* {0x3004, 0x03}, */
+		{0x3003, 0x20}, /* fps = 15fps. */
+		{0x3004, 0x3},
+		{0x3005, 0x20},
+		{0x3006, 0x91},
+		{0x3600, 0x74},
+		{0x3601, 0x2b},
+		{0x3612, 0x0},
+		{0x3611, 0x67},
+		{0x3633, 0xba},
+		{0x3602, 0x2f},
+		{0x3603, 0x0},
+		{0x3630, 0xa8},
+		{0x3631, 0x16},
+		{0x3714, 0x10},
+		{0x371d, 0x1},
+		{0x4300, 0x3A}, /* UYVY mode */
+		{0x3007, 0x1},
+		/*
+		 * RAW mode and Pixel CLK selct.
+		 * 0x3024[0] = 1 :: System CLK (0x3003,0x3004)
+		 * 0x3024[0] = 0 :: secondary CLK (0x3005,0x3006)
+		 */
+		{0x3024, 0x1},
+		{0x3020, 0xb},
+		{0x3702, 0xd},
+		{0x3703, 0x20},
+		{0x3704, 0x15},
+		{0x3709, 0x28},
+		{0x370d, 0x0},
+		{0x3712, 0x0},
+		{0x3713, 0x20},
+		{0x3715, 0x4},
+		{0x381d, 0x40},
+		{0x381c, 0x0},
+		{0x3824, 0x10},
+		{0x3815, 0x8c},
+		{0x3804, 0x5},
+		{0x3805, 0x1f},
+		{0x3800, 0x0},
+		{0x3801, 0x0},
+		{0x3806, 0x3},
+		{0x3807, 0x1},
+		{0x3802, 0x0},
+		{0x3803, 0x2e},
+		{0x3808, 0x5},
+		{0x3809, 0x0},
+		{0x380a, 0x2},
+		{0x380b, 0xd0},
+		{0x380c, 0x6},
+		{0x380d, 0xf6}, /* 1280x720 */
+		{0x380e, 0x2},
+		{0x380f, 0xec}, /* 1280x720 */
+		{0x3811, 0x8},
+		{0x381f, 0xc},
+		{0x3621, 0x63},
+		{0x5005, 0x8},
+		{0x56d5, 0x0},
+		{0x56d6, 0x80},
+		{0x56d7, 0x0},
+		{0x56d8, 0x0},
+		{0x56d9, 0x0},
+		{0x56da, 0x80},
+		{0x56db, 0x0},
+		{0x56dc, 0x0},
+		{0x56e8, 0x0},
+		{0x56e9, 0x7f},
+		{0x56ea, 0x0},
+		{0x56eb, 0x7f},
+		{0x5100, 0x0},
+		{0x5101, 0x80},
+		{0x5102, 0x0},
+		{0x5103, 0x80},
+		{0x5104, 0x0},
+		{0x5105, 0x80},
+		{0x5106, 0x0},
+		{0x5107, 0x80},
+		{0x5108, 0x0},
+		{0x5109, 0x0},
+		{0x510a, 0x0},
+		{0x510b, 0x0},
+		{0x510c, 0x0},
+		{0x510d, 0x0},
+		{0x510e, 0x0},
+		{0x510f, 0x0},
+		{0x5110, 0x0},
+		{0x5111, 0x80},
+		{0x5112, 0x0},
+		{0x5113, 0x80},
+		{0x5114, 0x0},
+		{0x5115, 0x80},
+		{0x5116, 0x0},
+		{0x5117, 0x80},
+		{0x5118, 0x0},
+		{0x5119, 0x0},
+		{0x511a, 0x0},
+		{0x511b, 0x0},
+		{0x511c, 0x0},
+		{0x511d, 0x0},
+		{0x511e, 0x0},
+		{0x511f, 0x0},
+		{0x56d0, 0x0},
+		{0x5006, 0x24},
+		{0x5608, 0x0},
+		{0x52d7, 0x6},
+		{0x528d, 0x8},
+		{0x5293, 0x12},
+		{0x52d3, 0x12},
+		{0x5288, 0x6},
+		{0x5289, 0x20},
+		{0x52c8, 0x6},
+		{0x52c9, 0x20},
+		{0x52cd, 0x4},
+		{0x5381, 0x0},
+		{0x5382, 0xff},
+		{0x5589, 0x76},
+		{0x558a, 0x47},
+		{0x558b, 0xef},
+		{0x558c, 0xc9},
+		{0x558d, 0x49},
+		{0x558e, 0x30},
+		{0x558f, 0x67},
+		{0x5590, 0x3f},
+		{0x5591, 0xf0},
+		{0x5592, 0x10},
+		{0x55a2, 0x6d},
+		{0x55a3, 0x55},
+		{0x55a4, 0xc3},
+		{0x55a5, 0xb5},
+		{0x55a6, 0x43},
+		{0x55a7, 0x38},
+		{0x55a8, 0x5f},
+		{0x55a9, 0x4b},
+		{0x55aa, 0xf0},
+		{0x55ab, 0x10},
+		{0x5581, 0x52},
+		{0x5300, 0x1},
+		{0x5301, 0x0},
+		{0x5302, 0x0},
+		{0x5303, 0xe},
+		{0x5304, 0x0},
+		{0x5305, 0xe},
+		{0x5306, 0x0},
+		{0x5307, 0x36},
+		{0x5308, 0x0},
+		{0x5309, 0xd9},
+		{0x530a, 0x0},
+		{0x530b, 0xf},
+		{0x530c, 0x0},
+		{0x530d, 0x2c},
+		{0x530e, 0x0},
+		{0x530f, 0x59},
+		{0x5310, 0x0},
+		{0x5311, 0x7b},
+		{0x5312, 0x0},
+		{0x5313, 0x22},
+		{0x5314, 0x0},
+		{0x5315, 0xd5},
+		{0x5316, 0x0},
+		{0x5317, 0x13},
+		{0x5318, 0x0},
+		{0x5319, 0x18},
+		{0x531a, 0x0},
+		{0x531b, 0x26},
+		{0x531c, 0x0},
+		{0x531d, 0xdc},
+		{0x531e, 0x0},
+		{0x531f, 0x2},
+		{0x5320, 0x0},
+		{0x5321, 0x24},
+		{0x5322, 0x0},
+		{0x5323, 0x56},
+		{0x5324, 0x0},
+		{0x5325, 0x85},
+		{0x5326, 0x0},
+		{0x5327, 0x20},
+		{0x5609, 0x1},
+		{0x560a, 0x40},
+		{0x560b, 0x1},
+		{0x560c, 0x40},
+		{0x560d, 0x0},
+		{0x560e, 0xfa},
+		{0x560f, 0x0},
+		{0x5610, 0xfa},
+		{0x5611, 0x2},
+		{0x5612, 0x80},
+		{0x5613, 0x2},
+		{0x5614, 0x80},
+		{0x5615, 0x1},
+		{0x5616, 0x2c},
+		{0x5617, 0x1},
+		{0x5618, 0x2c},
+		{0x563b, 0x1},
+		{0x563c, 0x1},
+		{0x563d, 0x1},
+		{0x563e, 0x1},
+		{0x563f, 0x3},
+		{0x5640, 0x3},
+		{0x5641, 0x3},
+		{0x5642, 0x5},
+		{0x5643, 0x9},
+		{0x5644, 0x5},
+		{0x5645, 0x5},
+		{0x5646, 0x5},
+		{0x5647, 0x5},
+		{0x5651, 0x0},
+		{0x5652, 0x80},
+		{0x521a, 0x1},
+		{0x521b, 0x3},
+		{0x521c, 0x6},
+		{0x521d, 0xa},
+		{0x521e, 0xe},
+		{0x521f, 0x12},
+		{0x5220, 0x16},
+		{0x5223, 0x2},
+		{0x5225, 0x4},
+		{0x5227, 0x8},
+		{0x5229, 0xc},
+		{0x522b, 0x12},
+		{0x522d, 0x18},
+		{0x522f, 0x1e},
+		{0x5241, 0x4},
+		{0x5242, 0x1},
+		{0x5243, 0x3},
+		{0x5244, 0x6},
+		{0x5245, 0xa},
+		{0x5246, 0xe},
+		{0x5247, 0x12},
+		{0x5248, 0x16},
+		{0x524a, 0x3},
+		{0x524c, 0x4},
+		{0x524e, 0x8},
+		{0x5250, 0xc},
+		{0x5252, 0x12},
+		{0x5254, 0x18},
+		{0x5256, 0x1e},
+		{0x4605, 0x00}, /* 8-bit YUV mode. */
+		{0x4606, 0x7},
+		{0x4607, 0x71},
+		{0x460a, 0x2},
+		{0x460b, 0x70},
+		{0x460c, 0x0},
+		{0x4620, 0xe},
+		{0x4700, 0x4},
+		{0x4701, 0x01},
+		/* {0x4702, 0x1}, */
+		{0x4702, 0x00},  /* 01 */
+		{0x4703, 0x00},
+		{0x4704, 0x00}, /* 01 */
+		/*
+		 * Non-overlapping HSYNC-VSYNC.
+		 * Therefore do not set the VSYNC delay registers.
+		 */
+		{0x4705, 0x00}, /* Vsync delay high byte */
+		{0x4706, 0x00}, /* Vsync delay middle byte */
+		{0x4707, 0x00}, /* Vsync delay low byte{0x4708, 0x1}, */
+		/* {0x4709, 0x50}, */
+		{0x4004, 0x8},
+		{0x4005, 0x18},
+		{0x4001, 0x4},
+		{0x4050, 0x20},
+		{0x4051, 0x22},
+		{0x4057, 0x9c},
+		{0x405a, 0x0},
+		{0x4202, 0x2},
+		{0x3023, 0x10},
+		{0x100, 0x1},
+		{0x100, 0x1},
+		{0x6f0e, 0x0},
+		{0x6f0f, 0x0},
+		{0x460e, 0x8},
+		{0x460f, 0x1},
+		{0x4610, 0x0},
+		{0x4611, 0x1},
+		{0x4612, 0x0},
+		{0x4613, 0x1},
+		{0x4605, 0x00},
+		{0x4608, 0x0},
+		{0x4609, 0x8},
+		{0x6804, 0x0},
+		{0x6805, 0x6},
+		{0x6806, 0x0},
+		{0x5120, 0x0},
+		{0x3510, 0x0},
+		{0x3504, 0x0},
+		{0x6800, 0x0},
+		{0x6f0d, 0x0},
+		{0x5000, 0xff},
+		{0x5001, 0xbf},
+		{0x5002, 0xfe},
+		{0x503d, 0x0},
+		/* {0x503e, 0x00}, */
+		{0xc450, 0x1},
+		{0xc452, 0x4},
+		{0xc453, 0x0},
+		{0xc454, 0x0},
+		{0xc455, 0x0},
+		{0xc456, 0x0},
+		{0xc457, 0x0},
+		{0xc458, 0x0},
+		{0xc459, 0x0},
+		{0xc45b, 0x0},
+		{0xc45c, 0x0},
+		{0xc45d, 0x0},
+		{0xc45e, 0x0},
+		{0xc45f, 0x0},
+		{0xc460, 0x0},
+		{0xc461, 0x1},
+		{0xc462, 0x1},
+		{0xc464, 0x88},
+		{0xc465, 0x0},
+		{0xc466, 0x8a},
+		{0xc467, 0x0},
+		{0xc468, 0x86},
+		{0xc469, 0x0},
+		{0xc46a, 0x40},
+		{0xc46b, 0x50},
+		{0xc46c, 0x30},
+		{0xc46d, 0x28},
+		{0xc46e, 0x60},
+		{0xc46f, 0x40},
+		{0xc47c, 0x1},
+		{0xc47d, 0x38},
+		{0xc47e, 0x0},
+		{0xc47f, 0x0},
+		{0xc480, 0x0},
+		{0xc481, 0xff},
+		{0xc482, 0x0},
+		{0xc483, 0x40},
+		{0xc484, 0x0},
+		{0xc485, 0x18},
+		{0xc486, 0x0},
+		{0xc487, 0x18},
+		{0xc488, 0x2e},
+		{0xc489, 0x80},
+		{0xc48a, 0x2e},
+		{0xc48b, 0x80},
+		{0xc48c, 0x0},
+		{0xc48d, 0x4},
+		{0xc48e, 0x0},
+		{0xc48f, 0x4},
+		{0xc490, 0x7},
+		{0xc492, 0x20},
+		{0xc493, 0x8},
+		{0xc498, 0x2},
+		{0xc499, 0x0},
+		{0xc49a, 0x2},
+		{0xc49b, 0x0},
+		{0xc49c, 0x2},
+		{0xc49d, 0x0},
+		{0xc49e, 0x2},
+		{0xc49f, 0x60},
+		{0xc4a0, 0x4},
+		{0xc4a1, 0x0},
+		{0xc4a2, 0x6},
+		{0xc4a3, 0x0},
+		{0xc4a4, 0x0},
+		{0xc4a5, 0x10},
+		{0xc4a6, 0x0},
+		{0xc4a7, 0x40},
+		{0xc4a8, 0x0},
+		{0xc4a9, 0x80},
+		{0xc4aa, 0xd},
+		{0xc4ab, 0x0},
+		{0xc4ac, 0xf},
+		{0xc4ad, 0xc0},
+		{0xc4b4, 0x1},
+		{0xc4b5, 0x1},
+		{0xc4b6, 0x0},
+		{0xc4b7, 0x1},
+		{0xc4b8, 0x0},
+		{0xc4b9, 0x1},
+		{0xc4ba, 0x1},
+		{0xc4bb, 0x0},
+		{0xc4be, 0x2},
+		{0xc4bf, 0x33},
+		{0xc4c8, 0x3},
+		{0xc4c9, 0xd0},
+		{0xc4ca, 0xe},
+		{0xc4cb, 0x0},
+		{0xc4cc, 0xe},
+		{0xc4cd, 0x51},
+		{0xc4ce, 0xe},
+		{0xc4cf, 0x51},
+		{0xc4d0, 0x4},
+		{0xc4d1, 0x80},
+		{0xc4e0, 0x4},
+		{0xc4e1, 0x2},
+		{0xc4e2, 0x1},
+		{0xc4e4, 0x10},
+		{0xc4e5, 0x20},
+		{0xc4e6, 0x30},
+		{0xc4e7, 0x40},
+		{0xc4e8, 0x50},
+		{0xc4e9, 0x60},
+		{0xc4ea, 0x70},
+		{0xc4eb, 0x80},
+		{0xc4ec, 0x90},
+		{0xc4ed, 0xa0},
+		{0xc4ee, 0xb0},
+		{0xc4ef, 0xc0},
+		{0xc4f0, 0xd0},
+		{0xc4f1, 0xe0},
+		{0xc4f2, 0xf0},
+		{0xc4f3, 0x80},
+		{0xc4f4, 0x0},
+		{0xc4f5, 0x20},
+		{0xc4f6, 0x2},
+		{0xc4f7, 0x0},
+		{0xc4f8, 0x4},
+		{0xc4f9, 0xb},
+		{0xc4fa, 0x0},
+		{0xc4fb, 0x1},
+		{0xc4fc, 0x1},
+		{0xc4fd, 0x1},
+		{0xc4fe, 0x4},
+		{0xc4ff, 0x2},
+		{0xc500, 0x68},
+		{0xc501, 0x74},
+		{0xc502, 0x70},
+		{0xc503, 0x80},
+		{0xc504, 0x5},
+		{0xc505, 0x80},
+		{0xc506, 0x3},
+		{0xc507, 0x80},
+		{0xc508, 0x1},
+		{0xc509, 0xc0},
+		{0xc50a, 0x1},
+		{0xc50b, 0xa0},
+		{0xc50c, 0x1},
+		{0xc50d, 0x2c},
+		{0xc50e, 0x1},
+		{0xc50f, 0xa},
+		{0xc510, 0x0},
+		{0xc511, 0x0},
+		{0xc512, 0xe5},
+		{0xc513, 0x14},
+		{0xc514, 0x4},
+		{0xc515, 0x0},
+		{0xc518, 0x3},
+		{0xc519, 0x48},
+		{0xc51a, 0x7},
+		{0xc51b, 0x70},
+		{0xc2e0, 0x0},
+		{0xc2e1, 0x51},
+		{0xc2e2, 0x0},
+		{0xc2e3, 0xd6},
+		{0xc2e4, 0x1},
+		{0xc2e5, 0x5e},
+		{0xc2e9, 0x1},
+		{0xc2ea, 0x7a},
+		{0xc2eb, 0x90},
+		{0xc2ed, 0x1},
+		{0xc2ee, 0x7a},
+		{0xc2ef, 0x64},
+		{0xc308, 0x0},
+		{0xc309, 0x0},
+		{0xc30a, 0x0},
+		{0xc30c, 0x0},
+		{0xc30d, 0x1},
+		{0xc30e, 0x0},
+		{0xc30f, 0x0},
+		{0xc310, 0x1},
+		{0xc311, 0x60},
+		{0xc312, 0xff},
+		{0xc313, 0x8},
+		{0xc314, 0x1},
+		{0xc315, 0x7f},
+		{0xc316, 0xff},
+		{0xc317, 0xb},
+		{0xc318, 0x0},
+		{0xc319, 0xc},
+		{0xc31a, 0x0},
+		{0xc31b, 0xe0},
+		{0xc31c, 0x0},
+		{0xc31d, 0x14},
+		{0xc31e, 0x0},
+		{0xc31f, 0xc5},
+		{0xc320, 0xff},
+		{0xc321, 0x4b},
+		{0xc322, 0xff},
+		{0xc323, 0xf0},
+		{0xc324, 0xff},
+		{0xc325, 0xe8},
+		{0xc326, 0x0},
+		{0xc327, 0x46},
+		{0xc328, 0xff},
+		{0xc329, 0xd2},
+		{0xc32a, 0xff},
+		{0xc32b, 0xe4},
+		{0xc32c, 0xff},
+		{0xc32d, 0xbb},
+		{0xc32e, 0x0},
+		{0xc32f, 0x61},
+		{0xc330, 0xff},
+		{0xc331, 0xf9},
+		{0xc332, 0x0},
+		{0xc333, 0xd9},
+		{0xc334, 0x0},
+		{0xc335, 0x2e},
+		{0xc336, 0x0},
+		{0xc337, 0xb1},
+		{0xc338, 0xff},
+		{0xc339, 0x64},
+		{0xc33a, 0xff},
+		{0xc33b, 0xeb},
+		{0xc33c, 0xff},
+		{0xc33d, 0xe8},
+		{0xc33e, 0x0},
+		{0xc33f, 0x48},
+		{0xc340, 0xff},
+		{0xc341, 0xd0},
+		{0xc342, 0xff},
+		{0xc343, 0xed},
+		{0xc344, 0xff},
+		{0xc345, 0xad},
+		{0xc346, 0x0},
+		{0xc347, 0x66},
+		{0xc348, 0x1},
+		{0xc349, 0x0},
+		{0x6700, 0x4},
+		{0x6701, 0x7b},
+		{0x6702, 0xfd},
+		{0x6703, 0xf9},
+		{0x6704, 0x3d},
+		{0x6705, 0x71},
+		/*
+		 * 0x6706[3:0] :: XVCLK
+		 * 0x6706[3:0] :: 0 = 6MHz
+		 * 0x6706[3:0] :: 1 = 9MHz
+		 * 0x6706[3:0] :: 8 = 24MHz
+		 * 0x6706[3:0] :: 9 = 27MHz
+		 */
+		{0x6706, 0x71},
+		{0x6708, 0x5},
+		{0x3822, 0x50},
+		{0x6f06, 0x6f},
+		{0x6f07, 0x0},
+		{0x6f0a, 0x6f},
+		{0x6f0b, 0x0},
+		{0x6f00, 0x3},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x3042, 0xf0},
+		{0x301b, 0xf0},
+		{0x301c, 0xf0},
+		{0x301a, 0xf0},
+};
+
+static const struct ov1063x_reg ov1063x_regs_change_mode[] = {
+	{ 0x301b, 0xff }, { 0x301c, 0xff }, { 0x301a, 0xff }, { 0x5005, 0x08 },
+	{ 0x3007, 0x01 }, { 0x381c, 0x00 }, { 0x381f, 0x0C }, { 0x4001, 0x04 },
+	{ 0x4004, 0x08 }, { 0x4050, 0x20 }, { 0x4051, 0x22 }, { 0x6e47, 0x0C },
+	{ 0x4610, 0x05 }, { 0x4613, 0x10 },
+};
+
+static const struct ov1063x_reg ov1063x_regs_bt656[] = {
+	{ 0x4700, 0x02 }, { 0x4302, 0x03 }, { 0x4303, 0xf8 }, { 0x4304, 0x00 },
+	{ 0x4305, 0x08 }, { 0x4306, 0x03 }, { 0x4307, 0xf8 }, { 0x4308, 0x00 },
+	{ 0x4309, 0x08 },
+};
+
+static const struct ov1063x_reg ov1063x_regs_bt656_10bit[] = {
+	{ 0x4700, 0x02 }, { 0x4302, 0x03 }, { 0x4303, 0xfe }, { 0x4304, 0x00 },
+	{ 0x4305, 0x02 }, { 0x4306, 0x03 }, { 0x4307, 0xfe }, { 0x4308, 0x00 },
+	{ 0x4309, 0x02 },
+};
+
+static const struct ov1063x_reg ov1063x_regs_vert_sub_sample[] = {
+	{ 0x381f, 0x06 }, { 0x4001, 0x02 }, { 0x4004, 0x02 }, { 0x4050, 0x10 },
+	{ 0x4051, 0x11 }, { 0x6e47, 0x06 }, { 0x4610, 0x03 }, { 0x4613, 0x0a },
+};
+
+static const struct ov1063x_reg ov1063x_regs_enable[] = {
+	{ 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x301b, 0xf0 }, { 0x301c, 0xf0 },
+	{ 0x301a, 0xf0 },
+};
+
+/*
+ * Datasheet highlight the following sequence to enable and disable
+ * Test Pattern mode i.e. colobar
+ */
+static const struct ov1063x_reg ov1063x_regs_colorbar_enable[] = {
+	{0x5120, 0x01}, {0x56d0, 0x01}, {0x5300, 0x01}, {0x5301, 0x00},
+	{0x5302, 0x00},	{0x5303, 0x00},	{0x5304, 0x00},	{0x5305, 0x00},
+	{0x5306, 0x00},	{0x5307, 0x00},	{0x5308, 0x01},	{0x5309, 0x00},
+	{0x530a, 0x00},	{0x530b, 0x00},	{0x530c, 0x00},	{0x530d, 0x00},
+	{0x530e, 0x00},	{0x530f, 0x00},	{0x5310, 0x01},	{0x5311, 0x00},
+	{0x5312, 0x00},	{0x5313, 0x00},	{0x5314, 0x01},	{0x5315, 0x00},
+	{0x5316, 0x00},	{0x5317, 0x00},	{0x5318, 0x00},	{0x5319, 0x00},
+	{0x531a, 0x00},	{0x531b, 0x00},	{0x531c, 0x01},	{0x531d, 0x00},
+	{0x531e, 0x00},	{0x531f, 0x00},	{0x5320, 0x00},	{0x5321, 0x00},
+	{0x5322, 0x00},	{0x5323, 0x00},	{0x5324, 0x01},	{0x5325, 0x00},
+	{0x5326, 0x00},	{0x5327, 0x00},	{0xc2ea, 0x80},	{0xc2eb, 0x80},
+	{0x5000, 0x79},	{0x503d, 0x80},
+};
+
+static const struct ov1063x_reg ov1063x_regs_colorbar_disable[] = {
+	{0x503d, 0x00},	{0x5120, 0x00},	{0x56d0, 0x00},	{0x5300, 0x01},
+	{0x5301, 0x00},	{0x5302, 0x00},	{0x5303, 0x0e},	{0x5304, 0x00},
+	{0x5305, 0x0e},	{0x5306, 0x00},	{0x5307, 0x36},	{0x5308, 0x00},
+	{0x5309, 0xd9},	{0x530a, 0x00},	{0x530b, 0x0f},	{0x530c, 0x00},
+	{0x530d, 0x2c},	{0x530e, 0x00},	{0x530f, 0x59},	{0x5310, 0x00},
+	{0x5311, 0x7b},	{0x5312, 0x00},	{0x5313, 0x22},	{0x5314, 0x00},
+	{0x5315, 0xd5},	{0x5316, 0x00},	{0x5317, 0x13},	{0x5318, 0x00},
+	{0x5319, 0x18},	{0x531a, 0x00},	{0x531b, 0x26},	{0x531c, 0x00},
+	{0x531d, 0xdc},	{0x531e, 0x00},	{0x531f, 0x02},	{0x5320, 0x00},
+	{0x5321, 0x24},	{0x5322, 0x00},	{0x5323, 0x56},	{0x5324, 0x00},
+	{0x5325, 0x85},	{0x5326, 0x00},	{0x5327, 0x20},	{0xc2ea, 0x7a},
+	{0xc2eb, 0x90},	{0x5000, 0xff},
+};

+ 21 - 4
drivers/media/i2c/ov2659.c

@@ -32,6 +32,8 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_graph.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
 #include <linux/videodev2.h>
@@ -232,6 +234,8 @@ struct ov2659 {
 	struct sensor_register *format_ctrl_regs;
 	struct ov2659_pll_ctrl pll;
 	int streaming;
+	/* used to control the sensor powerdownN pin */
+	struct gpio_desc *pwrdn_gpio;
 };
 
 static const struct sensor_register ov2659_init_regs[] = {
@@ -419,10 +423,14 @@ static struct sensor_register ov2659_720p[] = {
 	{ REG_TIMING_YINC, 0x11 },
 	{ REG_TIMING_VERT_FORMAT, 0x80 },
 	{ REG_TIMING_HORIZ_FORMAT, 0x00 },
+	{ 0x370a, 0x12 },
 	{ 0x3a03, 0xe8 },
 	{ 0x3a09, 0x6f },
 	{ 0x3a0b, 0x5d },
 	{ 0x3a15, 0x9a },
+	{ REG_VFIFO_READ_START_H, 0x00 },
+	{ REG_VFIFO_READ_START_L, 0x80 },
+	{ REG_ISP_CTRL02, 0x00 },
 	{ REG_NULL, 0x00 },
 };
 
@@ -661,7 +669,7 @@ static struct sensor_register ov2659_vga[] = {
 	{ REG_TIMING_HORIZ_FORMAT, 0x01 },
 	{ 0x370a, 0x52 },
 	{ REG_VFIFO_READ_START_H, 0x00 },
-	{ REG_VFIFO_READ_START_L, 0x80 },
+	{ REG_VFIFO_READ_START_L, 0xa0 },
 	{ REG_ISP_CTRL02, 0x10 },
 	{ REG_NULL, 0x00 },
 };
@@ -709,7 +717,7 @@ static  struct sensor_register ov2659_qvga[] = {
 	{ REG_TIMING_HORIZ_FORMAT, 0x01 },
 	{ 0x370a, 0x52 },
 	{ REG_VFIFO_READ_START_H, 0x00 },
-	{ REG_VFIFO_READ_START_L, 0x80 },
+	{ REG_VFIFO_READ_START_L, 0xa0 },
 	{ REG_ISP_CTRL02, 0x10 },
 	{ REG_NULL, 0x00 },
 };
@@ -1330,11 +1338,12 @@ static int ov2659_detect(struct v4l2_subdev *sd)
 		unsigned short id;
 
 		id = OV265X_ID(pid, ver);
-		if (id != OV2659_ID)
+		if (id != OV2659_ID) {
 			dev_err(&client->dev,
 				"Sensor detection failed (%04X, %d)\n",
 				id, ret);
-		else {
+			ret = -ENODEV;
+		} else {
 			dev_info(&client->dev, "Found OV%04X sensor\n", id);
 			ret = ov2659_init(sd, 0);
 		}
@@ -1389,6 +1398,7 @@ static int ov2659_probe(struct i2c_client *client,
 	struct v4l2_subdev *sd;
 	struct ov2659 *ov2659;
 	struct clk *clk;
+	struct gpio_desc *gpio;
 	int ret;
 
 	if (!pdata) {
@@ -1412,6 +1422,13 @@ static int ov2659_probe(struct i2c_client *client,
 	    ov2659->xvclk_frequency > 27000000)
 		return -EINVAL;
 
+	/* Optional gpio don't fail if not present */
+	gpio = devm_gpiod_get_optional(&client->dev, "pwrdn", GPIOD_OUT_HIGH);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+
+	ov2659->pwrdn_gpio = gpio;
+
 	v4l2_ctrl_handler_init(&ov2659->ctrls, 2);
 	ov2659->link_frequency =
 			v4l2_ctrl_new_std(&ov2659->ctrls, &ov2659_ctrl_ops,

+ 675 - 0
drivers/media/i2c/ov490.c

@@ -0,0 +1,675 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * OmniVision OV490 Camera Driver
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated -  http://www.ti.com/
+ * Author: Nikhil Devshatwar <nikhil.nd@ti.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-subdev.h>
+
+/* Register definitions */
+#define OV490_PID			0x300a
+#define OV490_VER			0x300b
+#define OV490_BANK_HIGH			0xfffd
+#define OV490_BANK_LOW			0xfffe
+
+#define OV490_MIPI_TX_LANE_CTRL2	0x8029202D
+#define OV490_MIPI_TX_LANE_CTRL0	0x80292015
+
+#define OV490_SC_RESET1			0x80800011
+#define OV490_FW_VER_HIGH		0x80800102
+#define OV490_FW_VER_LOW		0x80800103
+
+#define OV490_IMAGE0_CTRL		0x8082000a
+#define OV490_IMAGE0_BYTE_INVERT	BIT(7)
+#define OV490_IMAGE0_BYTE_SEQUENCE	GEN_MASK(6, 4)
+#define OV490_IMAGE0_BYTE_SEQUENCE_1	BIT(4)
+#define OV490_IMAGE0_FORMAT_SELECT	GEN_MASK(3, 0)
+#define OV490_IMAGE0_FORMAT_PURE_RAW12		0x0
+#define OV490_IMAGE0_FORMAT_3X12_RAW		0x2
+#define OV490_IMAGE0_FORMAT_2X12_RAW		0x3
+#define OV490_IMAGE0_FORMAT_20_COMBINED_RAW	0x4
+#define OV490_IMAGE0_FORMAT_16_COMPRESSED_RAW	0x5
+#define OV490_IMAGE0_FORMAT_12_COMPRESSED_RAW	0x6
+#define OV490_IMAGE0_FORMAT_2X12_COMPRESSED_RAW	0x7
+#define OV490_IMAGE0_FORMAT_12_SELECTED_RAW	0x8
+#define OV490_IMAGE0_FORMAT_12_YUV422		0xf
+
+/* IDs */
+#define OV490_VERSION_REG		0x0490
+#define OV490_VERSION(pid, ver)	(((pid) << 8) | ((ver) & 0xff))
+
+#define OV490_MAX_WIDTH			1280
+#define OV490_MAX_HEIGHT		800
+
+#define MAX_NUM_GPIOS			10
+
+/* Host Command Flow */
+#define OV490_STATUS_ADDR		(0x80195ffc)
+#define OV490_HOST_CMD_PARA_ADDR	(0x80195000)
+#define OV490_HOST_CMD_RESULT_ADDR	(0x80195000)
+#define OV490_HOST_INT_ADDR		(0x808000c0)
+#define OV490_STATUS_FINISH		(0x99)
+#define OV490_STATUS_ERROR		(0x55)
+
+/* Host Command List */
+#define OV490_CMD_BRIGHTNESS_SET	(0xf1)
+#define OV490_CMD_SATURATION_SET	(0xf3)
+#define OV490_CMD_HUE_SET		(0xf5)
+#define OV490_CMD_FRAMERATE_SET		(0xf7)
+#define OV490_CMD_GAMMA_SET		(0xf9)
+#define OV490_CMD_SHARPNESS_SET		(0xfb)
+#define OV490_CMD_CONTRAST_SET		(0xfd)
+#define OV490_CMD_GROUPWRITE_SET	(0xe1)
+#define OV490_CMD_STREAMING_CTRL	(0xe2)
+#define OV490_CMD_CONTEXT_SWITCH_CONFIG	(0xe3)
+#define OV490_CMD_CONTEXT_SWITCH_CTRL	(0xe4)
+#define OV490_CMD_MULT_CMD		(0xe5)
+#define OV490_CMD_GPIO_SET		(0xe6)
+#define OV490_CMD_GPIO_GET		(0xe7)
+#define OV490_CMD_FORMAT_SET		(0xe8)
+#define OV490_CMD_TEMP_GET		(0xe9)
+#define OV490_CMD_EXPOSURE_GAIN_SET	(0xea)
+#define OV490_CMD_AWBGAIN_SET		(0xeb)
+#define OV490_CMD_DENOISE_SET		(0xec)
+#define OV490_CMD_TONECURVE_SET		(0xed)
+#define OV490_CMD_COMB_WEIGHT_SET	(0xee)
+#define OV490_CMD_AEC_WEIGHT_SET	(0xd2)
+#define OV490_CMD_AWB_ROI_SET		(0xd3)
+#define OV490_CMD_TONEMAPPING_ROI_SET	(0xd4)
+#define OV490_CMD_STAT_ROI_SET		(0xd5)
+#define OV490_CMD_TESTPATTERN_SET	(0xd6)
+#define OV490_CMD_MTF_SET		(0xd7)
+#define OV490_CMD_LENC_SET		(0xd8)
+#define OV490_CMD_BLC_SET		(0xd9)
+#define OV490_CMD_GROUPWRITE_LAUNCH	(0xda)
+#define OV490_CMD_EMBLINE_CTRL		(0xdb)
+#define OV490_CMD_MIRRFLIP_CTRL		(0xdc)
+#define OV490_CMD_EXTRA_VTS_SET		(0xde)
+#define OV490_CMD_SNR_REG_ACCESS	(0xc1)
+#define OV490_CMD_POSTAWBGAIN_SET	(0xc2)
+#define OV490_CMD_CROP_SET		(0xc3)
+#define OV490_CMD_FRAMESYNC		(0xc4)
+#define OV490_CMD_BANDING_SET		(0xc5)
+#define OV490_CMD_TOPEMB_SET		(0xc7)
+#define OV490_CMD_FWREG_ACCESS		(0x35)
+#define OV490_CMD_FADE_CTRL		(0x37)
+#define OV490_CMD_INIT_CTRL		(0x39)
+#define OV490_CMD_RESET_CTRL		(0x3a)
+
+/*
+ * = fvco / pixel_width * num_lanes
+ * = 804,000,000 / 16 bits * 4 lanes
+ */
+#define OV490_PIXEL_RATE_PER_LANE	50250000
+
+static u8 ov490_init_param[] = {
+	0x31,	/* [3:0]input port, [7:4]  input format */
+	0x01,	/* [3:0]output port, [7:4]  output format */
+	0x05,	/* in width: 1288 */
+	0x08,
+	0x04,	/* in height:1080 */
+	0x40,
+	0x05,	/* out width: 1288 */
+	0x08,
+	0x04,	/* out height:1080 */
+	0x40,
+	0x00,
+};
+
+struct ov490_color_format {
+	u32 code;
+	u32 colorspace;
+};
+
+struct ov490_priv {
+	struct v4l2_subdev		subdev;
+	struct v4l2_async_subdev	asd;
+	const struct ov490_color_format	*cfmt;
+	int				width;
+	int				height;
+	int				num_lanes;
+
+	struct regmap			*regmap;
+
+	struct gpio_descs		*mux_gpios;
+
+	struct v4l2_ctrl_handler	handler;
+	struct v4l2_ctrl		*pixel_rate;
+};
+
+/* Main access control */
+DEFINE_MUTEX(ov490_lock);
+static int ov490_init_gpios(struct i2c_client *client);
+
+/*
+ * supported color format list
+ */
+static const struct ov490_color_format ov490_cfmts[] = {
+	{
+		.code		= MEDIA_BUS_FMT_YUYV8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+	},
+};
+
+static struct ov490_priv *to_ov490(const struct i2c_client *client)
+{
+	return container_of(i2c_get_clientdata(client), struct ov490_priv,
+			    subdev);
+}
+
+static struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+	return &container_of(ctrl->handler, struct ov490_priv, handler)->subdev;
+}
+
+static int ov490_reg_write32(struct regmap *map, u32 reg, u8 val)
+{
+	u8 bank_high = (reg >> 24) & 0xff;
+	u8 bank_low  = (reg >> 16) & 0xff;
+	u16 reg_addr = reg & 0xffff;
+	int ret = 0;
+
+	/* For writing a register with 32 bit address, First set the bank
+	 * address by writing to two BANK address registers. Then access
+	 * the register using 16LSB bits.
+	 */
+	ret = regmap_write(map, OV490_BANK_HIGH, bank_high);
+	if (!ret)
+		ret = regmap_write(map, OV490_BANK_LOW, bank_low);
+	if (!ret)
+		ret = regmap_write(map, reg_addr, val);
+	return ret;
+}
+
+static int ov490_reg_read32(struct regmap *map, u32 reg, u8 *val)
+{
+	u8 bank_high = (reg >> 24) & 0xff;
+	u8 bank_low  = (reg >> 16) & 0xff;
+	u16 reg_addr = reg & 0xffff;
+	int ret = 0;
+	u32 tval = 0;
+
+	/*
+	 * For reading a register with 32 bit address, First set the bank
+	 * address by writing to two BANK address registers. Then access
+	 * the register using 16LSB bits.
+	 */
+	ret = regmap_write(map, OV490_BANK_HIGH, bank_high);
+	if (!ret)
+		ret = regmap_write(map, OV490_BANK_LOW, bank_low);
+	if (!ret)
+		ret = regmap_read(map, reg_addr, &tval);
+
+	*val = (u8)tval;
+	return ret;
+}
+
+static int ov490_host_control_set(struct regmap *map, u8 host_cmd,
+				  u8 *param, u16 number)
+{
+	int i, ret;
+	u8 status = 0;
+
+	/* Host reset OV490_status_register */
+	ret = ov490_reg_write32(map, OV490_STATUS_ADDR, 0);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < number; i++) {
+		ret = ov490_reg_write32(map, OV490_HOST_CMD_PARA_ADDR + i,
+					*(param + i));
+		if (ret)
+			return ret;
+	}
+
+	ret = ov490_reg_write32(map, OV490_HOST_INT_ADDR, host_cmd);
+	if (ret)
+		return ret;
+
+	for (i = 500; i && status != OV490_STATUS_FINISH; i--) {
+		ret = ov490_reg_read32(map, OV490_STATUS_ADDR, &status);
+		if (ret)
+			return ret;
+		usleep_range(500, 1000);
+	}
+
+	if (!i)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int __maybe_unused ov490_host_control_get(struct regmap *map,
+						 u8 host_cmd, u8 *param,
+						 u16 number)
+{
+	int i, ret;
+	u8 status = 0;
+
+	/* Host reset OV490_status_register */
+	ret = ov490_reg_write32(map, OV490_STATUS_ADDR, 0);
+	if (ret)
+		return ret;
+
+	ret = ov490_reg_write32(map, OV490_HOST_INT_ADDR, host_cmd);
+	if (ret)
+		return ret;
+
+	for (i = 500; i && status != OV490_STATUS_FINISH; i--) {
+		ret = ov490_reg_read32(map, OV490_STATUS_ADDR, &status);
+		if (ret)
+			return ret;
+		usleep_range(500, 1000);
+	}
+
+	if (!i)
+		return -ETIMEDOUT;
+
+	for (i = 0; i < number; i++) {
+		ret = ov490_reg_read32(map, OV490_HOST_CMD_PARA_ADDR + i,
+				       (param + i));
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static int ov490_get_fw_version(struct regmap *map, u16 *id)
+{
+	u8 hi, lo;
+	int ret;
+
+	ret = ov490_reg_read32(map, OV490_FW_VER_HIGH, &hi);
+	if (ret)
+		return ret;
+
+	ret = ov490_reg_read32(map, OV490_FW_VER_LOW, &lo);
+	if (ret)
+		return ret;
+
+	*id = hi << 8 | lo;
+
+	return 0;
+}
+
+/* Start/Stop streaming from the device */
+static int ov490_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov490_priv *priv = to_ov490(client);
+	struct regmap *map = priv->regmap;
+	int ret, val;
+	u8 streaming;
+
+	mutex_lock(&ov490_lock);
+	ret = ov490_init_gpios(client);
+	if (ret) {
+		dev_err(&client->dev, "Failed to request gpios");
+		goto unlock;
+	}
+
+	if (!enable) {
+		streaming = 0;
+		/* Stop Streaming */
+		ret = ov490_host_control_set(map, OV490_CMD_STREAMING_CTRL,
+					     &streaming, 1);
+		if (ret)
+			goto unlock;
+		ret = ov490_reg_write32(map, OV490_MIPI_TX_LANE_CTRL0, 0xa0);
+		if (ret)
+			goto unlock;
+		/* Put MIPI_TX in reset */
+		ret = ov490_reg_write32(map, OV490_SC_RESET1, 0x80);
+		goto unlock;
+	}
+
+	/* Take MIPI_TX out of reset */
+	ret = ov490_reg_write32(map, OV490_SC_RESET1, 0x00);
+	if (ret)
+		goto unlock;
+	ret = ov490_reg_write32(map, OV490_MIPI_TX_LANE_CTRL0, 0x80);
+	if (ret)
+		goto unlock;
+
+	/* Initialize slave */
+	ret = ov490_host_control_set(map, OV490_CMD_INIT_CTRL, ov490_init_param,
+				     ARRAY_SIZE(ov490_init_param));
+	if (ret)
+		goto unlock;
+
+	ret = ov490_reg_write32(map, OV490_IMAGE0_CTRL,
+				OV490_IMAGE0_BYTE_INVERT |
+				OV490_IMAGE0_BYTE_SEQUENCE_1 |
+				OV490_IMAGE0_FORMAT_3X12_RAW);
+	if (ret)
+		goto unlock;
+
+	/* Set number of data lane to use */
+	val = priv->num_lanes == 2 ? 0x03 : priv->num_lanes == 4 ? 0x0F : 0x0F;
+	ret = ov490_reg_write32(map, OV490_MIPI_TX_LANE_CTRL2, val);
+	if (ret)
+		goto unlock;
+
+	/* Start Streaming */
+	streaming = 0x1;
+	ret = ov490_host_control_set(map, OV490_CMD_STREAMING_CTRL,
+				     &streaming, 1);
+
+unlock:
+	mutex_unlock(&ov490_lock);
+	return ret;
+}
+
+static int ov490_get_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_format *fmt)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov490_priv *priv = to_ov490(client);
+	struct v4l2_mbus_framefmt *mf = &fmt->format;
+
+	mf->width	= priv->width;
+	mf->height	= priv->height;
+	mf->code	= priv->cfmt->code;
+	mf->colorspace	= priv->cfmt->colorspace;
+	mf->field	= V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+/* Fixed format - no configurability */
+static int ov490_set_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_format *fmt)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov490_priv *priv = to_ov490(client);
+	struct v4l2_mbus_framefmt *mf = &fmt->format;
+
+	mf->width	= priv->width;
+	mf->height	= priv->height;
+	mf->code	= priv->cfmt->code;
+	mf->colorspace	= priv->cfmt->colorspace;
+	mf->field	= V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int ov490_enum_code(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index >= ARRAY_SIZE(ov490_cfmts))
+		return -EINVAL;
+
+	code->code = ov490_cfmts[code->index].code;
+
+	return 0;
+}
+
+static int ov490_enum_size(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_frame_size_enum *fse)
+{
+	int cam_width[] =	{ OV490_MAX_WIDTH };
+	int cam_height[] =	{ OV490_MAX_HEIGHT };
+
+	if (fse->index >= ARRAY_SIZE(cam_width))
+		return -EINVAL;
+
+	fse->min_width  = cam_width[fse->index];
+	fse->max_width  = fse->min_width;
+	fse->min_height = cam_height[fse->index];
+	fse->max_height = fse->min_height;
+	return 0;
+}
+
+static int ov490_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	/* If the board has programmable pixel clock, set it here */
+	if (ctrl->id == V4L2_CID_PIXEL_RATE)
+		dev_info(&client->dev, "Pixel rate set to %d\n",
+			 ctrl->val);
+	return 0;
+}
+
+static int ov490_init_gpios(struct i2c_client *client)
+{
+	struct ov490_priv *priv = to_ov490(client);
+	int ret = 0;
+
+	/* Request the gpio lines and set the values
+	 * then release them so that other drivers can use them
+	 * This allows changing common board muxes which are
+	 * controlled by multiple drivers
+	 */
+	priv->mux_gpios = gpiod_get_array(&client->dev, "mux", GPIOD_OUT_HIGH);
+	if (IS_ERR(priv->mux_gpios))
+		goto done;
+	gpiod_put_array(priv->mux_gpios);
+done:
+	return ret;
+}
+
+static int ov490_video_probe(struct i2c_client *client)
+{
+	struct ov490_priv *priv = i2c_get_clientdata(client);
+	u32 pid, ver;
+	int ret;
+	u16 version;
+
+	/* check and show product ID and manufacturer ID */
+	ret = regmap_read(priv->regmap, OV490_PID, &pid);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(priv->regmap, OV490_VER, &ver);
+	if (ret)
+		return ret;
+
+	if (OV490_VERSION(pid, ver) != OV490_VERSION_REG) {
+		dev_err(&client->dev, "Product ID error %02x:%02x\n", pid, ver);
+		return -ENODEV;
+	}
+
+	dev_info(&client->dev, "ov490 Product ID %02x Manufacturer ID %02x\n",
+		 pid, ver);
+
+	ret = ov490_get_fw_version(priv->regmap, &version);
+	if (ret)
+		return ret;
+
+	dev_info(&client->dev, "ov490 Firmware ID 0x%04x\n", version);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops ov490_video_ops = {
+	.s_stream		= ov490_s_stream,
+};
+
+static const struct v4l2_subdev_core_ops ov490_core_ops = {
+	.log_status		= v4l2_ctrl_subdev_log_status,
+	.subscribe_event	= v4l2_ctrl_subdev_subscribe_event,
+	.unsubscribe_event	= v4l2_event_subdev_unsubscribe,
+
+};
+
+static const struct v4l2_subdev_pad_ops ov490_pad_ops = {
+	.enum_mbus_code		= ov490_enum_code,
+	.enum_frame_size	= ov490_enum_size,
+	.get_fmt		= ov490_get_fmt,
+	.set_fmt		= ov490_set_fmt,
+};
+
+static const struct v4l2_subdev_ops ov490_subdev_ops = {
+	.video	= &ov490_video_ops,
+	.core	= &ov490_core_ops,
+	.pad	= &ov490_pad_ops,
+};
+
+static const struct v4l2_ctrl_ops ov490_ctrl_ops = {
+	.s_ctrl = ov490_s_ctrl,
+};
+
+static const struct regmap_config ov490_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 8,
+};
+
+/*
+ * i2c_driver function
+ */
+
+static int ov490_of_probe(struct i2c_client *client,
+			  struct device_node *node)
+{
+	struct ov490_priv *priv = to_ov490(client);
+	struct v4l2_fwnode_endpoint endpoint = {};
+	struct device_node *ep;
+	int num_lanes = 0;
+
+	ep = of_graph_get_next_endpoint(node, NULL);
+	if (ep) {
+		v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &endpoint);
+		if (endpoint.bus_type == V4L2_MBUS_CSI2) {
+			num_lanes = endpoint.bus.mipi_csi2.num_data_lanes;
+			if (num_lanes == 2 || num_lanes == 4)
+				priv->num_lanes = num_lanes;
+		} else {
+			dev_err(&client->dev, "Endpoint bus is not CSI bus!");
+		}
+	}
+
+	dev_info(&client->dev, "Using %d data lanes\n", priv->num_lanes);
+
+	return 0;
+}
+
+static int ov490_probe(struct i2c_client *client,
+		       const struct i2c_device_id *did)
+{
+	struct device_node *node = client->dev.of_node;
+	struct v4l2_ctrl_handler *hdl;
+	struct ov490_priv *priv;
+	struct v4l2_subdev *sd;
+	int ret = 0;
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, priv);
+	priv->num_lanes = 4;
+	priv->cfmt = &ov490_cfmts[0];
+	priv->width = OV490_MAX_WIDTH;
+	priv->height = OV490_MAX_HEIGHT;
+
+	priv->regmap = devm_regmap_init_i2c(client, &ov490_regmap_config);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	ret = ov490_video_probe(client);
+	if (ret)
+		goto err;
+
+	ret = ov490_of_probe(client, node);
+	if (ret)
+		goto err;
+
+	sd = &priv->subdev;
+	v4l2_i2c_subdev_init(sd, client, &ov490_subdev_ops);
+
+	hdl = &priv->handler;
+	sd->ctrl_handler = hdl;
+	v4l2_ctrl_handler_init(hdl, 1);
+	priv->pixel_rate =
+		v4l2_ctrl_new_std(hdl, &ov490_ctrl_ops,
+				  V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1,
+				  OV490_PIXEL_RATE_PER_LANE * priv->num_lanes);
+
+	if (hdl->error) {
+		dev_err(&client->dev, "Failed to add controls");
+		ret = hdl->error;
+		goto err;
+	}
+
+	ret = ov490_init_gpios(client);
+	if (ret) {
+		dev_err(&client->dev, "Failed to request gpios");
+		goto err;
+	}
+
+	sd->dev = &client->dev;
+	ret = v4l2_async_register_subdev(sd);
+
+err:
+	return ret;
+}
+
+static int ov490_remove(struct i2c_client *client)
+{
+	struct ov490_priv *priv = i2c_get_clientdata(client);
+
+	v4l2_device_unregister_subdev(&priv->subdev);
+	v4l2_ctrl_handler_free(&priv->handler);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov490_id[] = {
+	{ "ov490", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov490_id);
+
+static const struct of_device_id ov490_dt_id[] = {
+	{
+		.compatible = "ovti,ov490",
+	},
+	{
+	}
+};
+
+static struct i2c_driver ov490_i2c_driver = {
+	.driver = {
+		.name	= "ov490",
+		.of_match_table = ov490_dt_id,
+	},
+	.probe = ov490_probe,
+	.remove = ov490_remove,
+	.id_table = ov490_id,
+};
+
+module_i2c_driver(ov490_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV490");
+MODULE_AUTHOR("Nikhil Devshatwar <nikhil.nd@ti.com>");
+MODULE_LICENSE("GPL v2");

+ 14 - 1
drivers/media/platform/Kconfig

@@ -141,7 +141,7 @@ source "drivers/media/platform/atmel/Kconfig"
 config VIDEO_TI_CAL
 	tristate "TI CAL (Camera Adaptation Layer) driver"
 	depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
-	depends on SOC_DRA7XX || COMPILE_TEST
+	depends on SOC_DRA7XX || ARCH_K3 || COMPILE_TEST
 	select VIDEOBUF2_DMA_CONTIG
 	select V4L2_FWNODE
 	default n
@@ -151,6 +151,19 @@ config VIDEO_TI_CAL
 	  In TI Technical Reference Manual this module is referred as
 	  Camera Interface Subsystem (CAMSS).
 
+config VIDEO_TI_VIP
+	tristate "TI Video Input Port"
+	default n
+	depends on VIDEO_DEV && VIDEO_V4L2 && SOC_DRA7XX
+	depends on HAS_DMA
+	select VIDEOBUF2_DMA_CONTIG
+	select VIDEO_TI_VPDMA
+	select VIDEO_TI_SC
+	select VIDEO_TI_CSC
+	help
+	Driver support for VIP module on certain TI SoC's
+	VIP = Video Input Port.
+
 endif # V4L_PLATFORM_DRIVERS
 
 menuconfig V4L_MEM2MEM_DRIVERS

+ 1 - 3
drivers/media/platform/Makefile

@@ -18,9 +18,7 @@ obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
 obj-$(CONFIG_VIDEO_VIM2M)		+= vim2m.o
 obj-$(CONFIG_VIDEO_VICODEC)		+= vicodec/
 
-obj-$(CONFIG_VIDEO_TI_VPE)		+= ti-vpe/
-
-obj-$(CONFIG_VIDEO_TI_CAL)		+= ti-vpe/
+obj-y	+= ti-vpe/
 
 obj-$(CONFIG_VIDEO_MX2_EMMAPRP)		+= mx2_emmaprp.o
 obj-$(CONFIG_VIDEO_CODA)		+= coda/

+ 390 - 474
drivers/media/platform/am437x/am437x-vpfe.c

@@ -69,137 +69,64 @@ static const struct vpfe_standard vpfe_standards[] = {
 	{V4L2_STD_625_50, 720, 576, {54, 59}, 1},
 };
 
-struct bus_format {
-	unsigned int width;
-	unsigned int bpp;
-};
-
-/*
- * struct vpfe_fmt - VPFE media bus format information
- * @name: V4L2 format description
- * @code: V4L2 media bus format code
- * @shifted: V4L2 media bus format code for the same pixel layout but
- *	shifted to be 8 bits per pixel. =0 if format is not shiftable.
- * @pixelformat: V4L2 pixel format FCC identifier
- * @width: Bits per pixel (when transferred over a bus)
- * @bpp: Bytes per pixel (when stored in memory)
- * @supported: Indicates format supported by subdev
- */
-struct vpfe_fmt {
-	const char *name;
-	u32 fourcc;
-	u32 code;
-	struct bus_format l;
-	struct bus_format s;
-	bool supported;
-	u32 index;
-};
-
 static struct vpfe_fmt formats[] = {
 	{
-		.name		= "YUV 4:2:2 packed, YCbYCr",
 		.fourcc		= V4L2_PIX_FMT_YUYV,
 		.code		= MEDIA_BUS_FMT_YUYV8_2X8,
-		.l.width	= 10,
-		.l.bpp		= 4,
-		.s.width	= 8,
-		.s.bpp		= 2,
-		.supported	= false,
+		.bitsperpixel	= 16,
 	}, {
-		.name		= "YUV 4:2:2 packed, CbYCrY",
 		.fourcc		= V4L2_PIX_FMT_UYVY,
 		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
-		.l.width	= 10,
-		.l.bpp		= 4,
-		.s.width	= 8,
-		.s.bpp		= 2,
-		.supported	= false,
+		.bitsperpixel	= 16,
 	}, {
-		.name		= "YUV 4:2:2 packed, YCrYCb",
 		.fourcc		= V4L2_PIX_FMT_YVYU,
 		.code		= MEDIA_BUS_FMT_YVYU8_2X8,
-		.l.width	= 10,
-		.l.bpp		= 4,
-		.s.width	= 8,
-		.s.bpp		= 2,
-		.supported	= false,
+		.bitsperpixel	= 16,
 	}, {
-		.name		= "YUV 4:2:2 packed, CrYCbY",
 		.fourcc		= V4L2_PIX_FMT_VYUY,
 		.code		= MEDIA_BUS_FMT_VYUY8_2X8,
-		.l.width	= 10,
-		.l.bpp		= 4,
-		.s.width	= 8,
-		.s.bpp		= 2,
-		.supported	= false,
+		.bitsperpixel	= 16,
 	}, {
-		.name		= "RAW8 BGGR",
 		.fourcc		= V4L2_PIX_FMT_SBGGR8,
 		.code		= MEDIA_BUS_FMT_SBGGR8_1X8,
-		.l.width	= 10,
-		.l.bpp		= 2,
-		.s.width	= 8,
-		.s.bpp		= 1,
-		.supported	= false,
+		.bitsperpixel	= 8,
 	}, {
-		.name		= "RAW8 GBRG",
 		.fourcc		= V4L2_PIX_FMT_SGBRG8,
 		.code		= MEDIA_BUS_FMT_SGBRG8_1X8,
-		.l.width	= 10,
-		.l.bpp		= 2,
-		.s.width	= 8,
-		.s.bpp		= 1,
-		.supported	= false,
+		.bitsperpixel	= 8,
 	}, {
-		.name		= "RAW8 GRBG",
 		.fourcc		= V4L2_PIX_FMT_SGRBG8,
 		.code		= MEDIA_BUS_FMT_SGRBG8_1X8,
-		.l.width	= 10,
-		.l.bpp		= 2,
-		.s.width	= 8,
-		.s.bpp		= 1,
-		.supported	= false,
+		.bitsperpixel	= 8,
 	}, {
-		.name		= "RAW8 RGGB",
 		.fourcc		= V4L2_PIX_FMT_SRGGB8,
 		.code		= MEDIA_BUS_FMT_SRGGB8_1X8,
-		.l.width	= 10,
-		.l.bpp		= 2,
-		.s.width	= 8,
-		.s.bpp		= 1,
-		.supported	= false,
+		.bitsperpixel	= 8,
 	}, {
-		.name		= "RGB565 (LE)",
 		.fourcc		= V4L2_PIX_FMT_RGB565,
 		.code		= MEDIA_BUS_FMT_RGB565_2X8_LE,
-		.l.width	= 10,
-		.l.bpp		= 4,
-		.s.width	= 8,
-		.s.bpp		= 2,
-		.supported	= false,
+		.bitsperpixel	= 16,
 	}, {
-		.name		= "RGB565 (BE)",
 		.fourcc		= V4L2_PIX_FMT_RGB565X,
 		.code		= MEDIA_BUS_FMT_RGB565_2X8_BE,
-		.l.width	= 10,
-		.l.bpp		= 4,
-		.s.width	= 8,
-		.s.bpp		= 2,
-		.supported	= false,
+		.bitsperpixel	= 16,
 	},
 };
 
-static int
-__vpfe_get_format(struct vpfe_device *vpfe,
-		  struct v4l2_format *format, unsigned int *bpp);
+static int __subdev_get_format(struct vpfe_device *vpfe,
+			       struct v4l2_mbus_framefmt *fmt);
+static int vpfe_calc_format_size(struct vpfe_device *vpfe,
+				 const struct vpfe_fmt *fmt,
+				 struct v4l2_format *f);
 
-static struct vpfe_fmt *find_format_by_code(unsigned int code)
+static const struct vpfe_fmt *find_format_by_code(struct vpfe_device *vpfe,
+						  unsigned int code)
 {
 	struct vpfe_fmt *fmt;
 	unsigned int k;
 
-	for (k = 0; k < ARRAY_SIZE(formats); k++) {
-		fmt = &formats[k];
+	for (k = 0; k < vpfe->num_active_fmt; k++) {
+		fmt = vpfe->active_fmt[k];
 		if (fmt->code == code)
 			return fmt;
 	}
@@ -207,13 +134,14 @@ static struct vpfe_fmt *find_format_by_code(unsigned int code)
 	return NULL;
 }
 
-static struct vpfe_fmt *find_format_by_pix(unsigned int pixelformat)
+static const struct vpfe_fmt *find_format_by_pix(struct vpfe_device *vpfe,
+						 unsigned int pixelformat)
 {
 	struct vpfe_fmt *fmt;
 	unsigned int k;
 
-	for (k = 0; k < ARRAY_SIZE(formats); k++) {
-		fmt = &formats[k];
+	for (k = 0; k < vpfe->num_active_fmt; k++) {
+		fmt = vpfe->active_fmt[k];
 		if (fmt->fourcc == pixelformat)
 			return fmt;
 	}
@@ -221,76 +149,18 @@ static struct vpfe_fmt *find_format_by_pix(unsigned int pixelformat)
 	return NULL;
 }
 
-static void
-mbus_to_pix(struct vpfe_device *vpfe,
-	    const struct v4l2_mbus_framefmt *mbus,
-	    struct v4l2_pix_format *pix, unsigned int *bpp)
+static unsigned int __get_bytesperpixel(struct vpfe_device *vpfe,
+					const struct vpfe_fmt *fmt)
 {
 	struct vpfe_subdev_info *sdinfo = vpfe->current_subdev;
 	unsigned int bus_width = sdinfo->vpfe_param.bus_width;
-	struct vpfe_fmt *fmt;
-
-	fmt = find_format_by_code(mbus->code);
-	if (WARN_ON(fmt == NULL)) {
-		pr_err("Invalid mbus code set\n");
-		*bpp = 1;
-		return;
-	}
-
-	memset(pix, 0, sizeof(*pix));
-	v4l2_fill_pix_format(pix, mbus);
-	pix->pixelformat = fmt->fourcc;
-	*bpp = (bus_width == 10) ?  fmt->l.bpp : fmt->s.bpp;
-
-	/* pitch should be 32 bytes aligned */
-	pix->bytesperline = ALIGN(pix->width * *bpp, 32);
-	pix->sizeimage = pix->bytesperline * pix->height;
-}
-
-static void pix_to_mbus(struct vpfe_device *vpfe,
-			struct v4l2_pix_format *pix_fmt,
-			struct v4l2_mbus_framefmt *mbus_fmt)
-{
-	struct vpfe_fmt *fmt;
-
-	fmt = find_format_by_pix(pix_fmt->pixelformat);
-	if (!fmt) {
-		/* default to first entry */
-		vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n",
-			pix_fmt->pixelformat);
-		fmt = &formats[0];
-	}
-
-	memset(mbus_fmt, 0, sizeof(*mbus_fmt));
-	v4l2_fill_mbus_format(mbus_fmt, pix_fmt, fmt->code);
-}
-
-/*  Print Four-character-code (FOURCC) */
-static char *print_fourcc(u32 fmt)
-{
-	static char code[5];
-
-	code[0] = (unsigned char)(fmt & 0xff);
-	code[1] = (unsigned char)((fmt >> 8) & 0xff);
-	code[2] = (unsigned char)((fmt >> 16) & 0xff);
-	code[3] = (unsigned char)((fmt >> 24) & 0xff);
-	code[4] = '\0';
+	u32 bpp, bus_width_bytes, clocksperpixel;
 
-	return code;
-}
+	bus_width_bytes = ALIGN(bus_width, 8) >> 3;
+	clocksperpixel = DIV_ROUND_UP(fmt->bitsperpixel, bus_width);
+	bpp = clocksperpixel * bus_width_bytes;
 
-static int
-cmp_v4l2_format(const struct v4l2_format *lhs, const struct v4l2_format *rhs)
-{
-	return lhs->type == rhs->type &&
-		lhs->fmt.pix.width == rhs->fmt.pix.width &&
-		lhs->fmt.pix.height == rhs->fmt.pix.height &&
-		lhs->fmt.pix.pixelformat == rhs->fmt.pix.pixelformat &&
-		lhs->fmt.pix.field == rhs->fmt.pix.field &&
-		lhs->fmt.pix.colorspace == rhs->fmt.pix.colorspace &&
-		lhs->fmt.pix.ycbcr_enc == rhs->fmt.pix.ycbcr_enc &&
-		lhs->fmt.pix.quantization == rhs->fmt.pix.quantization &&
-		lhs->fmt.pix.xfer_func == rhs->fmt.pix.xfer_func;
+	return bpp;
 }
 
 static inline u32 vpfe_reg_read(struct vpfe_ccdc *ccdc, u32 offset)
@@ -357,13 +227,9 @@ static void vpfe_ccdc_setwin(struct vpfe_ccdc *ccdc,
 	if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
 		vert_nr_lines = (image_win->height >> 1) - 1;
 		vert_start >>= 1;
-		/* Since first line doesn't have any data */
-		vert_start += 1;
 		/* configure VDINT0 */
 		val = (vert_start << VPFE_VDINT_VDINT0_SHIFT);
 	} else {
-		/* Since first line doesn't have any data */
-		vert_start += 1;
 		vert_nr_lines = image_win->height - 1;
 		/*
 		 * configure VDINT0 and VDINT1. VDINT1 will be at half
@@ -457,40 +323,25 @@ static void vpfe_ccdc_restore_defaults(struct vpfe_ccdc *ccdc)
 
 static int vpfe_ccdc_close(struct vpfe_ccdc *ccdc, struct device *dev)
 {
-	int dma_cntl, i, pcr;
+	struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
+	u32 dma_cntl, pcr;
 
-	/* If the CCDC module is still busy wait for it to be done */
-	for (i = 0; i < 10; i++) {
-		usleep_range(5000, 6000);
-		pcr = vpfe_reg_read(ccdc, VPFE_PCR);
-		if (!pcr)
-			break;
+	pcr = vpfe_reg_read(ccdc, VPFE_PCR);
+	if (pcr)
+		vpfe_dbg(1, vpfe, "VPFE_PCR is still set (%x)", pcr);
 
-		/* make sure it it is disabled */
-		vpfe_pcr_enable(ccdc, 0);
-	}
+	dma_cntl = vpfe_reg_read(ccdc, VPFE_DMA_CNTL);
+	if ((dma_cntl & VPFE_DMA_CNTL_OVERFLOW))
+		vpfe_dbg(1, vpfe, "VPFE_DMA_CNTL_OVERFLOW is still set (%x)",
+			 dma_cntl);
 
 	/* Disable CCDC by resetting all register to default POR values */
 	vpfe_ccdc_restore_defaults(ccdc);
 
-	/* if DMA_CNTL overflow bit is set. Clear it
-	 *  It appears to take a while for this to become quiescent ~20ms
-	 */
-	for (i = 0; i < 10; i++) {
-		dma_cntl = vpfe_reg_read(ccdc, VPFE_DMA_CNTL);
-		if (!(dma_cntl & VPFE_DMA_CNTL_OVERFLOW))
-			break;
-
-		/* Clear the overflow bit */
-		vpfe_reg_write(ccdc, dma_cntl, VPFE_DMA_CNTL);
-		usleep_range(5000, 6000);
-	}
-
 	/* Disabled the module at the CONFIG level */
 	vpfe_config_enable(ccdc, 0);
 
 	pm_runtime_put_sync(dev);
-
 	return 0;
 }
 
@@ -763,8 +614,8 @@ static int vpfe_ccdc_set_pixel_format(struct vpfe_ccdc *ccdc, u32 pixfmt)
 {
 	struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);
 
-	vpfe_dbg(1, vpfe, "vpfe_ccdc_set_pixel_format: if_type: %d, pixfmt:%s\n",
-		 ccdc->ccdc_cfg.if_type, print_fourcc(pixfmt));
+	vpfe_dbg(1, vpfe, "vpfe_ccdc_set_pixel_format: if_type: %d, pixfmt:%4.4s\n",
+		 ccdc->ccdc_cfg.if_type, (char *)&pixfmt);
 
 	if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) {
 		ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
@@ -1048,23 +899,25 @@ static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe,
 static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe)
 {
 	enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED;
+	u32 bpp;
 	int ret = 0;
 
 	vpfe_dbg(2, vpfe, "vpfe_config_ccdc_image_format\n");
 
-	vpfe_dbg(1, vpfe, "pixelformat: %s\n",
-		print_fourcc(vpfe->fmt.fmt.pix.pixelformat));
+	vpfe_dbg(1, vpfe, "pixelformat: %4.4s\n",
+		 (char *)&vpfe->v_fmt.fmt.pix.pixelformat);
 
 	if (vpfe_ccdc_set_pixel_format(&vpfe->ccdc,
-			vpfe->fmt.fmt.pix.pixelformat) < 0) {
+			vpfe->v_fmt.fmt.pix.pixelformat) < 0) {
 		vpfe_err(vpfe, "couldn't set pix format in ccdc\n");
 		return -EINVAL;
 	}
 
 	/* configure the image window */
-	vpfe_ccdc_set_image_window(&vpfe->ccdc, &vpfe->crop, vpfe->bpp);
+	bpp = __get_bytesperpixel(vpfe, vpfe->fmt);
+	vpfe_ccdc_set_image_window(&vpfe->ccdc, &vpfe->crop, bpp);
 
-	switch (vpfe->fmt.fmt.pix.field) {
+	switch (vpfe->v_fmt.fmt.pix.field) {
 	case V4L2_FIELD_INTERLACED:
 		/* do nothing, since it is default */
 		ret = vpfe_ccdc_set_buftype(
@@ -1106,7 +959,8 @@ static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe)
 static int vpfe_config_image_format(struct vpfe_device *vpfe,
 				    v4l2_std_id std_id)
 {
-	struct v4l2_pix_format *pix = &vpfe->fmt.fmt.pix;
+	const struct vpfe_fmt *fmt;
+	struct v4l2_mbus_framefmt mbus_fmt;
 	int i, ret;
 
 	for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) {
@@ -1128,26 +982,29 @@ static int vpfe_config_image_format(struct vpfe_device *vpfe,
 		return -EINVAL;
 	}
 
-	vpfe->crop.top = vpfe->crop.left = 0;
-	vpfe->crop.width = vpfe->std_info.active_pixels;
-	vpfe->crop.height = vpfe->std_info.active_lines;
-	pix->width = vpfe->crop.width;
-	pix->height = vpfe->crop.height;
-	pix->pixelformat = V4L2_PIX_FMT_YUYV;
-
-	/* first field and frame format based on standard frame format */
-	if (vpfe->std_info.frame_format)
-		pix->field = V4L2_FIELD_INTERLACED;
-	else
-		pix->field = V4L2_FIELD_NONE;
-
-	ret = __vpfe_get_format(vpfe, &vpfe->fmt, &vpfe->bpp);
+	ret = __subdev_get_format(vpfe, &mbus_fmt);
 	if (ret)
 		return ret;
 
+	fmt = find_format_by_code(vpfe, mbus_fmt.code);
+	if (!fmt) {
+		vpfe_dbg(3, vpfe, "mbus code format (0x%08x) not found.\n",
+			 mbus_fmt.code);
+		return -EINVAL;
+	}
+
+	/* Save current subdev format */
+	v4l2_fill_pix_format(&vpfe->v_fmt.fmt.pix, &mbus_fmt);
+	vpfe->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	vpfe->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
+	vpfe_calc_format_size(vpfe, fmt, &vpfe->v_fmt);
+	vpfe->fmt = fmt;
+
 	/* Update the crop window based on found values */
-	vpfe->crop.width = pix->width;
-	vpfe->crop.height = pix->height;
+	vpfe->crop.top = 0;
+	vpfe->crop.left = 0;
+	vpfe->crop.width = mbus_fmt.width;
+	vpfe->crop.height = mbus_fmt.height;
 
 	return vpfe_config_ccdc_image_format(vpfe);
 }
@@ -1249,22 +1106,29 @@ unlock:
  * This function will get next buffer from the dma queue and
  * set the buffer address in the vpfe register for capture.
  * the buffer is marked active
- *
- * Assumes caller is holding vpfe->dma_queue_lock already
  */
-static inline void vpfe_schedule_next_buffer(struct vpfe_device *vpfe)
+static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe)
 {
+	dma_addr_t addr;
+
+	spin_lock(&vpfe->dma_queue_lock);
+	if (list_empty(&vpfe->dma_queue)) {
+		spin_unlock(&vpfe->dma_queue_lock);
+		return;
+	}
+
 	vpfe->next_frm = list_entry(vpfe->dma_queue.next,
 				    struct vpfe_cap_buffer, list);
 	list_del(&vpfe->next_frm->list);
+	spin_unlock(&vpfe->dma_queue_lock);
 
-	vpfe_set_sdr_addr(&vpfe->ccdc,
-	       vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb.vb2_buf, 0));
+	addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb.vb2_buf, 0);
+	vpfe_set_sdr_addr(&vpfe->ccdc, addr);
 }
 
 static inline void vpfe_schedule_bottom_field(struct vpfe_device *vpfe)
 {
-	unsigned long addr;
+	dma_addr_t addr;
 
 	addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb.vb2_buf, 0) +
 					vpfe->field_off;
@@ -1283,12 +1147,64 @@ static inline void vpfe_schedule_bottom_field(struct vpfe_device *vpfe)
 static inline void vpfe_process_buffer_complete(struct vpfe_device *vpfe)
 {
 	vpfe->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
-	vpfe->cur_frm->vb.field = vpfe->fmt.fmt.pix.field;
+	vpfe->cur_frm->vb.field = vpfe->v_fmt.fmt.pix.field;
 	vpfe->cur_frm->vb.sequence = vpfe->sequence++;
 	vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
 	vpfe->cur_frm = vpfe->next_frm;
 }
 
+static void vpfe_handle_interlaced_irq(struct vpfe_device *vpfe,
+				       enum v4l2_field field)
+{
+	int fid;
+
+	/* interlaced or TB capture check which field
+	 * we are in hardware
+	 */
+	fid = vpfe_ccdc_getfid(&vpfe->ccdc);
+
+	/* switch the software maintained field id */
+	vpfe->field ^= 1;
+	if (fid == vpfe->field) {
+		/* we are in-sync here,continue */
+		if (fid == 0) {
+			/*
+			 * One frame is just being captured. If the
+			 * next frame is available, release the
+			 * current frame and move on
+			 */
+			if (vpfe->cur_frm != vpfe->next_frm)
+				vpfe_process_buffer_complete(vpfe);
+
+			if (vpfe->stopping)
+				return;
+
+			/*
+			 * based on whether the two fields are stored
+			 * interleave or separately in memory,
+			 * reconfigure the CCDC memory address
+			 */
+			if (field == V4L2_FIELD_SEQ_TB)
+				vpfe_schedule_bottom_field(vpfe);
+		} else {
+			/*
+			 * if one field is just being captured configure
+			 * the next frame get the next frame from the empty
+			 * queue if no frame is available hold on to the
+			 * current buffer
+			 */
+			if (vpfe->cur_frm == vpfe->next_frm)
+				vpfe_schedule_next_buffer(vpfe);
+		}
+	} else if (fid == 0) {
+		/*
+		 * out of sync. Recover from any hardware out-of-sync.
+		 * May loose one frame
+		 */
+		vpfe->field = fid;
+	}
+}
+
 /*
  * vpfe_isr : ISR handler for vpfe capture (VINT0)
  * @irq: irq number
@@ -1300,76 +1216,28 @@ static inline void vpfe_process_buffer_complete(struct vpfe_device *vpfe)
 static irqreturn_t vpfe_isr(int irq, void *dev)
 {
 	struct vpfe_device *vpfe = (struct vpfe_device *)dev;
-	enum v4l2_field field;
-	int intr_status;
-	int fid;
+	enum v4l2_field field = vpfe->v_fmt.fmt.pix.field;
+	int intr_status, stopping = vpfe->stopping;
 
 	intr_status = vpfe_reg_read(&vpfe->ccdc, VPFE_IRQ_STS);
 
 	if (intr_status & VPFE_VDINT0) {
-		field = vpfe->fmt.fmt.pix.field;
-
 		if (field == V4L2_FIELD_NONE) {
-			/* handle progressive frame capture */
 			if (vpfe->cur_frm != vpfe->next_frm)
 				vpfe_process_buffer_complete(vpfe);
-			goto next_intr;
+		} else {
+			vpfe_handle_interlaced_irq(vpfe, field);
 		}
-
-		/* interlaced or TB capture check which field
-		   we are in hardware */
-		fid = vpfe_ccdc_getfid(&vpfe->ccdc);
-
-		/* switch the software maintained field id */
-		vpfe->field ^= 1;
-		if (fid == vpfe->field) {
-			/* we are in-sync here,continue */
-			if (fid == 0) {
-				/*
-				 * One frame is just being captured. If the
-				 * next frame is available, release the
-				 * current frame and move on
-				 */
-				if (vpfe->cur_frm != vpfe->next_frm)
-					vpfe_process_buffer_complete(vpfe);
-				/*
-				 * based on whether the two fields are stored
-				 * interleave or separately in memory,
-				 * reconfigure the CCDC memory address
-				 */
-				if (field == V4L2_FIELD_SEQ_TB)
-					vpfe_schedule_bottom_field(vpfe);
-
-				goto next_intr;
-			}
-			/*
-			 * if one field is just being captured configure
-			 * the next frame get the next frame from the empty
-			 * queue if no frame is available hold on to the
-			 * current buffer
-			 */
-			spin_lock(&vpfe->dma_queue_lock);
-			if (!list_empty(&vpfe->dma_queue) &&
-			    vpfe->cur_frm == vpfe->next_frm)
-				vpfe_schedule_next_buffer(vpfe);
-			spin_unlock(&vpfe->dma_queue_lock);
-		} else if (fid == 0) {
-			/*
-			 * out of sync. Recover from any hardware out-of-sync.
-			 * May loose one frame
-			 */
-			vpfe->field = fid;
+		if (stopping) {
+			vpfe->stopping = false;
+			complete(&vpfe->capture_stop);
 		}
 	}
 
-next_intr:
-	if (intr_status & VPFE_VDINT1) {
-		spin_lock(&vpfe->dma_queue_lock);
-		if (vpfe->fmt.fmt.pix.field == V4L2_FIELD_NONE &&
-		    !list_empty(&vpfe->dma_queue) &&
+	if (intr_status & VPFE_VDINT1 && !stopping) {
+		if (field == V4L2_FIELD_NONE &&
 		    vpfe->cur_frm == vpfe->next_frm)
 			vpfe_schedule_next_buffer(vpfe);
-		spin_unlock(&vpfe->dma_queue_lock);
 	}
 
 	vpfe_clear_intr(&vpfe->ccdc, intr_status);
@@ -1420,83 +1288,75 @@ static int vpfe_querycap(struct file *file, void  *priv,
 }
 
 /* get the format set at output pad of the adjacent subdev */
-static int __vpfe_get_format(struct vpfe_device *vpfe,
-			     struct v4l2_format *format, unsigned int *bpp)
+static int __subdev_get_format(struct vpfe_device *vpfe,
+			       struct v4l2_mbus_framefmt *fmt)
 {
-	struct v4l2_mbus_framefmt mbus_fmt;
-	struct vpfe_subdev_info *sdinfo;
-	struct v4l2_subdev_format fmt;
+	struct v4l2_subdev *sd = vpfe->current_subdev->sd;
+	struct v4l2_subdev_format sd_fmt;
+	struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
 	int ret;
 
-	sdinfo = vpfe->current_subdev;
-	if (!sdinfo->sd)
-		return -EINVAL;
+	sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	sd_fmt.pad = 0;
 
-	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
-	fmt.pad = 0;
-
-	ret = v4l2_subdev_call(sdinfo->sd, pad, get_fmt, NULL, &fmt);
-	if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
+	ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt);
+	if (ret)
 		return ret;
 
-	if (!ret) {
-		v4l2_fill_pix_format(&format->fmt.pix, &fmt.format);
-		mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp);
-	} else {
-		ret = v4l2_device_call_until_err(&vpfe->v4l2_dev,
-						 sdinfo->grp_id,
-						 pad, get_fmt,
-						 NULL, &fmt);
-		if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
-			return ret;
-		v4l2_fill_pix_format(&format->fmt.pix, &mbus_fmt);
-		mbus_to_pix(vpfe, &mbus_fmt, &format->fmt.pix, bpp);
-	}
-
-	format->type = vpfe->fmt.type;
+	*fmt = *mbus_fmt;
 
-	vpfe_dbg(1, vpfe,
-		 "%s size %dx%d (%s) bytesperline = %d, size = %d, bpp = %d\n",
-		 __func__, format->fmt.pix.width, format->fmt.pix.height,
-		 print_fourcc(format->fmt.pix.pixelformat),
-		 format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp);
+	vpfe_dbg(1, vpfe, "%s %dx%d code:%04X\n", __func__,
+		 fmt->width, fmt->height, fmt->code);
 
 	return 0;
 }
 
 /* set the format at output pad of the adjacent subdev */
-static int __vpfe_set_format(struct vpfe_device *vpfe,
-			     struct v4l2_format *format, unsigned int *bpp)
+static int __subdev_set_format(struct vpfe_device *vpfe,
+			       struct v4l2_mbus_framefmt *fmt)
 {
-	struct vpfe_subdev_info *sdinfo;
-	struct v4l2_subdev_format fmt;
+	struct v4l2_subdev *sd = vpfe->current_subdev->sd;
+	struct v4l2_subdev_format sd_fmt;
+	struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
 	int ret;
 
-	vpfe_dbg(2, vpfe, "__vpfe_set_format\n");
+	sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	sd_fmt.pad = 0;
+	*mbus_fmt = *fmt;
 
-	sdinfo = vpfe->current_subdev;
-	if (!sdinfo->sd)
-		return -EINVAL;
+	ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sd_fmt);
+	if (ret)
+		return ret;
 
-	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
-	fmt.pad = 0;
+	vpfe_dbg(1, vpfe, "%s %dx%d code:%04X\n", __func__,
+		 fmt->width, fmt->height, fmt->code);
 
-	pix_to_mbus(vpfe, &format->fmt.pix, &fmt.format);
+	return 0;
+}
 
-	ret = v4l2_subdev_call(sdinfo->sd, pad, set_fmt, NULL, &fmt);
-	if (ret)
-		return ret;
+/* Needs more work */
+static int vpfe_calc_format_size(struct vpfe_device *vpfe,
+				 const struct vpfe_fmt *fmt,
+				 struct v4l2_format *f)
+{
+	u32 bpp;
 
-	v4l2_fill_pix_format(&format->fmt.pix, &fmt.format);
-	mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp);
+	if (!fmt) {
+		vpfe_dbg(3, vpfe, "No vpfe_fmt provided!\n");
+		return -EINVAL;
+	}
 
-	format->type = vpfe->fmt.type;
+	bpp = __get_bytesperpixel(vpfe, fmt);
 
-	vpfe_dbg(1, vpfe,
-		 "%s size %dx%d (%s) bytesperline = %d, size = %d, bpp = %d\n",
-		 __func__,  format->fmt.pix.width, format->fmt.pix.height,
-		 print_fourcc(format->fmt.pix.pixelformat),
-		 format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp);
+	/* pitch should be 32 bytes aligned */
+	f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.width * bpp, 32);
+	f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
+			       f->fmt.pix.height;
+
+	vpfe_dbg(3, vpfe, "%s: fourcc: %4.4s size: %dx%d bpl:%d img_size:%d\n",
+		 __func__, (char *)&f->fmt.pix.pixelformat,
+		 f->fmt.pix.width, f->fmt.pix.height,
+		 f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
 
 	return 0;
 }
@@ -1508,7 +1368,7 @@ static int vpfe_g_fmt(struct file *file, void *priv,
 
 	vpfe_dbg(2, vpfe, "vpfe_g_fmt\n");
 
-	*fmt = vpfe->fmt;
+	*fmt = vpfe->v_fmt;
 
 	return 0;
 }
@@ -1518,8 +1378,7 @@ static int vpfe_enum_fmt(struct file *file, void  *priv,
 {
 	struct vpfe_device *vpfe = video_drvdata(file);
 	struct vpfe_subdev_info *sdinfo;
-	struct vpfe_fmt *fmt = NULL;
-	unsigned int k;
+	struct vpfe_fmt *fmt;
 
 	vpfe_dbg(2, vpfe, "vpfe_enum_format index:%d\n",
 		f->index);
@@ -1528,78 +1387,122 @@ static int vpfe_enum_fmt(struct file *file, void  *priv,
 	if (!sdinfo->sd)
 		return -EINVAL;
 
-	if (f->index > ARRAY_SIZE(formats))
+	if (f->index >= vpfe->num_active_fmt)
 		return -EINVAL;
 
-	for (k = 0; k < ARRAY_SIZE(formats); k++) {
-		if (formats[k].index == f->index) {
-			fmt = &formats[k];
-			break;
-		}
-	}
-	if (!fmt)
-		return -EINVAL;
+	fmt = vpfe->active_fmt[f->index];
 
-	strncpy(f->description, fmt->name, sizeof(f->description) - 1);
 	f->pixelformat = fmt->fourcc;
-	f->type = vpfe->fmt.type;
-
-	vpfe_dbg(1, vpfe, "vpfe_enum_format: mbus index: %d code: %x pixelformat: %s [%s]\n",
-		f->index, fmt->code, print_fourcc(fmt->fourcc), fmt->name);
+	f->type = vpfe->v_fmt.type;
+	vpfe_dbg(1, vpfe, "vpfe_enum_format: mbus index: %d code: %x pixelformat: %4.4s\n",
+		 f->index, fmt->code, (char *)&fmt->fourcc);
 
 	return 0;
 }
 
 static int vpfe_try_fmt(struct file *file, void *priv,
-			struct v4l2_format *fmt)
+			struct v4l2_format *f)
 {
 	struct vpfe_device *vpfe = video_drvdata(file);
-	unsigned int bpp;
+	struct v4l2_subdev *sd = vpfe->current_subdev->sd;
+	const struct vpfe_fmt *fmt;
+	struct v4l2_subdev_frame_size_enum fse;
+	int ret, found;
+
+	fmt = find_format_by_pix(vpfe, f->fmt.pix.pixelformat);
+	if (!fmt) {
+		/* default to first entry */
+		vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n",
+			 f->fmt.pix.pixelformat);
+		fmt = vpfe->active_fmt[0];
+		f->fmt.pix.pixelformat = fmt->fourcc;
+	}
+
+	f->fmt.pix.field = vpfe->v_fmt.fmt.pix.field;
+
+	/* check for/find a valid width/height */
+	ret = 0;
+	found = false;
+	fse.pad = 0;
+	fse.code = fmt->code;
+	fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	for (fse.index = 0; ; fse.index++) {
+		ret = v4l2_subdev_call(sd, pad, enum_frame_size,
+				       NULL, &fse);
+		if (ret)
+			break;
 
-	vpfe_dbg(2, vpfe, "vpfe_try_fmt\n");
+		if (f->fmt.pix.width == fse.max_width &&
+		    f->fmt.pix.height == fse.max_height) {
+			found = true;
+			break;
+		} else if (f->fmt.pix.width >= fse.min_width &&
+			   f->fmt.pix.width <= fse.max_width &&
+			   f->fmt.pix.height >= fse.min_height &&
+			   f->fmt.pix.height <= fse.max_height) {
+			found = true;
+			break;
+		}
+	}
 
-	return __vpfe_get_format(vpfe, fmt, &bpp);
+	if (!found) {
+		/* use existing values as default */
+		f->fmt.pix.width = vpfe->v_fmt.fmt.pix.width;
+		f->fmt.pix.height =  vpfe->v_fmt.fmt.pix.height;
+	}
+
+	/*
+	 * Use current colorspace for now, it will get
+	 * updated properly during s_fmt
+	 */
+	f->fmt.pix.colorspace = vpfe->v_fmt.fmt.pix.colorspace;
+	return vpfe_calc_format_size(vpfe, fmt, f);
 }
 
 static int vpfe_s_fmt(struct file *file, void *priv,
-		      struct v4l2_format *fmt)
+		      struct v4l2_format *f)
 {
 	struct vpfe_device *vpfe = video_drvdata(file);
-	struct v4l2_format format;
-	unsigned int bpp;
+	const struct vpfe_fmt *fmt;
+	struct v4l2_mbus_framefmt mbus_fmt;
 	int ret;
 
-	vpfe_dbg(2, vpfe, "vpfe_s_fmt\n");
-
 	/* If streaming is started, return error */
 	if (vb2_is_busy(&vpfe->buffer_queue)) {
 		vpfe_err(vpfe, "%s device busy\n", __func__);
 		return -EBUSY;
 	}
 
-	ret = __vpfe_get_format(vpfe, &format, &bpp);
-	if (ret)
+	ret = vpfe_try_fmt(file, priv, f);
+	if (ret < 0)
 		return ret;
 
+	fmt = find_format_by_pix(vpfe, f->fmt.pix.pixelformat);
 
-	if (!cmp_v4l2_format(fmt, &format)) {
-		/* Sensor format is different from the requested format
-		 * so we need to change it
-		 */
-		ret = __vpfe_set_format(vpfe, fmt, &bpp);
-		if (ret)
-			return ret;
-	} else /* Just make sure all of the fields are consistent */
-		*fmt = format;
+	v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
 
-	/* First detach any IRQ if currently attached */
-	vpfe_detach_irq(vpfe);
-	vpfe->fmt = *fmt;
-	vpfe->bpp = bpp;
+	ret = __subdev_set_format(vpfe, &mbus_fmt);
+	if (ret)
+		return ret;
+
+	/* Just double check nothing has gone wrong */
+	if (mbus_fmt.code != fmt->code) {
+		vpfe_dbg(3, vpfe,
+			 "%s subdev changed format on us, this should not happen\n",
+			 __func__);
+		return -EINVAL;
+	}
+
+	v4l2_fill_pix_format(&vpfe->v_fmt.fmt.pix, &mbus_fmt);
+	vpfe->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	vpfe->v_fmt.fmt.pix.pixelformat  = fmt->fourcc;
+	vpfe_calc_format_size(vpfe, fmt, &vpfe->v_fmt);
+	*f = vpfe->v_fmt;
+	vpfe->fmt = fmt;
 
 	/* Update the crop window based on found values */
-	vpfe->crop.width = fmt->fmt.pix.width;
-	vpfe->crop.height = fmt->fmt.pix.height;
+	vpfe->crop.width = f->fmt.pix.width;
+	vpfe->crop.height = f->fmt.pix.height;
 
 	/* set image capture parameters in the ccdc */
 	return vpfe_config_ccdc_image_format(vpfe);
@@ -1610,45 +1513,30 @@ static int vpfe_enum_size(struct file *file, void  *priv,
 {
 	struct vpfe_device *vpfe = video_drvdata(file);
 	struct v4l2_subdev_frame_size_enum fse;
-	struct vpfe_subdev_info *sdinfo;
-	struct v4l2_mbus_framefmt mbus;
-	struct v4l2_pix_format pix;
-	struct vpfe_fmt *fmt;
+	struct v4l2_subdev *sd = vpfe->current_subdev->sd;
+	const struct vpfe_fmt *fmt;
 	int ret;
 
 	vpfe_dbg(2, vpfe, "vpfe_enum_size\n");
 
 	/* check for valid format */
-	fmt = find_format_by_pix(fsize->pixel_format);
+	fmt = find_format_by_pix(vpfe, fsize->pixel_format);
 	if (!fmt) {
-		vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n",
-			fsize->pixel_format);
+		vpfe_dbg(3, vpfe, "Invalid pixel code: %x\n",
+			 fsize->pixel_format);
 		return -EINVAL;
 	}
 
 	memset(fsize->reserved, 0x0, sizeof(fsize->reserved));
 
-	sdinfo = vpfe->current_subdev;
-	if (!sdinfo->sd)
-		return -EINVAL;
-
-	memset(&pix, 0x0, sizeof(pix));
-	/* Construct pix from parameter and use default for the rest */
-	pix.pixelformat = fsize->pixel_format;
-	pix.width = 640;
-	pix.height = 480;
-	pix.colorspace = V4L2_COLORSPACE_SRGB;
-	pix.field = V4L2_FIELD_NONE;
-	pix_to_mbus(vpfe, &pix, &mbus);
-
 	memset(&fse, 0x0, sizeof(fse));
 	fse.index = fsize->index;
 	fse.pad = 0;
-	fse.code = mbus.code;
+	fse.code = fmt->code;
 	fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
-	ret = v4l2_subdev_call(sdinfo->sd, pad, enum_frame_size, NULL, &fse);
+	ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse);
 	if (ret)
-		return -EINVAL;
+		return ret;
 
 	vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
 		fse.index, fse.code, fse.min_width, fse.max_width,
@@ -1658,9 +1546,9 @@ static int vpfe_enum_size(struct file *file, void  *priv,
 	fsize->discrete.width = fse.max_width;
 	fsize->discrete.height = fse.max_height;
 
-	vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d pixformat: %s size: %dx%d\n",
-		fsize->index, print_fourcc(fsize->pixel_format),
-		fsize->discrete.width, fsize->discrete.height);
+	vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d pixformat: %4.4s size: %dx%d\n",
+		 fsize->index, (char *)&fsize->pixel_format,
+		 fsize->discrete.width, fsize->discrete.height);
 
 	return 0;
 }
@@ -1848,6 +1736,10 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id)
 	if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD))
 		return -ENODATA;
 
+	/* if trying to set the same std then nothing to do */
+	if (vpfe_standards[vpfe->std_index].std_id == std_id)
+		return 0;
+
 	/* If streaming is started, return error */
 	if (vb2_is_busy(&vpfe->buffer_queue)) {
 		vpfe_err(vpfe, "%s device busy\n", __func__);
@@ -1912,7 +1804,7 @@ static int vpfe_queue_setup(struct vb2_queue *vq,
 			    unsigned int sizes[], struct device *alloc_devs[])
 {
 	struct vpfe_device *vpfe = vb2_get_drv_priv(vq);
-	unsigned size = vpfe->fmt.fmt.pix.sizeimage;
+	unsigned int size = vpfe->v_fmt.fmt.pix.sizeimage;
 
 	if (vq->num_buffers + *nbuffers < 3)
 		*nbuffers = 3 - vq->num_buffers;
@@ -1948,12 +1840,12 @@ static int vpfe_buffer_prepare(struct vb2_buffer *vb)
 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
 	struct vpfe_device *vpfe = vb2_get_drv_priv(vb->vb2_queue);
 
-	vb2_set_plane_payload(vb, 0, vpfe->fmt.fmt.pix.sizeimage);
+	vb2_set_plane_payload(vb, 0, vpfe->v_fmt.fmt.pix.sizeimage);
 
 	if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
 		return -EINVAL;
 
-	vbuf->field = vpfe->fmt.fmt.pix.field;
+	vbuf->field = vpfe->v_fmt.fmt.pix.field;
 
 	return 0;
 }
@@ -1975,6 +1867,29 @@ static void vpfe_buffer_queue(struct vb2_buffer *vb)
 	spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags);
 }
 
+static void vpfe_return_all_buffers(struct vpfe_device *vpfe,
+				    enum vb2_buffer_state state)
+{
+	struct vpfe_cap_buffer *buf, *node;
+	unsigned long flags;
+
+	spin_lock_irqsave(&vpfe->dma_queue_lock, flags);
+	list_for_each_entry_safe(buf, node, &vpfe->dma_queue, list) {
+		vb2_buffer_done(&buf->vb.vb2_buf, state);
+		list_del(&buf->list);
+	}
+
+	if (vpfe->cur_frm)
+		vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf, state);
+
+	if (vpfe->next_frm && vpfe->next_frm != vpfe->cur_frm)
+		vb2_buffer_done(&vpfe->next_frm->vb.vb2_buf, state);
+
+	vpfe->cur_frm = NULL;
+	vpfe->next_frm = NULL;
+	spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags);
+}
+
 /*
  * vpfe_start_streaming : Starts the DMA engine for streaming
  * @vb: ptr to vb2_buffer
@@ -1983,7 +1898,6 @@ static void vpfe_buffer_queue(struct vb2_buffer *vb)
 static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count)
 {
 	struct vpfe_device *vpfe = vb2_get_drv_priv(vq);
-	struct vpfe_cap_buffer *buf, *tmp;
 	struct vpfe_subdev_info *sdinfo;
 	unsigned long flags;
 	unsigned long addr;
@@ -1998,6 +1912,9 @@ static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count)
 
 	vpfe_attach_irq(vpfe);
 
+	vpfe->stopping = false;
+	init_completion(&vpfe->capture_stop);
+
 	if (vpfe->ccdc.ccdc_cfg.if_type == VPFE_RAW_BAYER)
 		vpfe_ccdc_config_raw(&vpfe->ccdc);
 	else
@@ -2026,11 +1943,8 @@ static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count)
 	return 0;
 
 err:
-	list_for_each_entry_safe(buf, tmp, &vpfe->dma_queue, list) {
-		list_del(&buf->list);
-		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
-	}
-
+	vpfe_return_all_buffers(vpfe, VB2_BUF_STATE_QUEUED);
+	vpfe_pcr_enable(&vpfe->ccdc, 0);
 	return ret;
 }
 
@@ -2045,11 +1959,15 @@ static void vpfe_stop_streaming(struct vb2_queue *vq)
 {
 	struct vpfe_device *vpfe = vb2_get_drv_priv(vq);
 	struct vpfe_subdev_info *sdinfo;
-	unsigned long flags;
 	int ret;
 
 	vpfe_pcr_enable(&vpfe->ccdc, 0);
 
+	/* Wait for the last frame to be captured */
+	vpfe->stopping = true;
+	wait_for_completion_timeout(&vpfe->capture_stop,
+				    msecs_to_jiffies(250));
+
 	vpfe_detach_irq(vpfe);
 
 	sdinfo = vpfe->current_subdev;
@@ -2058,27 +1976,7 @@ static void vpfe_stop_streaming(struct vb2_queue *vq)
 		vpfe_dbg(1, vpfe, "stream off failed in subdev\n");
 
 	/* release all active buffers */
-	spin_lock_irqsave(&vpfe->dma_queue_lock, flags);
-	if (vpfe->cur_frm == vpfe->next_frm) {
-		vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf,
-				VB2_BUF_STATE_ERROR);
-	} else {
-		if (vpfe->cur_frm != NULL)
-			vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf,
-					VB2_BUF_STATE_ERROR);
-		if (vpfe->next_frm != NULL)
-			vb2_buffer_done(&vpfe->next_frm->vb.vb2_buf,
-					VB2_BUF_STATE_ERROR);
-	}
-
-	while (!list_empty(&vpfe->dma_queue)) {
-		vpfe->next_frm = list_entry(vpfe->dma_queue.next,
-						struct vpfe_cap_buffer, list);
-		list_del(&vpfe->next_frm->list);
-		vb2_buffer_done(&vpfe->next_frm->vb.vb2_buf,
-				VB2_BUF_STATE_ERROR);
-	}
-	spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags);
+	vpfe_return_all_buffers(vpfe, VB2_BUF_STATE_ERROR);
 }
 
 static int vpfe_cropcap(struct file *file, void *priv,
@@ -2088,6 +1986,9 @@ static int vpfe_cropcap(struct file *file, void *priv,
 
 	vpfe_dbg(2, vpfe, "vpfe_cropcap\n");
 
+	if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
 	if (vpfe->std_index >= ARRAY_SIZE(vpfe_standards))
 		return -EINVAL;
 
@@ -2147,6 +2048,7 @@ vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
 	struct vpfe_device *vpfe = video_drvdata(file);
 	struct v4l2_rect cr = vpfe->crop;
 	struct v4l2_rect r = s->r;
+	u32 bpp;
 
 	/* If streaming is started, return error */
 	if (vb2_is_busy(&vpfe->buffer_queue)) {
@@ -2172,12 +2074,14 @@ vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
 
 	s->r = vpfe->crop = r;
 
-	vpfe_ccdc_set_image_window(&vpfe->ccdc, &r, vpfe->bpp);
-	vpfe->fmt.fmt.pix.width = r.width;
-	vpfe->fmt.fmt.pix.height = r.height;
-	vpfe->fmt.fmt.pix.bytesperline = vpfe_ccdc_get_line_length(&vpfe->ccdc);
-	vpfe->fmt.fmt.pix.sizeimage = vpfe->fmt.fmt.pix.bytesperline *
-						vpfe->fmt.fmt.pix.height;
+	bpp = __get_bytesperpixel(vpfe, vpfe->fmt);
+	vpfe_ccdc_set_image_window(&vpfe->ccdc, &r, bpp);
+	vpfe->v_fmt.fmt.pix.width = r.width;
+	vpfe->v_fmt.fmt.pix.height = r.height;
+	vpfe->v_fmt.fmt.pix.bytesperline =
+		vpfe_ccdc_get_line_length(&vpfe->ccdc);
+	vpfe->v_fmt.fmt.pix.sizeimage = vpfe->v_fmt.fmt.pix.bytesperline *
+						vpfe->v_fmt.fmt.pix.height;
 
 	vpfe_dbg(1, vpfe, "cropped (%d,%d)/%dx%d of %dx%d\n",
 		 r.left, r.top, r.width, r.height, cr.width, cr.height);
@@ -2213,7 +2117,7 @@ static long vpfe_ioctl_default(struct file *file, void *priv,
 			return ret;
 		}
 		ret = vpfe_get_ccdc_image_format(vpfe,
-						 &vpfe->fmt);
+						 &vpfe->v_fmt);
 		if (ret < 0) {
 			vpfe_dbg(2, vpfe,
 				"Invalid image format at CCDC\n");
@@ -2298,8 +2202,10 @@ vpfe_async_bound(struct v4l2_async_notifier *notifier,
 					       struct vpfe_device, v4l2_dev);
 	struct v4l2_subdev_mbus_code_enum mbus_code;
 	struct vpfe_subdev_info *sdinfo;
+	struct vpfe_fmt *fmt;
+	int ret = 0;
 	bool found = false;
-	int i, j;
+	int i, j, k;
 
 	vpfe_dbg(1, vpfe, "vpfe_async_bound\n");
 
@@ -2321,27 +2227,37 @@ vpfe_async_bound(struct v4l2_async_notifier *notifier,
 
 	vpfe->video_dev.tvnorms |= sdinfo->inputs[0].std;
 
-	/* setup the supported formats & indexes */
-	for (j = 0, i = 0; ; ++j) {
-		struct vpfe_fmt *fmt;
-		int ret;
-
+	vpfe->num_active_fmt = 0;
+	for (j = 0, i = 0; (ret != -EINVAL); ++j) {
 		memset(&mbus_code, 0, sizeof(mbus_code));
 		mbus_code.index = j;
 		mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
 		ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
-			       NULL, &mbus_code);
+				       NULL, &mbus_code);
 		if (ret)
-			break;
-
-		fmt = find_format_by_code(mbus_code.code);
-		if (!fmt)
 			continue;
 
-		fmt->supported = true;
-		fmt->index = i++;
+		vpfe_dbg(3, vpfe,
+			 "subdev %s: code: %04x idx: %d\n",
+			 subdev->name, mbus_code.code, j);
+
+		for (k = 0; k < ARRAY_SIZE(formats); k++) {
+			fmt = &formats[k];
+			if (mbus_code.code != fmt->code)
+				continue;
+			vpfe->active_fmt[i] = fmt;
+			vpfe_dbg(3, vpfe,
+				 "matched fourcc: %4.4s code: %04x idx: %d\n",
+				 (char *)&fmt->fourcc, mbus_code.code, i);
+			vpfe->num_active_fmt = ++i;
+		}
 	}
 
+	if (!i) {
+		vpfe_err(vpfe, "No suitable format reported by subdev %s\n",
+			 subdev->name);
+		return -EINVAL;
+	}
 	return 0;
 }
 
@@ -2354,7 +2270,7 @@ static int vpfe_probe_complete(struct vpfe_device *vpfe)
 	spin_lock_init(&vpfe->dma_queue_lock);
 	mutex_init(&vpfe->lock);
 
-	vpfe->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	vpfe->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
 	/* set first sub device as current one */
 	vpfe->current_subdev = &vpfe->cfg->sub_devs[0];
@@ -2667,22 +2583,22 @@ static int vpfe_suspend(struct device *dev)
 	struct vpfe_device *vpfe = dev_get_drvdata(dev);
 	struct vpfe_ccdc *ccdc = &vpfe->ccdc;
 
-	/* if streaming has not started we don't care */
-	if (!vb2_start_streaming_called(&vpfe->buffer_queue))
-		return 0;
+	/* only do full suspend if streaming has started */
+	if (vb2_start_streaming_called(&vpfe->buffer_queue)) {
 
-	pm_runtime_get_sync(dev);
-	vpfe_config_enable(ccdc, 1);
+		pm_runtime_get_sync(dev);
+		vpfe_config_enable(ccdc, 1);
 
-	/* Save VPFE context */
-	vpfe_save_context(ccdc);
+		/* Save VPFE context */
+		vpfe_save_context(ccdc);
 
-	/* Disable CCDC */
-	vpfe_pcr_enable(ccdc, 0);
-	vpfe_config_enable(ccdc, 0);
+		/* Disable CCDC */
+		vpfe_pcr_enable(ccdc, 0);
+		vpfe_config_enable(ccdc, 0);
 
-	/* Disable both master and slave clock */
-	pm_runtime_put_sync(dev);
+		/* Disable both master and slave clock */
+		pm_runtime_put_sync(dev);
+	}
 
 	/* Select sleep pin state */
 	pinctrl_pm_select_sleep_state(dev);
@@ -2724,19 +2640,19 @@ static int vpfe_resume(struct device *dev)
 	struct vpfe_device *vpfe = dev_get_drvdata(dev);
 	struct vpfe_ccdc *ccdc = &vpfe->ccdc;
 
-	/* if streaming has not started we don't care */
-	if (!vb2_start_streaming_called(&vpfe->buffer_queue))
-		return 0;
+	/* only do full resume if streaming has started */
+	if (vb2_start_streaming_called(&vpfe->buffer_queue)) {
 
-	/* Enable both master and slave clock */
-	pm_runtime_get_sync(dev);
-	vpfe_config_enable(ccdc, 1);
+		/* Enable both master and slave clock */
+		pm_runtime_get_sync(dev);
+		vpfe_config_enable(ccdc, 1);
 
-	/* Restore VPFE context */
-	vpfe_restore_context(ccdc);
+		/* Restore VPFE context */
+		vpfe_restore_context(ccdc);
 
-	vpfe_config_enable(ccdc, 0);
-	pm_runtime_put_sync(dev);
+		vpfe_config_enable(ccdc, 0);
+		pm_runtime_put_sync(dev);
+	}
 
 	/* Select default pin state */
 	pinctrl_pm_select_default_state(dev);

+ 27 - 3
drivers/media/platform/am437x/am437x-vpfe.h

@@ -23,6 +23,7 @@
 
 #include <linux/am437x-vpfe.h>
 #include <linux/clk.h>
+#include <linux/completion.h>
 #include <linux/device.h>
 #include <linux/io.h>
 #include <linux/i2c.h>
@@ -220,6 +221,25 @@ struct vpfe_ccdc {
 	u32 ccdc_ctx[VPFE_REG_END / sizeof(u32)];
 };
 
+/*
+ * struct vpfe_fmt - VPFE media bus format information
+ * @fourcc: V4L2 pixel format code
+ * @code: V4L2 media bus format code
+ * @bitsperpixel: Bits per pixel over the bus
+ */
+struct vpfe_fmt {
+	u32 fourcc;
+	u32 code;
+	u32 bitsperpixel;
+};
+
+/*
+ * This value needs to be at least as large as the number of entry in
+ * formats[].
+ * When formats[] is modified make sure to adjust this value also.
+ */
+#define VPFE_MAX_ACTIVE_FMT	10
+
 struct vpfe_device {
 	/* V4l2 specific parameters */
 	/* Identifies video device for this channel */
@@ -254,9 +274,11 @@ struct vpfe_device {
 	/* Pointer pointing to next v4l2_buffer */
 	struct vpfe_cap_buffer *next_frm;
 	/* Used to store pixel format */
-	struct v4l2_format fmt;
-	/* Used to store current bytes per pixel based on current format */
-	unsigned int bpp;
+	const struct vpfe_fmt *fmt;
+	struct v4l2_format v_fmt;
+	struct vpfe_fmt	*active_fmt[VPFE_MAX_ACTIVE_FMT];
+	unsigned int num_active_fmt;
+
 	/*
 	 * used when IMP is chained to store the crop window which
 	 * is different from the image window
@@ -276,6 +298,8 @@ struct vpfe_device {
 	 */
 	u32 field_off;
 	struct vpfe_ccdc ccdc;
+	int stopping;
+	struct completion capture_stop;
 };
 
 #endif	/* AM437X_VPFE_H */

+ 2 - 0
drivers/media/platform/ti-vpe/Makefile

@@ -3,11 +3,13 @@ obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe.o
 obj-$(CONFIG_VIDEO_TI_VPDMA) += ti-vpdma.o
 obj-$(CONFIG_VIDEO_TI_SC) += ti-sc.o
 obj-$(CONFIG_VIDEO_TI_CSC) += ti-csc.o
+obj-$(CONFIG_VIDEO_TI_VIP) += ti-vip.o
 
 ti-vpe-y := vpe.o
 ti-vpdma-y := vpdma.o
 ti-sc-y := sc.o
 ti-csc-y := csc.o
+ti-vip-y := vip.o
 
 ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG
 

+ 595 - 160
drivers/media/platform/ti-vpe/cal.c

@@ -17,6 +17,8 @@
 #include <linux/delay.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
 #include <linux/videodev2.h>
 #include <linux/of_device.h>
 #include <linux/of_graph.h>
@@ -74,8 +76,6 @@ static const struct v4l2_fract
 #define CAL_NUM_INPUT 1
 #define CAL_NUM_CONTEXT 2
 
-#define bytes_per_line(pixel, bpp) (ALIGN(pixel * bpp, 16))
-
 #define reg_read(dev, offset) ioread32(dev->base + offset)
 #define reg_write(dev, offset, val) iowrite32(val, dev->base + offset)
 
@@ -94,102 +94,103 @@ static const struct v4l2_fract
 struct cal_fmt {
 	u32	fourcc;
 	u32	code;
-	u8	depth;
+	/* Bits per pixel */
+	u8	bpp;
 };
 
 static struct cal_fmt cal_formats[] = {
 	{
 		.fourcc		= V4L2_PIX_FMT_YUYV,
 		.code		= MEDIA_BUS_FMT_YUYV8_2X8,
-		.depth		= 16,
+		.bpp		= 16,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_UYVY,
 		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
-		.depth		= 16,
+		.bpp		= 16,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_YVYU,
 		.code		= MEDIA_BUS_FMT_YVYU8_2X8,
-		.depth		= 16,
+		.bpp		= 16,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_VYUY,
 		.code		= MEDIA_BUS_FMT_VYUY8_2X8,
-		.depth		= 16,
+		.bpp		= 16,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
 		.code		= MEDIA_BUS_FMT_RGB565_2X8_LE,
-		.depth		= 16,
+		.bpp		= 16,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
 		.code		= MEDIA_BUS_FMT_RGB565_2X8_BE,
-		.depth		= 16,
+		.bpp		= 16,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
 		.code		= MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
-		.depth		= 16,
+		.bpp		= 16,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
 		.code		= MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
-		.depth		= 16,
+		.bpp		= 16,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_RGB24, /* rgb */
 		.code		= MEDIA_BUS_FMT_RGB888_2X12_LE,
-		.depth		= 24,
+		.bpp		= 24,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_BGR24, /* bgr */
 		.code		= MEDIA_BUS_FMT_RGB888_2X12_BE,
-		.depth		= 24,
+		.bpp		= 24,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_RGB32, /* argb */
 		.code		= MEDIA_BUS_FMT_ARGB8888_1X32,
-		.depth		= 32,
+		.bpp		= 32,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_SBGGR8,
 		.code		= MEDIA_BUS_FMT_SBGGR8_1X8,
-		.depth		= 8,
+		.bpp		= 8,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_SGBRG8,
 		.code		= MEDIA_BUS_FMT_SGBRG8_1X8,
-		.depth		= 8,
+		.bpp		= 8,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_SGRBG8,
 		.code		= MEDIA_BUS_FMT_SGRBG8_1X8,
-		.depth		= 8,
+		.bpp		= 8,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_SRGGB8,
 		.code		= MEDIA_BUS_FMT_SRGGB8_1X8,
-		.depth		= 8,
+		.bpp		= 8,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_SBGGR10,
 		.code		= MEDIA_BUS_FMT_SBGGR10_1X10,
-		.depth		= 16,
+		.bpp		= 10,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_SGBRG10,
 		.code		= MEDIA_BUS_FMT_SGBRG10_1X10,
-		.depth		= 16,
+		.bpp		= 10,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_SGRBG10,
 		.code		= MEDIA_BUS_FMT_SGRBG10_1X10,
-		.depth		= 16,
+		.bpp		= 10,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_SRGGB10,
 		.code		= MEDIA_BUS_FMT_SRGGB10_1X10,
-		.depth		= 16,
+		.bpp		= 10,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_SBGGR12,
 		.code		= MEDIA_BUS_FMT_SBGGR12_1X12,
-		.depth		= 16,
+		.bpp		= 12,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_SGBRG12,
 		.code		= MEDIA_BUS_FMT_SGBRG12_1X12,
-		.depth		= 16,
+		.bpp		= 12,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_SGRBG12,
 		.code		= MEDIA_BUS_FMT_SGRBG12_1X12,
-		.depth		= 16,
+		.bpp		= 12,
 	}, {
 		.fourcc		= V4L2_PIX_FMT_SRGGB12,
 		.code		= MEDIA_BUS_FMT_SRGGB12_1X12,
-		.depth		= 16,
+		.bpp		= 12,
 	},
 };
 
@@ -223,20 +224,125 @@ struct cal_dmaqueue {
 	int			ini_jiffies;
 };
 
-struct cm_data {
+struct cc_data {
 	void __iomem		*base;
 	struct resource		*res;
 
-	unsigned int		camerrx_control;
-
 	struct platform_device *pdev;
 };
 
-struct cc_data {
-	void __iomem		*base;
-	struct resource		*res;
+/* CTRL_CORE_CAMERRX_CONTROL register field id */
+enum cal_camerarx_field {
+	F_CTRLCLKEN,
+	F_CAMMODE,
+	F_LANEENABLE,
+	F_CSI_MODE,
 
-	struct platform_device *pdev;
+	F_MAX_FIELDS,
+};
+
+struct cal_csi2_phy {
+	struct regmap_field *fields[F_MAX_FIELDS];
+	struct reg_field *base_fields;
+	const int num_lanes;
+};
+
+struct cal_data {
+	const int num_csi2_phy;
+	struct cal_csi2_phy *csi2_phy_core;
+
+	const unsigned int flags;
+};
+
+static struct reg_field dra72x_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = {
+	[F_CTRLCLKEN] = REG_FIELD(0, 10, 10),
+	[F_CAMMODE] = REG_FIELD(0, 11, 12),
+	[F_LANEENABLE] = REG_FIELD(0, 13, 16),
+	[F_CSI_MODE] = REG_FIELD(0, 17, 17),
+};
+
+static struct reg_field dra72x_ctrl_core_csi1_reg_fields[F_MAX_FIELDS] = {
+	[F_CTRLCLKEN] = REG_FIELD(0, 0, 0),
+	[F_CAMMODE] = REG_FIELD(0, 1, 2),
+	[F_LANEENABLE] = REG_FIELD(0, 3, 4),
+	[F_CSI_MODE] = REG_FIELD(0, 5, 5),
+};
+
+static struct cal_csi2_phy dra72x_cal_csi_phy[] = {
+	{
+		.base_fields = dra72x_ctrl_core_csi0_reg_fields,
+		.num_lanes = 4,
+	},
+	{
+		.base_fields = dra72x_ctrl_core_csi1_reg_fields,
+		.num_lanes = 2,
+	},
+};
+
+static struct cal_data dra72x_cal_data = {
+	.csi2_phy_core = dra72x_cal_csi_phy,
+	.num_csi2_phy = ARRAY_SIZE(dra72x_cal_csi_phy),
+
+	.flags = 0,
+};
+
+static struct cal_data dra72x_es1_cal_data = {
+	.csi2_phy_core = dra72x_cal_csi_phy,
+	.num_csi2_phy = ARRAY_SIZE(dra72x_cal_csi_phy),
+
+	.flags = DRA72_CAL_PRE_ES2_LDO_DISABLE,
+};
+
+static struct reg_field dra76x_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = {
+	[F_CTRLCLKEN] = REG_FIELD(0, 8, 8),
+	[F_CAMMODE] = REG_FIELD(0, 9, 10),
+	[F_CSI_MODE] = REG_FIELD(0, 11, 11),
+	[F_LANEENABLE] = REG_FIELD(0, 27, 31),
+};
+
+static struct reg_field dra76x_ctrl_core_csi1_reg_fields[F_MAX_FIELDS] = {
+	[F_CTRLCLKEN] = REG_FIELD(0, 0, 0),
+	[F_CAMMODE] = REG_FIELD(0, 1, 2),
+	[F_CSI_MODE] = REG_FIELD(0, 3, 3),
+	[F_LANEENABLE] = REG_FIELD(0, 24, 26),
+};
+
+static struct cal_csi2_phy dra76x_cal_csi_phy[] = {
+	{
+		.base_fields = dra76x_ctrl_core_csi0_reg_fields,
+		.num_lanes = 5,
+	},
+	{
+		.base_fields = dra76x_ctrl_core_csi1_reg_fields,
+		.num_lanes = 3,
+	},
+};
+
+static struct cal_data dra76x_cal_data = {
+	.csi2_phy_core = dra76x_cal_csi_phy,
+	.num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy),
+
+	.flags = 0,
+};
+
+static struct reg_field am654_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = {
+	[F_CTRLCLKEN] = REG_FIELD(0, 15, 15),
+	[F_CAMMODE] = REG_FIELD(0, 24, 25),
+	[F_LANEENABLE] = REG_FIELD(0, 0, 4),
+};
+
+static struct cal_csi2_phy am654_cal_csi_phy[] = {
+	{
+		.base_fields = am654_ctrl_core_csi0_reg_fields,
+		.num_lanes = 5,
+	},
+};
+
+static struct cal_data am654_cal_data = {
+	.csi2_phy_core = am654_cal_csi_phy,
+	.num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy),
+
+	.flags = 0,
 };
 
 /*
@@ -250,8 +356,15 @@ struct cal_dev {
 	struct platform_device	*pdev;
 	struct v4l2_device	v4l2_dev;
 
+	/* Controller flags for special cases */
+	unsigned int		flags;
+
+	struct cal_data		*data;
+
 	/* Control Module handle */
-	struct cm_data		*cm;
+	struct regmap		*syscon_camerrx;
+	u32			syscon_camerrx_offset;
+
 	/* Camera Core Module handle */
 	struct cc_data		*cc[CAL_NUM_CSI2_PORTS];
 
@@ -363,73 +476,115 @@ static inline void set_field(u32 *valp, u32 field, u32 mask)
 	*valp = val;
 }
 
-/*
- * Control Module block access
- */
-static struct cm_data *cm_create(struct cal_dev *dev)
+static u32 cal_data_get_phy_max_lanes(struct cal_ctx *ctx)
 {
-	struct platform_device *pdev = dev->pdev;
-	struct cm_data *cm;
+	struct cal_dev *dev = ctx->dev;
+	u32 phy_id = ctx->csi2_port - 1;
 
-	cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL);
-	if (!cm)
-		return ERR_PTR(-ENOMEM);
+	return dev->data->csi2_phy_core[phy_id].num_lanes;
+}
+
+static u32 cal_data_get_num_csi2_phy(struct cal_dev *dev)
+{
+	return dev->data->num_csi2_phy;
+}
+
+static int cal_camerarx_regmap_init(struct cal_dev *dev)
+{
+	struct reg_field *field;
+	struct cal_csi2_phy *phy;
+	int i, j;
+
+	if (!dev->data)
+		return -EINVAL;
+
+	for (i = 0; i < cal_data_get_num_csi2_phy(dev); i++) {
+		phy = &dev->data->csi2_phy_core[i];
+		for (j = 0; j < F_MAX_FIELDS; j++) {
+			field = &phy->base_fields[j];
+			/*
+			 * Here we update the reg offset with the
+			 * value found in DT
+			 */
+			field->reg = dev->syscon_camerrx_offset;
+			phy->fields[j] =
+				devm_regmap_field_alloc(&dev->pdev->dev,
+							dev->syscon_camerrx,
+							*field);
+			if (IS_ERR(phy->fields[j])) {
+				cal_err(dev, "Unable to allocate regmap fields\n");
+				return PTR_ERR(phy->fields[j]);
+			}
+		}
+	}
+	return 0;
+}
 
-	cm->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-						"camerrx_control");
-	cm->base = devm_ioremap_resource(&pdev->dev, cm->res);
-	if (IS_ERR(cm->base)) {
+static const struct regmap_config cal_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev)
+{
+	struct platform_device *pdev = dev->pdev;
+	struct regmap *regmap;
+	void __iomem *base;
+	u32 reg_io_width;
+	struct regmap_config r_config = cal_regmap_config;
+	struct resource *res;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+					   "camerrx_control");
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base)) {
 		cal_err(dev, "failed to ioremap\n");
-		return ERR_CAST(cm->base);
+		return ERR_CAST(base);
 	}
 
 	cal_dbg(1, dev, "ioresource %s at %pa - %pa\n",
-		cm->res->name, &cm->res->start, &cm->res->end);
+		res->name, &res->start, &res->end);
 
-	return cm;
+	reg_io_width = 4;
+	r_config.reg_stride = reg_io_width;
+	r_config.val_bits = reg_io_width * 8;
+	r_config.max_register = resource_size(res) - reg_io_width;
+
+	regmap = regmap_init_mmio(NULL, base, &r_config);
+	if (IS_ERR(regmap))
+		pr_err("regmap init failed\n");
+
+	return regmap;
 }
 
+/*
+ * Control Module CAMERARX block access
+ */
 static void camerarx_phy_enable(struct cal_ctx *ctx)
 {
-	u32 val;
-
-	if (!ctx->dev->cm->base) {
-		ctx_err(ctx, "cm not mapped\n");
-		return;
-	}
-
-	val = reg_read(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL);
-	if (ctx->csi2_port == 1) {
-		set_field(&val, 1, CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK);
-		set_field(&val, 0, CM_CAMERRX_CTRL_CSI0_CAMMODE_MASK);
-		/* enable all lanes by default */
-		set_field(&val, 0xf, CM_CAMERRX_CTRL_CSI0_LANEENABLE_MASK);
-		set_field(&val, 1, CM_CAMERRX_CTRL_CSI0_MODE_MASK);
-	} else if (ctx->csi2_port == 2) {
-		set_field(&val, 1, CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK);
-		set_field(&val, 0, CM_CAMERRX_CTRL_CSI1_CAMMODE_MASK);
-		/* enable all lanes by default */
-		set_field(&val, 0x3, CM_CAMERRX_CTRL_CSI1_LANEENABLE_MASK);
-		set_field(&val, 1, CM_CAMERRX_CTRL_CSI1_MODE_MASK);
-	}
-	reg_write(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL, val);
+	struct cal_csi2_phy *phy;
+	u32 phy_id = ctx->csi2_port - 1;
+	u32 max_lanes;
+
+	phy = &ctx->dev->data->csi2_phy_core[phy_id];
+	regmap_field_write(phy->fields[F_CAMMODE], 0);
+	/* Always enable all lanes at the phy control level */
+	max_lanes = (1 << cal_data_get_phy_max_lanes(ctx)) - 1;
+	regmap_field_write(phy->fields[F_LANEENABLE], max_lanes);
+	/* F_CSI_MODE is not present on every architecture */
+	if (phy->fields[F_CSI_MODE])
+		regmap_field_write(phy->fields[F_CSI_MODE], 1);
+	regmap_field_write(phy->fields[F_CTRLCLKEN], 1);
 }
 
 static void camerarx_phy_disable(struct cal_ctx *ctx)
 {
-	u32 val;
-
-	if (!ctx->dev->cm->base) {
-		ctx_err(ctx, "cm not mapped\n");
-		return;
-	}
+	struct cal_csi2_phy *phy;
+	u32 phy_id = ctx->csi2_port - 1;
 
-	val = reg_read(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL);
-	if (ctx->csi2_port == 1)
-		set_field(&val, 0x0, CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK);
-	else if (ctx->csi2_port == 2)
-		set_field(&val, 0x0, CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK);
-	reg_write(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL, val);
+	phy = &ctx->dev->data->csi2_phy_core[phy_id];
+	regmap_field_write(phy->fields[F_CTRLCLKEN], 0);
 }
 
 /*
@@ -478,9 +633,52 @@ static void cal_get_hwinfo(struct cal_dev *dev)
 		hwinfo);
 }
 
+/*
+ *   Errata i913: CSI2 LDO Needs to be disabled when module is powered on
+ *
+ *   Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2
+ *   LDOs on the device are disabled if CSI-2 module is powered on
+ *   (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304
+ *   | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high
+ *   current draw on the module supply in active mode.
+ *
+ *   Errata does not apply when CSI-2 module is powered off
+ *   (0x4845 B304 | 0x4845 B384 [28:27] = 0x0).
+ *
+ * SW Workaround:
+ *	Set the following register bits to disable the LDO,
+ *	which is essentially CSI2 REG10 bit 6:
+ *
+ *		Core 0:  0x4845 B828 = 0x0000 0040
+ *		Core 1:  0x4845 B928 = 0x0000 0040
+ */
+static void i913_errata(struct cal_dev *dev, unsigned int port)
+{
+	u32 reg10 = reg_read(dev->cc[port], CAL_CSI2_PHY_REG10);
+
+	set_field(&reg10, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE,
+		  CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK);
+
+	cal_dbg(1, dev, "CSI2_%d_REG10 = 0x%08x\n", port, reg10);
+	reg_write(dev->cc[port], CAL_CSI2_PHY_REG10, reg10);
+}
+
 static inline int cal_runtime_get(struct cal_dev *dev)
 {
-	return pm_runtime_get_sync(&dev->pdev->dev);
+	int r;
+
+	r = pm_runtime_get_sync(&dev->pdev->dev);
+
+	if (dev->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) {
+		/*
+		 * Apply errata on both paort eveytime we (re-)enable
+		 * the clock
+		 */
+		i913_errata(dev, 0);
+		i913_errata(dev, 1);
+	}
+
+	return r;
 }
 
 static inline void cal_runtime_put(struct cal_dev *dev)
@@ -512,12 +710,6 @@ static void cal_quickdump_regs(struct cal_dev *dev)
 			       resource_size(dev->ctx[1]->cc->res),
 			       false);
 	}
-
-	cal_info(dev, "CAMERRX_Control Registers @ %pa:\n",
-		 &dev->cm->res->start);
-	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
-		       (__force const void *)dev->cm->base,
-		       resource_size(dev->cm->res), false);
 }
 
 /*
@@ -555,29 +747,76 @@ static void disable_irqs(struct cal_ctx *ctx)
 	reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0);
 }
 
-static void csi2_init(struct cal_ctx *ctx)
+static void csi2_phy_config(struct cal_ctx *ctx);
+
+static void csi2_phy_init(struct cal_ctx *ctx)
 {
 	int i;
 	u32 val;
 
+	/* Steps
+	 *  1. Configure D-PHY mode and enable required lanes
+	 *  2. Reset complex IO - Wait for completion of reset
+	 *          Note if the external sensor is not sending byte clock,
+	 *          the reset will timeout
+	 *  3 Program Stop States
+	 *      A. Program THS_TERM, THS_SETTLE, etc... Timings parameters
+	 *              in terms of DDR clock periods
+	 *      B. Enable stop state transition timeouts
+	 *  4.Force FORCERXMODE
+	 *      D. Enable pull down using pad control
+	 *      E. Power up PHY
+	 *      F. Wait for power up completion
+	 *      G. Wait for all enabled lane to reach stop state
+	 *      H. Disable pull down using pad control
+	 */
+
+	/* 1. Configure D-PHY mode and enable required lanes */
+	camerarx_phy_enable(ctx);
+
+	/* 2. Reset complex IO - Do not wait for reset completion */
+	val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
+	set_field(&val, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL,
+		  CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);
+	reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
+	ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n",
+		ctx->csi2_port,
+		reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)));
+
+	/* Dummy read to allow SCP to complete */
+	val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
+
+	/* 3.A. Program Phy Timing Parameters */
+	csi2_phy_config(ctx);
+
+	/* 3.B. Program Stop States */
 	val = reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port));
-	set_field(&val, CAL_GEN_ENABLE,
-		  CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK);
 	set_field(&val, CAL_GEN_ENABLE,
 		  CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK);
 	set_field(&val, CAL_GEN_DISABLE,
 		  CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK);
 	set_field(&val, 407, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK);
 	reg_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val);
-	ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x\n", ctx->csi2_port,
+	ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n",
+		ctx->csi2_port,
+		reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)));
+
+	/* 4. Force FORCERXMODE */
+	val = reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port));
+	set_field(&val, CAL_GEN_ENABLE,
+		  CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK);
+	reg_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val);
+	ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n",
+		ctx->csi2_port,
 		reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)));
 
+	/* E. Power up the PHY using the complex IO */
 	val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
-	set_field(&val, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL,
-		  CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);
 	set_field(&val, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON,
 		  CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK);
 	reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
+
+	/* F. Wait for power up completion */
 	for (i = 0; i < 10; i++) {
 		if (reg_read_field(ctx->dev,
 				   CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port),
@@ -586,18 +825,104 @@ static void csi2_init(struct cal_ctx *ctx)
 			break;
 		usleep_range(1000, 1100);
 	}
-	ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", ctx->csi2_port,
-		reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)));
+	ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Powered UP %s\n",
+		ctx->csi2_port,
+		reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)),
+		(i >= 10) ? "(timeout)" : "");
+}
 
-	val = reg_read(ctx->dev, CAL_CTRL);
-	set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK);
-	set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK);
-	set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED,
-		  CAL_CTRL_POSTED_WRITES_MASK);
-	set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK);
-	set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK);
-	reg_write(ctx->dev, CAL_CTRL, val);
-	ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->dev, CAL_CTRL));
+static void csi2_wait_for_phy(struct cal_ctx *ctx)
+{
+	int i;
+
+	/* Steps
+	 *  2. Wait for completion of reset
+	 *          Note if the external sensor is not sending byte clock,
+	 *          the reset will timeout
+	 *  4.Force FORCERXMODE
+	 *      G. Wait for all enabled lane to reach stop state
+	 *      H. Disable pull down using pad control
+	 */
+
+	/* 2. Wait for reset completion */
+	for (i = 0; i < 250; i++) {
+		if (reg_read_field(ctx->dev,
+				   CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port),
+				   CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) ==
+		    CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED)
+			break;
+		usleep_range(1000, 1100);
+	}
+	ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO Reset Done (%d) %s\n",
+		ctx->csi2_port,
+		reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), i,
+		(i >= 250) ? "(timeout)" : "");
+
+	/* 4. G. Wait for all enabled lane to reach stop state */
+	for (i = 0; i < 10; i++) {
+		if (reg_read_field(ctx->dev,
+				   CAL_CSI2_TIMING(ctx->csi2_port),
+				   CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) ==
+		    CAL_GEN_DISABLE)
+			break;
+		usleep_range(1000, 1100);
+	}
+	ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Stop State Reached %s\n",
+		ctx->csi2_port,
+		reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)),
+		(i >= 10) ? "(timeout)" : "");
+
+	ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n",
+		(ctx->csi2_port - 1), reg_read(ctx->cc, CAL_CSI2_PHY_REG1));
+}
+
+static void csi2_phy_deinit(struct cal_ctx *ctx)
+{
+	int i;
+	u32 val;
+
+	/* Power down the PHY using the complex IO */
+	val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
+	set_field(&val, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF,
+		  CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK);
+	reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
+
+	/* Wait for power down completion */
+	for (i = 0; i < 10; i++) {
+		if (reg_read_field(ctx->dev,
+				   CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port),
+				   CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK) ==
+		    CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_OFF)
+			break;
+		usleep_range(1000, 1100);
+	}
+	ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Powered Down %s\n",
+		ctx->csi2_port,
+		reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)),
+		(i >= 10) ? "(timeout)" : "");
+
+	/* Assert Comple IO Reset */
+	val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
+	set_field(&val, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL,
+		  CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);
+	reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
+
+	/* Wait for power down completion */
+	for (i = 0; i < 10; i++) {
+		if (reg_read_field(ctx->dev,
+				   CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port),
+				   CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) ==
+		    CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING)
+			break;
+		usleep_range(1000, 1100);
+	}
+	ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n",
+		ctx->csi2_port,
+		reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), i,
+		(i >= 10) ? "(timeout)" : "");
+
+	/* Disable the phy */
+	camerarx_phy_disable(ctx);
 }
 
 static void csi2_lane_config(struct cal_ctx *ctx)
@@ -669,13 +994,45 @@ static void csi2_ctx_config(struct cal_ctx *ctx)
 
 static void pix_proc_config(struct cal_ctx *ctx)
 {
-	u32 val;
+	u32 val, extract, pack;
+
+	switch (ctx->fmt->bpp) {
+	default:
+		/*
+		 * If you see this warning then it means that you added
+		 * some new entry in the cal_formats[] array with a different
+		 * bit per pixel values then the one supported below.
+		 * Either add support for the new bpp value below or adjust
+		 * the new entry to use one of the value below.
+		 *
+		 * Instead of failing here just use 8 bpp as a default.
+		 */
+		dev_warn_once(&ctx->dev->pdev->dev,
+			      "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n",
+			      __FILE__, __LINE__, __func__, ctx->fmt->bpp);
+	case 8:
+		extract = CAL_PIX_PROC_EXTRACT_B8;
+		pack = CAL_PIX_PROC_PACK_B8;
+		break;
+	case 10:
+		extract = CAL_PIX_PROC_EXTRACT_B10_MIPI;
+		pack = CAL_PIX_PROC_PACK_B16;
+		break;
+	case 12:
+		extract = CAL_PIX_PROC_EXTRACT_B12_MIPI;
+		pack = CAL_PIX_PROC_PACK_B16;
+		break;
+	case 16:
+		extract = CAL_PIX_PROC_EXTRACT_B16_LE;
+		pack = CAL_PIX_PROC_PACK_B16;
+		break;
+	}
 
 	val = reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port));
-	set_field(&val, CAL_PIX_PROC_EXTRACT_B8, CAL_PIX_PROC_EXTRACT_MASK);
+	set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK);
 	set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK);
 	set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK);
-	set_field(&val, CAL_PIX_PROC_PACK_B8, CAL_PIX_PROC_PACK_MASK);
+	set_field(&val, pack, CAL_PIX_PROC_PACK_MASK);
 	set_field(&val, ctx->csi2_port, CAL_PIX_PROC_CPORT_MASK);
 	set_field(&val, CAL_GEN_ENABLE, CAL_PIX_PROC_EN_MASK);
 	reg_write(ctx->dev, CAL_PIX_PROC(ctx->csi2_port), val);
@@ -684,12 +1041,13 @@ static void pix_proc_config(struct cal_ctx *ctx)
 }
 
 static void cal_wr_dma_config(struct cal_ctx *ctx,
-			      unsigned int width)
+			      unsigned int width, unsigned int height)
 {
 	u32 val;
 
 	val = reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port));
 	set_field(&val, ctx->csi2_port, CAL_WR_DMA_CTRL_CPORT_MASK);
+	set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK);
 	set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT,
 		  CAL_WR_DMA_CTRL_DTAG_MASK);
 	set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST,
@@ -724,6 +1082,16 @@ static void cal_wr_dma_config(struct cal_ctx *ctx,
 	reg_write(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port), val);
 	ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port,
 		reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port)));
+
+	val = reg_read(ctx->dev, CAL_CTRL);
+	set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK);
+	set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK);
+	set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED,
+		  CAL_CTRL_POSTED_WRITES_MASK);
+	set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK);
+	set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK);
+	reg_write(ctx->dev, CAL_CTRL, val);
+	ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->dev, CAL_CTRL));
 }
 
 static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr)
@@ -737,41 +1105,28 @@ static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr)
 #define TCLK_TERM	0
 #define TCLK_MISS	1
 #define TCLK_SETTLE	14
-#define THS_SETTLE	15
 
 static void csi2_phy_config(struct cal_ctx *ctx)
 {
 	unsigned int reg0, reg1;
 	unsigned int ths_term, ths_settle;
-	unsigned int ddrclkperiod_us;
+	unsigned int csi2_ddrclk_khz;
+	struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 =
+			&ctx->endpoint.bus.mipi_csi2;
+	u32 num_lanes = mipi_csi2->num_data_lanes;
 
-	/*
-	 * THS_TERM: Programmed value = floor(20 ns/DDRClk period) - 2.
-	 */
-	ddrclkperiod_us = ctx->external_rate / 2000000;
-	ddrclkperiod_us = 1000000 / ddrclkperiod_us;
-	ctx_dbg(1, ctx, "ddrclkperiod_us: %d\n", ddrclkperiod_us);
+	/* DPHY timing configuration */
+	/* CSI-2 is DDR and we only count used lanes. */
+	csi2_ddrclk_khz = ctx->external_rate / 1000
+		/ (2 * num_lanes) * ctx->fmt->bpp;
+	ctx_dbg(1, ctx, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz);
 
-	ths_term = 20000 / ddrclkperiod_us;
-	ths_term = (ths_term >= 2) ? ths_term - 2 : ths_term;
+	/* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */
+	ths_term = 20 * csi2_ddrclk_khz / 1000000;
 	ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term);
 
-	/*
-	 * THS_SETTLE: Programmed value = floor(176.3 ns/CtrlClk period) - 1.
-	 *	Since CtrlClk is fixed at 96Mhz then we get
-	 *	ths_settle = floor(176.3 / 10.416) - 1 = 15
-	 * If we ever switch to a dynamic clock then this code might be useful
-	 *
-	 * unsigned int ctrlclkperiod_us;
-	 * ctrlclkperiod_us = 96000000 / 1000000;
-	 * ctrlclkperiod_us = 1000000 / ctrlclkperiod_us;
-	 * ctx_dbg(1, ctx, "ctrlclkperiod_us: %d\n", ctrlclkperiod_us);
-
-	 * ths_settle = 176300  / ctrlclkperiod_us;
-	 * ths_settle = (ths_settle > 1) ? ths_settle - 1 : ths_settle;
-	 */
-
-	ths_settle = THS_SETTLE;
+	/* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */
+	ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4;
 	ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle);
 
 	reg0 = reg_read(ctx->cc, CAL_CSI2_PHY_REG0);
@@ -986,6 +1341,8 @@ static int cal_calc_format_size(struct cal_ctx *ctx,
 				const struct cal_fmt *fmt,
 				struct v4l2_format *f)
 {
+	u32 bpl;
+
 	if (!fmt) {
 		ctx_dbg(3, ctx, "No cal_fmt provided!\n");
 		return -EINVAL;
@@ -993,8 +1350,10 @@ static int cal_calc_format_size(struct cal_ctx *ctx,
 
 	v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,
 			      &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
-	f->fmt.pix.bytesperline = bytes_per_line(f->fmt.pix.width,
-						 fmt->depth >> 3);
+
+	bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3;
+	f->fmt.pix.bytesperline = ALIGN(bpl, 16);
+
 	f->fmt.pix.sizeimage = f->fmt.pix.height *
 			       f->fmt.pix.bytesperline;
 
@@ -1308,16 +1667,14 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
 
 	cal_runtime_get(ctx->dev);
 
-	enable_irqs(ctx);
-	camerarx_phy_enable(ctx);
-	csi2_init(ctx);
-	csi2_phy_config(ctx);
-	csi2_lane_config(ctx);
 	csi2_ctx_config(ctx);
 	pix_proc_config(ctx);
-	cal_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline);
-	cal_wr_dma_addr(ctx, addr);
-	csi2_ppi_enable(ctx);
+	cal_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline,
+			  ctx->v_fmt.fmt.pix.height);
+	csi2_lane_config(ctx);
+
+	enable_irqs(ctx);
+	csi2_phy_init(ctx);
 
 	ret = v4l2_subdev_call(ctx->sensor, video, s_stream, 1);
 	if (ret) {
@@ -1326,6 +1683,10 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
 		goto err;
 	}
 
+	csi2_wait_for_phy(ctx);
+	cal_wr_dma_addr(ctx, addr);
+	csi2_ppi_enable(ctx);
+
 	if (debug >= 4)
 		cal_quickdump_regs(ctx->dev);
 
@@ -1346,11 +1707,12 @@ static void cal_stop_streaming(struct vb2_queue *vq)
 	struct cal_buffer *buf, *tmp;
 	unsigned long flags;
 
-	if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0))
-		ctx_err(ctx, "stream off failed in subdev\n");
-
 	csi2_ppi_disable(ctx);
 	disable_irqs(ctx);
+	csi2_phy_deinit(ctx);
+
+	if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0))
+		ctx_err(ctx, "stream off failed in subdev\n");
 
 	/* Release all active buffers */
 	spin_lock_irqsave(&ctx->slock, flags);
@@ -1406,6 +1768,7 @@ static const struct v4l2_ioctl_ops cal_ioctl_ops = {
 	.vidioc_querybuf      = vb2_ioctl_querybuf,
 	.vidioc_qbuf          = vb2_ioctl_qbuf,
 	.vidioc_dqbuf         = vb2_ioctl_dqbuf,
+	.vidioc_expbuf        = vb2_ioctl_expbuf,
 	.vidioc_enum_input    = cal_enum_input,
 	.vidioc_g_input       = cal_g_input,
 	.vidioc_s_input       = cal_s_input,
@@ -1807,9 +2170,15 @@ err_exit:
 	return NULL;
 }
 
+static const struct of_device_id cal_of_match[];
+
 static int cal_probe(struct platform_device *pdev)
 {
 	struct cal_dev *dev;
+	const struct of_device_id *match;
+	struct device_node *parent = pdev->dev.of_node;
+	struct regmap *syscon_camerrx = NULL;
+	u32 syscon_camerrx_offset = 0;
 	int ret;
 	int irq;
 
@@ -1817,6 +2186,18 @@ static int cal_probe(struct platform_device *pdev)
 	if (!dev)
 		return -ENOMEM;
 
+	match = of_match_device(of_match_ptr(cal_of_match), &pdev->dev);
+	if (!match)
+		return -ENODEV;
+
+	if (match->data) {
+		dev->data = (struct cal_data *)match->data;
+		dev->flags = dev->data->flags;
+	} else {
+		dev_err(&pdev->dev, "Could not get feature data based on compatible version\n");
+		return -ENODEV;
+	}
+
 	/* set pseudo v4l2 device name so we can use v4l2_printk */
 	strlcpy(dev->v4l2_dev.name, CAL_MODULE_NAME,
 		sizeof(dev->v4l2_dev.name));
@@ -1824,6 +2205,43 @@ static int cal_probe(struct platform_device *pdev)
 	/* save pdev pointer */
 	dev->pdev = pdev;
 
+	if (parent && of_property_read_bool(parent, "syscon-camerrx")) {
+		syscon_camerrx =
+			syscon_regmap_lookup_by_phandle(parent,
+							"syscon-camerrx");
+		if (IS_ERR(syscon_camerrx)) {
+			dev_err(&pdev->dev, "failed to get syscon-camerrx regmap\n");
+			return PTR_ERR(syscon_camerrx);
+		}
+
+		if (of_property_read_u32_index(parent, "syscon-camerrx", 1,
+					       &syscon_camerrx_offset)) {
+			dev_err(&pdev->dev, "failed to get syscon-camerrx offset\n");
+			return -EINVAL;
+		}
+	} else {
+		/*
+		 * Backward DTS compatibility.
+		 * If syscon entry is not present then check if the
+		 * camerrx_control resource is present.
+		 */
+		syscon_camerrx = cal_get_camerarx_regmap(dev);
+		if (IS_ERR(syscon_camerrx)) {
+			dev_err(&pdev->dev, "failed to get camerrx_control regmap\n");
+			return PTR_ERR(syscon_camerrx);
+		}
+		/* In this case the base already point to the direct
+		 * CM register so no need for an offset
+		 */
+		syscon_camerrx_offset = 0;
+	}
+
+	dev->syscon_camerrx = syscon_camerrx;
+	dev->syscon_camerrx_offset = syscon_camerrx_offset;
+	ret = cal_camerarx_regmap_init(dev);
+	if (ret)
+		return ret;
+
 	dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 						"cal_top");
 	dev->base = devm_ioremap_resource(&pdev->dev, dev->res);
@@ -1842,23 +2260,25 @@ static int cal_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, dev);
 
-	dev->cm = cm_create(dev);
-	if (IS_ERR(dev->cm))
-		return PTR_ERR(dev->cm);
-
 	dev->cc[0] = cc_create(dev, 0);
 	if (IS_ERR(dev->cc[0]))
 		return PTR_ERR(dev->cc[0]);
 
-	dev->cc[1] = cc_create(dev, 1);
-	if (IS_ERR(dev->cc[1]))
-		return PTR_ERR(dev->cc[1]);
+	if (cal_data_get_num_csi2_phy(dev) > 1) {
+		dev->cc[1] = cc_create(dev, 1);
+		if (IS_ERR(dev->cc[1]))
+			return PTR_ERR(dev->cc[1]);
+	} else {
+		dev->cc[1] = NULL;
+		cal_info(dev, "One phy/port only.\n");
+	}
 
 	dev->ctx[0] = NULL;
 	dev->ctx[1] = NULL;
 
 	dev->ctx[0] = cal_create_instance(dev, 0);
-	dev->ctx[1] = cal_create_instance(dev, 1);
+	if (cal_data_get_num_csi2_phy(dev) > 1)
+		dev->ctx[1] = cal_create_instance(dev, 1);
 	if (!dev->ctx[0] && !dev->ctx[1]) {
 		cal_err(dev, "Neither port is configured, no point in staying up\n");
 		return -ENODEV;
@@ -1914,7 +2334,22 @@ static int cal_remove(struct platform_device *pdev)
 
 #if defined(CONFIG_OF)
 static const struct of_device_id cal_of_match[] = {
-	{ .compatible = "ti,dra72-cal", },
+	{
+		.compatible = "ti,dra72-cal",
+		.data = (void *)&dra72x_cal_data,
+	},
+	{
+		.compatible = "ti,dra72-pre-es2-cal",
+		.data = (void *)&dra72x_es1_cal_data,
+	},
+	{
+		.compatible = "ti,dra76-cal",
+		.data = (void *)&dra76x_cal_data,
+	},
+	{
+		.compatible = "ti,am654-cal",
+		.data = (void *)&am654_cal_data,
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, cal_of_match);

+ 27 - 0
drivers/media/platform/ti-vpe/cal_regs.h

@@ -13,6 +13,30 @@
 #ifndef __TI_CAL_REGS_H
 #define __TI_CAL_REGS_H
 
+/*
+ * struct cal_dev.flags possibilities
+ *
+ * DRA72_CAL_PRE_ES2_LDO_DISABLE:
+ *   Errata i913: CSI2 LDO Needs to be disabled when module is powered on
+ *
+ *   Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2
+ *   LDOs on the device are disabled if CSI-2 module is powered on
+ *   (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304
+ *   | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high
+ *   current draw on the module supply in active mode.
+ *
+ *   Errata does not apply when CSI-2 module is powered off
+ *   (0x4845 B304 | 0x4845 B384 [28:27] = 0x0).
+ *
+ * SW Workaround:
+ *	Set the following register bits to disable the LDO,
+ *	which is essentially CSI2 REG10 bit 6:
+ *
+ *		Core 0:  0x4845 B828 = 0x0000 0040
+ *		Core 1:  0x4845 B928 = 0x0000 0040
+ */
+#define DRA72_CAL_PRE_ES2_LDO_DISABLE BIT(0)
+
 #define CAL_NUM_CSI2_PORTS		2
 
 /* CAL register offsets */
@@ -74,6 +98,7 @@
 #define CAL_CSI2_PHY_REG0		0x000
 #define CAL_CSI2_PHY_REG1		0x004
 #define CAL_CSI2_PHY_REG2		0x008
+#define CAL_CSI2_PHY_REG10		0x028
 
 /* CAL Control Module Core Camerrx Control register offsets */
 #define CM_CTRL_CORE_CAMERRX_CONTROL	0x000
@@ -461,6 +486,8 @@
 #define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_SUCCESS		0
 #define CAL_CSI2_PHY_REG1_RESET_DONE_STATUS_MASK		GENMASK(29, 28)
 
+#define CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK		BIT_MASK(6)
+
 #define CAL_CSI2_PHY_REG2_CCP2_SYNC_PATTERN_MASK		GENMASK(23, 0)
 #define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC3_MASK		GENMASK(25, 24)
 #define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC2_MASK		GENMASK(27, 26)

+ 4032 - 0
drivers/media/platform/ti-vpe/vip.c

@@ -0,0 +1,4032 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI VIP capture driver
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated -  http://www.ti.com/
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Nikhil Devshatwar, <nikhil.nd@ti.com>
+ * Benoit Parrot, <bparrot@ti.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-async.h>
+
+#include "vip.h"
+
+#define VIP_MODULE_NAME "vip"
+#define VIP_INPUT_NAME "Vin0"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-8)");
+
+/*
+ * Minimum and maximum frame sizes
+ */
+#define MIN_W		128
+#define MIN_H		128
+#define MAX_W		2048
+#define MAX_H		1536
+
+/*
+ * Required alignments
+ */
+#define S_ALIGN		0 /* multiple of 1 */
+#define H_ALIGN		1 /* multiple of 2 */
+#define W_ALIGN		1 /* multiple of 2 */
+#define L_ALIGN		7 /* multiple of 128, line stride, 16 bytes */
+
+/*
+ * Need a descriptor entry for each of up to 15 outputs,
+ * and up to 2 control transfers.
+ */
+#define VIP_DESC_LIST_SIZE	(17 * sizeof(struct vpdma_dtd))
+
+#define vip_dbg(level, dev, fmt, arg...)	\
+		v4l2_dbg(level, debug, dev, fmt, ##arg)
+#define vip_err(dev, fmt, arg...)	\
+		v4l2_err(dev, fmt, ##arg)
+#define vip_info(dev, fmt, arg...)	\
+		v4l2_info(dev, fmt, ##arg)
+
+#define CTRL_CORE_SMA_SW_1      0x534
+/*
+ * The srce_info structure contains per-srce data.
+ */
+struct vip_srce_info {
+	u8	base_channel;	/* the VPDMA channel nummber */
+	u8	vb_index;	/* input frame f, f-1, f-2 index */
+	u8	vb_part;	/* identifies section of co-planar formats */
+};
+
+#define VIP_VPDMA_FIFO_SIZE	2
+#define VIP_DROPQ_SIZE		3
+
+/*
+ * Define indices into the srce_info tables
+ */
+
+#define VIP_SRCE_MULT_PORT		0
+#define VIP_SRCE_MULT_ANC		1
+#define VIP_SRCE_LUMA		2
+#define VIP_SRCE_CHROMA		3
+#define VIP_SRCE_RGB		4
+
+static struct vip_srce_info srce_info[5] = {
+	[VIP_SRCE_MULT_PORT] = {
+		.base_channel	= VIP1_CHAN_NUM_MULT_PORT_A_SRC0,
+		.vb_index	= 0,
+		.vb_part	= VIP_CHROMA,
+	},
+	[VIP_SRCE_MULT_ANC] = {
+		.base_channel	= VIP1_CHAN_NUM_MULT_ANC_A_SRC0,
+		.vb_index	= 0,
+		.vb_part	= VIP_LUMA,
+	},
+	[VIP_SRCE_LUMA] = {
+		.base_channel	= VIP1_CHAN_NUM_PORT_A_LUMA,
+		.vb_index	= 1,
+		.vb_part	= VIP_LUMA,
+	},
+	[VIP_SRCE_CHROMA] = {
+		.base_channel	= VIP1_CHAN_NUM_PORT_A_CHROMA,
+		.vb_index	= 1,
+		.vb_part	= VIP_CHROMA,
+	},
+	[VIP_SRCE_RGB] = {
+		.base_channel	= VIP1_CHAN_NUM_PORT_A_RGB,
+		.vb_part	= VIP_LUMA,
+	},
+};
+
+static struct vip_fmt vip_formats[VIP_MAX_ACTIVE_FMT] = {
+	{
+		.fourcc		= V4L2_PIX_FMT_NV12,
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+		.coplanar	= 1,
+		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420],
+				    &vpdma_yuv_fmts[VPDMA_DATA_FMT_C420],
+				  },
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_UYVY,
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CBY422],
+				  },
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_YUYV,
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCB422],
+				  },
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_VYUY,
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CRY422],
+				  },
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_YVYU,
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCR422],
+				  },
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_RGB24,
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24],
+				  },
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_RGB32,
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32],
+				  },
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_BGR24,
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_BGR24],
+				  },
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_BGR32,
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ABGR32],
+				  },
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_RGB24,
+		.code		= MEDIA_BUS_FMT_RGB888_1X24,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24],
+				  },
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_RGB32,
+		.code		= MEDIA_BUS_FMT_ARGB8888_1X32,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32],
+				  },
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_SBGGR8,
+		.code		= MEDIA_BUS_FMT_SBGGR8_1X8,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8],
+				  },
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_SGBRG8,
+		.code		= MEDIA_BUS_FMT_SGBRG8_1X8,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8],
+				  },
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_SGRBG8,
+		.code		= MEDIA_BUS_FMT_SGRBG8_1X8,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8],
+				  },
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_SRGGB8,
+		.code		= MEDIA_BUS_FMT_SRGGB8_1X8,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8],
+				  },
+	},
+	{
+		/* V4L2 currently only defines one 16 bit variant */
+		.fourcc		= V4L2_PIX_FMT_SBGGR16,
+		.code		= MEDIA_BUS_FMT_SBGGR16_1X16,
+		.colorspace	= V4L2_COLORSPACE_SMPTE170M,
+		.coplanar	= 0,
+		.vpdma_fmt	= { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW16],
+				  },
+	},
+};
+
+/*  Print Four-character-code (FOURCC) */
+static char *fourcc_to_str(u32 fmt)
+{
+	static char code[5];
+
+	code[0] = (unsigned char)(fmt & 0xff);
+	code[1] = (unsigned char)((fmt >> 8) & 0xff);
+	code[2] = (unsigned char)((fmt >> 16) & 0xff);
+	code[3] = (unsigned char)((fmt >> 24) & 0xff);
+	code[4] = '\0';
+
+	return code;
+}
+
+/*
+ * Find our format description corresponding to the passed v4l2_format
+ */
+
+static struct vip_fmt *find_port_format_by_pix(struct vip_port *port,
+					       u32 pixelformat)
+{
+	struct vip_fmt *fmt;
+	unsigned int k;
+
+	for (k = 0; k < port->num_active_fmt; k++) {
+		fmt = port->active_fmt[k];
+		if (fmt->fourcc == pixelformat)
+			return fmt;
+	}
+
+	return NULL;
+}
+
+static struct vip_fmt *find_port_format_by_code(struct vip_port *port,
+						u32 code)
+{
+	struct vip_fmt *fmt;
+	unsigned int k;
+
+	for (k = 0; k < port->num_active_fmt; k++) {
+		fmt = port->active_fmt[k];
+		if (fmt->code == code)
+			return fmt;
+	}
+
+	return NULL;
+}
+
+inline struct vip_port *notifier_to_vip_port(struct v4l2_async_notifier *n)
+{
+	return container_of(n, struct vip_port, notifier);
+}
+
+static bool __maybe_unused vip_is_fmt_yuv(u32 fourcc)
+{
+	if (fourcc == V4L2_PIX_FMT_NV12 ||
+	    fourcc == V4L2_PIX_FMT_UYVY ||
+	    fourcc == V4L2_PIX_FMT_YUYV ||
+	    fourcc == V4L2_PIX_FMT_VYUY ||
+	    fourcc == V4L2_PIX_FMT_YVYU)
+		return true;
+
+	return false;
+}
+
+static bool vip_is_fmt_rgb(u32 fourcc)
+{
+	if (fourcc == V4L2_PIX_FMT_RGB24 ||
+	    fourcc == V4L2_PIX_FMT_BGR24 ||
+	    fourcc == V4L2_PIX_FMT_RGB32 ||
+	    fourcc == V4L2_PIX_FMT_BGR32)
+		return true;
+
+	return false;
+}
+
+static bool __maybe_unused vip_is_mbuscode_yuv(u32 code)
+{
+	return ((code & 0xFF00) == 0x2000);
+}
+
+static bool vip_is_mbuscode_rgb(u32 code)
+{
+	return ((code & 0xFF00) == 0x1000);
+}
+
+static bool vip_is_mbuscode_raw(u32 code)
+{
+	return ((code & 0xFF00) == 0x3000);
+}
+
+static enum  v4l2_colorspace vip_fourcc_to_colorspace(u32 fourcc)
+{
+	if (vip_is_fmt_rgb(fourcc))
+		return V4L2_COLORSPACE_SRGB;
+
+	return V4L2_COLORSPACE_SMPTE170M;
+}
+
+static enum  v4l2_colorspace vip_code_to_colorspace(u32 code)
+{
+	if (vip_is_mbuscode_rgb(code))
+		return V4L2_COLORSPACE_SRGB;
+
+	return V4L2_COLORSPACE_SMPTE170M;
+}
+
+static enum vip_csc_state vip_csc_direction(u32 src_code, u32 dst_fourcc)
+{
+	if (vip_is_mbuscode_yuv(src_code) && vip_is_fmt_rgb(dst_fourcc))
+		return VIP_CSC_Y2R;
+	else if (vip_is_mbuscode_rgb(src_code) && vip_is_fmt_yuv(dst_fourcc))
+		return VIP_CSC_R2Y;
+	else
+		return VIP_CSC_NA;
+}
+
+/*
+ * port flag bits
+ */
+#define FLAG_FRAME_1D		BIT(0)
+#define FLAG_EVEN_LINE_SKIP	BIT(1)
+#define FLAG_ODD_LINE_SKIP	BIT(2)
+#define FLAG_MODE_TILED		BIT(3)
+#define FLAG_INTERLACED		BIT(4)
+#define FLAG_MULTIPLEXED	BIT(5)
+#define FLAG_MULT_PORT		BIT(6)
+#define FLAG_MULT_ANC		BIT(7)
+
+/*
+ * Function prototype declarations
+ */
+static int alloc_port(struct vip_dev *, int);
+static void free_port(struct vip_port *);
+static int vip_setup_parser(struct vip_port *port);
+static int vip_setup_scaler(struct vip_stream *stream);
+static void vip_enable_parser(struct vip_port *port, bool on);
+static void vip_reset_parser(struct vip_port *port, bool on);
+static void vip_parser_stop_imm(struct vip_port *port, bool on);
+static void stop_dma(struct vip_stream *stream, bool clear_list);
+static int vip_load_vpdma_list_fifo(struct vip_stream *stream);
+static inline bool is_scaler_available(struct vip_port *port);
+static inline bool allocate_scaler(struct vip_port *port);
+static inline void free_scaler(struct vip_port *port);
+static bool is_csc_available(struct vip_port *port);
+static bool allocate_csc(struct vip_port *port,
+				enum vip_csc_state csc_direction);
+static void free_csc(struct vip_port *port);
+
+#define reg_read(dev, offset) ioread32(dev->base + offset)
+#define reg_write(dev, offset, val) iowrite32(val, dev->base + offset)
+
+/*
+ * Insert a masked field into a 32-bit field
+ */
+static void insert_field(u32 *valp, u32 field, u32 mask, int shift)
+{
+	u32 val = *valp;
+
+	val &= ~(mask << shift);
+	val |= (field & mask) << shift;
+	*valp = val;
+}
+
+/*
+ * DMA address/data block for the shadow registers
+ */
+struct vip_mmr_adb {
+	struct vpdma_adb_hdr	sc_hdr0;
+	u32			sc_regs0[7];
+	u32			sc_pad0[1];
+	struct vpdma_adb_hdr	sc_hdr8;
+	u32			sc_regs8[6];
+	u32			sc_pad8[2];
+	struct vpdma_adb_hdr	sc_hdr17;
+	u32			sc_regs17[9];
+	u32			sc_pad17[3];
+	struct vpdma_adb_hdr	csc_hdr;
+	u32			csc_regs[6];
+	u32			csc_pad[2];
+};
+
+#define GET_OFFSET_TOP(port, obj, reg)	\
+	((obj)->res->start - port->dev->res->start + reg)
+
+#define VIP_SET_MMR_ADB_HDR(port, hdr, regs, offset_a)	\
+	VPDMA_SET_MMR_ADB_HDR(port->mmr_adb, vip_mmr_adb, hdr, regs, offset_a)
+
+/*
+ * Set the headers for all of the address/data block structures.
+ */
+static void init_adb_hdrs(struct vip_port *port)
+{
+	VIP_SET_MMR_ADB_HDR(port, sc_hdr0, sc_regs0,
+			    GET_OFFSET_TOP(port, port->dev->sc, CFG_SC0));
+	VIP_SET_MMR_ADB_HDR(port, sc_hdr8, sc_regs8,
+			    GET_OFFSET_TOP(port, port->dev->sc, CFG_SC8));
+	VIP_SET_MMR_ADB_HDR(port, sc_hdr17, sc_regs17,
+			    GET_OFFSET_TOP(port, port->dev->sc, CFG_SC17));
+	VIP_SET_MMR_ADB_HDR(port, csc_hdr, csc_regs,
+			    GET_OFFSET_TOP(port, port->dev->csc, CSC_CSC00));
+
+};
+
+/*
+ * These represent the module resets bit for slice 1
+ * Upon detecting slice2 we simply left shift by 1
+ */
+#define VIP_DP_RST	BIT(16)
+#define VIP_PARSER_RST	BIT(18)
+#define VIP_CSC_RST	BIT(20)
+#define VIP_SC_RST	BIT(22)
+#define VIP_DS0_RST	BIT(25)
+#define VIP_DS1_RST	BIT(27)
+
+static void vip_module_reset(struct vip_dev *dev, uint32_t module, bool on)
+{
+	u32 val = 0;
+
+	val = reg_read(dev, VIP_CLK_RESET);
+
+	if (dev->slice_id == VIP_SLICE2)
+		module <<= 1;
+
+	if (on)
+		val |= module;
+	else
+		val &= ~module;
+
+	reg_write(dev, VIP_CLK_RESET, val);
+}
+
+/*
+ * Enable or disable the VIP clocks
+ */
+static void vip_set_clock_enable(struct vip_dev *dev, bool on)
+{
+	u32 val = 0;
+
+	val = reg_read(dev, VIP_CLK_ENABLE);
+	if (on) {
+		val |= VIP_VPDMA_CLK_ENABLE;
+		if (dev->slice_id == VIP_SLICE1)
+			val |= VIP_VIP1_DATA_PATH_CLK_ENABLE;
+		else
+			val |= VIP_VIP2_DATA_PATH_CLK_ENABLE;
+	} else {
+		if (dev->slice_id == VIP_SLICE1)
+			val &= ~VIP_VIP1_DATA_PATH_CLK_ENABLE;
+		else
+			val &= ~VIP_VIP2_DATA_PATH_CLK_ENABLE;
+
+		/* Both VIP are disabled then shutdown VPDMA also */
+		if (!(val & (VIP_VIP1_DATA_PATH_CLK_ENABLE |
+			     VIP_VIP2_DATA_PATH_CLK_ENABLE)))
+			val = 0;
+	}
+
+	reg_write(dev, VIP_CLK_ENABLE, val);
+}
+
+/* This helper function is used to enable the clock early on to
+ * enable vpdma firmware loading before the slice device are created
+ */
+static void vip_shared_set_clock_enable(struct vip_shared *shared, bool on)
+{
+	u32 val = 0;
+
+	if (on)
+		val = VIP_VIP1_DATA_PATH_CLK_ENABLE | VIP_VPDMA_CLK_ENABLE;
+
+	reg_write(shared, VIP_CLK_ENABLE, val);
+}
+
+static void vip_top_reset(struct vip_dev *dev)
+{
+	u32 val = 0;
+
+	val = reg_read(dev, VIP_CLK_RESET);
+
+	if (dev->slice_id == VIP_SLICE1)
+		insert_field(&val, 1, VIP_DATA_PATH_CLK_RESET_MASK,
+			     VIP_VIP1_DATA_PATH_RESET_SHIFT);
+	else
+		insert_field(&val, 1, VIP_DATA_PATH_CLK_RESET_MASK,
+			     VIP_VIP2_DATA_PATH_RESET_SHIFT);
+
+	reg_write(dev, VIP_CLK_RESET, val);
+
+	usleep_range(200, 250);
+
+	val = reg_read(dev, VIP_CLK_RESET);
+
+	if (dev->slice_id == VIP_SLICE1)
+		insert_field(&val, 0, VIP_DATA_PATH_CLK_RESET_MASK,
+			     VIP_VIP1_DATA_PATH_RESET_SHIFT);
+	else
+		insert_field(&val, 0, VIP_DATA_PATH_CLK_RESET_MASK,
+			     VIP_VIP2_DATA_PATH_RESET_SHIFT);
+	reg_write(dev, VIP_CLK_RESET, val);
+}
+
+static void vip_top_vpdma_reset(struct vip_shared *shared)
+{
+	u32 val;
+
+	val = reg_read(shared, VIP_CLK_RESET);
+	insert_field(&val, 1, VIP_VPDMA_CLK_RESET_MASK,
+		     VIP_VPDMA_CLK_RESET_SHIFT);
+	reg_write(shared, VIP_CLK_RESET, val);
+
+	usleep_range(200, 250);
+
+	val = reg_read(shared, VIP_CLK_RESET);
+	insert_field(&val, 0, VIP_VPDMA_CLK_RESET_MASK,
+		     VIP_VPDMA_CLK_RESET_SHIFT);
+	reg_write(shared, VIP_CLK_RESET, val);
+}
+
+static void vip_set_pclk_invert(struct vip_port *port)
+{
+	u32 offset;
+	/*
+	 * When the VIP parser is configured to so that the pixel clock
+	 * is to be sampled at falling edge, the pixel clock needs to be
+	 * inverted before it is given to the VIP module. This is done
+	 * by setting a bit in the CTRL_CORE_SMA_SW1 register.
+	 */
+
+	if (port->dev->instance_id == VIP_INSTANCE1) {
+		offset = 0 + 2 * port->port_id + port->dev->slice_id;
+	} else if (port->dev->instance_id == VIP_INSTANCE2) {
+		offset = 4 + 2 * port->port_id + port->dev->slice_id;
+	} else if (port->dev->instance_id == VIP_INSTANCE3) {
+		offset = 10 - port->dev->slice_id;
+	} else {
+		vip_err(port, "%s: VIP instance id out of range...\n",
+			__func__);
+		return;
+	}
+
+	if (port->dev->syscon_pol)
+		regmap_update_bits(port->dev->syscon_pol,
+				   port->dev->syscon_pol_offset,
+				   1 << offset, 1 << offset);
+}
+
+#define VIP_PARSER_PORT(p)	(VIP_PARSER_PORTA_0 + (p * 0x8U))
+#define VIP_PARSER_EXTRA_PORT(p)	(VIP_PARSER_PORTA_1 + (p * 0x8U))
+#define VIP_PARSER_CROP_H_PORT(p)	(VIP_PARSER_PORTA_EXTRA4 + (p * 0x10U))
+#define VIP_PARSER_CROP_V_PORT(p)	(VIP_PARSER_PORTA_EXTRA5 + (p * 0x10U))
+#define VIP_PARSER_STOP_IMM_PORT(p)	(VIP_PARSER_PORTA_EXTRA6 + (p * 0x4U))
+
+static void vip_set_data_interface(struct vip_port *port,
+				   enum data_interface_modes mode)
+{
+	u32 val = 0;
+
+	insert_field(&val, mode, VIP_DATA_INTERFACE_MODE_MASK,
+		     VIP_DATA_INTERFACE_MODE_SHFT);
+
+	reg_write(port->dev->parser, VIP_PARSER_MAIN_CFG, val);
+}
+
+static void vip_set_slice_path(struct vip_dev *dev,
+			       enum data_path_select data_path, u32 path_val)
+{
+	u32 val = 0;
+	int data_path_reg;
+
+	vip_dbg(3, dev, "%s:\n", __func__);
+
+	data_path_reg = VIP_VIP1_DATA_PATH_SELECT + 4 * dev->slice_id;
+
+	switch (data_path) {
+	case ALL_FIELDS_DATA_SELECT:
+		val |= path_val;
+		break;
+	case VIP_CSC_SRC_DATA_SELECT:
+		insert_field(&val, path_val, VIP_CSC_SRC_SELECT_MASK,
+			     VIP_CSC_SRC_SELECT_SHFT);
+		break;
+	case VIP_SC_SRC_DATA_SELECT:
+		insert_field(&val, path_val, VIP_SC_SRC_SELECT_MASK,
+			     VIP_SC_SRC_SELECT_SHFT);
+		break;
+	case VIP_RGB_SRC_DATA_SELECT:
+		val |= (path_val) ? VIP_RGB_SRC_SELECT : 0;
+		break;
+	case VIP_RGB_OUT_LO_DATA_SELECT:
+		val |= (path_val) ? VIP_RGB_OUT_LO_SRC_SELECT : 0;
+		break;
+	case VIP_RGB_OUT_HI_DATA_SELECT:
+		val |= (path_val) ? VIP_RGB_OUT_HI_SRC_SELECT : 0;
+		break;
+	case VIP_CHR_DS_1_SRC_DATA_SELECT:
+		insert_field(&val, path_val, VIP_DS1_SRC_SELECT_MASK,
+			     VIP_DS1_SRC_SELECT_SHFT);
+		break;
+	case VIP_CHR_DS_2_SRC_DATA_SELECT:
+		insert_field(&val, path_val, VIP_DS2_SRC_SELECT_MASK,
+			     VIP_DS2_SRC_SELECT_SHFT);
+		break;
+	case VIP_MULTI_CHANNEL_DATA_SELECT:
+		val |= (path_val) ? VIP_MULTI_CHANNEL_SELECT : 0;
+		break;
+	case VIP_CHR_DS_1_DATA_BYPASS:
+		val |= (path_val) ? VIP_DS1_BYPASS : 0;
+		break;
+	case VIP_CHR_DS_2_DATA_BYPASS:
+		val |= (path_val) ? VIP_DS2_BYPASS : 0;
+		break;
+	default:
+		vip_err(dev, "%s: data_path 0x%x is not valid\n",
+			__func__, data_path);
+		return;
+	}
+	insert_field(&val, data_path, VIP_DATAPATH_SELECT_MASK,
+		     VIP_DATAPATH_SELECT_SHFT);
+	reg_write(dev, data_path_reg, val);
+	vip_dbg(3, dev, "%s: DATA_PATH_SELECT(%08X): %08X\n", __func__,
+		data_path_reg, reg_read(dev, data_path_reg));
+}
+
+/*
+ * Return the vip_stream structure for a given struct file
+ */
+static inline struct vip_stream *file2stream(struct file *file)
+{
+	return video_drvdata(file);
+}
+
+/*
+ * Append a destination descriptor to the current descriptor list,
+ * setting up dma to the given srce.
+ */
+static int add_out_dtd(struct vip_stream *stream, int srce_type)
+{
+	struct vip_port *port = stream->port;
+	struct vip_dev *dev = port->dev;
+	struct vip_srce_info *sinfo = &srce_info[srce_type];
+	struct v4l2_rect *c_rect = &port->c_rect;
+	struct vip_fmt *fmt = port->fmt;
+	int channel, plane = 0;
+	int max_width, max_height;
+	dma_addr_t dma_addr;
+	u32 flags;
+	u32 width = stream->width;
+
+	channel = sinfo->base_channel;
+
+	switch (srce_type) {
+	case VIP_SRCE_MULT_PORT:
+	case VIP_SRCE_MULT_ANC:
+		if (port->port_id == VIP_PORTB)
+			channel += VIP_CHAN_MULT_PORTB_OFFSET;
+		channel += stream->stream_id;
+		flags = 0;
+		break;
+	case VIP_SRCE_CHROMA:
+		plane = 1;
+	case VIP_SRCE_LUMA:
+		if (port->port_id == VIP_PORTB) {
+			if (port->scaler && !port->fmt->coplanar)
+				/*
+				 * In this case Port A Chroma channel
+				 * is used to carry Port B scaled YUV422
+				 */
+				channel += 1;
+			else
+				channel += VIP_CHAN_YUV_PORTB_OFFSET;
+		}
+		flags = port->flags;
+		break;
+	case VIP_SRCE_RGB:
+		if ((port->port_id == VIP_PORTB) ||
+		    ((port->port_id == VIP_PORTA) &&
+		     (port->csc == VIP_CSC_NA) &&
+		     vip_is_fmt_rgb(port->fmt->fourcc)))
+			/*
+			 * RGB sensor only connect to Y_LO
+			 * channel i.e. port B channel.
+			 */
+			channel += VIP_CHAN_RGB_PORTB_OFFSET;
+		flags = port->flags;
+		break;
+	default:
+		vip_err(stream, "%s: srce_type 0x%x is not valid\n",
+			__func__, srce_type);
+		return -1;
+	}
+
+	if (dev->slice_id == VIP_SLICE2)
+		channel += VIP_CHAN_VIP2_OFFSET;
+
+	/* This is just for initialization purposes.
+	 * The actual dma_addr will be configured in vpdma_update_dma_addr
+	 */
+	dma_addr = 0;
+
+	if (port->fmt->vpdma_fmt[0] == &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8]) {
+		/*
+		 * Special case since we are faking a YUV422 16bit format
+		 * to have the vpdma perform the needed byte swap
+		 * we need to adjust the pixel width accordingly
+		 * otherwise the parser will attempt to collect more pixels
+		 * then available and the vpdma transfer will exceed the
+		 * allocated frame buffer.
+		 */
+		width >>= 1;
+		vip_dbg(1, stream, "%s: 8 bit raw detected, adjusting width to %d\n",
+			__func__, width);
+	}
+
+	/*
+	 * Use VPDMA_MAX_SIZE1 or VPDMA_MAX_SIZE2 register for slice0/1
+	 */
+
+	if (dev->slice_id == VIP_SLICE1) {
+		vpdma_set_max_size(dev->shared->vpdma, VPDMA_MAX_SIZE1,
+				   width, stream->height);
+
+		max_width = MAX_OUT_WIDTH_REG1;
+		max_height = MAX_OUT_HEIGHT_REG1;
+	} else {
+		vpdma_set_max_size(dev->shared->vpdma, VPDMA_MAX_SIZE2,
+				   width, stream->height);
+
+		max_width = MAX_OUT_WIDTH_REG2;
+		max_height = MAX_OUT_HEIGHT_REG2;
+	}
+
+	/*
+	 * Mark this channel to be cleared while cleaning up resources
+	 * This will make sure that an abort descriptor for this channel
+	 * would be submitted to VPDMA causing any ongoing  transaction to be
+	 * aborted and cleanup the VPDMA FSM for this channel
+	 */
+	stream->vpdma_channels[channel] = 1;
+
+	vpdma_rawchan_add_out_dtd(&stream->desc_list, c_rect->width,
+				  stream->bytesperline, c_rect,
+				  fmt->vpdma_fmt[plane], dma_addr,
+				  max_width, max_height, channel, flags);
+
+	return 0;
+}
+
+/*
+ * add_stream_dtds - prepares and starts DMA for pending transfers
+ */
+static void add_stream_dtds(struct vip_stream *stream)
+{
+	struct vip_port *port = stream->port;
+	int srce_type;
+
+	if (port->flags & FLAG_MULT_PORT)
+		srce_type = VIP_SRCE_MULT_PORT;
+	else if (port->flags & FLAG_MULT_ANC)
+		srce_type = VIP_SRCE_MULT_ANC;
+	else if (vip_is_fmt_rgb(port->fmt->fourcc))
+		srce_type = VIP_SRCE_RGB;
+	else
+		srce_type = VIP_SRCE_LUMA;
+
+	add_out_dtd(stream, srce_type);
+
+	if (srce_type == VIP_SRCE_LUMA && port->fmt->coplanar)
+		add_out_dtd(stream, VIP_SRCE_CHROMA);
+}
+
+#define PARSER_IRQ_MASK (VIP_PORTA_OUTPUT_FIFO_YUV | \
+			 VIP_PORTB_OUTPUT_FIFO_YUV)
+
+static void enable_irqs(struct vip_dev *dev, int irq_num, int list_num)
+{
+	struct vip_parser_data *parser = dev->parser;
+	u32 reg_addr = VIP_INT0_ENABLE0_SET +
+			VIP_INTC_INTX_OFFSET * irq_num;
+	u32 irq_val = (1 << (list_num * 2)) |
+		      (VIP_VIP1_PARSER_INT << (irq_num * 1));
+
+	/* Enable Parser Interrupt */
+	reg_write(parser, VIP_PARSER_FIQ_MASK, ~PARSER_IRQ_MASK);
+
+	reg_write(dev->shared, reg_addr, irq_val);
+
+	vpdma_enable_list_complete_irq(dev->shared->vpdma,
+				       irq_num, list_num, true);
+}
+
+static void disable_irqs(struct vip_dev *dev, int irq_num, int list_num)
+{
+	struct vip_parser_data *parser = dev->parser;
+	u32 reg_addr = VIP_INT0_ENABLE0_CLR +
+			VIP_INTC_INTX_OFFSET * irq_num;
+	u32 irq_val = (1 << (list_num * 2)) |
+		      (VIP_VIP1_PARSER_INT << (irq_num * 1));
+
+	/* Disable all Parser Interrupt */
+	reg_write(parser, VIP_PARSER_FIQ_MASK, 0xffffffff);
+
+	reg_write(dev->shared, reg_addr, irq_val);
+
+	vpdma_enable_list_complete_irq(dev->shared->vpdma,
+				       irq_num, list_num, false);
+}
+
+static void clear_irqs(struct vip_dev *dev, int irq_num, int list_num)
+{
+	struct vip_parser_data *parser = dev->parser;
+	u32 reg_addr = VIP_INT0_STATUS0_CLR +
+			VIP_INTC_INTX_OFFSET * irq_num;
+	u32 irq_val = (1 << (list_num * 2)) |
+		      (VIP_VIP1_PARSER_INT << (irq_num * 1));
+
+	/* Clear all Parser Interrupt */
+	reg_write(parser, VIP_PARSER_FIQ_CLR, 0xffffffff);
+	reg_write(parser, VIP_PARSER_FIQ_CLR, 0x0);
+
+	reg_write(dev->shared, reg_addr, irq_val);
+
+	vpdma_clear_list_stat(dev->shared->vpdma, irq_num, dev->slice_id);
+}
+
+static void populate_desc_list(struct vip_stream *stream)
+{
+	struct vip_port *port = stream->port;
+	struct vip_dev *dev = port->dev;
+	unsigned int list_length;
+
+	stream->desc_next = stream->desc_list.buf.addr;
+	add_stream_dtds(stream);
+
+	list_length = stream->desc_next - stream->desc_list.buf.addr;
+	vpdma_map_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
+}
+
+/*
+ * start_dma - adds descriptors to the dma list and submits them.
+ * Should be called after a new vb is queued and on a vpdma list
+ * completion interrupt.
+ */
+static void start_dma(struct vip_stream *stream, struct vip_buffer *buf)
+{
+	struct vip_dev *dev = stream->port->dev;
+	struct vpdma_data *vpdma = dev->shared->vpdma;
+	int list_num = stream->list_num;
+	dma_addr_t dma_addr;
+	int drop_data;
+
+	if (vpdma_list_busy(vpdma, list_num)) {
+		vip_err(stream, "vpdma list busy, cannot post\n");
+		return;				/* nothing to do */
+	}
+
+	if (buf) {
+		dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+		drop_data = 0;
+		vip_dbg(4, stream, "%s: vb2 buf idx:%d, dma_addr:%pad\n",
+			__func__, buf->vb.vb2_buf.index, &dma_addr);
+	} else {
+		dma_addr = 0;
+		drop_data = 1;
+		vip_dbg(4, stream, "%s: dropped\n", __func__);
+	}
+
+	vpdma_update_dma_addr(dev->shared->vpdma, &stream->desc_list,
+			      dma_addr, stream->write_desc, drop_data, 0);
+
+	if (stream->port->fmt->coplanar) {
+		dma_addr += stream->bytesperline * stream->height;
+		vpdma_update_dma_addr(dev->shared->vpdma, &stream->desc_list,
+				      dma_addr, stream->write_desc + 1,
+				      drop_data, 1);
+	}
+
+	vpdma_submit_descs(dev->shared->vpdma,
+			   &stream->desc_list, stream->list_num);
+}
+
+static void vip_schedule_next_buffer(struct vip_stream *stream)
+{
+	struct vip_dev *dev = stream->port->dev;
+	struct vip_buffer *buf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->slock, flags);
+	if (list_empty(&stream->vidq)) {
+		vip_dbg(4, stream, "Dropping frame\n");
+		if (list_empty(&stream->dropq)) {
+			vip_err(stream, "No dropq buffer left!");
+			spin_unlock_irqrestore(&dev->slock, flags);
+			return;
+		}
+		buf = list_entry(stream->dropq.next,
+				 struct vip_buffer, list);
+
+		buf->drop = true;
+		list_move_tail(&buf->list, &stream->post_bufs);
+		buf = NULL;
+	} else {
+		buf = list_entry(stream->vidq.next,
+				 struct vip_buffer, list);
+		buf->drop = false;
+		list_move_tail(&buf->list, &stream->post_bufs);
+		vip_dbg(4, stream, "added next buffer\n");
+	}
+
+	spin_unlock_irqrestore(&dev->slock, flags);
+	start_dma(stream, buf);
+}
+
+static void vip_process_buffer_complete(struct vip_stream *stream)
+{
+	struct vip_dev *dev = stream->port->dev;
+	struct vip_buffer *buf;
+	struct vb2_v4l2_buffer *vb = NULL;
+	unsigned long flags, fld;
+
+	buf = list_first_entry(&stream->post_bufs, struct vip_buffer, list);
+
+	if (stream->port->flags & FLAG_INTERLACED) {
+		vpdma_unmap_desc_buf(dev->shared->vpdma,
+				     &stream->desc_list.buf);
+
+		fld = dtd_get_field(stream->write_desc);
+		stream->field = fld ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP;
+
+		vpdma_map_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
+	}
+
+	if (buf) {
+		vip_dbg(4, stream, "vip buffer complete 0x%x, 0x%x\n",
+			(unsigned int)buf, buf->drop);
+
+		vb = &buf->vb;
+		vb->field = stream->field;
+		vb->sequence = stream->sequence;
+		vb->vb2_buf.timestamp = ktime_get_ns();
+
+		if (buf->drop) {
+			spin_lock_irqsave(&dev->slock, flags);
+			list_move_tail(&buf->list, &stream->dropq);
+			spin_unlock_irqrestore(&dev->slock, flags);
+		} else {
+			spin_lock_irqsave(&dev->slock, flags);
+			list_del(&buf->list);
+			spin_unlock_irqrestore(&dev->slock, flags);
+			vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE);
+		}
+	} else {
+		vip_err(stream, "%s: buf is null!!!\n", __func__);
+		return;
+	}
+
+	stream->sequence++;
+}
+
+static int vip_reset_vpdma(struct vip_stream *stream)
+{
+	struct vip_port *port = stream->port;
+	struct vip_dev *dev = port->dev;
+	struct vip_buffer *buf;
+	unsigned long flags;
+
+	stop_dma(stream, false);
+
+	spin_lock_irqsave(&dev->slock, flags);
+	/* requeue all active buffers in the opposite order */
+	while (!list_empty(&stream->post_bufs)) {
+		buf = list_last_entry(&stream->post_bufs,
+				      struct vip_buffer, list);
+		list_del(&buf->list);
+		if (buf->drop == 1) {
+			list_add_tail(&buf->list, &stream->dropq);
+			vip_dbg(4, stream, "requeueing drop buffer on dropq\n");
+		} else {
+			list_add(&buf->list, &stream->vidq);
+			vip_dbg(4, stream, "requeueing vb2 buf idx:%d on vidq\n",
+				buf->vb.vb2_buf.index);
+		}
+	}
+	spin_unlock_irqrestore(&dev->slock, flags);
+
+	/* Make sure the desc_list is unmapped */
+	vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
+
+	return 0;
+}
+
+static void vip_overflow_recovery_work(struct work_struct *work)
+{
+	struct vip_stream *stream = container_of(work, struct vip_stream,
+						 recovery_work);
+	struct vip_port *port = stream->port;
+	struct vip_dev *dev = port->dev;
+
+	vip_err(stream, "%s: Port %c\n", __func__,
+		port->port_id == VIP_PORTA ? 'A' : 'B');
+
+	disable_irqs(dev, dev->slice_id, stream->list_num);
+	clear_irqs(dev, dev->slice_id, stream->list_num);
+
+	/* 1.	Set VIP_XTRA6_PORT_A[31:16] YUV_SRCNUM_STOP_IMMEDIATELY */
+	/* 2.	Set VIP_XTRA6_PORT_A[15:0] ANC_SRCNUM_STOP_IMMEDIATELY */
+	vip_parser_stop_imm(port, 1);
+
+	/* 3.	Clear VIP_PORT_A[8] ENABLE */
+	/*
+	 * 4.	Set VIP_PORT_A[7] CLR_ASYNC_FIFO_RD
+	 *      Set VIP_PORT_A[6] CLR_ASYNC_FIFO_WR
+	 */
+	vip_enable_parser(port, false);
+
+	/* 5.	Set VIP_PORT_A[23] SW_RESET */
+	vip_reset_parser(port, 1);
+
+	/*
+	 * 6.	Reset other VIP modules
+	 *	For each module used downstream of VIP_PARSER, write 1 to the
+	 *      bit location of the VIP_CLKC_RST register which is connected
+	 *      to VIP_PARSER
+	 */
+	vip_module_reset(dev, VIP_DP_RST, true);
+
+	usleep_range(200, 250);
+
+	/*
+	 * 7.	Abort VPDMA channels
+	 *	Write to list attribute to stop list 0
+	 *	Write to list address register location of abort list
+	 *	Write to list attribute register list 0 and size of abort list
+	 */
+	vip_reset_vpdma(stream);
+
+	/* 8.	Clear VIP_PORT_A[23] SW_RESET */
+	vip_reset_parser(port, 0);
+
+	/*
+	 * 9.	Un-reset other VIP modules
+	 *	For each module used downstream of VIP_PARSER, write 0 to
+	 *	the bit location of the VIP_CLKC_RST register which is
+	 *	connected to VIP_PARSER
+	 */
+	vip_module_reset(dev, VIP_DP_RST, false);
+
+	/* 10.	(Delay) */
+	/* 11.	SC coeff downloaded (if VIP_SCALER is being used) */
+	vip_setup_scaler(stream);
+
+	/* 12.	(Delay) */
+		/* the above are not needed here yet */
+
+	populate_desc_list(stream);
+	stream->num_recovery++;
+	if (stream->num_recovery < 5) {
+		/* Reload the vpdma */
+		vip_load_vpdma_list_fifo(stream);
+
+		enable_irqs(dev, dev->slice_id, stream->list_num);
+		vip_schedule_next_buffer(stream);
+
+		/* 13.	Clear VIP_XTRA6_PORT_A[31:16] YUV_SRCNUM_STOP_IMM */
+		/* 14.	Clear VIP_XTRA6_PORT_A[15:0] ANC_SRCNUM_STOP_IMM */
+
+		vip_parser_stop_imm(port, 0);
+
+		/* 15.	Set VIP_PORT_A[8] ENABLE */
+		/*
+		 * 16.	Clear VIP_PORT_A[7] CLR_ASYNC_FIFO_RD
+		 *	Clear VIP_PORT_A[6] CLR_ASYNC_FIFO_WR
+		 */
+		vip_enable_parser(port, true);
+	} else {
+		vip_err(stream, "%s: num_recovery limit exceeded leaving disabled\n",
+			__func__);
+	}
+}
+
+static void handle_parser_irqs(struct vip_dev *dev)
+{
+	struct vip_parser_data *parser = dev->parser;
+	struct vip_port *porta = dev->ports[VIP_PORTA];
+	struct vip_port *portb = dev->ports[VIP_PORTB];
+	struct vip_stream *stream = NULL;
+	u32 irq_stat = reg_read(parser, VIP_PARSER_FIQ_STATUS);
+	int i;
+
+	vip_dbg(3, dev, "%s: FIQ_STATUS: 0x%08x\n", __func__, irq_stat);
+
+	/* Clear all Parser Interrupt */
+	reg_write(parser, VIP_PARSER_FIQ_CLR, irq_stat);
+	reg_write(parser, VIP_PARSER_FIQ_CLR, 0x0);
+
+	if (irq_stat & VIP_PORTA_VDET)
+		vip_dbg(3, dev, "VIP_PORTA_VDET\n");
+	if (irq_stat & VIP_PORTB_VDET)
+		vip_dbg(3, dev, "VIP_PORTB_VDET\n");
+	if (irq_stat & VIP_PORTA_ASYNC_FIFO_OF)
+		vip_err(dev, "VIP_PORTA_ASYNC_FIFO_OF\n");
+	if (irq_stat & VIP_PORTB_ASYNC_FIFO_OF)
+		vip_err(dev, "VIP_PORTB_ASYNC_FIFO_OF\n");
+	if (irq_stat & VIP_PORTA_OUTPUT_FIFO_YUV)
+		vip_err(dev, "VIP_PORTA_OUTPUT_FIFO_YUV\n");
+	if (irq_stat & VIP_PORTA_OUTPUT_FIFO_ANC)
+		vip_err(dev, "VIP_PORTA_OUTPUT_FIFO_ANC\n");
+	if (irq_stat & VIP_PORTB_OUTPUT_FIFO_YUV)
+		vip_err(dev, "VIP_PORTB_OUTPUT_FIFO_YUV\n");
+	if (irq_stat & VIP_PORTB_OUTPUT_FIFO_ANC)
+		vip_err(dev, "VIP_PORTB_OUTPUT_FIFO_ANC\n");
+	if (irq_stat & VIP_PORTA_CONN)
+		vip_dbg(3, dev, "VIP_PORTA_CONN\n");
+	if (irq_stat & VIP_PORTA_DISCONN)
+		vip_dbg(3, dev, "VIP_PORTA_DISCONN\n");
+	if (irq_stat & VIP_PORTB_CONN)
+		vip_dbg(3, dev, "VIP_PORTB_CONN\n");
+	if (irq_stat & VIP_PORTB_DISCONN)
+		vip_dbg(3, dev, "VIP_PORTB_DISCONN\n");
+	if (irq_stat & VIP_PORTA_SRC0_SIZE)
+		vip_dbg(3, dev, "VIP_PORTA_SRC0_SIZE\n");
+	if (irq_stat & VIP_PORTB_SRC0_SIZE)
+		vip_dbg(3, dev, "VIP_PORTB_SRC0_SIZE\n");
+	if (irq_stat & VIP_PORTA_YUV_PROTO_VIOLATION)
+		vip_dbg(3, dev, "VIP_PORTA_YUV_PROTO_VIOLATION\n");
+	if (irq_stat & VIP_PORTA_ANC_PROTO_VIOLATION)
+		vip_dbg(3, dev, "VIP_PORTA_ANC_PROTO_VIOLATION\n");
+	if (irq_stat & VIP_PORTB_YUV_PROTO_VIOLATION)
+		vip_dbg(3, dev, "VIP_PORTB_YUV_PROTO_VIOLATION\n");
+	if (irq_stat & VIP_PORTB_ANC_PROTO_VIOLATION)
+		vip_dbg(3, dev, "VIP_PORTB_ANC_PROTO_VIOLATION\n");
+	if (irq_stat & VIP_PORTA_CFG_DISABLE_COMPLETE)
+		vip_dbg(3, dev, "VIP_PORTA_CFG_DISABLE_COMPLETE\n");
+	if (irq_stat & VIP_PORTB_CFG_DISABLE_COMPLETE)
+		vip_dbg(3, dev, "VIP_PORTB_CFG_DISABLE_COMPLETE\n");
+
+	if (irq_stat & (VIP_PORTA_ASYNC_FIFO_OF |
+			VIP_PORTA_OUTPUT_FIFO_YUV |
+			VIP_PORTA_OUTPUT_FIFO_ANC)) {
+		for (i = 0; i < VIP_CAP_STREAMS_PER_PORT; i++) {
+			if (porta->cap_streams[i] &&
+			    porta->cap_streams[i]->port->port_id ==
+			    porta->port_id) {
+				stream = porta->cap_streams[i];
+				break;
+			}
+		}
+		if (stream) {
+			disable_irqs(dev, dev->slice_id,
+				     stream->list_num);
+			schedule_work(&stream->recovery_work);
+			return;
+		}
+	}
+	if (irq_stat & (VIP_PORTB_ASYNC_FIFO_OF |
+			VIP_PORTB_OUTPUT_FIFO_YUV |
+			VIP_PORTB_OUTPUT_FIFO_ANC)) {
+		for (i = 0; i < VIP_CAP_STREAMS_PER_PORT; i++) {
+			if (portb->cap_streams[i] &&
+			    portb->cap_streams[i]->port->port_id ==
+			    portb->port_id) {
+				stream = portb->cap_streams[i];
+				break;
+			}
+		}
+		if (stream) {
+			disable_irqs(dev, dev->slice_id,
+				     stream->list_num);
+			schedule_work(&stream->recovery_work);
+			return;
+		}
+	}
+}
+
+static irqreturn_t vip_irq(int irq_vip, void *data)
+{
+	struct vip_dev *dev = (struct vip_dev *)data;
+	struct vpdma_data *vpdma;
+	struct vip_stream *stream;
+	int list_num;
+	int irq_num = dev->slice_id;
+	u32 irqst, irqst_saved, reg_addr;
+
+	if (!dev->shared)
+		return IRQ_HANDLED;
+
+	vpdma = dev->shared->vpdma;
+	reg_addr = VIP_INT0_STATUS0 +
+			VIP_INTC_INTX_OFFSET * irq_num;
+	irqst_saved = reg_read(dev->shared, reg_addr);
+	irqst = irqst_saved;
+
+	vip_dbg(8, dev, "IRQ %d VIP_INT%d_STATUS0 0x%x\n",
+		irq_vip, irq_num, irqst);
+	if (irqst) {
+		if (irqst & (VIP_VIP1_PARSER_INT << (irq_num * 1))) {
+			irqst &= ~(VIP_VIP1_PARSER_INT << (irq_num * 1));
+			handle_parser_irqs(dev);
+		}
+
+		for (list_num = 0; irqst && (list_num < 8);  list_num++) {
+			/* Check for LIST_COMPLETE IRQ */
+			if (!(irqst & (1 << list_num * 2)))
+				continue;
+
+			vip_dbg(8, dev, "IRQ %d: handling LIST%d_COMPLETE\n",
+				irq_num, list_num);
+
+			stream = vpdma_hwlist_get_priv(vpdma, list_num);
+			if (!stream || stream->list_num != list_num) {
+				vip_err(dev, "IRQ occurred for unused list");
+				continue;
+			}
+
+			vpdma_clear_list_stat(vpdma, irq_num, list_num);
+
+			vip_process_buffer_complete(stream);
+
+			vip_schedule_next_buffer(stream);
+
+			irqst &= ~((1 << list_num * 2));
+		}
+	}
+
+	/* Acknowledge that we are done with all interrupts */
+	reg_write(dev->shared, VIP_INTC_E0I, 1 << irq_num);
+
+	/* Clear handled events from status register */
+	reg_addr = VIP_INT0_STATUS0_CLR +
+		   VIP_INTC_INTX_OFFSET * irq_num;
+	reg_write(dev->shared, reg_addr, irqst_saved);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * video ioctls
+ */
+static int vip_querycap(struct file *file, void *priv,
+			struct v4l2_capability *cap)
+{
+	struct vip_stream *stream = file2stream(file);
+	struct vip_port *port = stream->port;
+	struct vip_dev *dev = port->dev;
+	u32 vin_id = 1 + ((dev->instance_id - 1) * 2) + dev->slice_id;
+
+	strlcpy(cap->driver, VIP_MODULE_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, VIP_MODULE_NAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "platform:vip%1d:vin%1d%c:stream%1d", dev->instance_id, vin_id,
+		 port->port_id == VIP_PORTA ? 'a' : 'b', stream->stream_id);
+	cap->device_caps  = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
+			    V4L2_CAP_READWRITE;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+	return 0;
+}
+
+static int vip_enuminput(struct file *file, void *priv,
+			 struct v4l2_input *inp)
+{
+	struct vip_stream *stream = file2stream(file);
+
+	if (inp->index)
+		return -EINVAL;
+
+	inp->type = V4L2_INPUT_TYPE_CAMERA;
+	inp->std = stream->vfd->tvnorms;
+	sprintf(inp->name, "camera %u", stream->vfd->num);
+
+	return 0;
+}
+
+static int vip_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int vip_s_input(struct file *file, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vip_querystd(struct file *file, void *fh, v4l2_std_id *std)
+{
+	struct vip_stream *stream = file2stream(file);
+	struct vip_port *port = stream->port;
+
+	*std = stream->vfd->tvnorms;
+	v4l2_subdev_call(port->subdev, video, querystd, std);
+	vip_dbg(1, stream, "querystd: 0x%lx\n", (unsigned long)*std);
+	return 0;
+}
+
+static int vip_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+	struct vip_stream *stream = file2stream(file);
+	struct vip_port *port = stream->port;
+
+	*std = stream->vfd->tvnorms;
+	v4l2_subdev_call(port->subdev, video, g_std_output, std);
+	vip_dbg(1, stream, "g_std: 0x%lx\n", (unsigned long)*std);
+
+	return 0;
+}
+
+static int vip_s_std(struct file *file, void *fh, v4l2_std_id std)
+{
+	struct vip_stream *stream = file2stream(file);
+	struct vip_port *port = stream->port;
+
+	vip_dbg(1, stream, "s_std: 0x%lx\n", (unsigned long)std);
+
+	if (!(std & stream->vfd->tvnorms)) {
+		vip_dbg(1, stream, "s_std after check: 0x%lx\n",
+			(unsigned long)std);
+		return -EINVAL;
+	}
+
+	v4l2_subdev_call(port->subdev, video, s_std_output, std);
+	return 0;
+}
+
+static int vip_enum_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_fmtdesc *f)
+{
+	struct vip_stream *stream = file2stream(file);
+	struct vip_port *port = stream->port;
+	struct vip_fmt *fmt;
+
+	vip_dbg(3, stream, "enum_fmt index:%d\n", f->index);
+	if (f->index >= port->num_active_fmt)
+		return -EINVAL;
+
+	fmt = port->active_fmt[f->index];
+
+	f->pixelformat = fmt->fourcc;
+	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	vip_dbg(3, stream, "enum_fmt fourcc:%s\n",
+		fourcc_to_str(f->pixelformat));
+
+	return 0;
+}
+
+static int vip_enum_framesizes(struct file *file, void *priv,
+			       struct v4l2_frmsizeenum *f)
+{
+	struct vip_stream *stream = file2stream(file);
+	struct vip_port *port = stream->port;
+	struct vip_fmt *fmt;
+	struct v4l2_subdev_frame_size_enum fse;
+	int ret;
+
+	fmt = find_port_format_by_pix(port, f->pixel_format);
+	if (!fmt)
+		return -EINVAL;
+
+	fse.index = f->index;
+	fse.pad = 0;
+	fse.code = fmt->code;
+
+	ret = v4l2_subdev_call(port->subdev, pad, enum_frame_size, NULL, &fse);
+	if (ret)
+		return -EINVAL;
+
+	vip_dbg(1, stream, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
+		__func__, fse.index, fse.code, fse.min_width, fse.max_width,
+		fse.min_height, fse.max_height);
+
+	f->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+	f->discrete.width = fse.max_width;
+	f->discrete.height = fse.max_height;
+
+	return 0;
+}
+
+static int vip_enum_frameintervals(struct file *file, void *priv,
+				   struct v4l2_frmivalenum *f)
+{
+	struct vip_stream *stream = file2stream(file);
+	struct vip_port *port = stream->port;
+	struct vip_fmt *fmt;
+	struct v4l2_subdev_frame_interval_enum fie = {
+		.index = f->index,
+		.width = f->width,
+		.height = f->height,
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+	};
+	int ret;
+
+	fmt = find_port_format_by_pix(port, f->pixel_format);
+	if (!fmt)
+		return -EINVAL;
+
+	fie.code = fmt->code;
+	ret = v4l2_subdev_call(port->subdev, pad, enum_frame_interval,
+			       NULL, &fie);
+	if (ret)
+		return ret;
+	f->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+	f->discrete = fie.interval;
+
+	return 0;
+}
+
+static int vip_g_parm(struct file *file, void *priv,
+		      struct v4l2_streamparm *parm)
+{
+	if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	parm->parm.capture.capability   = V4L2_CAP_TIMEPERFRAME;
+	parm->parm.capture.timeperframe.numerator = 1;
+	parm->parm.capture.timeperframe.denominator = 30;
+	parm->parm.capture.readbuffers  = 4;
+	return 0;
+}
+
+static int vip_s_parm(struct file *file, void *priv,
+		      struct v4l2_streamparm *parm)
+{
+	if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	parm->parm.capture.timeperframe.numerator = 1;
+	parm->parm.capture.timeperframe.denominator = 30;
+	parm->parm.capture.readbuffers  = 4;
+
+	return 0;
+}
+
+static int vip_calc_format_size(struct vip_port *port,
+				struct vip_fmt *fmt,
+				struct v4l2_format *f)
+{
+	enum v4l2_field *field;
+	unsigned int stride;
+
+	if (!fmt) {
+		vip_dbg(2, port,
+			"no vip_fmt format provided!\n");
+		return -EINVAL;
+	}
+
+	field = &f->fmt.pix.field;
+	if (*field == V4L2_FIELD_ANY)
+		*field = V4L2_FIELD_NONE;
+	else if (V4L2_FIELD_NONE != *field && V4L2_FIELD_ALTERNATE != *field)
+		return -EINVAL;
+
+	v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W, W_ALIGN,
+			      &f->fmt.pix.height, MIN_H, MAX_H, H_ALIGN,
+			      S_ALIGN);
+
+	stride = f->fmt.pix.width * (fmt->vpdma_fmt[0]->depth >> 3);
+	if (stride > f->fmt.pix.bytesperline)
+		f->fmt.pix.bytesperline = stride;
+	f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline,
+					VPDMA_STRIDE_ALIGN);
+
+	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+	if (fmt->coplanar) {
+		f->fmt.pix.sizeimage += f->fmt.pix.height *
+					f->fmt.pix.bytesperline *
+					fmt->vpdma_fmt[VIP_CHROMA]->depth >> 3;
+	}
+
+	f->fmt.pix.colorspace = fmt->colorspace;
+	f->fmt.pix.priv = 0;
+
+	vip_dbg(3, port, "calc_format_size: fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
+		fourcc_to_str(f->fmt.pix.pixelformat),
+		f->fmt.pix.width, f->fmt.pix.height,
+		f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+	return 0;
+}
+
+static inline bool vip_is_size_dma_aligned(u32 bpp, u32 width)
+{
+	return ((width * bpp) == ALIGN(width * bpp, VPDMA_STRIDE_ALIGN));
+}
+
+static int vip_try_fmt_vid_cap(struct file *file, void *priv,
+			       struct v4l2_format *f)
+{
+	struct vip_stream *stream = file2stream(file);
+	struct vip_port *port = stream->port;
+	struct v4l2_subdev_frame_size_enum fse;
+	struct vip_fmt *fmt;
+	u32 best_width, best_height, largest_width, largest_height;
+	int ret, found;
+	enum vip_csc_state csc_direction;
+
+	vip_dbg(3, stream, "try_fmt fourcc:%s size: %dx%d\n",
+		fourcc_to_str(f->fmt.pix.pixelformat),
+		f->fmt.pix.width, f->fmt.pix.height);
+
+	fmt = find_port_format_by_pix(port, f->fmt.pix.pixelformat);
+	if (!fmt) {
+		vip_dbg(2, stream,
+			"Fourcc format (0x%08x) not found.\n",
+			f->fmt.pix.pixelformat);
+
+		/* Just get the first one enumerated */
+		fmt = port->active_fmt[0];
+		f->fmt.pix.pixelformat = fmt->fourcc;
+	}
+
+	csc_direction =  vip_csc_direction(fmt->code, fmt->fourcc);
+	if (csc_direction != VIP_CSC_NA) {
+		if (!is_csc_available(port)) {
+			vip_dbg(2, stream,
+				"CSC not available for Fourcc format (0x%08x).\n",
+				f->fmt.pix.pixelformat);
+
+			/* Just get the first one enumerated */
+			fmt = port->active_fmt[0];
+			f->fmt.pix.pixelformat = fmt->fourcc;
+			/* re-evaluate the csc_direction here */
+			csc_direction =  vip_csc_direction(fmt->code,
+							   fmt->fourcc);
+		} else {
+			vip_dbg(3, stream, "CSC active on Port %c: going %s\n",
+				port->port_id == VIP_PORTA ? 'A' : 'B',
+				(csc_direction == VIP_CSC_Y2R) ? "Y2R" : "R2Y");
+		}
+	}
+
+	/*
+	 * Given that sensors might support multiple mbus code we need
+	 * to use the one that matches the requested pixel format
+	 */
+	port->try_mbus_framefmt = port->mbus_framefmt;
+	port->try_mbus_framefmt.code = fmt->code;
+
+	/* check for/find a valid width/height */
+	ret = 0;
+	found = false;
+	best_width = 0;
+	best_height = 0;
+	largest_width = 0;
+	largest_height = 0;
+	fse.pad = 0;
+	fse.code = fmt->code;
+	fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	for (fse.index = 0; ; fse.index++) {
+		u32 bpp = fmt->vpdma_fmt[0]->depth >> 3;
+
+		ret = v4l2_subdev_call(port->subdev, pad,
+				       enum_frame_size, NULL, &fse);
+		if (ret)
+			break;
+
+		vip_dbg(3, stream, "try_fmt loop:%d fourcc:%s size: %dx%d\n",
+			fse.index, fourcc_to_str(f->fmt.pix.pixelformat),
+			fse.max_width, fse.max_height);
+
+		if (!vip_is_size_dma_aligned(bpp, fse.max_width))
+			continue;
+
+		if ((fse.max_width >= largest_width) &&
+		    (fse.max_height >= largest_height)) {
+			vip_dbg(3, stream, "try_fmt loop:%d found new larger: %dx%d\n",
+				fse.index, fse.max_width, fse.max_height);
+			largest_width = fse.max_width;
+			largest_height = fse.max_height;
+		}
+
+		if ((fse.max_width >= f->fmt.pix.width) &&
+		    (fse.max_height >= f->fmt.pix.height)) {
+			vip_dbg(3, stream, "try_fmt loop:%d found at least larger: %dx%d\n",
+				fse.index, fse.max_width, fse.max_height);
+
+			if (!best_width ||
+			    ((abs(best_width - f->fmt.pix.width) >=
+			      abs(fse.max_width - f->fmt.pix.width)) &&
+			     (abs(best_height - f->fmt.pix.height) >=
+			      abs(fse.max_height - f->fmt.pix.height)))) {
+				best_width = fse.max_width;
+				best_height = fse.max_height;
+				vip_dbg(3, stream, "try_fmt loop:%d found new best: %dx%d\n",
+					fse.index, fse.max_width,
+					fse.max_height);
+			}
+		}
+
+		if ((f->fmt.pix.width == fse.max_width) &&
+		    (f->fmt.pix.height == fse.max_height)) {
+			found = true;
+			vip_dbg(3, stream, "try_fmt loop:%d found direct match: %dx%d\n",
+				fse.index, fse.max_width,
+				fse.max_height);
+			break;
+		}
+
+		if ((f->fmt.pix.width >= fse.min_width) &&
+		    (f->fmt.pix.width <= fse.max_width) &&
+		    (f->fmt.pix.height >= fse.min_height) &&
+		    (f->fmt.pix.height <= fse.max_height)) {
+			found = true;
+			vip_dbg(3, stream, "try_fmt loop:%d found direct range match: %dx%d\n",
+				fse.index, fse.max_width,
+				fse.max_height);
+			break;
+		}
+	}
+
+	if (found) {
+		port->try_mbus_framefmt.width = f->fmt.pix.width;
+		port->try_mbus_framefmt.height = f->fmt.pix.height;
+		/* No need to check for scaling */
+		goto calc_size;
+	} else if (f->fmt.pix.width > largest_width) {
+		port->try_mbus_framefmt.width = largest_width;
+		port->try_mbus_framefmt.height = largest_height;
+	} else if (best_width) {
+		port->try_mbus_framefmt.width = best_width;
+		port->try_mbus_framefmt.height = best_height;
+	} else {
+		/* use existing values as default */
+	}
+
+	vip_dbg(3, stream, "try_fmt best subdev size: %dx%d\n",
+		port->try_mbus_framefmt.width,
+		port->try_mbus_framefmt.height);
+
+	if (is_scaler_available(port) &&
+	    csc_direction != VIP_CSC_Y2R &&
+	    !vip_is_mbuscode_raw(fmt->code) &&
+	    f->fmt.pix.height <= port->try_mbus_framefmt.height &&
+	    port->try_mbus_framefmt.height <= SC_MAX_PIXEL_HEIGHT &&
+	    port->try_mbus_framefmt.width <= SC_MAX_PIXEL_WIDTH) {
+		/*
+		 * Scaler is only accessible if the dst colorspace is YUV.
+		 * As the input to the scaler must be in YUV mode only.
+		 *
+		 * Scaling up is allowed only horizontally.
+		 */
+		unsigned int hratio, vratio, width_align, height_align;
+		u32 bpp = fmt->vpdma_fmt[0]->depth >> 3;
+
+		vip_dbg(3, stream, "Scaler active on Port %c: requesting %dx%d\n",
+			port->port_id == VIP_PORTA ? 'A' : 'B',
+			f->fmt.pix.width, f->fmt.pix.height);
+
+		/* Just make sure everything is properly aligned */
+		width_align = ALIGN(f->fmt.pix.width * bpp, VPDMA_STRIDE_ALIGN);
+		width_align /= bpp;
+		height_align = ALIGN(f->fmt.pix.height, 2);
+
+		f->fmt.pix.width = width_align;
+		f->fmt.pix.height = height_align;
+
+		hratio = f->fmt.pix.width * 1000 /
+			 port->try_mbus_framefmt.width;
+		vratio = f->fmt.pix.height * 1000 /
+			 port->try_mbus_framefmt.height;
+		if (hratio < 125) {
+			f->fmt.pix.width = port->try_mbus_framefmt.width / 8;
+			vip_dbg(3, stream, "Horizontal scaling ratio out of range adjusting -> %d\n",
+				f->fmt.pix.width);
+		}
+
+		if (vratio < 188) {
+			f->fmt.pix.height = port->try_mbus_framefmt.height / 4;
+			vip_dbg(3, stream, "Vertical scaling ratio out of range adjusting -> %d\n",
+				f->fmt.pix.height);
+		}
+		vip_dbg(3, stream, "Scaler: got %dx%d\n",
+			f->fmt.pix.width, f->fmt.pix.height);
+	} else {
+		/* use existing values as default */
+		f->fmt.pix.width = port->try_mbus_framefmt.width;
+		f->fmt.pix.height = port->try_mbus_framefmt.height;
+	}
+
+calc_size:
+	/* That we have a fmt calculate imagesize and bytesperline */
+	return vip_calc_format_size(port, fmt, f);
+}
+
+static int vip_g_fmt_vid_cap(struct file *file, void *priv,
+			     struct v4l2_format *f)
+{
+	struct vip_stream *stream = file2stream(file);
+	struct vip_port *port = stream->port;
+	struct vip_fmt *fmt = port->fmt;
+
+	/* Use last known values or defaults */
+	f->fmt.pix.width	= stream->width;
+	f->fmt.pix.height	= stream->height;
+	f->fmt.pix.pixelformat	= port->fmt->fourcc;
+	f->fmt.pix.field	= stream->sup_field;
+	f->fmt.pix.colorspace	= port->fmt->colorspace;
+	f->fmt.pix.bytesperline	= stream->bytesperline;
+	f->fmt.pix.sizeimage	= stream->sizeimage;
+
+	vip_dbg(3, stream,
+		"g_fmt fourcc:%s code: %04x size: %dx%d bpl:%d img_size:%d\n",
+		fourcc_to_str(f->fmt.pix.pixelformat),
+		fmt->code,
+		f->fmt.pix.width, f->fmt.pix.height,
+		f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+	vip_dbg(3, stream, "g_fmt vpdma data type: 0x%02X\n",
+		port->fmt->vpdma_fmt[0]->data_type);
+
+	return 0;
+}
+
+static int vip_s_fmt_vid_cap(struct file *file, void *priv,
+			     struct v4l2_format *f)
+{
+	struct vip_stream *stream = file2stream(file);
+	struct vip_port *port = stream->port;
+	struct v4l2_subdev_format sfmt;
+	struct v4l2_mbus_framefmt *mf;
+	enum vip_csc_state csc_direction;
+	int ret;
+
+	vip_dbg(3, stream, "s_fmt input fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
+		fourcc_to_str(f->fmt.pix.pixelformat),
+		f->fmt.pix.width, f->fmt.pix.height,
+		f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+	ret = vip_try_fmt_vid_cap(file, priv, f);
+	if (ret)
+		return ret;
+
+	vip_dbg(3, stream, "s_fmt try_fmt fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
+		fourcc_to_str(f->fmt.pix.pixelformat),
+		f->fmt.pix.width, f->fmt.pix.height,
+		f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+	if (vb2_is_busy(&stream->vb_vidq)) {
+		vip_err(stream, "%s queue busy\n", __func__);
+		return -EBUSY;
+	}
+
+	/*
+	 * Check if we need the scaler or not
+	 *
+	 * Since on previous S_FMT call the scaler might have been
+	 * allocated if it is not needed in this instance we will
+	 * attempt to free it just in case.
+	 *
+	 * free_scaler() is harmless unless the current port
+	 * allocated it.
+	 */
+	if (f->fmt.pix.width == port->try_mbus_framefmt.width &&
+	    f->fmt.pix.height == port->try_mbus_framefmt.height)
+		free_scaler(port);
+	else
+		allocate_scaler(port);
+
+	port->fmt = find_port_format_by_pix(port,
+					    f->fmt.pix.pixelformat);
+	stream->width		= f->fmt.pix.width;
+	stream->height		= f->fmt.pix.height;
+	stream->bytesperline	= f->fmt.pix.bytesperline;
+	stream->sizeimage	= f->fmt.pix.sizeimage;
+	stream->sup_field	= f->fmt.pix.field;
+
+	port->c_rect.left	= 0;
+	port->c_rect.top	= 0;
+	port->c_rect.width	= stream->width;
+	port->c_rect.height	= stream->height;
+
+	/*
+	 * Check if we need the csc unit or not
+	 *
+	 * Since on previous S_FMT call, the csc might have been
+	 * allocated if it is not needed in this instance we will
+	 * attempt to free it just in case.
+	 *
+	 * free_csc() is harmless unless the current port
+	 * allocated it.
+	 */
+	csc_direction =  vip_csc_direction(port->fmt->code, port->fmt->fourcc);
+	if (csc_direction == VIP_CSC_NA)
+		free_csc(port);
+	else
+		allocate_csc(port, csc_direction);
+
+	if (stream->sup_field == V4L2_FIELD_ALTERNATE)
+		port->flags |= FLAG_INTERLACED;
+	else
+		port->flags &= ~FLAG_INTERLACED;
+
+	vip_dbg(3, stream, "s_fmt fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
+		fourcc_to_str(f->fmt.pix.pixelformat),
+		f->fmt.pix.width, f->fmt.pix.height,
+		f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+	mf = &sfmt.format;
+	v4l2_fill_mbus_format(mf, &f->fmt.pix, port->fmt->code);
+	/* Make sure to use the subdev size found in the try_fmt */
+	mf->width = port->try_mbus_framefmt.width;
+	mf->height = port->try_mbus_framefmt.height;
+
+	vip_dbg(3, stream, "s_fmt pix_to_mbus mbus_code: %04X size: %dx%d\n",
+		mf->code,
+		mf->width, mf->height);
+
+	sfmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	sfmt.pad = 0;
+	ret = v4l2_subdev_call(port->subdev, pad, set_fmt, NULL, &sfmt);
+	if (ret) {
+		vip_dbg(1, stream, "set_fmt failed in subdev\n");
+		return ret;
+	}
+
+	/* Save it */
+	port->mbus_framefmt = *mf;
+
+	vip_dbg(3, stream, "s_fmt subdev fmt mbus_code: %04X size: %dx%d\n",
+		port->mbus_framefmt.code,
+		port->mbus_framefmt.width, port->mbus_framefmt.height);
+	vip_dbg(3, stream, "s_fmt vpdma data type: 0x%02X\n",
+		port->fmt->vpdma_fmt[0]->data_type);
+
+	return 0;
+}
+
+/*
+ * Does the exact opposite of set_fmt_params
+ * It makes sure the DataPath register is sane after tear down
+ */
+static void unset_fmt_params(struct vip_stream *stream)
+{
+	struct vip_dev *dev = stream->port->dev;
+	struct vip_port *port = stream->port;
+
+	stream->sequence = 0;
+	stream->field = V4L2_FIELD_TOP;
+
+	if (port->csc == VIP_CSC_Y2R) {
+		if (port->port_id == VIP_PORTA) {
+			vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0);
+			vip_set_slice_path(dev,
+					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+			vip_set_slice_path(dev, VIP_RGB_SRC_DATA_SELECT, 0);
+		} else {
+			vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0);
+			vip_set_slice_path(dev,
+					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+		}
+		/* We are done */
+		return;
+	} else if (port->csc == VIP_CSC_R2Y) {
+		if (port->scaler && port->fmt->coplanar) {
+			if (port->port_id == VIP_PORTA) {
+				vip_set_slice_path(dev,
+						   VIP_CSC_SRC_DATA_SELECT, 0);
+				vip_set_slice_path(dev,
+						   VIP_SC_SRC_DATA_SELECT, 0);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_SRC_DATA_SELECT,
+						   0);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_DATA_BYPASS, 0);
+				vip_set_slice_path(dev,
+						   VIP_RGB_OUT_HI_DATA_SELECT,
+						   0);
+			}
+		} else if (port->scaler) {
+			if (port->port_id == VIP_PORTA) {
+				vip_set_slice_path(dev,
+						   VIP_CSC_SRC_DATA_SELECT, 0);
+				vip_set_slice_path(dev,
+						   VIP_SC_SRC_DATA_SELECT, 0);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_SRC_DATA_SELECT,
+						   0);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_DATA_BYPASS, 0);
+				vip_set_slice_path(dev,
+						   VIP_RGB_OUT_HI_DATA_SELECT,
+						   0);
+			}
+		} else if (port->fmt->coplanar) {
+			if (port->port_id == VIP_PORTA) {
+				vip_set_slice_path(dev,
+						   VIP_CSC_SRC_DATA_SELECT, 0);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_SRC_DATA_SELECT,
+						   0);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_DATA_BYPASS, 0);
+				vip_set_slice_path(dev,
+						   VIP_RGB_OUT_HI_DATA_SELECT,
+						   0);
+			}
+		} else {
+			if (port->port_id == VIP_PORTA) {
+				vip_set_slice_path(dev,
+						   VIP_CSC_SRC_DATA_SELECT, 0);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_SRC_DATA_SELECT,
+						   0);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_DATA_BYPASS, 0);
+				vip_set_slice_path(dev,
+						   VIP_RGB_OUT_HI_DATA_SELECT,
+						   0);
+			}
+		}
+		/* We are done */
+		return;
+	} else if (vip_is_fmt_rgb(port->fmt->fourcc)) {
+		if (port->port_id == VIP_PORTA) {
+			vip_set_slice_path(dev,
+					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+		}
+		/* We are done */
+		return;
+	}
+
+	if (port->scaler && port->fmt->coplanar) {
+		if (port->port_id == VIP_PORTA) {
+			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0);
+			vip_set_slice_path(dev,
+					   VIP_CHR_DS_1_SRC_DATA_SELECT, 0);
+			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+		} else {
+			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0);
+			vip_set_slice_path(dev,
+					   VIP_CHR_DS_2_SRC_DATA_SELECT, 0);
+			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+			vip_set_slice_path(dev,
+					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+		}
+	} else if (port->scaler) {
+		if (port->port_id == VIP_PORTA) {
+			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0);
+			vip_set_slice_path(dev,
+					   VIP_CHR_DS_1_SRC_DATA_SELECT, 0);
+			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+		} else {
+			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0);
+			vip_set_slice_path(dev,
+					   VIP_CHR_DS_2_SRC_DATA_SELECT, 0);
+			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+			vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+		}
+	} else if (port->fmt->coplanar) {
+		if (port->port_id == VIP_PORTA) {
+			vip_set_slice_path(dev,
+					   VIP_CHR_DS_1_SRC_DATA_SELECT, 0);
+			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+		} else {
+			vip_set_slice_path(dev,
+					   VIP_CHR_DS_2_SRC_DATA_SELECT, 0);
+			vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0);
+			vip_set_slice_path(dev,
+					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+		}
+	} else {
+		/*
+		 * We undo all data path setting except for the multi
+		 * stream case.
+		 * Because we cannot disrupt other on-going capture if only
+		 * one stream is terminated the other might still be going
+		 */
+		vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1);
+		vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+	}
+}
+
+/*
+ * Set the registers that are modified when the video format changes.
+ */
+static void set_fmt_params(struct vip_stream *stream)
+{
+	struct vip_dev *dev = stream->port->dev;
+	struct vip_port *port = stream->port;
+
+	stream->sequence = 0;
+	stream->field = V4L2_FIELD_TOP;
+
+	if (port->csc == VIP_CSC_Y2R) {
+		port->flags &= ~FLAG_MULT_PORT;
+		/* Set alpha component in background color */
+		vpdma_set_bg_color(dev->shared->vpdma,
+				   (struct vpdma_data_format *)
+				   port->fmt->vpdma_fmt[0],
+				   0xff);
+		if (port->port_id == VIP_PORTA) {
+			/*
+			 * Input A: YUV422
+			 * Output: Y_UP/UV_UP: RGB
+			 * CSC_SRC_SELECT       = 1
+			 * RGB_OUT_HI_SELECT    = 1
+			 * RGB_SRC_SELECT       = 1
+			 * MULTI_CHANNEL_SELECT = 0
+			 */
+			vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 1);
+			vip_set_slice_path(dev,
+					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 1);
+			vip_set_slice_path(dev, VIP_RGB_SRC_DATA_SELECT, 1);
+		} else {
+			/*
+			 * Input B: YUV422
+			 * Output: Y_UP/UV_UP: RGB
+			 * CSC_SRC_SELECT       = 2
+			 * RGB_OUT_LO_SELECT    = 1
+			 * MULTI_CHANNEL_SELECT = 0
+			 */
+			vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 2);
+			vip_set_slice_path(dev,
+					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 1);
+		}
+		/* We are done */
+		return;
+	} else if (port->csc == VIP_CSC_R2Y) {
+		port->flags &= ~FLAG_MULT_PORT;
+		if (port->scaler && port->fmt->coplanar) {
+			if (port->port_id == VIP_PORTA) {
+				/*
+				 * Input A: RGB
+				 * Output: Y_UP/UV_UP: Scaled YUV420
+				 * CSC_SRC_SELECT       = 4
+				 * SC_SRC_SELECT        = 1
+				 * CHR_DS_1_SRC_SELECT  = 1
+				 * CHR_DS_1_BYPASS      = 0
+				 * RGB_OUT_HI_SELECT    = 0
+				 */
+				vip_set_slice_path(dev,
+						   VIP_CSC_SRC_DATA_SELECT, 4);
+				vip_set_slice_path(dev,
+						   VIP_SC_SRC_DATA_SELECT, 1);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_SRC_DATA_SELECT,
+						   1);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_DATA_BYPASS, 0);
+				vip_set_slice_path(dev,
+						   VIP_RGB_OUT_HI_DATA_SELECT,
+						   0);
+			} else {
+				vip_err(stream, "RGB sensor can only be on Port A\n");
+			}
+		} else if (port->scaler) {
+			if (port->port_id == VIP_PORTA) {
+				/*
+				 * Input A: RGB
+				 * Output: Y_UP: Scaled YUV422
+				 * CSC_SRC_SELECT       = 4
+				 * SC_SRC_SELECT        = 1
+				 * CHR_DS_1_SRC_SELECT  = 1
+				 * CHR_DS_1_BYPASS      = 1
+				 * RGB_OUT_HI_SELECT    = 0
+				 */
+				vip_set_slice_path(dev,
+						   VIP_CSC_SRC_DATA_SELECT, 4);
+				vip_set_slice_path(dev,
+						   VIP_SC_SRC_DATA_SELECT, 1);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_SRC_DATA_SELECT,
+						   1);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_DATA_BYPASS, 1);
+				vip_set_slice_path(dev,
+						   VIP_RGB_OUT_HI_DATA_SELECT,
+						   0);
+			} else {
+				vip_err(stream, "RGB sensor can only be on Port A\n");
+			}
+		} else if (port->fmt->coplanar) {
+			if (port->port_id == VIP_PORTA) {
+				/*
+				 * Input A: RGB
+				 * Output: Y_UP/UV_UP: YUV420
+				 * CSC_SRC_SELECT       = 4
+				 * CHR_DS_1_SRC_SELECT  = 2
+				 * CHR_DS_1_BYPASS      = 0
+				 * RGB_OUT_HI_SELECT    = 0
+				 */
+				vip_set_slice_path(dev,
+						   VIP_CSC_SRC_DATA_SELECT, 4);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_SRC_DATA_SELECT,
+						   2);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_DATA_BYPASS, 0);
+				vip_set_slice_path(dev,
+						   VIP_RGB_OUT_HI_DATA_SELECT,
+						   0);
+			} else {
+				vip_err(stream, "RGB sensor can only be on Port A\n");
+			}
+		} else {
+			if (port->port_id == VIP_PORTA) {
+				/*
+				 * Input A: RGB
+				 * Output: Y_UP/UV_UP: YUV420
+				 * CSC_SRC_SELECT       = 4
+				 * CHR_DS_1_SRC_SELECT  = 2
+				 * CHR_DS_1_BYPASS      = 1
+				 * RGB_OUT_HI_SELECT    = 0
+				 */
+				vip_set_slice_path(dev,
+						   VIP_CSC_SRC_DATA_SELECT, 4);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_SRC_DATA_SELECT,
+						   2);
+				vip_set_slice_path(dev,
+						   VIP_CHR_DS_1_DATA_BYPASS, 1);
+				vip_set_slice_path(dev,
+						   VIP_RGB_OUT_HI_DATA_SELECT,
+						   0);
+			} else {
+				vip_err(stream, "RGB sensor can only be on Port A\n");
+			}
+		}
+		/* We are done */
+		return;
+	} else if (vip_is_fmt_rgb(port->fmt->fourcc)) {
+		port->flags &= ~FLAG_MULT_PORT;
+		/* Set alpha component in background color */
+		vpdma_set_bg_color(dev->shared->vpdma,
+				   (struct vpdma_data_format *)
+				   port->fmt->vpdma_fmt[0],
+				   0xff);
+		if (port->port_id == VIP_PORTA) {
+			/*
+			 * Input A: RGB
+			 * Output: Y_LO/UV_LO: RGB
+			 * RGB_OUT_LO_SELECT    = 1
+			 * MULTI_CHANNEL_SELECT = 1
+			 */
+			vip_set_slice_path(dev,
+					   VIP_MULTI_CHANNEL_DATA_SELECT, 1);
+			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 1);
+		} else {
+			vip_err(stream, "RGB sensor can only be on Port A\n");
+		}
+		/* We are done */
+		return;
+	}
+
+	if (port->scaler && port->fmt->coplanar) {
+		port->flags &= ~FLAG_MULT_PORT;
+		if (port->port_id == VIP_PORTA) {
+			/*
+			 * Input A: YUV422
+			 * Output: Y_UP/UV_UP: Scaled YUV420
+			 * SC_SRC_SELECT        = 2
+			 * CHR_DS_1_SRC_SELECT  = 1
+			 * CHR_DS_1_BYPASS      = 0
+			 * RGB_OUT_HI_SELECT    = 0
+			 */
+			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 2);
+			vip_set_slice_path(dev,
+					   VIP_CHR_DS_1_SRC_DATA_SELECT, 1);
+			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+		} else {
+			/*
+			 * Input B: YUV422
+			 * Output: Y_LO/UV_LO: Scaled YUV420
+			 * SC_SRC_SELECT        = 3
+			 * CHR_DS_2_SRC_SELECT  = 1
+			 * RGB_OUT_LO_SELECT    = 0
+			 * MULTI_CHANNEL_SELECT = 0
+			 */
+			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 3);
+			vip_set_slice_path(dev,
+					   VIP_CHR_DS_2_SRC_DATA_SELECT, 1);
+			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+			vip_set_slice_path(dev,
+					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+		}
+	} else if (port->scaler) {
+		port->flags &= ~FLAG_MULT_PORT;
+		if (port->port_id == VIP_PORTA) {
+			/*
+			 * Input A: YUV422
+			 * Output: Y_UP: Scaled YUV422
+			 * SC_SRC_SELECT        = 2
+			 * CHR_DS_1_SRC_SELECT  = 1
+			 * CHR_DS_1_BYPASS      = 1
+			 * RGB_OUT_HI_SELECT    = 0
+			 */
+			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 2);
+			vip_set_slice_path(dev,
+					   VIP_CHR_DS_1_SRC_DATA_SELECT, 1);
+			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 1);
+			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+		} else {
+			/*
+			 * Input B: YUV422
+			 * Output: UV_UP: Scaled YUV422
+			 * SC_SRC_SELECT        = 3
+			 * CHR_DS_2_SRC_SELECT  = 1
+			 * CHR_DS_1_BYPASS      = 1
+			 * CHR_DS_2_BYPASS      = 1
+			 * RGB_OUT_HI_SELECT    = 0
+			 */
+			vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 3);
+			vip_set_slice_path(dev,
+					   VIP_CHR_DS_2_SRC_DATA_SELECT, 1);
+			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 1);
+			vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 1);
+			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+		}
+	} else if (port->fmt->coplanar) {
+		port->flags &= ~FLAG_MULT_PORT;
+		if (port->port_id == VIP_PORTA) {
+			/*
+			 * Input A: YUV422
+			 * Output: Y_UP/UV_UP: YUV420
+			 * CHR_DS_1_SRC_SELECT  = 3
+			 * CHR_DS_1_BYPASS      = 0
+			 * RGB_OUT_HI_SELECT    = 0
+			 */
+			vip_set_slice_path(dev,
+					   VIP_CHR_DS_1_SRC_DATA_SELECT, 3);
+			vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0);
+		} else {
+			/*
+			 * Input B: YUV422
+			 * Output: Y_LO/UV_LO: YUV420
+			 * CHR_DS_2_SRC_SELECT  = 4
+			 * CHR_DS_2_BYPASS      = 0
+			 * RGB_OUT_LO_SELECT    = 0
+			 * MULTI_CHANNEL_SELECT = 0
+			 */
+			vip_set_slice_path(dev,
+					   VIP_CHR_DS_2_SRC_DATA_SELECT, 4);
+			vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0);
+			vip_set_slice_path(dev,
+					   VIP_MULTI_CHANNEL_DATA_SELECT, 0);
+			vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+		}
+	} else {
+		port->flags |= FLAG_MULT_PORT;
+		/*
+		 * Input A/B: YUV422
+		 * Output: Y_LO: YUV422 - UV_LO: YUV422
+		 * MULTI_CHANNEL_SELECT = 1
+		 * RGB_OUT_LO_SELECT    = 0
+		 */
+		vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1);
+		vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0);
+	}
+}
+
+static int vip_g_selection(struct file *file, void *fh,
+			   struct v4l2_selection *s)
+{
+	struct vip_stream *stream = file2stream(file);
+
+	switch (s->target) {
+	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		s->r.left = 0;
+		s->r.top = 0;
+		s->r.width = stream->width;
+		s->r.height = stream->height;
+		return 0;
+
+	case V4L2_SEL_TGT_COMPOSE:
+	case V4L2_SEL_TGT_CROP:
+		s->r = stream->port->c_rect;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
+{
+	if (a->left < b->left || a->top < b->top)
+		return 0;
+	if (a->left + a->width > b->left + b->width)
+		return 0;
+	if (a->top + a->height > b->top + b->height)
+		return 0;
+
+	return 1;
+}
+
+static int vip_s_selection(struct file *file, void *fh,
+			   struct v4l2_selection *s)
+{
+	struct vip_stream *stream = file2stream(file);
+	struct v4l2_rect r = s->r;
+
+	v4l_bound_align_image(&r.width, 0, stream->width, 0,
+			      &r.height, 0, stream->height, 0, 0);
+
+	r.left = clamp_t(unsigned int, r.left, 0, stream->width - r.width);
+	r.top  = clamp_t(unsigned int, r.top, 0, stream->height - r.height);
+
+	if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&r, &s->r))
+		return -ERANGE;
+
+	if (s->flags & V4L2_SEL_FLAG_GE && !enclosed_rectangle(&s->r, &r))
+		return -ERANGE;
+
+	s->r = r;
+	stream->port->c_rect = r;
+
+	vip_dbg(1, stream, "cropped (%d,%d)/%dx%d of %dx%d\n",
+		r.left, r.top, r.width, r.height,
+		stream->width, stream->height);
+
+	return 0;
+}
+
+static long vip_ioctl_default(struct file *file, void *fh, bool valid_prio,
+			      unsigned int cmd, void *arg)
+{
+	struct vip_stream *stream = file2stream(file);
+
+	if (!valid_prio) {
+		vip_err(stream, "%s device busy\n", __func__);
+		return -EBUSY;
+	}
+
+	switch (cmd) {
+	default:
+		return -ENOTTY;
+	}
+}
+
+static const struct v4l2_ioctl_ops vip_ioctl_ops = {
+	.vidioc_querycap	= vip_querycap,
+	.vidioc_enum_input	= vip_enuminput,
+	.vidioc_g_input		= vip_g_input,
+	.vidioc_s_input		= vip_s_input,
+
+	.vidioc_querystd	= vip_querystd,
+	.vidioc_g_std		= vip_g_std,
+	.vidioc_s_std		= vip_s_std,
+
+	.vidioc_enum_fmt_vid_cap = vip_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap	= vip_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap	= vip_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap	= vip_s_fmt_vid_cap,
+
+	.vidioc_enum_frameintervals	= vip_enum_frameintervals,
+	.vidioc_enum_framesizes		= vip_enum_framesizes,
+	.vidioc_s_parm			= vip_s_parm,
+	.vidioc_g_parm			= vip_g_parm,
+	.vidioc_g_selection	= vip_g_selection,
+	.vidioc_s_selection	= vip_s_selection,
+	.vidioc_reqbufs		= vb2_ioctl_reqbufs,
+	.vidioc_create_bufs	= vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf	= vb2_ioctl_prepare_buf,
+	.vidioc_querybuf	= vb2_ioctl_querybuf,
+	.vidioc_qbuf		= vb2_ioctl_qbuf,
+	.vidioc_dqbuf		= vb2_ioctl_dqbuf,
+	.vidioc_expbuf		= vb2_ioctl_expbuf,
+
+	.vidioc_streamon	= vb2_ioctl_streamon,
+	.vidioc_streamoff	= vb2_ioctl_streamoff,
+	.vidioc_log_status	= v4l2_ctrl_log_status,
+	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+	.vidioc_default		= vip_ioctl_default,
+};
+
+/*
+ * Videobuf operations
+ */
+static int vip_queue_setup(struct vb2_queue *vq,
+			   unsigned int *nbuffers, unsigned int *nplanes,
+			   unsigned int sizes[], struct device *alloc_devs[])
+{
+	struct vip_stream *stream = vb2_get_drv_priv(vq);
+	unsigned int size = stream->sizeimage;
+
+	if (vq->num_buffers + *nbuffers < 3)
+		*nbuffers = 3 - vq->num_buffers;
+
+	if (*nplanes) {
+		if (sizes[0] < size)
+			return -EINVAL;
+		size = sizes[0];
+	}
+
+	*nplanes = 1;
+	sizes[0] = size;
+
+	vip_dbg(1, stream, "get %d buffer(s) of size %d each.\n",
+		*nbuffers, sizes[0]);
+
+	return 0;
+}
+
+static int vip_buf_prepare(struct vb2_buffer *vb)
+{
+	struct vip_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
+
+	if (vb2_plane_size(vb, 0) < stream->sizeimage) {
+		vip_dbg(1, stream,
+			"%s data will not fit into plane (%lu < %lu)\n",
+			__func__, vb2_plane_size(vb, 0),
+			(long)stream->sizeimage);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(vb, 0, stream->sizeimage);
+
+	return 0;
+}
+
+static void vip_buf_queue(struct vb2_buffer *vb)
+{
+	struct vip_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
+	struct vip_dev *dev = stream->port->dev;
+	struct vip_buffer *buf = container_of(vb, struct vip_buffer,
+					      vb.vb2_buf);
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->slock, flags);
+	list_add_tail(&buf->list, &stream->vidq);
+	spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static int vip_setup_scaler(struct vip_stream *stream)
+{
+	struct vip_port *port = stream->port;
+	struct vip_dev *dev = port->dev;
+	struct sc_data *sc = dev->sc;
+	struct csc_data *csc = dev->csc;
+	struct vpdma_data *vpdma = dev->shared->vpdma;
+	struct vip_mmr_adb *mmr_adb = port->mmr_adb.addr;
+	int list_num = stream->list_num;
+	int timeout = 500;
+
+	/* if scaler not associated with this port then skip */
+	if (port->scaler) {
+		sc_set_hs_coeffs(sc, port->sc_coeff_h.addr,
+				 port->mbus_framefmt.width,
+				 port->c_rect.width);
+		sc_set_vs_coeffs(sc, port->sc_coeff_v.addr,
+				 port->mbus_framefmt.height,
+				 port->c_rect.height);
+		sc_config_scaler(sc, &mmr_adb->sc_regs0[0],
+				 &mmr_adb->sc_regs8[0], &mmr_adb->sc_regs17[0],
+				 port->mbus_framefmt.width,
+				 port->mbus_framefmt.height,
+				 port->c_rect.width,
+				 port->c_rect.height);
+		port->load_mmrs = true;
+	}
+
+	/* if csc not associated with this port then skip */
+	if (port->csc) {
+		csc_set_coeff(csc, &mmr_adb->csc_regs[0],
+			      vip_code_to_colorspace(port->fmt->code),
+			      vip_fourcc_to_colorspace(port->fmt->fourcc));
+		port->load_mmrs = true;
+	}
+
+	/* If coeff are already loaded then skip */
+	if (!sc->load_coeff_v && !sc->load_coeff_h && !port->load_mmrs)
+		return 0;
+
+	if (vpdma_list_busy(vpdma, list_num)) {
+		vip_dbg(3, stream, "%s: List %d is busy\n",
+			__func__, list_num);
+	}
+
+	/* Make sure we start with a clean list */
+	vpdma_reset_desc_list(&stream->desc_list);
+
+	/* config descriptors */
+	if (port->load_mmrs) {
+		vpdma_map_desc_buf(vpdma, &port->mmr_adb);
+		vpdma_add_cfd_adb(&stream->desc_list, CFD_MMR_CLIENT,
+				  &port->mmr_adb);
+
+		port->load_mmrs = false;
+		vip_dbg(3, stream, "Added mmr_adb config desc\n");
+	}
+
+	if (sc->loaded_coeff_h != port->sc_coeff_h.dma_addr ||
+	    sc->load_coeff_h) {
+		vpdma_map_desc_buf(vpdma, &port->sc_coeff_h);
+		vpdma_add_cfd_block(&stream->desc_list,
+				    VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id,
+				    &port->sc_coeff_h, 0);
+
+		sc->loaded_coeff_h = port->sc_coeff_h.dma_addr;
+		sc->load_coeff_h = false;
+		vip_dbg(3, stream, "Added sc_coeff_h config desc\n");
+	}
+
+	if (sc->loaded_coeff_v != port->sc_coeff_v.dma_addr ||
+	    sc->load_coeff_v) {
+		vpdma_map_desc_buf(vpdma, &port->sc_coeff_v);
+		vpdma_add_cfd_block(&stream->desc_list,
+				    VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id,
+				    &port->sc_coeff_v, SC_COEF_SRAM_SIZE >> 4);
+
+		sc->loaded_coeff_v = port->sc_coeff_v.dma_addr;
+		sc->load_coeff_v = false;
+		vip_dbg(3, stream, "Added sc_coeff_v config desc\n");
+	}
+	vip_dbg(3, stream, "CFD_SC_CLIENT %d slice_id: %d\n",
+		VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id, dev->slice_id);
+
+	vpdma_map_desc_buf(vpdma, &stream->desc_list.buf);
+	vip_dbg(3, stream, "Submitting desc on list# %d\n", list_num);
+	vpdma_submit_descs(vpdma, &stream->desc_list, list_num);
+
+	while (vpdma_list_busy(vpdma, list_num) && timeout--)
+		usleep_range(1000, 1100);
+
+	vpdma_unmap_desc_buf(dev->shared->vpdma, &port->mmr_adb);
+	vpdma_unmap_desc_buf(dev->shared->vpdma, &port->sc_coeff_h);
+	vpdma_unmap_desc_buf(dev->shared->vpdma, &port->sc_coeff_v);
+	vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
+
+	vpdma_reset_desc_list(&stream->desc_list);
+
+	if (timeout <= 0) {
+		vip_err(stream, "Timed out setting up scaler through VPDMA list\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int vip_load_vpdma_list_fifo(struct vip_stream *stream)
+{
+	struct vip_port *port = stream->port;
+	struct vip_dev *dev = port->dev;
+	struct vpdma_data *vpdma = dev->shared->vpdma;
+	int list_num = stream->list_num;
+	struct vip_buffer *buf;
+	unsigned long flags;
+	int timeout, i;
+
+	if (vpdma_list_busy(dev->shared->vpdma, stream->list_num))
+		return -EBUSY;
+
+	for (i = 0; i < VIP_VPDMA_FIFO_SIZE; i++) {
+		spin_lock_irqsave(&dev->slock, flags);
+		if (list_empty(&stream->vidq)) {
+			vip_err(stream, "No buffer left!");
+			spin_unlock_irqrestore(&dev->slock, flags);
+			return -EINVAL;
+		}
+
+		buf = list_entry(stream->vidq.next,
+				 struct vip_buffer, list);
+		buf->drop = false;
+
+		list_move_tail(&buf->list, &stream->post_bufs);
+		spin_unlock_irqrestore(&dev->slock, flags);
+
+		vip_dbg(2, stream, "%s: start_dma vb2 buf idx:%d\n",
+			__func__, buf->vb.vb2_buf.index);
+		start_dma(stream, buf);
+
+		timeout = 500;
+		while (vpdma_list_busy(vpdma, list_num) && timeout--)
+			usleep_range(1000, 1100);
+
+		if (timeout <= 0) {
+			vip_err(stream, "Timed out loading VPDMA list fifo\n");
+			return -EBUSY;
+		}
+	}
+	return 0;
+}
+
+static int vip_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct vip_stream *stream = vb2_get_drv_priv(vq);
+	struct vip_port *port = stream->port;
+	struct vip_dev *dev = port->dev;
+	int ret;
+
+	vip_setup_scaler(stream);
+
+	/*
+	 * Make sure the scaler is configured before the datapath is
+	 * enabled. The scaler can only load the coefficient
+	 * parameters when it is idle. If the scaler path is enabled
+	 * and video data is being received then the VPDMA transfer will
+	 * stall indefinetely.
+	 */
+	set_fmt_params(stream);
+	vip_setup_parser(port);
+
+	if (port->subdev) {
+		ret = v4l2_subdev_call(port->subdev, video, s_stream, 1);
+		if (ret) {
+			vip_dbg(1, stream, "stream on failed in subdev\n");
+			return ret;
+		}
+	}
+
+	stream->sequence = 0;
+	stream->field = V4L2_FIELD_TOP;
+	populate_desc_list(stream);
+
+	ret = vip_load_vpdma_list_fifo(stream);
+	if (ret)
+		return ret;
+
+	stream->num_recovery = 0;
+
+	clear_irqs(dev, dev->slice_id, stream->list_num);
+	enable_irqs(dev, dev->slice_id, stream->list_num);
+	vip_schedule_next_buffer(stream);
+	vip_parser_stop_imm(port, false);
+	vip_enable_parser(port, true);
+
+	return 0;
+}
+
+/*
+ * Abort streaming and wait for last buffer
+ */
+static void vip_stop_streaming(struct vb2_queue *vq)
+{
+	struct vip_stream *stream = vb2_get_drv_priv(vq);
+	struct vip_port *port = stream->port;
+	struct vip_dev *dev = port->dev;
+	struct vip_buffer *buf;
+	int ret;
+
+	vip_dbg(2, stream, "%s:\n", __func__);
+
+	vip_parser_stop_imm(port, true);
+	vip_enable_parser(port, false);
+	unset_fmt_params(stream);
+
+	disable_irqs(dev, dev->slice_id, stream->list_num);
+	clear_irqs(dev, dev->slice_id, stream->list_num);
+
+	if (port->subdev) {
+		ret = v4l2_subdev_call(port->subdev, video, s_stream, 0);
+		if (ret)
+			vip_dbg(1, stream, "stream on failed in subdev\n");
+	}
+
+	stop_dma(stream, true);
+
+	/* release all active buffers */
+	while (!list_empty(&stream->post_bufs)) {
+		buf = list_entry(stream->post_bufs.next,
+				 struct vip_buffer, list);
+		list_del(&buf->list);
+		if (buf->drop == 1)
+			list_add_tail(&buf->list, &stream->dropq);
+		else
+			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+	}
+	while (!list_empty(&stream->vidq)) {
+		buf = list_entry(stream->vidq.next, struct vip_buffer, list);
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+	}
+
+	if (!vb2_is_streaming(vq))
+		return;
+
+	vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
+	vpdma_reset_desc_list(&stream->desc_list);
+}
+
+static const struct vb2_ops vip_video_qops = {
+	.queue_setup		= vip_queue_setup,
+	.buf_prepare		= vip_buf_prepare,
+	.buf_queue		= vip_buf_queue,
+	.start_streaming	= vip_start_streaming,
+	.stop_streaming		= vip_stop_streaming,
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+/*
+ * File operations
+ */
+
+static int vip_init_dev(struct vip_dev *dev)
+{
+	if (dev->num_ports != 0)
+		goto done;
+
+	vip_set_clock_enable(dev, 1);
+	vip_module_reset(dev, VIP_SC_RST, false);
+	vip_module_reset(dev, VIP_CSC_RST, false);
+done:
+	dev->num_ports++;
+
+	return 0;
+}
+
+static inline bool is_scaler_available(struct vip_port *port)
+{
+	if (port->endpoint->bus_type == V4L2_MBUS_PARALLEL)
+		if (port->dev->sc_assigned == VIP_NOT_ASSIGNED ||
+		    port->dev->sc_assigned == port->port_id)
+			return true;
+	return false;
+}
+
+static inline bool allocate_scaler(struct vip_port *port)
+{
+	if (is_scaler_available(port)) {
+		if (port->dev->sc_assigned == VIP_NOT_ASSIGNED ||
+		    port->dev->sc_assigned == port->port_id) {
+			port->dev->sc_assigned = port->port_id;
+			port->scaler = true;
+			return true;
+		}
+	}
+	return false;
+}
+
+static inline void free_scaler(struct vip_port *port)
+{
+	if (port->dev->sc_assigned == port->port_id) {
+		port->dev->sc_assigned = VIP_NOT_ASSIGNED;
+		port->scaler = false;
+	}
+}
+
+static bool is_csc_available(struct vip_port *port)
+{
+	if (port->endpoint->bus_type == V4L2_MBUS_PARALLEL)
+		if (port->dev->csc_assigned == VIP_NOT_ASSIGNED ||
+		    port->dev->csc_assigned == port->port_id)
+			return true;
+	return false;
+}
+
+static bool allocate_csc(struct vip_port *port,
+				enum vip_csc_state csc_direction)
+{
+	/* Is CSC needed? */
+	if (csc_direction != VIP_CSC_NA) {
+		if (is_csc_available(port)) {
+			port->dev->csc_assigned = port->port_id;
+			port->csc = csc_direction;
+			vip_dbg(1, port, "%s: csc allocated: dir: %d\n",
+				__func__, csc_direction);
+			return true;
+		}
+	}
+	return false;
+}
+
+static void free_csc(struct vip_port *port)
+{
+	if (port->dev->csc_assigned == port->port_id) {
+		port->dev->csc_assigned = VIP_NOT_ASSIGNED;
+		port->csc = VIP_CSC_NA;
+		vip_dbg(1, port, "%s: csc freed\n",
+			__func__);
+	}
+}
+
+static int vip_init_port(struct vip_port *port)
+{
+	int ret;
+	struct vip_fmt *fmt;
+	struct v4l2_subdev_format sd_fmt;
+	struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+
+	if (port->num_streams != 0)
+		goto done;
+
+	ret = vip_init_dev(port->dev);
+	if (ret)
+		goto done;
+
+	/* Get subdevice current frame format */
+	sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	sd_fmt.pad = 0;
+	ret = v4l2_subdev_call(port->subdev, pad, get_fmt, NULL, &sd_fmt);
+	if (ret)
+		vip_dbg(1, port, "init_port get_fmt failed in subdev: (%d)\n",
+			ret);
+
+	/* try to find one that matches */
+	fmt = find_port_format_by_code(port, mbus_fmt->code);
+	if (!fmt) {
+		vip_dbg(1, port, "subdev default mbus_fmt %04x is not matched.\n",
+			mbus_fmt->code);
+		/* if all else fails just pick the first one */
+		fmt = port->active_fmt[0];
+
+		mbus_fmt->code = fmt->code;
+		sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+		sd_fmt.pad = 0;
+		ret = v4l2_subdev_call(port->subdev, pad, set_fmt,
+				       NULL, &sd_fmt);
+		if (ret)
+			vip_dbg(1, port, "init_port set_fmt failed in subdev: (%d)\n",
+				ret);
+	}
+
+	/* Assign current format */
+	port->fmt = fmt;
+	port->mbus_framefmt = *mbus_fmt;
+
+	vip_dbg(3, port, "%s: g_mbus_fmt subdev mbus_code: %04X fourcc:%s size: %dx%d\n",
+		__func__, fmt->code,
+		fourcc_to_str(fmt->fourcc),
+		mbus_fmt->width, mbus_fmt->height);
+
+	if (mbus_fmt->field == V4L2_FIELD_ALTERNATE)
+		port->flags |= FLAG_INTERLACED;
+	else
+		port->flags &= ~FLAG_INTERLACED;
+
+	port->c_rect.left	= 0;
+	port->c_rect.top	= 0;
+	port->c_rect.width	= mbus_fmt->width;
+	port->c_rect.height	= mbus_fmt->height;
+
+	ret = vpdma_alloc_desc_buf(&port->sc_coeff_h, SC_COEF_SRAM_SIZE);
+	if (ret != 0)
+		return ret;
+
+	ret = vpdma_alloc_desc_buf(&port->sc_coeff_v, SC_COEF_SRAM_SIZE);
+	if (ret != 0)
+		goto free_sc_h;
+
+	ret = vpdma_alloc_desc_buf(&port->mmr_adb, sizeof(struct vip_mmr_adb));
+	if (ret != 0)
+		goto free_sc_v;
+
+	init_adb_hdrs(port);
+
+	vip_enable_parser(port, false);
+done:
+	port->num_streams++;
+	return 0;
+
+free_sc_v:
+	vpdma_free_desc_buf(&port->sc_coeff_v);
+free_sc_h:
+	vpdma_free_desc_buf(&port->sc_coeff_h);
+	return ret;
+}
+
+static int vip_init_stream(struct vip_stream *stream)
+{
+	struct vip_port *port = stream->port;
+	struct vip_fmt *fmt;
+	struct v4l2_mbus_framefmt *mbus_fmt;
+	struct v4l2_format f;
+	int ret;
+
+	ret = vip_init_port(port);
+	if (ret != 0)
+		return ret;
+
+	fmt = port->fmt;
+	mbus_fmt = &port->mbus_framefmt;
+
+	memset(&f, 0, sizeof(f));
+
+	/* Properly calculate the sizeimage and bytesperline values. */
+	v4l2_fill_pix_format(&f.fmt.pix, mbus_fmt);
+	f.fmt.pix.pixelformat = fmt->fourcc;
+	ret = vip_calc_format_size(port, fmt, &f);
+	if (ret)
+		return ret;
+
+	stream->width = f.fmt.pix.width;
+	stream->height = f.fmt.pix.height;
+	stream->sup_field = f.fmt.pix.field;
+	stream->bytesperline = f.fmt.pix.bytesperline;
+	stream->sizeimage = f.fmt.pix.sizeimage;
+
+	vip_dbg(3, stream, "init_stream fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
+		fourcc_to_str(f.fmt.pix.pixelformat),
+		f.fmt.pix.width, f.fmt.pix.height,
+		f.fmt.pix.bytesperline, f.fmt.pix.sizeimage);
+	vip_dbg(3, stream, "init_stream vpdma data type: 0x%02X\n",
+		port->fmt->vpdma_fmt[0]->data_type);
+
+	ret = vpdma_create_desc_list(&stream->desc_list, VIP_DESC_LIST_SIZE,
+				     VPDMA_LIST_TYPE_NORMAL);
+
+	if (ret != 0)
+		return ret;
+
+	stream->write_desc = (struct vpdma_dtd *)stream->desc_list.buf.addr
+				+ 15;
+
+	vip_dbg(1, stream, "%s: stream instance %pa\n",
+		__func__, &stream);
+
+	return 0;
+}
+
+static void vip_release_dev(struct vip_dev *dev)
+{
+	/*
+	 * On last close, disable clocks to conserve power
+	 */
+
+	if (--dev->num_ports == 0) {
+		/* reset the scaler module */
+		vip_module_reset(dev, VIP_SC_RST, true);
+		vip_module_reset(dev, VIP_CSC_RST, true);
+		vip_set_clock_enable(dev, 0);
+	}
+}
+
+static int vip_set_crop_parser(struct vip_port *port)
+{
+	struct vip_dev *dev = port->dev;
+	struct vip_parser_data *parser = dev->parser;
+	u32 hcrop = 0, vcrop = 0;
+	u32 width = port->mbus_framefmt.width;
+
+	if (port->fmt->vpdma_fmt[0] == &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8]) {
+		/*
+		 * Special case since we are faking a YUV422 16bit format
+		 * to have the vpdma perform the needed byte swap
+		 * we need to adjust the pixel width accordingly
+		 * otherwise the parser will attempt to collect more pixels
+		 * then available and the vpdma transfer will exceed the
+		 * allocated frame buffer.
+		 */
+		width >>= 1;
+		vip_dbg(1, port, "%s: 8 bit raw detected, adjusting width to %d\n",
+			__func__, width);
+	}
+
+	/*
+	 * Set Parser Crop parameters to source size otherwise
+	 * scaler and colorspace converter will yield garbage.
+	 */
+	hcrop = VIP_ACT_BYPASS;
+	insert_field(&hcrop, 0, VIP_ACT_SKIP_NUMPIX_MASK,
+		     VIP_ACT_SKIP_NUMPIX_SHFT);
+	insert_field(&hcrop, width,
+		     VIP_ACT_USE_NUMPIX_MASK, VIP_ACT_USE_NUMPIX_SHFT);
+	reg_write(parser, VIP_PARSER_CROP_H_PORT(port->port_id), hcrop);
+
+	insert_field(&vcrop, 0, VIP_ACT_SKIP_NUMLINES_MASK,
+		     VIP_ACT_SKIP_NUMLINES_SHFT);
+	insert_field(&vcrop, port->mbus_framefmt.height,
+		     VIP_ACT_USE_NUMLINES_MASK, VIP_ACT_USE_NUMLINES_SHFT);
+	reg_write(parser, VIP_PARSER_CROP_V_PORT(port->port_id), vcrop);
+
+	return 0;
+}
+
+static int vip_setup_parser(struct vip_port *port)
+{
+	struct vip_dev *dev = port->dev;
+	struct vip_parser_data *parser = dev->parser;
+	struct v4l2_fwnode_endpoint *endpoint = port->endpoint;
+	int iface, sync_type;
+	u32 flags = 0, config0;
+
+	/* Reset the port */
+	vip_reset_parser(port, true);
+	usleep_range(200, 250);
+	vip_reset_parser(port, false);
+
+	config0 = reg_read(parser, VIP_PARSER_PORT(port->port_id));
+
+	if (endpoint->bus_type == V4L2_MBUS_BT656) {
+		flags = endpoint->bus.parallel.flags;
+		iface = DUAL_8B_INTERFACE;
+
+		/*
+		 * Ideally, this should come from subdev
+		 * port->fmt can be anything once CSC is enabled
+		 */
+		if (vip_is_mbuscode_rgb(port->fmt->code)) {
+			sync_type = EMBEDDED_SYNC_SINGLE_RGB_OR_YUV444;
+		} else {
+			switch (endpoint->bus.parallel.num_channels) {
+			case 4:
+				sync_type = EMBEDDED_SYNC_4X_MULTIPLEXED_YUV422;
+				break;
+			case 2:
+				sync_type = EMBEDDED_SYNC_2X_MULTIPLEXED_YUV422;
+				break;
+			case 1:
+				sync_type = EMBEDDED_SYNC_SINGLE_YUV422;
+				break;
+			default:
+				sync_type =
+				EMBEDDED_SYNC_LINE_MULTIPLEXED_YUV422;
+			}
+			if (endpoint->bus.parallel.pixmux == 0)
+				sync_type =
+				EMBEDDED_SYNC_LINE_MULTIPLEXED_YUV422;
+		}
+
+	} else if (endpoint->bus_type == V4L2_MBUS_PARALLEL) {
+		flags = endpoint->bus.parallel.flags;
+
+		switch (endpoint->bus.parallel.bus_width) {
+		case 24:
+			iface = SINGLE_24B_INTERFACE;
+		break;
+		case 16:
+			iface = SINGLE_16B_INTERFACE;
+		break;
+		case 8:
+		default:
+			iface = DUAL_8B_INTERFACE;
+		}
+
+		if (vip_is_mbuscode_rgb(port->fmt->code))
+			sync_type = DISCRETE_SYNC_SINGLE_RGB_24B;
+		else
+			sync_type = DISCRETE_SYNC_SINGLE_YUV422;
+
+		if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+			config0 |= VIP_HSYNC_POLARITY;
+		else if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+			config0 &= ~VIP_HSYNC_POLARITY;
+
+		if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+			config0 |= VIP_VSYNC_POLARITY;
+		else if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+			config0 &= ~VIP_VSYNC_POLARITY;
+
+		config0 &= ~VIP_USE_ACTVID_HSYNC_ONLY;
+		config0 |= VIP_ACTVID_POLARITY;
+		config0 |= VIP_DISCRETE_BASIC_MODE;
+
+	} else {
+		vip_err(port, "Device doesn't support CSI2");
+		return -EINVAL;
+	}
+
+	if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) {
+		vip_set_pclk_invert(port);
+		config0 |= VIP_PIXCLK_EDGE_POLARITY;
+	} else {
+		config0 &= ~VIP_PIXCLK_EDGE_POLARITY;
+	}
+
+	config0 |= ((sync_type & VIP_SYNC_TYPE_MASK) << VIP_SYNC_TYPE_SHFT);
+
+	reg_write(parser, VIP_PARSER_PORT(port->port_id), config0);
+
+	vip_set_data_interface(port, iface);
+	vip_set_crop_parser(port);
+
+	return 0;
+}
+
+static __maybe_unused void vip_enable_parser(struct vip_port *port, bool on)
+{
+	u32 config0;
+	struct vip_dev *dev = port->dev;
+	struct vip_parser_data *parser = dev->parser;
+
+	config0 = reg_read(parser, VIP_PARSER_PORT(port->port_id));
+
+	if (on) {
+		config0 |= VIP_PORT_ENABLE;
+		config0 &= ~(VIP_ASYNC_FIFO_RD | VIP_ASYNC_FIFO_WR);
+	} else {
+		config0 &= ~VIP_PORT_ENABLE;
+		config0 |= (VIP_ASYNC_FIFO_RD | VIP_ASYNC_FIFO_WR);
+	}
+	reg_write(parser, VIP_PARSER_PORT(port->port_id), config0);
+}
+
+static __maybe_unused void vip_reset_parser(struct vip_port *port, bool on)
+{
+	u32 config0;
+	struct vip_dev *dev = port->dev;
+	struct vip_parser_data *parser = dev->parser;
+
+	config0 = reg_read(parser, VIP_PARSER_PORT(port->port_id));
+
+	if (on)
+		config0 |= VIP_SW_RESET;
+	else
+		config0 &= ~VIP_SW_RESET;
+
+	reg_write(parser, VIP_PARSER_PORT(port->port_id), config0);
+}
+
+static __maybe_unused void vip_parser_stop_imm(struct vip_port *port, bool on)
+{
+	u32 config0;
+	struct vip_dev *dev = port->dev;
+	struct vip_parser_data *parser = dev->parser;
+
+	config0 = reg_read(parser, VIP_PARSER_STOP_IMM_PORT(port->port_id));
+
+	if (on)
+		config0 = 0xffffffff;
+	else
+		config0 = 0;
+
+	reg_write(parser, VIP_PARSER_STOP_IMM_PORT(port->port_id), config0);
+}
+
+static void vip_release_stream(struct vip_stream *stream)
+{
+	struct vip_dev *dev = stream->port->dev;
+
+	vip_dbg(1, stream, "%s: stream instance %pa\n",
+		__func__, &stream);
+
+	vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
+	vpdma_free_desc_buf(&stream->desc_list.buf);
+	vpdma_free_desc_list(&stream->desc_list);
+}
+
+static void vip_release_port(struct vip_port *port)
+{
+	vip_dbg(1, port, "%s: port instance %pa\n",
+		__func__, &port);
+
+	vpdma_free_desc_buf(&port->mmr_adb);
+	vpdma_free_desc_buf(&port->sc_coeff_h);
+	vpdma_free_desc_buf(&port->sc_coeff_v);
+}
+
+static void stop_dma(struct vip_stream *stream, bool clear_list)
+{
+	struct vip_dev *dev = stream->port->dev;
+	int ch, size = 0;
+
+	/* Create a list of channels to be cleared */
+	for (ch = 0; ch < VPDMA_MAX_CHANNELS; ch++) {
+		if (stream->vpdma_channels[ch] == 1) {
+			stream->vpdma_channels_to_abort[size++] = ch;
+			vip_dbg(2, stream, "Clear channel no: %d\n", ch);
+		}
+	}
+
+	/* Clear all the used channels for the list */
+	vpdma_list_cleanup(dev->shared->vpdma, stream->list_num,
+			   stream->vpdma_channels_to_abort, size);
+
+	if (clear_list)
+		for (ch = 0; ch < VPDMA_MAX_CHANNELS; ch++)
+			stream->vpdma_channels[ch] = 0;
+}
+
+static int vip_open(struct file *file)
+{
+	struct vip_stream *stream = video_drvdata(file);
+	struct vip_port *port = stream->port;
+	struct vip_dev *dev = port->dev;
+	int ret = 0;
+
+	vip_dbg(2, stream, "%s\n", __func__);
+
+	mutex_lock(&dev->mutex);
+
+	ret = v4l2_fh_open(file);
+	if (ret) {
+		vip_err(stream, "v4l2_fh_open failed\n");
+		goto unlock;
+	}
+
+	/*
+	 * If this is the first open file.
+	 * Then initialize hw module.
+	 */
+	if (!v4l2_fh_is_singular_file(file))
+		goto unlock;
+
+	if (vip_init_stream(stream))
+		ret = -ENODEV;
+unlock:
+	mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+static int vip_release(struct file *file)
+{
+	struct vip_stream *stream = video_drvdata(file);
+	struct vip_port *port = stream->port;
+	struct vip_dev *dev = port->dev;
+	bool fh_singular;
+	int ret;
+
+	vip_dbg(2, stream, "%s\n", __func__);
+
+	mutex_lock(&dev->mutex);
+
+	/* Save the singular status before we call the clean-up helper */
+	fh_singular = v4l2_fh_is_singular_file(file);
+
+	/* the release helper will cleanup any on-going streaming */
+	ret = _vb2_fop_release(file, NULL);
+
+	free_csc(port);
+	free_scaler(port);
+
+	/*
+	 * If this is the last open file.
+	 * Then de-initialize hw module.
+	 */
+	if (fh_singular) {
+		vip_release_stream(stream);
+
+		if (--port->num_streams == 0) {
+			vip_release_port(port);
+			vip_release_dev(port->dev);
+		}
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return ret;
+}
+
+static const struct v4l2_file_operations vip_fops = {
+	.owner		= THIS_MODULE,
+	.open		= vip_open,
+	.release	= vip_release,
+	.read		= vb2_fop_read,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= vb2_fop_mmap,
+};
+
+static struct video_device vip_videodev = {
+	.name		= VIP_MODULE_NAME,
+	.fops		= &vip_fops,
+	.ioctl_ops	= &vip_ioctl_ops,
+	.minor		= -1,
+	.release	= video_device_release,
+	.tvnorms	= V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+};
+
+static int alloc_stream(struct vip_port *port, int stream_id, int vfl_type)
+{
+	struct vip_stream *stream;
+	struct vip_dev *dev = port->dev;
+	struct vb2_queue *q;
+	struct video_device *vfd;
+	struct vip_buffer *buf;
+	struct list_head *pos, *tmp;
+	int ret, i;
+	u32 vin_id;
+
+	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+	if (!stream)
+		return -ENOMEM;
+
+	stream->port = port;
+	stream->stream_id = stream_id;
+	stream->vfl_type = vfl_type;
+
+	vin_id = 1 + ((dev->instance_id - 1) * 2) + dev->slice_id;
+	snprintf(stream->name, sizeof(stream->name), "vin%d%c-%d",
+		 vin_id, (port->port_id == VIP_PORTA) ? 'a' : 'b', stream_id);
+
+	stream->list_num = vpdma_hwlist_alloc(dev->shared->vpdma, stream);
+	if (stream->list_num < 0) {
+		vip_err(stream, "Could not get VPDMA hwlist");
+		ret = -ENODEV;
+		goto do_free_stream;
+	}
+
+	INIT_LIST_HEAD(&stream->post_bufs);
+
+	if (vfl_type == VFL_TYPE_GRABBER)
+		port->cap_streams[stream_id] = stream;
+	else
+		port->vbi_streams[stream_id] = stream;
+
+	/*
+	 * Initialize queue
+	 */
+	q = &stream->vb_vidq;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+	q->drv_priv = stream;
+	q->buf_struct_size = sizeof(struct vip_buffer);
+	q->ops = &vip_video_qops;
+	q->mem_ops = &vb2_dma_contig_memops;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->lock = &dev->mutex;
+	q->min_buffers_needed = 3;
+	q->dev = dev->v4l2_dev->dev;
+
+	ret = vb2_queue_init(q);
+	if (ret)
+		goto do_free_hwlist;
+
+	INIT_WORK(&stream->recovery_work, vip_overflow_recovery_work);
+
+	INIT_LIST_HEAD(&stream->vidq);
+
+	/* Allocate/populate Drop queue entries */
+	INIT_LIST_HEAD(&stream->dropq);
+	for (i = 0; i < VIP_DROPQ_SIZE; i++) {
+		buf = kzalloc(sizeof(*buf), GFP_ATOMIC);
+		if (!buf) {
+			ret = -ENOMEM;
+			goto do_free_dropq;
+		}
+		buf->drop = true;
+		list_add(&buf->list, &stream->dropq);
+	}
+
+	vfd = video_device_alloc();
+	if (!vfd)
+		goto do_free_dropq;
+	*vfd = vip_videodev;
+	vfd->v4l2_dev = dev->v4l2_dev;
+	vfd->queue = q;
+
+	vfd->lock = &dev->mutex;
+	video_set_drvdata(vfd, stream);
+
+	ret = video_register_device(vfd, vfl_type, -1);
+	if (ret) {
+		vip_err(stream, "Failed to register video device\n");
+		goto do_free_vfd;
+	}
+
+	stream->vfd = vfd;
+
+	vip_info(stream, "device registered as %s\n",
+		 video_device_node_name(vfd));
+	return 0;
+
+do_free_vfd:
+	video_device_release(vfd);
+do_free_dropq:
+	list_for_each_safe(pos, tmp, &stream->dropq) {
+		buf = list_entry(pos,
+				 struct vip_buffer, list);
+		vip_dbg(1, dev, "dropq buffer\n");
+		list_del(pos);
+		kfree(buf);
+	}
+do_free_hwlist:
+	vpdma_hwlist_release(dev->shared->vpdma, stream->list_num);
+do_free_stream:
+	kfree(stream);
+	return ret;
+}
+
+static void free_stream(struct vip_stream *stream)
+{
+	struct vip_dev *dev;
+	struct vip_buffer *buf;
+	struct list_head *pos, *q;
+
+	if (!stream)
+		return;
+
+	dev = stream->port->dev;
+	/* Free up the Drop queue */
+	list_for_each_safe(pos, q, &stream->dropq) {
+		buf = list_entry(pos,
+				 struct vip_buffer, list);
+		vip_dbg(1, stream, "dropq buffer\n");
+		list_del(pos);
+		kfree(buf);
+	}
+
+	video_unregister_device(stream->vfd);
+	video_device_release(stream->vfd);
+	vpdma_hwlist_release(dev->shared->vpdma, stream->list_num);
+	stream->port->cap_streams[stream->stream_id] = NULL;
+	kfree(stream);
+}
+
+static int get_subdev_active_format(struct vip_port *port,
+				    struct v4l2_subdev *subdev)
+{
+	struct vip_fmt *fmt;
+	struct v4l2_subdev_mbus_code_enum mbus_code;
+	int ret = 0;
+	unsigned int k, i, j;
+	enum vip_csc_state csc;
+
+	/* Enumerate sub device formats and enable all matching local formats */
+	port->num_active_fmt = 0;
+	for (k = 0, i = 0; (ret != -EINVAL); k++) {
+		memset(&mbus_code, 0, sizeof(mbus_code));
+		mbus_code.index = k;
+		ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
+				       NULL, &mbus_code);
+		if (ret)
+			continue;
+
+		vip_dbg(2, port,
+			"subdev %s: code: %04x idx: %d\n",
+			subdev->name, mbus_code.code, k);
+
+		for (j = 0; j < ARRAY_SIZE(vip_formats); j++) {
+			fmt = &vip_formats[j];
+			if (mbus_code.code != fmt->code)
+				continue;
+
+			/*
+			 * When the port is configured for BT656
+			 * then none of the downstream unit can be used.
+			 * So here we need to skip all format requiring
+			 * either CSC or CHR_DS
+			 */
+			csc = vip_csc_direction(fmt->code, fmt->fourcc);
+			if (port->endpoint->bus_type == V4L2_MBUS_BT656 &&
+			    (csc != VIP_CSC_NA || fmt->coplanar))
+				continue;
+
+			port->active_fmt[i] = fmt;
+			vip_dbg(2, port,
+				"matched fourcc: %s: code: %04x idx: %d\n",
+				fourcc_to_str(fmt->fourcc), fmt->code, i);
+			port->num_active_fmt = ++i;
+		}
+	}
+
+	if (i == 0) {
+		vip_err(port, "No suitable format reported by subdev %s\n",
+			subdev->name);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int alloc_port(struct vip_dev *dev, int id)
+{
+	struct vip_port *port;
+	u32 vin_id;
+
+	port = devm_kzalloc(&dev->pdev->dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	dev->ports[id] = port;
+	port->dev = dev;
+	port->port_id = id;
+	vin_id = 1 + ((dev->instance_id - 1) * 2) + dev->slice_id;
+	snprintf(port->name, sizeof(port->name),
+		 "vin%d%c", vin_id, (id == VIP_PORTA) ? 'a' : 'b');
+	port->num_streams = 0;
+	return 0;
+}
+
+static void free_port(struct vip_port *port)
+{
+	if (!port)
+		return;
+
+	v4l2_async_notifier_unregister(&port->notifier);
+	free_stream(port->cap_streams[0]);
+}
+
+static int get_field(u32 value, u32 mask, int shift)
+{
+	return (value & (mask << shift)) >> shift;
+}
+
+static int vip_of_probe(struct platform_device *pdev);
+static void vip_vpdma_fw_cb(struct platform_device *pdev)
+{
+	dev_info(&pdev->dev, "VPDMA firmware loaded\n");
+
+	if (pdev->dev.of_node)
+		vip_of_probe(pdev);
+}
+
+static int vip_create_streams(struct vip_port *port,
+			      struct v4l2_subdev *subdev)
+{
+	struct v4l2_fwnode_bus_parallel *bus;
+	int i;
+
+	for (i = 0; i < VIP_CAP_STREAMS_PER_PORT; i++)
+		free_stream(port->cap_streams[i]);
+
+	if (get_subdev_active_format(port, subdev))
+		return -ENODEV;
+
+	port->subdev = subdev;
+
+	if (port->endpoint->bus_type == V4L2_MBUS_PARALLEL) {
+		port->flags |= FLAG_MULT_PORT;
+		port->num_streams_configured = 1;
+		alloc_stream(port, 0, VFL_TYPE_GRABBER);
+	} else if (port->endpoint->bus_type == V4L2_MBUS_BT656) {
+		port->flags |= FLAG_MULT_PORT;
+		bus = &port->endpoint->bus.parallel;
+		port->num_streams_configured = bus->num_channels;
+		for (i = 0; i < bus->num_channels; i++) {
+			if (bus->channels[i] >= 16)
+				continue;
+			alloc_stream(port, bus->channels[i], VFL_TYPE_GRABBER);
+		}
+	}
+	return 0;
+}
+
+static int vip_async_bound(struct v4l2_async_notifier *notifier,
+			   struct v4l2_subdev *subdev,
+			   struct v4l2_async_subdev *asd)
+{
+	struct vip_port *port = notifier_to_vip_port(notifier);
+	struct vip_async_config *config = &port->config;
+	unsigned int idx = asd - &config->asd[0];
+	int ret;
+
+	vip_dbg(1, port, "%s\n", __func__);
+	if (idx > config->asd_sizes)
+		return -EINVAL;
+
+	if (port->subdev) {
+		if (asd < port->subdev->asd)
+			/* Notified of a subdev earlier in the array */
+			vip_info(port, "Switching to subdev %s (High priority)",
+				 subdev->name);
+
+		else {
+			vip_info(port, "Rejecting subdev %s (Low priority)",
+				 subdev->name);
+			return 0;
+		}
+	}
+
+	port->endpoint = &config->endpoints[idx];
+	vip_info(port, "Port %c: Using subdev %s for capture\n",
+		 port->port_id == VIP_PORTA ? 'A' : 'B', subdev->name);
+
+	ret = vip_create_streams(port, subdev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int vip_async_complete(struct v4l2_async_notifier *notifier)
+{
+	struct vip_port *port = notifier_to_vip_port(notifier);
+
+	vip_dbg(1, port, "%s\n", __func__);
+	return 0;
+}
+
+static const struct v4l2_async_notifier_operations vip_async_ops = {
+	.bound = vip_async_bound,
+	.complete = vip_async_complete,
+};
+
+static struct device_node *
+of_get_next_available_port(const struct device_node *parent,
+			   struct device_node *prev)
+{
+	struct device_node *port = NULL;
+
+	if (!parent)
+		return NULL;
+
+	do {
+		port = of_get_next_available_child(parent, prev);
+		if (!port)
+			return NULL;
+
+		prev = port;
+	} while (of_node_cmp(port->name, "port") != 0);
+
+	return port;
+}
+
+static struct device_node *
+of_get_next_endpoint(const struct device_node *parent,
+		     struct device_node *prev)
+{
+	struct device_node *ep = NULL;
+
+	if (!parent)
+		return NULL;
+
+	do {
+		ep = of_get_next_child(parent, prev);
+		if (!ep)
+			return NULL;
+		prev = ep;
+	} while (of_node_cmp(ep->name, "endpoint") != 0);
+
+	return ep;
+}
+
+static int vip_register_subdev_notif(struct vip_port *port,
+				     struct device_node *port_node)
+{
+	struct vip_async_config *config = &port->config;
+	struct v4l2_async_notifier *notifier = &port->notifier;
+	struct v4l2_async_subdev *asd;
+	struct vip_dev *dev = port->dev;
+	struct device_node *ep_node = NULL, *subdev_node, *subdev_ep;
+	int i = 0, ret;
+
+	while (i < VIP_MAX_SUBDEV) {
+		subdev_node = NULL;
+		subdev_ep = NULL;
+		ep_node = of_get_next_endpoint(port_node, ep_node);
+		if (!ep_node) {
+			vip_dbg(3, port, "can't get next endpoint: loop: %d\n",
+				i);
+			break;
+		}
+
+		subdev_node = of_graph_get_remote_port_parent(ep_node);
+		if (!subdev_node) {
+			vip_dbg(3, port, "can't get remote parent: loop: %d\n",
+				i);
+			goto of_node_cleanup;
+		}
+
+		subdev_ep = of_graph_get_remote_endpoint(ep_node);
+		if (!subdev_ep) {
+			vip_dbg(3, port, "can't get remote-endpoint: loop: %d\n",
+				i);
+			goto of_node_cleanup;
+		}
+
+		ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(subdev_ep),
+						 &config->endpoints[i]);
+		if (ret) {
+			vip_dbg(3, port, "Failed to parse endpoint: loop: %d\n",
+				i);
+			goto of_node_cleanup;
+		}
+
+		asd = &config->asd[i];
+		asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
+		asd->match.fwnode = of_fwnode_handle(subdev_node);
+		config->asd_list[i] = asd;
+		i++;
+
+of_node_cleanup:
+		if (!subdev_ep)
+			of_node_put(subdev_ep);
+		if (!subdev_node)
+			of_node_put(subdev_node);
+	}
+
+	if (i == 0) {
+		vip_err(port, "Port %c enabled but no endpoints found\n",
+			port->port_id == VIP_PORTA ? 'A' : 'B');
+		ret = -EINVAL;
+		goto skip_async;
+	}
+
+	config->asd_sizes = i;
+	notifier->ops = &vip_async_ops;
+	notifier->subdevs = config->asd_list;
+	notifier->num_subdevs = config->asd_sizes;
+
+	vip_dbg(1, port, "register async notifier for %d subdevs\n", i);
+	ret = v4l2_async_notifier_register(dev->v4l2_dev, notifier);
+	if (ret) {
+		vip_dbg(1, port, "Error registering async notifier\n");
+		ret = -EINVAL;
+	}
+
+skip_async:
+	if (!subdev_ep)
+		of_node_put(subdev_ep);
+	if (!subdev_node)
+		of_node_put(subdev_node);
+	if (!ep_node)
+		of_node_put(ep_node);
+
+	return ret;
+}
+
+static int vip_of_probe(struct platform_device *pdev)
+{
+	struct vip_shared *shared = platform_get_drvdata(pdev);
+	struct regmap *syscon_pol = NULL;
+	u32 syscon_pol_offset = 0;
+	struct vip_port *port;
+	struct vip_dev *dev;
+
+	struct device_node *parent = pdev->dev.of_node;
+	struct device_node *port_node = NULL;
+	int ret, slice_id, port_id;
+	u32 regval = 0;
+
+	if (parent && of_property_read_bool(parent, "syscon-pol")) {
+		syscon_pol = syscon_regmap_lookup_by_phandle(parent,
+							     "syscon-pol");
+		if (IS_ERR(syscon_pol)) {
+			dev_err(&pdev->dev, "failed to get syscon-pol regmap\n");
+			return PTR_ERR(syscon_pol);
+		}
+
+		if (of_property_read_u32_index(parent, "syscon-pol", 1,
+					       &syscon_pol_offset)) {
+			dev_err(&pdev->dev, "failed to get syscon-pol offset\n");
+			return -EINVAL;
+		}
+	}
+
+	while (1) {
+		port_node = of_get_next_available_port(parent, port_node);
+		if (!port_node)
+			break;
+
+		/* Find the port from <REG> */
+		of_property_read_u32(port_node, "reg", &regval);
+		switch (regval) {
+		case 0:
+			slice_id = VIP_SLICE1;	port_id = VIP_PORTA;
+			break;
+		case 1:
+			slice_id = VIP_SLICE2;	port_id = VIP_PORTA;
+			break;
+		case 2:
+			slice_id = VIP_SLICE1;	port_id = VIP_PORTB;
+			break;
+		case 3:
+			slice_id = VIP_SLICE2;	port_id = VIP_PORTB;
+			break;
+		default:
+			dev_err(&pdev->dev, "Unknown port reg=<%d>\n", regval);
+			continue;
+		}
+
+		dev = shared->devs[slice_id];
+		dev->syscon_pol = syscon_pol;
+		dev->syscon_pol_offset = syscon_pol_offset;
+		alloc_port(dev, port_id);
+		port = dev->ports[port_id];
+
+		ret = vip_register_subdev_notif(port, port_node);
+		of_node_put(port_node);
+	}
+	return 0;
+}
+
+static const struct of_device_id vip_of_match[];
+static int vip_probe(struct platform_device *pdev)
+{
+	struct vip_dev *dev;
+	struct vip_shared *shared;
+	struct vip_parser_data *parser;
+	const struct of_device_id *of_dev_id;
+	struct pinctrl *pinctrl;
+	int ret, slice = VIP_SLICE1;
+	u32 tmp, pid;
+	struct v4l2_ctrl_handler *hdl;
+
+	pm_runtime_enable(&pdev->dev);
+
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (ret)
+		goto err_runtime_get;
+
+	of_dev_id = of_match_device(vip_of_match, &pdev->dev);
+	if (!of_dev_id) {
+		dev_err(&pdev->dev, "%s: Unable to match device\n", __func__);
+		return -ENODEV;
+	}
+
+	ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(&pdev->dev,
+			"32-bit consistent DMA enable failed\n");
+		return ret;
+	}
+
+	shared = devm_kzalloc(&pdev->dev, sizeof(*shared), GFP_KERNEL);
+	if (!shared)
+		return -ENOMEM;
+
+	shared->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vip");
+	shared->base = devm_ioremap_resource(&pdev->dev, shared->res);
+	if (IS_ERR(shared->base)) {
+		dev_err(&pdev->dev, "failed to ioremap\n");
+		ret = PTR_ERR(shared->base);
+		goto err_runtime_get;
+	}
+
+	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+
+	/* Make sure H/W module has the right functionality */
+	pid = reg_read(shared, VIP_PID);
+	tmp = get_field(pid, VIP_PID_FUNC_MASK, VIP_PID_FUNC_SHIFT);
+
+	if (tmp != VIP_PID_FUNC) {
+		dev_info(&pdev->dev, "vip: unexpected PID function: 0x%x\n",
+			 tmp);
+		ret = -ENODEV;
+		goto err_runtime_get;
+	}
+
+	ret = v4l2_device_register(&pdev->dev, &shared->v4l2_dev);
+	if (ret)
+		goto err_runtime_get;
+
+	/* enable clocks, so the firmware will load properly */
+	vip_shared_set_clock_enable(shared, 1);
+	vip_top_vpdma_reset(shared);
+
+	platform_set_drvdata(pdev, shared);
+
+	for (slice = VIP_SLICE1; slice < VIP_NUM_SLICES; slice++) {
+		u32 vin_id;
+
+		dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+		if (!dev) {
+			ret = -ENOMEM;
+			goto err_runtime_get;
+		}
+		dev->instance_id = (int)of_dev_id->data;
+		vin_id = 1 + ((dev->instance_id - 1) * 2) + slice;
+		snprintf(dev->name, sizeof(dev->name),
+			 "vin%d", vin_id);
+
+		dev->irq = platform_get_irq(pdev, slice);
+		if (!dev->irq) {
+			dev_err(&pdev->dev, "Could not get IRQ");
+			goto err_runtime_get;
+		}
+
+		if (devm_request_irq(&pdev->dev, dev->irq, vip_irq,
+				     0, dev->name, dev) < 0) {
+			ret = -ENOMEM;
+			goto dev_unreg;
+		}
+
+		spin_lock_init(&dev->slock);
+		spin_lock_init(&dev->lock);
+
+		mutex_init(&dev->mutex);
+
+		hdl = &dev->ctrl_handler;
+		v4l2_ctrl_handler_init(hdl, 11);
+		shared->v4l2_dev.ctrl_handler = hdl;
+
+		dev->slice_id = slice;
+		dev->pdev = pdev;
+		dev->res = shared->res;
+		dev->base = shared->base;
+		dev->v4l2_dev = &shared->v4l2_dev;
+
+		dev->shared = shared;
+		shared->devs[slice] = dev;
+
+		vip_top_reset(dev);
+		vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1);
+
+		parser = devm_kzalloc(&pdev->dev, sizeof(*dev->parser),
+				      GFP_KERNEL);
+		if (!parser)
+			return PTR_ERR(parser);
+
+		parser->res = platform_get_resource_byname(pdev,
+							   IORESOURCE_MEM,
+							   (slice == 0) ?
+							   "parser0" :
+							   "parser1");
+		parser->base = devm_ioremap_resource(&pdev->dev, parser->res);
+		if (IS_ERR(parser->base)) {
+			ret = PTR_ERR(parser->base);
+			goto dev_unreg;
+		}
+		parser->pdev = pdev;
+		dev->parser = parser;
+
+		dev->sc_assigned = VIP_NOT_ASSIGNED;
+		dev->sc = sc_create(pdev, (slice == 0) ? "sc0" : "sc1");
+		if (IS_ERR(dev->sc)) {
+			ret = PTR_ERR(dev->sc);
+			goto dev_unreg;
+		}
+
+		dev->csc_assigned = VIP_NOT_ASSIGNED;
+		dev->csc = csc_create(pdev, (slice == 0) ? "csc0" : "csc1");
+		if (IS_ERR(dev->sc)) {
+			ret = PTR_ERR(dev->sc);
+			goto dev_unreg;
+		}
+	}
+
+	shared->vpdma = &shared->vpdma_data;
+	ret = vpdma_create(pdev, shared->vpdma, vip_vpdma_fw_cb);
+	if (ret) {
+		dev_err(&pdev->dev, "Creating VPDMA failed");
+		goto dev_unreg;
+	}
+
+	return 0;
+dev_unreg:
+	v4l2_device_unregister(&shared->v4l2_dev);
+err_runtime_get:
+	if (slice == VIP_SLICE1) {
+		pm_runtime_disable(&pdev->dev);
+		return ret;
+	} else {
+		return 0;
+	}
+}
+
+static int vip_remove(struct platform_device *pdev)
+{
+	struct vip_shared *shared = platform_get_drvdata(pdev);
+	struct vip_dev *dev;
+	int slice;
+
+	for (slice = 0; slice < VIP_NUM_SLICES; slice++) {
+		dev = shared->devs[slice];
+		if (!dev)
+			continue;
+
+		free_port(dev->ports[VIP_PORTA]);
+		free_port(dev->ports[VIP_PORTB]);
+	}
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id vip_of_match[] = {
+	{
+		.compatible = "ti,vip1", .data = (void *)VIP_INSTANCE1,
+	},
+
+	{
+		.compatible = "ti,vip2", .data = (void *)VIP_INSTANCE2,
+	},
+
+	{
+		.compatible = "ti,vip3", .data = (void *)VIP_INSTANCE3,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, vip_of_match);
+#endif
+
+static struct platform_driver vip_pdrv = {
+	.probe		= vip_probe,
+	.remove		= vip_remove,
+	.driver		= {
+		.name	= VIP_MODULE_NAME,
+		.of_match_table = of_match_ptr(vip_of_match),
+	},
+};
+
+module_platform_driver(vip_pdrv);
+
+MODULE_DESCRIPTION("TI VIP driver");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_LICENSE("GPL v2");

+ 727 - 0
drivers/media/platform/ti-vpe/vip.h

@@ -0,0 +1,727 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * TI VIP capture driver
+ *
+ * Copyright (C) 2018 Texas Instruments Incorpated - http://www.ti.com/
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Nikhil Devshatwar, <nikhil.nd@ti.com>
+ * Benoit Parrot, <bparrot@ti.com>
+ */
+
+#ifndef __TI_VIP_H
+#define __TI_VIP_H
+
+#include <linux/videodev2.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-memops.h>
+#include <media/v4l2-fwnode.h>
+
+#include "vpdma.h"
+#include "vpdma_priv.h"
+#include "sc.h"
+#include "csc.h"
+
+#define VIP_INSTANCE1	1
+#define VIP_INSTANCE2	2
+#define VIP_INSTANCE3	3
+
+#define VIP_SLICE1	0
+#define VIP_SLICE2	1
+#define VIP_NUM_SLICES	2
+
+/*
+ * Additionnal client identifiers used for VPDMA configuration descriptors
+ */
+#define VIP_SLICE1_CFD_SC_CLIENT	7
+#define VIP_SLICE2_CFD_SC_CLIENT	8
+
+#define VIP_PORTA	0
+#define VIP_PORTB	1
+#define VIP_NUM_PORTS	2
+
+#define VIP_MAX_PLANES	2
+#define	VIP_LUMA	0
+#define VIP_CHROMA	1
+
+#define VIP_CAP_STREAMS_PER_PORT	16
+#define VIP_VBI_STREAMS_PER_PORT	16
+
+#define VIP_MAX_SUBDEV			5
+/*
+ * This value needs to be at least as large as the number of entry in
+ * vip_formats[].
+ * When vip_formats[] is modified make sure to adjust this value also.
+ */
+#define VIP_MAX_ACTIVE_FMT		16
+/*
+ * Colorspace conversion unit can be in one of 3 modes:
+ * NA  - Not Available on this port
+ * Y2R - Needed for YUV to RGB on this port
+ * R2Y - Needed for RGB to YUV on this port
+ */
+enum vip_csc_state {
+	VIP_CSC_NA = 0,
+	VIP_CSC_Y2R,
+	VIP_CSC_R2Y,
+};
+
+/* buffer for one video frame */
+struct vip_buffer {
+	/* common v4l buffer stuff */
+	struct vb2_v4l2_buffer	vb;
+	struct list_head	list;
+	bool			drop;
+};
+
+/*
+ * struct vip_fmt - VIP media bus format information
+ * @fourcc: V4L2 pixel format FCC identifier
+ * @code: V4L2 media bus format code
+ * @colorspace: V4L2 colorspace identifier
+ * @coplanar: 1 if unpacked Luma and Chroma, 0 otherwise (packed/interleaved)
+ * @vpdma_fmt: VPDMA data format per plane.
+ */
+struct vip_fmt {
+	u32	fourcc;
+	u32	code;
+	u32	colorspace;
+	u8	coplanar;
+	const struct vpdma_data_format *vpdma_fmt[VIP_MAX_PLANES];
+};
+
+/*
+ * The vip_parser_data structures contains the memory mapped
+ * info to access the parser registers.
+ */
+struct vip_parser_data {
+	void __iomem		*base;
+	struct resource		*res;
+
+	int			slice_id;
+
+	struct platform_device *pdev;
+};
+
+/*
+ * The vip_shared structure contains data that is shared by both
+ * the VIP1 and VIP2 slices.
+ */
+struct vip_shared {
+	struct list_head	list;
+	struct resource		*res;
+	void __iomem		*base;
+	struct vpdma_data	vpdma_data;
+	struct vpdma_data	*vpdma;
+	struct v4l2_device	v4l2_dev;
+	struct vip_dev		*devs[VIP_NUM_SLICES];
+};
+
+/*
+ * There are two vip_dev structure, one for each vip slice: VIP1 & VIP2.
+ */
+
+struct vip_async_config {
+	struct v4l2_async_subdev *asd_list[VIP_MAX_SUBDEV];
+	struct v4l2_async_subdev asd[VIP_MAX_SUBDEV];
+	struct v4l2_fwnode_endpoint endpoints[VIP_MAX_SUBDEV];
+	int asd_sizes;
+};
+
+
+struct vip_dev {
+	struct v4l2_device	*v4l2_dev;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct platform_device *pdev;
+	struct vip_shared	*shared;
+	struct resource		*res;
+	struct regmap		*syscon_pol;
+	u32			syscon_pol_offset;
+	int			instance_id;
+	int			slice_id;
+	int			num_ports;	/* count of open ports */
+	struct mutex		mutex;
+	spinlock_t		slock;
+	spinlock_t		lock; /* used in videobuf2 callback */
+
+	int			irq;
+	void __iomem		*base;
+
+	struct vip_port		*ports[VIP_NUM_PORTS];
+
+	char			name[16];
+	/* parser data handle */
+	struct vip_parser_data	*parser;
+	/* scaler data handle */
+	struct sc_data		*sc;
+	/* scaler port assignation */
+	int			sc_assigned;
+	/* csc data handle */
+	struct csc_data		*csc;
+	/* csc port assignation */
+	int			csc_assigned;
+};
+
+/*
+ * There are two vip_port structures for each vip_dev, one for port A
+ * and one for port B.
+ */
+struct vip_port {
+	struct vip_dev		*dev;
+	int			port_id;
+
+	enum v4l2_colorspace	src_colorspace;
+	unsigned int		flags;
+	unsigned int		src_width;
+	unsigned int		src_height;
+	struct v4l2_rect	c_rect;		/* crop rectangle */
+	struct v4l2_mbus_framefmt mbus_framefmt;
+	struct v4l2_mbus_framefmt try_mbus_framefmt;
+
+	char			name[16];
+	struct vip_fmt		*fmt;		/* current format info */
+	/* Number of channels/streams configured */
+	int			num_streams_configured;
+	int			num_streams;	/* count of open streams */
+	struct vip_stream	*cap_streams[VIP_CAP_STREAMS_PER_PORT];
+	struct vip_stream	*vbi_streams[VIP_VBI_STREAMS_PER_PORT];
+
+	struct vip_async_config	config;
+	struct v4l2_async_notifier notifier;
+	struct v4l2_subdev	*subdev;
+	struct v4l2_fwnode_endpoint *endpoint;
+	struct vip_fmt		*active_fmt[VIP_MAX_ACTIVE_FMT];
+	int			num_active_fmt;
+	/* have new shadow reg values */
+	bool			load_mmrs;
+	/* shadow reg addr/data block */
+	struct vpdma_buf	mmr_adb;
+	/* h coeff buffer */
+	struct vpdma_buf	sc_coeff_h;
+	/* v coeff buffer */
+	struct vpdma_buf	sc_coeff_v;
+	/* Show if scaler resource is available on this port */
+	bool			scaler;
+	/* Show the csc resource state on this port */
+	enum vip_csc_state	csc;
+};
+
+/*
+ * When handling multiplexed video, there can be multiple streams for each
+ * port.  The vip_stream structure holds per-stream data.
+ */
+struct vip_stream {
+	struct video_device	*vfd;
+	struct vip_port		*port;
+	int			stream_id;
+	int			list_num;
+	int			vfl_type;
+	char			name[16];
+	struct work_struct	recovery_work;
+	int			num_recovery;
+	enum v4l2_field		field;		/* current field */
+	unsigned int		sequence;	/* current frame/field seq */
+	enum v4l2_field		sup_field;	/* supported field value */
+	unsigned int		width;		/* frame width */
+	unsigned int		height;		/* frame height */
+	unsigned int		bytesperline;	/* bytes per line in memory */
+	unsigned int		sizeimage;	/* image size in memory */
+	struct list_head	vidq;		/* incoming vip_bufs queue */
+	struct list_head	dropq;		/* drop vip_bufs queue */
+	struct list_head	post_bufs;	/* vip_bufs to be DMAed */
+	/* Maintain a list of used channels - Needed for VPDMA cleanup */
+	int			vpdma_channels[VPDMA_MAX_CHANNELS];
+	int			vpdma_channels_to_abort[VPDMA_MAX_CHANNELS];
+	struct vpdma_desc_list	desc_list;	/* DMA descriptor list */
+	struct vpdma_dtd	*write_desc;
+	/* next unused desc_list addr */
+	void			*desc_next;
+	struct vb2_queue	vb_vidq;
+};
+
+/*
+ * VIP Enumerations
+ */
+enum data_path_select {
+	ALL_FIELDS_DATA_SELECT = 0,
+	VIP_CSC_SRC_DATA_SELECT,
+	VIP_SC_SRC_DATA_SELECT,
+	VIP_RGB_SRC_DATA_SELECT,
+	VIP_RGB_OUT_LO_DATA_SELECT,
+	VIP_RGB_OUT_HI_DATA_SELECT,
+	VIP_CHR_DS_1_SRC_DATA_SELECT,
+	VIP_CHR_DS_2_SRC_DATA_SELECT,
+	VIP_MULTI_CHANNEL_DATA_SELECT,
+	VIP_CHR_DS_1_DATA_BYPASS,
+	VIP_CHR_DS_2_DATA_BYPASS,
+};
+
+
+enum data_interface_modes {
+	SINGLE_24B_INTERFACE = 0,
+	SINGLE_16B_INTERFACE = 1,
+	DUAL_8B_INTERFACE = 2,
+};
+
+enum sync_types {
+	EMBEDDED_SYNC_SINGLE_YUV422 = 0,
+	EMBEDDED_SYNC_2X_MULTIPLEXED_YUV422 = 1,
+	EMBEDDED_SYNC_4X_MULTIPLEXED_YUV422 = 2,
+	EMBEDDED_SYNC_LINE_MULTIPLEXED_YUV422 = 3,
+	DISCRETE_SYNC_SINGLE_YUV422 = 4,
+	EMBEDDED_SYNC_SINGLE_RGB_OR_YUV444 = 5,
+	DISCRETE_SYNC_SINGLE_RGB_24B = 10,
+};
+
+#define VIP_NOT_ASSIGNED	-1
+
+/*
+ * Register offsets and field selectors
+ */
+#define VIP_PID_FUNC			0xf02
+
+#define VIP_PID				0x0000
+#define VIP_PID_MINOR_MASK              0x3f
+#define VIP_PID_MINOR_SHIFT             0
+#define VIP_PID_CUSTOM_MASK             0x03
+#define VIP_PID_CUSTOM_SHIFT            6
+#define VIP_PID_MAJOR_MASK              0x07
+#define VIP_PID_MAJOR_SHIFT             8
+#define VIP_PID_RTL_MASK                0x1f
+#define VIP_PID_RTL_SHIFT               11
+#define VIP_PID_FUNC_MASK               0xfff
+#define VIP_PID_FUNC_SHIFT              16
+#define VIP_PID_SCHEME_MASK             0x03
+#define VIP_PID_SCHEME_SHIFT            30
+
+#define VIP_SYSCONFIG			0x0010
+#define VIP_SYSCONFIG_IDLE_MASK         0x03
+#define VIP_SYSCONFIG_IDLE_SHIFT        2
+#define VIP_SYSCONFIG_STANDBY_MASK      0x03
+#define VIP_SYSCONFIG_STANDBY_SHIFT     4
+#define VIP_FORCE_IDLE_MODE             0
+#define VIP_NO_IDLE_MODE                1
+#define VIP_SMART_IDLE_MODE             2
+#define VIP_SMART_IDLE_WAKEUP_MODE      3
+#define VIP_FORCE_STANDBY_MODE          0
+#define VIP_NO_STANDBY_MODE             1
+#define VIP_SMART_STANDBY_MODE          2
+#define VIP_SMART_STANDBY_WAKEUP_MODE   3
+
+#define VIP_INTC_INTX_OFFSET		0x0020
+
+#define VIP_INT0_STATUS0_RAW_SET	0x0020
+#define VIP_INT0_STATUS0_RAW		VIP_INT0_STATUS0_RAW_SET
+#define VIP_INT0_STATUS0_CLR		0x0028
+#define VIP_INT0_STATUS0		VIP_INT0_STATUS0_CLR
+#define VIP_INT0_ENABLE0_SET		0x0030
+#define VIP_INT0_ENABLE0		VIP_INT0_ENABLE0_SET
+#define VIP_INT0_ENABLE0_CLR		0x0038
+#define VIP_INT0_LIST0_COMPLETE         (1 << 0)
+#define VIP_INT0_LIST0_NOTIFY           (1 << 1)
+#define VIP_INT0_LIST1_COMPLETE         (1 << 2)
+#define VIP_INT0_LIST1_NOTIFY           (1 << 3)
+#define VIP_INT0_LIST2_COMPLETE         (1 << 4)
+#define VIP_INT0_LIST2_NOTIFY           (1 << 5)
+#define VIP_INT0_LIST3_COMPLETE         (1 << 6)
+#define VIP_INT0_LIST3_NOTIFY           (1 << 7)
+#define VIP_INT0_LIST4_COMPLETE         (1 << 8)
+#define VIP_INT0_LIST4_NOTIFY           (1 << 9)
+#define VIP_INT0_LIST5_COMPLETE         (1 << 10)
+#define VIP_INT0_LIST5_NOTIFY           (1 << 11)
+#define VIP_INT0_LIST6_COMPLETE         (1 << 12)
+#define VIP_INT0_LIST6_NOTIFY           (1 << 13)
+#define VIP_INT0_LIST7_COMPLETE         (1 << 14)
+#define VIP_INT0_LIST7_NOTIFY           (1 << 15)
+#define VIP_INT0_DESCRIPTOR             (1 << 16)
+#define VIP_VIP1_PARSER_INT		(1 << 20)
+#define VIP_VIP2_PARSER_INT		(1 << 21)
+
+#define VIP_INT0_STATUS1_RAW_SET        0x0024
+#define VIP_INT0_STATUS1_RAW            VIP_INT0_STATUS0_RAW_SET
+#define VIP_INT0_STATUS1_CLR            0x002c
+#define VIP_INT0_STATUS1                VIP_INT0_STATUS0_CLR
+#define VIP_INT0_ENABLE1_SET            0x0034
+#define VIP_INT0_ENABLE1                VIP_INT0_ENABLE0_SET
+#define VIP_INT0_ENABLE1_CLR            0x003c
+#define VIP_INT0_ENABLE1_STAT		0x004c
+#define VIP_INT0_CHANNEL_GROUP0		(1 << 0)
+#define VIP_INT0_CHANNEL_GROUP1		(1 << 1)
+#define VIP_INT0_CHANNEL_GROUP2		(1 << 2)
+#define VIP_INT0_CHANNEL_GROUP3		(1 << 3)
+#define VIP_INT0_CHANNEL_GROUP4		(1 << 4)
+#define VIP_INT0_CHANNEL_GROUP5		(1 << 5)
+#define VIP_INT0_CLIENT			(1 << 7)
+#define VIP_VIP1_DS1_UV_ERROR_INT	(1 << 22)
+#define VIP_VIP1_DS2_UV_ERROR_INT	(1 << 23)
+#define VIP_VIP2_DS1_UV_ERROR_INT	(1 << 24)
+#define VIP_VIP2_DS2_UV_ERROR_INT	(1 << 25)
+
+#define VIP_INTC_E0I			0x00a0
+
+#define VIP_CLK_ENABLE			0x0100
+#define VIP_VPDMA_CLK_ENABLE		(1 << 0)
+#define VIP_VIP1_DATA_PATH_CLK_ENABLE	(1 << 16)
+#define VIP_VIP2_DATA_PATH_CLK_ENABLE	(1 << 17)
+
+#define VIP_CLK_RESET			0x0104
+#define VIP_VPDMA_RESET			(1 << 0)
+#define VIP_VPDMA_CLK_RESET_MASK	0x1
+#define VIP_VPDMA_CLK_RESET_SHIFT	0
+#define VIP_DATA_PATH_CLK_RESET_MASK	0x1
+#define VIP_VIP1_DATA_PATH_RESET_SHIFT	16
+#define VIP_VIP2_DATA_PATH_RESET_SHIFT	17
+#define VIP_VIP1_DATA_PATH_RESET	(1 << 16)
+#define VIP_VIP2_DATA_PATH_RESET	(1 << 17)
+#define VIP_VIP1_PARSER_RESET		(1 << 18)
+#define VIP_VIP2_PARSER_RESET		(1 << 19)
+#define VIP_VIP1_CSC_RESET		(1 << 20)
+#define VIP_VIP2_CSC_RESET		(1 << 21)
+#define VIP_VIP1_SC_RESET		(1 << 22)
+#define VIP_VIP2_SC_RESET		(1 << 23)
+#define VIP_VIP1_DS1_RESET		(1 << 25)
+#define VIP_VIP2_DS1_RESET		(1 << 26)
+#define VIP_VIP1_DS2_RESET		(1 << 27)
+#define VIP_VIP2_DS2_RESET		(1 << 28)
+#define VIP_MAIN_RESET			(1 << 31)
+
+#define VIP_VIP1_DATA_PATH_SELECT	0x010c
+#define VIP_VIP2_DATA_PATH_SELECT	0x0110
+#define VIP_CSC_SRC_SELECT_MASK		0x07
+#define VIP_CSC_SRC_SELECT_SHFT		0
+#define VIP_SC_SRC_SELECT_MASK		0x07
+#define VIP_SC_SRC_SELECT_SHFT		3
+#define VIP_RGB_SRC_SELECT		(1 << 6)
+#define VIP_RGB_OUT_LO_SRC_SELECT	(1 << 7)
+#define VIP_RGB_OUT_HI_SRC_SELECT	(1 << 8)
+#define VIP_DS1_SRC_SELECT_MASK		0x07
+#define VIP_DS1_SRC_SELECT_SHFT		9
+#define VIP_DS2_SRC_SELECT_MASK		0x07
+#define VIP_DS2_SRC_SELECT_SHFT		12
+#define VIP_MULTI_CHANNEL_SELECT	(1 << 15)
+#define VIP_DS1_BYPASS			(1 << 16)
+#define VIP_DS2_BYPASS			(1 << 17)
+#define VIP_TESTPORT_B_SELECT		(1 << 26)
+#define VIP_TESTPORT_A_SELECT		(1 << 27)
+#define VIP_DATAPATH_SELECT_MASK	0x0f
+#define VIP_DATAPATH_SELECT_SHFT	28
+
+#define VIP1_PARSER_REG_OFFSET		0x5500
+#define VIP2_PARSER_REG_OFFSET		0x5a00
+
+#define VIP_PARSER_MAIN_CFG		0x0000
+#define VIP_DATA_INTERFACE_MODE_MASK	0x03
+#define VIP_DATA_INTERFACE_MODE_SHFT	0
+#define VIP_CLIP_BLANK			(1 << 4)
+#define VIP_CLIP_ACTIVE			(1 << 5)
+
+#define VIP_PARSER_PORTA_0		0x0004
+#define VIP_PARSER_PORTB_0		0x000c
+#define VIP_SYNC_TYPE_MASK		0x0f
+#define VIP_SYNC_TYPE_SHFT		0
+#define VIP_CTRL_CHANNEL_SEL_MASK	0x03
+#define VIP_CTRL_CHANNEL_SEL_SHFT	4
+#define VIP_ASYNC_FIFO_WR		(1 << 6)
+#define VIP_ASYNC_FIFO_RD		(1 << 7)
+#define VIP_PORT_ENABLE			(1 << 8)
+#define VIP_FID_POLARITY		(1 << 9)
+#define VIP_PIXCLK_EDGE_POLARITY	(1 << 10)
+#define VIP_HSYNC_POLARITY		(1 << 11)
+#define VIP_VSYNC_POLARITY		(1 << 12)
+#define VIP_ACTVID_POLARITY		(1 << 13)
+#define VIP_FID_DETECT_MODE		(1 << 14)
+#define VIP_USE_ACTVID_HSYNC_ONLY	(1 << 15)
+#define VIP_FID_SKEW_PRECOUNT_MASK	0x3f
+#define VIP_FID_SKEW_PRECOUNT_SHFT	16
+#define VIP_DISCRETE_BASIC_MODE		(1 << 22)
+#define VIP_SW_RESET			(1 << 23)
+#define VIP_FID_SKEW_POSTCOUNT_MASK	0x3f
+#define VIP_FID_SKEW_POSTCOUNT_SHFT	24
+#define VIP_ANALYZER_2X4X_SRCNUM_POS	(1 << 30)
+#define VIP_ANALYZER_FVH_ERR_COR_EN	(1 << 31)
+
+#define VIP_PARSER_PORTA_1		0x0008
+#define VIP_PARSER_PORTB_1		0x0010
+#define VIP_SRC0_NUMLINES_MASK		0x0fff
+#define VIP_SRC0_NUMLINES_SHFT		0
+#define VIP_ANC_CHAN_SEL_8B_MASK	0x03
+#define VIP_ANC_CHAN_SEL_8B_SHFT	13
+#define VIP_SRC0_NUMPIX_MASK		0x0fff
+#define VIP_SRC0_NUMPIX_SHFT		16
+#define VIP_REPACK_SEL_MASK		0x07
+#define VIP_REPACK_SEL_SHFT		28
+
+#define VIP_PARSER_FIQ_MASK		0x0014
+#define VIP_PARSER_FIQ_CLR		0x0018
+#define VIP_PARSER_FIQ_STATUS		0x001c
+#define VIP_PORTA_VDET			(1 << 0)
+#define VIP_PORTB_VDET			(1 << 1)
+#define VIP_PORTA_ASYNC_FIFO_OF		(1 << 2)
+#define VIP_PORTB_ASYNC_FIFO_OF		(1 << 3)
+#define VIP_PORTA_OUTPUT_FIFO_YUV	(1 << 4)
+#define VIP_PORTA_OUTPUT_FIFO_ANC	(1 << 6)
+#define VIP_PORTB_OUTPUT_FIFO_YUV	(1 << 7)
+#define VIP_PORTB_OUTPUT_FIFO_ANC	(1 << 9)
+#define VIP_PORTA_CONN			(1 << 10)
+#define VIP_PORTA_DISCONN		(1 << 11)
+#define VIP_PORTB_CONN			(1 << 12)
+#define VIP_PORTB_DISCONN		(1 << 13)
+#define VIP_PORTA_SRC0_SIZE		(1 << 14)
+#define VIP_PORTB_SRC0_SIZE		(1 << 15)
+#define VIP_PORTA_YUV_PROTO_VIOLATION	(1 << 16)
+#define VIP_PORTA_ANC_PROTO_VIOLATION	(1 << 17)
+#define VIP_PORTB_YUV_PROTO_VIOLATION	(1 << 18)
+#define VIP_PORTB_ANC_PROTO_VIOLATION	(1 << 19)
+#define VIP_PORTA_CFG_DISABLE_COMPLETE	(1 << 20)
+#define VIP_PORTB_CFG_DISABLE_COMPLETE	(1 << 21)
+
+#define VIP_PARSER_PORTA_SOURCE_FID	0x0020
+#define VIP_PARSER_PORTA_ENCODER_FID	0x0024
+#define VIP_PARSER_PORTB_SOURCE_FID	0x0028
+#define VIP_PARSER_PORTB_ENCODER_FID	0x002c
+
+#define VIP_PARSER_PORTA_SRC0_SIZE	0x0030
+#define VIP_PARSER_PORTB_SRC0_SIZE	0x0070
+#define VIP_SOURCE_HEIGHT_MASK		0x0fff
+#define VIP_SOURCE_HEIGHT_SHFT		0
+#define VIP_SOURCE_WIDTH_MASK		0x0fff
+#define VIP_SOURCE_WIDTH_SHFT		16
+
+#define VIP_PARSER_PORTA_VDET_VEC	0x00b0
+#define VIP_PARSER_PORTB_VDET_VEC	0x00b4
+
+#define VIP_PARSER_PORTA_EXTRA2		0x00b8
+#define VIP_PARSER_PORTB_EXTRA2		0x00c8
+#define VIP_ANC_SKIP_NUMPIX_MASK	0x0fff
+#define VIP_ANC_SKIP_NUMPIX_SHFT	0
+#define VIP_ANC_BYPASS			(1 << 15)
+#define VIP_ANC_USE_NUMPIX_MASK		0x0fff
+#define VIP_ANC_USE_NUMPIX_SHFT		16
+#define VIP_ANC_TARGET_SRCNUM_MASK	0x0f
+#define VIP_ANC_TARGET_SRCNUM_SHFT	28
+
+#define VIP_PARSER_PORTA_EXTRA3		0x00bc
+#define VIP_PARSER_PORTB_EXTRA3		0x00cc
+#define VIP_ANC_SKIP_NUMLINES_MASK	0x0fff
+#define VIP_ANC_SKIP_NUMLINES_SHFT	0
+#define VIP_ANC_USE_NUMLINES_MASK	0x0fff
+#define VIP_ANC_USE_NUMLINES_SHFT	16
+
+#define VIP_PARSER_PORTA_EXTRA4		0x00c0
+#define VIP_PARSER_PORTB_EXTRA4		0x00d0
+#define VIP_ACT_SKIP_NUMPIX_MASK	0x0fff
+#define VIP_ACT_SKIP_NUMPIX_SHFT	0
+#define VIP_ACT_BYPASS			(1 << 15)
+#define VIP_ACT_USE_NUMPIX_MASK		0x0fff
+#define VIP_ACT_USE_NUMPIX_SHFT		16
+#define VIP_ACT_TARGET_SRCNUM_MASK	0x0f
+#define VIP_ACT_TARGET_SRCNUM_SHFT	28
+
+#define VIP_PARSER_PORTA_EXTRA5		0x00c4
+#define VIP_PARSER_PORTB_EXTRA5		0x00d4
+#define VIP_ACT_SKIP_NUMLINES_MASK	0x0fff
+#define VIP_ACT_SKIP_NUMLINES_SHFT	0
+#define VIP_ACT_USE_NUMLINES_MASK	0x0fff
+#define VIP_ACT_USE_NUMLINES_SHFT	16
+
+#define VIP_PARSER_PORTA_EXTRA6		0x00d8
+#define VIP_PARSER_PORTB_EXTRA6		0x00dc
+#define VIP_ANC_SRCNUM_STOP_IMM_SHFT	0
+#define VIP_YUV_SRCNUM_STOP_IMM_SHFT	16
+
+#define VIP_CSC_CSC00			0x0200
+#define VIP_CSC_A0_MASK			0x1fff
+#define VIP_CSC_A0_SHFT			0
+#define VIP_CSC_B0_MASK			0x1fff
+#define VIP_CSC_B0_SHFT			16
+
+#define VIP_CSC_CSC01			0x0204
+#define VIP_CSC_C0_MASK			0x1fff
+#define VIP_CSC_C0_SHFT			0
+#define VIP_CSC_A1_MASK			0x1fff
+#define VIP_CSC_A1_SHFT			16
+
+#define VIP_CSC_CSC02			0x0208
+#define VIP_CSC_B1_MASK			0x1fff
+#define VIP_CSC_B1_SHFT			0
+#define VIP_CSC_C1_MASK			0x1fff
+#define VIP_CSC_C1_SHFT			16
+
+#define VIP_CSC_CSC03			0x020c
+#define VIP_CSC_A2_MASK			0x1fff
+#define VIP_CSC_A2_SHFT			0
+#define VIP_CSC_B2_MASK			0x1fff
+#define VIP_CSC_B2_SHFT			16
+
+#define VIP_CSC_CSC04			0x0210
+#define VIP_CSC_C2_MASK			0x1fff
+#define VIP_CSC_C2_SHFT			0
+#define VIP_CSC_D0_MASK			0x0fff
+#define VIP_CSC_D0_SHFT			16
+
+#define VIP_CSC_CSC05			0x0214
+#define VIP_CSC_D1_MASK			0x0fff
+#define VIP_CSC_D1_SHFT			0
+#define VIP_CSC_D2_MASK			0x0fff
+#define VIP_CSC_D2_SHFT			16
+#define VIP_CSC_BYPASS			(1 << 28)
+
+#define VIP_SC_MP_SC0			0x0300
+#define VIP_INTERLACE_O			(1 << 0)
+#define VIP_LINEAR			(1 << 1)
+#define VIP_SC_BYPASS			(1 << 2)
+#define VIP_INVT_FID			(1 << 3)
+#define VIP_USE_RAV			(1 << 4)
+#define VIP_ENABLE_EV			(1 << 5)
+#define VIP_AUTH_HS			(1 << 6)
+#define VIP_DCM_2X			(1 << 7)
+#define VIP_DCM_4X			(1 << 8)
+#define VIP_HP_BYPASS			(1 << 9)
+#define VIP_INTERLACE_I			(1 << 10)
+#define VIP_ENABLE_SIN2_VER_INTP	(1 << 11)
+#define VIP_Y_PK_EN			(1 << 14)
+#define VIP_TRIM			(1 << 15)
+#define VIP_SELFGEN_FID			(1 << 16)
+
+#define VIP_SC_MP_SC1			0x0304
+#define VIP_ROW_ACC_INC_MASK		0x07ffffff
+#define VIP_ROW_ACC_INC_SHFT		0
+
+#define VIP_SC_MP_SC2			0x0308
+#define VIP_ROW_ACC_OFFSET_MASK		0x0fffffff
+#define VIP_ROW_ACC_OFFSET_SHFT		0
+
+#define VIP_SC_MP_SC3			0x030c
+#define VIP_ROW_ACC_OFFSET_B_MASK	0x0fffffff
+#define VIP_ROW_ACC_OFFSET_B_SHFT	0
+
+#define VIP_SC_MP_SC4			0x0310
+#define VIP_TAR_H_MASK			0x07ff
+#define VIP_TAR_H_SHFT			0
+#define VIP_TAR_W_MASK			0x07ff
+#define VIP_TAR_W_SHFT			12
+#define VIP_LIN_ACC_INC_U_MASK		0x07
+#define VIP_LIN_ACC_INC_U_SHFT		24
+#define VIP_NLIN_ACC_INIT_U_MASK	0x07
+#define VIP_NLIN_ACC_INIT_U_SHFT	28
+
+#define VIP_SC_MP_SC5			0x0314
+#define VIP_SRC_H_MASK			0x03ff
+#define VIP_SRC_H_SHFT			0
+#define VIP_SRC_W_MASK			0x07ff
+#define VIP_SRC_W_SHFT			12
+#define VIP_NLIN_ACC_INC_U_MASK		0x07
+#define VIP_NLIN_ACC_INC_U_SHFT		24
+
+#define VIP_SC_MP_SC6			0x0318
+#define VIP_ROW_ACC_INIT_RAV_MASK	0x03ff
+#define VIP_ROW_ACC_INIT_RAV_SHFT	0
+#define VIP_ROW_ACC_INIT_RAV_B_MASK	0x03ff
+#define VIP_ROW_ACC_INIT_RAV_B_SHFT	10
+
+#define VIP_SC_MP_SC8			0x0320
+#define VIP_NLIN_LEFT_MASK		0x07ff
+#define VIP_NLIN_LEFT_SHFT		0
+#define VIP_NLIN_RIGHT_MASK		0x07ff
+#define VIP_NLIN_RIGHT_SHFT		12
+
+#define VIP_SC_MP_SC9			0x0324
+#define VIP_LIN_ACC_INC			VIP_SC_MP_SC9
+
+#define VIP_SC_MP_SC10			0x0328
+#define VIP_NLIN_ACC_INIT		VIP_SC_MP_SC10
+
+#define VIP_SC_MP_SC11			0x032c
+#define VIP_NLIN_ACC_INC		VIP_SC_MP_SC11
+
+#define VIP_SC_MP_SC12			0x0330
+#define VIP_COL_ACC_OFFSET_MASK		0x01ffffff
+#define VIP_COL_ACC_OFFSET_SHFT		0
+
+#define VIP_SC_MP_SC13			0x0334
+#define VIP_SC_FACTOR_RAV_MASK		0x03ff
+#define VIP_SC_FACTOR_RAV_SHFT		0
+#define VIP_CHROMA_INTP_THR_MASK	0x03ff
+#define VIP_CHROMA_INTP_THR_SHFT	12
+#define VIP_DELTA_CHROMA_THR_MASK	0x0f
+#define VIP_DELTA_CHROMA_THR_SHFT	24
+
+#define VIP_SC_MP_SC17			0x0344
+#define VIP_EV_THR_MASK			0x03ff
+#define VIP_EV_THR_SHFT			12
+#define VIP_DELTA_LUMA_THR_MASK		0x0f
+#define VIP_DELTA_LUMA_THR_SHFT		24
+#define VIP_DELTA_EV_THR_MASK		0x0f
+#define VIP_DELTA_EV_THR_SHFT		28
+
+#define VIP_SC_MP_SC18			0x0348
+#define VIP_HS_FACTOR_MASK		0x03ff
+#define VIP_HS_FACTOR_SHFT		0
+#define VIP_CONF_DEFAULT_MASK		0x01ff
+#define VIP_CONF_DEFAULT_SHFT		16
+
+#define VIP_SC_MP_SC19			0x034c
+#define VIP_HPF_COEFF0_MASK		0xff
+#define VIP_HPF_COEFF0_SHFT		0
+#define VIP_HPF_COEFF1_MASK		0xff
+#define VIP_HPF_COEFF1_SHFT		8
+#define VIP_HPF_COEFF2_MASK		0xff
+#define VIP_HPF_COEFF2_SHFT		16
+#define VIP_HPF_COEFF3_MASK		0xff
+#define VIP_HPF_COEFF3_SHFT		23
+
+#define VIP_SC_MP_SC20			0x0350
+#define VIP_HPF_COEFF4_MASK		0xff
+#define VIP_HPF_COEFF4_SHFT		0
+#define VIP_HPF_COEFF5_MASK		0xff
+#define VIP_HPF_COEFF5_SHFT		8
+#define VIP_HPF_NORM_SHFT_MASK		0x07
+#define VIP_HPF_NORM_SHFT_SHFT		16
+#define VIP_NL_LIMIT_MASK		0x1ff
+#define VIP_NL_LIMIT_SHFT		20
+
+#define VIP_SC_MP_SC21			0x0354
+#define VIP_NL_LO_THR_MASK		0x01ff
+#define VIP_NL_LO_THR_SHFT		0
+#define VIP_NL_LO_SLOPE_MASK		0xff
+#define VIP_NL_LO_SLOPE_SHFT		16
+
+#define VIP_SC_MP_SC22			0x0358
+#define VIP_NL_HI_THR_MASK		0x01ff
+#define VIP_NL_HI_THR_SHFT		0
+#define VIP_NL_HI_SLOPE_SH_MASK		0x07
+#define VIP_NL_HI_SLOPE_SH_SHFT		16
+
+#define VIP_SC_MP_SC23			0x035c
+#define VIP_GRADIENT_THR_MASK		0x07ff
+#define VIP_GRADIENT_THR_SHFT		0
+#define VIP_GRADIENT_THR_RANGE_MASK	0x0f
+#define VIP_GRADIENT_THR_RANGE_SHFT	12
+#define VIP_MIN_GY_THR_MASK		0xff
+#define VIP_MIN_GY_THR_SHFT		16
+#define VIP_MIN_GY_THR_RANGE_MASK	0x0f
+#define VIP_MIN_GY_THR_RANGE_SHFT	28
+
+#define VIP_SC_MP_SC24			0x0360
+#define VIP_ORG_H_MASK			0x07ff
+#define VIP_ORG_H_SHFT			0
+#define VIP_ORG_W_MASK			0x07ff
+#define VIP_ORG_W_SHFT			16
+
+#define VIP_SC_MP_SC25			0x0364
+#define VIP_OFF_H_MASK			0x07ff
+#define VIP_OFF_H_SHFT			0
+#define VIP_OFF_W_MASK			0x07ff
+#define VIP_OFF_W_SHFT			16
+
+#define VIP_VPDMA_REG_OFFSET		0xd000
+
+#endif

+ 9 - 2
drivers/media/platform/ti-vpe/vpdma.c

@@ -59,6 +59,11 @@ const struct vpdma_data_format vpdma_yuv_fmts[] = {
 		.data_type	= DATA_TYPE_C420,
 		.depth		= 4,
 	},
+	[VPDMA_DATA_FMT_CB420] = {
+		.type		= VPDMA_DATA_FMT_TYPE_YUV,
+		.data_type	= DATA_TYPE_CB420,
+		.depth		= 4,
+	},
 	[VPDMA_DATA_FMT_YCR422] = {
 		.type		= VPDMA_DATA_FMT_TYPE_YUV,
 		.data_type	= DATA_TYPE_YCR422,
@@ -826,7 +831,8 @@ void vpdma_rawchan_add_out_dtd(struct vpdma_desc_list *list, int width,
 	channel = next_chan = raw_vpdma_chan;
 
 	if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV &&
-			fmt->data_type == DATA_TYPE_C420) {
+	    (fmt->data_type == DATA_TYPE_C420 ||
+	     fmt->data_type == DATA_TYPE_CB420)) {
 		rect.height >>= 1;
 		rect.top >>= 1;
 		depth = 8;
@@ -894,7 +900,8 @@ void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width,
 	channel = next_chan = chan_info[chan].num;
 
 	if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV &&
-			fmt->data_type == DATA_TYPE_C420) {
+	    (fmt->data_type == DATA_TYPE_C420 ||
+	     fmt->data_type == DATA_TYPE_CB420)) {
 		rect.height >>= 1;
 		rect.top >>= 1;
 		depth = 8;

+ 2 - 0
drivers/media/platform/ti-vpe/vpdma.h

@@ -60,6 +60,7 @@ struct vpdma_data_format {
 						 * line stride of source and dest
 						 * buffers should be 16 byte aligned
 						 */
+#define VPDMA_MAX_STRIDE		65520	/* Max line stride 16 byte aligned */
 #define VPDMA_DTD_DESC_SIZE		32	/* 8 words */
 #define VPDMA_CFD_CTD_DESC_SIZE		16	/* 4 words */
 
@@ -74,6 +75,7 @@ enum vpdma_yuv_formats {
 	VPDMA_DATA_FMT_C444,
 	VPDMA_DATA_FMT_C422,
 	VPDMA_DATA_FMT_C420,
+	VPDMA_DATA_FMT_CB420,
 	VPDMA_DATA_FMT_YCR422,
 	VPDMA_DATA_FMT_YC444,
 	VPDMA_DATA_FMT_CRY422,

+ 3 - 2
drivers/media/platform/ti-vpe/vpdma_priv.h

@@ -95,6 +95,7 @@
 #define DATA_TYPE_C444				0x4
 #define DATA_TYPE_C422				0x5
 #define DATA_TYPE_C420				0x6
+#define DATA_TYPE_CB420				0x16
 #define DATA_TYPE_YC444				0x8
 #define DATA_TYPE_YCB422			0x7
 #define DATA_TYPE_YCR422			0x17
@@ -168,11 +169,11 @@ struct vpdma_dtd {
 		u32		xfer_length_height;
 		u32		w1;
 	};
-	dma_addr_t		start_addr;
+	u32			start_addr;
 	u32			pkt_ctl;
 	union {
 		u32		frame_width_height;	/* inbound */
-		dma_addr_t	desc_write_addr;	/* outbound */
+		u32		desc_write_addr;	/* outbound */
 	};
 	union {
 		u32		start_h_v;		/* inbound */

+ 135 - 64
drivers/media/platform/ti-vpe/vpe.c

@@ -55,7 +55,7 @@
 #define MIN_W		32
 #define MIN_H		32
 #define MAX_W		2048
-#define MAX_H		1184
+#define MAX_H		2048
 
 /* required alignments */
 #define S_ALIGN		0	/* multiple of 1 */
@@ -254,6 +254,15 @@ static struct vpe_fmt vpe_formats[] = {
 				    &vpdma_yuv_fmts[VPDMA_DATA_FMT_C420],
 				  },
 	},
+	{
+		.name		= "NV21 YUV 420 co-planar",
+		.fourcc		= V4L2_PIX_FMT_NV21,
+		.types		= VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT,
+		.coplanar	= 1,
+		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420],
+				    &vpdma_yuv_fmts[VPDMA_DATA_FMT_CB420],
+				  },
+	},
 	{
 		.name		= "YUYV 422 packed",
 		.fourcc		= V4L2_PIX_FMT_YUYV,
@@ -342,9 +351,14 @@ struct vpe_q_data {
 #define	Q_DATA_MODE_TILED		BIT(1)
 #define	Q_DATA_INTERLACED_ALTERNATE	BIT(2)
 #define	Q_DATA_INTERLACED_SEQ_TB	BIT(3)
+#define	Q_DATA_INTERLACED_SEQ_BT	BIT(4)
+
+#define Q_IS_SEQ_XX		(Q_DATA_INTERLACED_SEQ_TB | \
+				Q_DATA_INTERLACED_SEQ_BT)
 
 #define Q_IS_INTERLACED		(Q_DATA_INTERLACED_ALTERNATE | \
-				Q_DATA_INTERLACED_SEQ_TB)
+				Q_DATA_INTERLACED_SEQ_TB | \
+				Q_DATA_INTERLACED_SEQ_BT)
 
 enum {
 	Q_DATA_SRC = 0,
@@ -352,20 +366,25 @@ enum {
 };
 
 /* find our format description corresponding to the passed v4l2_format */
-static struct vpe_fmt *find_format(struct v4l2_format *f)
+static struct vpe_fmt *__find_format(u32 fourcc)
 {
 	struct vpe_fmt *fmt;
 	unsigned int k;
 
 	for (k = 0; k < ARRAY_SIZE(vpe_formats); k++) {
 		fmt = &vpe_formats[k];
-		if (fmt->fourcc == f->fmt.pix.pixelformat)
+		if (fmt->fourcc == fourcc)
 			return fmt;
 	}
 
 	return NULL;
 }
 
+static struct vpe_fmt *find_format(struct v4l2_format *f)
+{
+	return __find_format(f->fmt.pix.pixelformat);
+}
+
 /*
  * there is one vpe_dev structure in the driver, it is shared by
  * all instances.
@@ -695,7 +714,8 @@ static void set_cfg_modes(struct vpe_ctx *ctx)
 	 * Cfg Mode 1: YUV422 source, disable upsampler, DEI is de-interlacing.
 	 */
 
-	if (fmt->fourcc == V4L2_PIX_FMT_NV12)
+	if (fmt->fourcc == V4L2_PIX_FMT_NV12 ||
+	    fmt->fourcc == V4L2_PIX_FMT_NV21)
 		cfg_mode = 0;
 
 	write_field(us1_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT);
@@ -710,7 +730,8 @@ static void set_line_modes(struct vpe_ctx *ctx)
 	struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt;
 	int line_mode = 1;
 
-	if (fmt->fourcc == V4L2_PIX_FMT_NV12)
+	if (fmt->fourcc == V4L2_PIX_FMT_NV12 ||
+	    fmt->fourcc == V4L2_PIX_FMT_NV21)
 		line_mode = 0;		/* double lines to line buffer */
 
 	/* regs for now */
@@ -772,7 +793,8 @@ static void set_dst_registers(struct vpe_ctx *ctx)
 	 */
 	val |= VPE_DS_SRC_DEI_SCALER | VPE_CSC_SRC_DEI_SCALER;
 
-	if (fmt->fourcc != V4L2_PIX_FMT_NV12)
+	if (fmt->fourcc != V4L2_PIX_FMT_NV12 &&
+	    fmt->fourcc != V4L2_PIX_FMT_NV21)
 		val |= VPE_DS_BYPASS;
 
 	mmr_adb->out_fmt_reg[0] = val;
@@ -914,14 +936,6 @@ static int set_srcdst_params(struct vpe_ctx *ctx)
 	return 0;
 }
 
-/*
- * Return the vpe_ctx structure for a given struct file
- */
-static struct vpe_ctx *file2ctx(struct file *file)
-{
-	return container_of(file->private_data, struct vpe_ctx, fh);
-}
-
 /*
  * mem2mem callbacks
  */
@@ -1027,11 +1041,14 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port)
 	dma_addr_t dma_addr;
 	u32 flags = 0;
 	u32 offset = 0;
+	u32 stride;
 
 	if (port == VPE_PORT_MV_OUT) {
 		vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
 		dma_addr = ctx->mv_buf_dma[mv_buf_selector];
 		q_data = &ctx->q_data[Q_DATA_SRC];
+		stride = ALIGN((q_data->width * vpdma_fmt->depth) >> 3,
+			       VPDMA_STRIDE_ALIGN);
 	} else {
 		/* to incorporate interleaved formats */
 		int plane = fmt->coplanar ? p_data->vb_part : 0;
@@ -1058,6 +1075,7 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port)
 		}
 		/* Apply the offset */
 		dma_addr += offset;
+		stride = q_data->bytesperline[VPE_LUMA];
 	}
 
 	if (q_data->flags & Q_DATA_FRAME_1D)
@@ -1069,7 +1087,7 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port)
 			   MAX_W, MAX_H);
 
 	vpdma_add_out_dtd(&ctx->desc_list, q_data->width,
-			  q_data->bytesperline[VPE_LUMA], &q_data->c_rect,
+			  stride, &q_data->c_rect,
 			  vpdma_fmt, dma_addr, MAX_OUT_WIDTH_REG1,
 			  MAX_OUT_HEIGHT_REG1, p_data->channel, flags);
 }
@@ -1088,10 +1106,13 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port)
 	dma_addr_t dma_addr;
 	u32 flags = 0;
 	u32 offset = 0;
+	u32 stride;
 
 	if (port == VPE_PORT_MV_IN) {
 		vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
 		dma_addr = ctx->mv_buf_dma[mv_buf_selector];
+		stride = ALIGN((q_data->width * vpdma_fmt->depth) >> 3,
+			       VPDMA_STRIDE_ALIGN);
 	} else {
 		/* to incorporate interleaved formats */
 		int plane = fmt->coplanar ? p_data->vb_part : 0;
@@ -1118,25 +1139,38 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port)
 		}
 		/* Apply the offset */
 		dma_addr += offset;
+		stride = q_data->bytesperline[VPE_LUMA];
+
+		/*
+		 * field used in VPDMA desc  = 0 (top) / 1 (bottom)
+		 * Use top or bottom field from same vb alternately
+		 * For each de-interlacing operation, f,f-1,f-2 should be one
+		 * of TBT or BTB
+		 */
+		if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB ||
+		    q_data->flags & Q_DATA_INTERLACED_SEQ_TB) {
+			/* Select initial value based on format */
+			if (q_data->flags & Q_DATA_INTERLACED_SEQ_BT)
+				field = 1;
+			else
+				field = 0;
 
-		if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB) {
-			/*
-			 * Use top or bottom field from same vb alternately
-			 * f,f-1,f-2 = TBT when seq is even
-			 * f,f-1,f-2 = BTB when seq is odd
-			 */
-			field = (p_data->vb_index + (ctx->sequence % 2)) % 2;
+			/* Toggle for each vb_index and each operation */
+			field = (field + p_data->vb_index + ctx->sequence) % 2;
 
 			if (field) {
-				/*
-				 * bottom field of a SEQ_TB buffer
-				 * Skip the top field data by
-				 */
 				int height = q_data->height / 2;
-				int bpp = fmt->fourcc == V4L2_PIX_FMT_NV12 ?
-						1 : (vpdma_fmt->depth >> 3);
+				int bpp;
+
+				if (fmt->fourcc == V4L2_PIX_FMT_NV12 ||
+				    fmt->fourcc == V4L2_PIX_FMT_NV21)
+					bpp = 1;
+				else
+					bpp = vpdma_fmt->depth >> 3;
+
 				if (plane)
 					height /= 2;
+
 				dma_addr += q_data->width * height * bpp;
 			}
 		}
@@ -1150,13 +1184,14 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port)
 	frame_width = q_data->c_rect.width;
 	frame_height = q_data->c_rect.height;
 
-	if (p_data->vb_part && fmt->fourcc == V4L2_PIX_FMT_NV12)
+	if (p_data->vb_part && (fmt->fourcc == V4L2_PIX_FMT_NV12 ||
+				fmt->fourcc == V4L2_PIX_FMT_NV21))
 		frame_height /= 2;
 
-	vpdma_add_in_dtd(&ctx->desc_list, q_data->width,
-			 q_data->bytesperline[VPE_LUMA], &q_data->c_rect,
-		vpdma_fmt, dma_addr, p_data->channel, field, flags, frame_width,
-		frame_height, 0, 0);
+	vpdma_add_in_dtd(&ctx->desc_list, q_data->width, stride,
+			 &q_data->c_rect, vpdma_fmt, dma_addr,
+			 p_data->channel, field, flags, frame_width,
+			 frame_height, 0, 0);
 }
 
 /*
@@ -1191,12 +1226,14 @@ static void device_run(void *priv)
 	struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST];
 	struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC];
 
-	if (ctx->deinterlacing && s_q_data->flags & Q_DATA_INTERLACED_SEQ_TB &&
-		ctx->sequence % 2 == 0) {
-		/* When using SEQ_TB buffers, When using it first time,
-		 * No need to remove the buffer as the next field is present
-		 * in the same buffer. (so that job_ready won't fail)
-		 * It will be removed when using bottom field
+	if (ctx->deinterlacing && s_q_data->flags & Q_IS_SEQ_XX &&
+	    ctx->sequence % 2 == 0) {
+		/* When using SEQ_XX type buffers, each buffer has two fields
+		 * each buffer has two fields (top & bottom)
+		 * Removing one buffer is actually getting two fields
+		 * Alternate between two operations:-
+		 * Even : consume one field but DO NOT REMOVE from queue
+		 * Odd : consume other field and REMOVE from queue
 		 */
 		ctx->src_vbs[0] = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
 		WARN_ON(ctx->src_vbs[0] == NULL);
@@ -1405,9 +1442,6 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
 	 /* the previous dst mv buffer becomes the next src mv buffer */
 	ctx->src_mv_buf_selector = !ctx->src_mv_buf_selector;
 
-	if (ctx->aborting)
-		goto finished;
-
 	s_vb = ctx->src_vbs[0];
 	d_vb = ctx->dst_vb;
 
@@ -1418,6 +1452,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
 		d_vb->timecode = s_vb->timecode;
 
 	d_vb->sequence = ctx->sequence;
+	s_vb->sequence = ctx->sequence;
 
 	d_q_data = &ctx->q_data[Q_DATA_DST];
 	if (d_q_data->flags & Q_IS_INTERLACED) {
@@ -1471,6 +1506,9 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
 	ctx->src_vbs[0] = NULL;
 	ctx->dst_vb = NULL;
 
+	if (ctx->aborting)
+		goto finished;
+
 	ctx->bufs_completed++;
 	if (ctx->bufs_completed < ctx->bufs_per_job && job_ready(ctx)) {
 		device_run(ctx);
@@ -1536,7 +1574,7 @@ static int vpe_enum_fmt(struct file *file, void *priv,
 static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
 {
 	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
-	struct vpe_ctx *ctx = file2ctx(file);
+	struct vpe_ctx *ctx = file->private_data;
 	struct vb2_queue *vq;
 	struct vpe_q_data *q_data;
 	int i;
@@ -1546,6 +1584,8 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
 		return -EINVAL;
 
 	q_data = get_q_data(ctx, f->type);
+	if (!q_data)
+		return -EINVAL;
 
 	pix->width = q_data->width;
 	pix->height = q_data->height;
@@ -1583,13 +1623,15 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
 	unsigned int stride = 0;
 
 	if (!fmt || !(fmt->types & type)) {
-		vpe_err(ctx->dev, "Fourcc format (0x%08x) invalid.\n",
+		vpe_dbg(ctx->dev, "Fourcc format (0x%08x) invalid.\n",
 			pix->pixelformat);
-		return -EINVAL;
+		fmt = __find_format(V4L2_PIX_FMT_YUYV);
 	}
 
-	if (pix->field != V4L2_FIELD_NONE && pix->field != V4L2_FIELD_ALTERNATE
-			&& pix->field != V4L2_FIELD_SEQ_TB)
+	if (pix->field != V4L2_FIELD_NONE &&
+	    pix->field != V4L2_FIELD_ALTERNATE &&
+	    pix->field != V4L2_FIELD_SEQ_TB &&
+	    pix->field != V4L2_FIELD_SEQ_BT)
 		pix->field = V4L2_FIELD_NONE;
 
 	depth = fmt->vpdma_fmt[VPE_LUMA]->depth;
@@ -1632,7 +1674,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
 			      &pix->height, MIN_H, MAX_H, H_ALIGN,
 			      S_ALIGN);
 
-	if (!pix->num_planes)
+	if (!pix->num_planes || pix->num_planes > 2)
 		pix->num_planes = fmt->coplanar ? 2 : 1;
 	else if (pix->num_planes > 1 && !fmt->coplanar)
 		pix->num_planes = 1;
@@ -1641,9 +1683,9 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
 
 	/*
 	 * For the actual image parameters, we need to consider the field
-	 * height of the image for SEQ_TB buffers.
+	 * height of the image for SEQ_XX buffers.
 	 */
-	if (pix->field == V4L2_FIELD_SEQ_TB)
+	if (pix->field == V4L2_FIELD_SEQ_TB || pix->field == V4L2_FIELD_SEQ_BT)
 		height = pix->height / 2;
 	else
 		height = pix->height;
@@ -1671,6 +1713,10 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
 		if (stride > plane_fmt->bytesperline)
 			plane_fmt->bytesperline = stride;
 
+		plane_fmt->bytesperline = clamp_t(u32, plane_fmt->bytesperline,
+						  stride,
+						  VPDMA_MAX_STRIDE);
+
 		plane_fmt->bytesperline = ALIGN(plane_fmt->bytesperline,
 						VPDMA_STRIDE_ALIGN);
 
@@ -1696,7 +1742,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
 
 static int vpe_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
 {
-	struct vpe_ctx *ctx = file2ctx(file);
+	struct vpe_ctx *ctx = file->private_data;
 	struct vpe_fmt *fmt = find_format(f);
 
 	if (V4L2_TYPE_IS_OUTPUT(f->type))
@@ -1749,11 +1795,13 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f)
 		q_data->flags |= Q_DATA_INTERLACED_ALTERNATE;
 	else if (q_data->field == V4L2_FIELD_SEQ_TB)
 		q_data->flags |= Q_DATA_INTERLACED_SEQ_TB;
+	else if (q_data->field == V4L2_FIELD_SEQ_BT)
+		q_data->flags |= Q_DATA_INTERLACED_SEQ_BT;
 	else
 		q_data->flags &= ~Q_IS_INTERLACED;
 
-	/* the crop height is halved for the case of SEQ_TB buffers */
-	if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB)
+	/* the crop height is halved for the case of SEQ_XX buffers */
+	if (q_data->flags & Q_IS_SEQ_XX)
 		q_data->c_rect.height /= 2;
 
 	vpe_dbg(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d bpl_y %d",
@@ -1769,7 +1817,7 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f)
 static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
 {
 	int ret;
-	struct vpe_ctx *ctx = file2ctx(file);
+	struct vpe_ctx *ctx = file->private_data;
 
 	ret = vpe_try_fmt(file, priv, f);
 	if (ret)
@@ -1826,10 +1874,10 @@ static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s)
 	}
 
 	/*
-	 * For SEQ_TB buffers, crop height should be less than the height of
+	 * For SEQ_XX buffers, crop height should be less than the height of
 	 * the field height, not the buffer height
 	 */
-	if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB)
+	if (q_data->flags & Q_IS_SEQ_XX)
 		height = q_data->height / 2;
 	else
 		height = q_data->height;
@@ -1854,7 +1902,7 @@ static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s)
 static int vpe_g_selection(struct file *file, void *fh,
 		struct v4l2_selection *s)
 {
-	struct vpe_ctx *ctx = file2ctx(file);
+	struct vpe_ctx *ctx = file->private_data;
 	struct vpe_q_data *q_data;
 	bool use_c_rect = false;
 
@@ -1915,7 +1963,7 @@ static int vpe_g_selection(struct file *file, void *fh,
 static int vpe_s_selection(struct file *file, void *fh,
 		struct v4l2_selection *s)
 {
-	struct vpe_ctx *ctx = file2ctx(file);
+	struct vpe_ctx *ctx = file->private_data;
 	struct vpe_q_data *q_data;
 	struct v4l2_selection sel = *s;
 	int ret;
@@ -2010,6 +2058,8 @@ static int vpe_queue_setup(struct vb2_queue *vq,
 	struct vpe_q_data *q_data;
 
 	q_data = get_q_data(ctx, vq->type);
+	if (!q_data)
+		return -EINVAL;
 
 	*nplanes = q_data->nplanes;
 
@@ -2034,6 +2084,8 @@ static int vpe_buf_prepare(struct vb2_buffer *vb)
 	vpe_dbg(ctx->dev, "type: %d\n", vb->vb2_queue->type);
 
 	q_data = get_q_data(ctx, vb->vb2_queue->type);
+	if (!q_data)
+		return -EINVAL;
 	num_planes = q_data->nplanes;
 
 	if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
@@ -2042,7 +2094,8 @@ static int vpe_buf_prepare(struct vb2_buffer *vb)
 		} else {
 			if (vbuf->field != V4L2_FIELD_TOP &&
 			    vbuf->field != V4L2_FIELD_BOTTOM &&
-			    vbuf->field != V4L2_FIELD_SEQ_TB)
+			    vbuf->field != V4L2_FIELD_SEQ_TB &&
+			    vbuf->field != V4L2_FIELD_SEQ_BT)
 				return -EINVAL;
 		}
 	}
@@ -2278,7 +2331,7 @@ static int vpe_open(struct file *file)
 	init_adb_hdrs(ctx);
 
 	v4l2_fh_init(&ctx->fh, video_devdata(file));
-	file->private_data = &ctx->fh;
+	file->private_data = ctx;
 
 	hdl = &ctx->hdl;
 	v4l2_ctrl_handler_init(hdl, 1);
@@ -2291,7 +2344,7 @@ static int vpe_open(struct file *file)
 	v4l2_ctrl_handler_setup(hdl);
 
 	s_q_data = &ctx->q_data[Q_DATA_SRC];
-	s_q_data->fmt = &vpe_formats[2];
+	s_q_data->fmt = __find_format(V4L2_PIX_FMT_YUYV);
 	s_q_data->width = 1920;
 	s_q_data->height = 1080;
 	s_q_data->nplanes = 1;
@@ -2363,12 +2416,18 @@ free_ctx:
 static int vpe_release(struct file *file)
 {
 	struct vpe_dev *dev = video_drvdata(file);
-	struct vpe_ctx *ctx = file2ctx(file);
+	struct vpe_ctx *ctx = file->private_data;
 
 	vpe_dbg(dev, "releasing instance %p\n", ctx);
 
 	mutex_lock(&dev->dev_mutex);
 	free_mv_buffers(ctx);
+
+	vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf);
+	vpdma_unmap_desc_buf(dev->vpdma, &ctx->mmr_adb);
+	vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_h);
+	vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_v);
+
 	vpdma_free_desc_list(&ctx->desc_list);
 	vpdma_free_desc_buf(&ctx->mmr_adb);
 
@@ -2475,6 +2534,13 @@ static int vpe_probe(struct platform_device *pdev)
 	struct vpe_dev *dev;
 	int ret, irq, func;
 
+	ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(&pdev->dev,
+			"32-bit consistent DMA enable failed\n");
+		return ret;
+	}
+
 	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
 	if (!dev)
 		return -ENOMEM;
@@ -2489,7 +2555,12 @@ static int vpe_probe(struct platform_device *pdev)
 	mutex_init(&dev->dev_mutex);
 
 	dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-			"vpe_top");
+						"vpe_top");
+	if (!dev->res) {
+		dev_err(&pdev->dev, "missing 'vpe_top' resources data\n");
+		return -ENODEV;
+	}
+
 	/*
 	 * HACK: we get resource info from device tree in the form of a list of
 	 * VPE sub blocks, the driver currently uses only the base of vpe_top

+ 21 - 0
drivers/media/v4l2-core/v4l2-fwnode.c

@@ -114,6 +114,7 @@ static void v4l2_fwnode_endpoint_parse_parallel_bus(
 	struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel;
 	unsigned int flags = 0;
 	u32 v;
+	int rval;
 
 	if (!fwnode_property_read_u32(fwnode, "hsync-active", &v))
 		flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH :
@@ -158,6 +159,26 @@ static void v4l2_fwnode_endpoint_parse_parallel_bus(
 		flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH :
 			V4L2_MBUS_DATA_ENABLE_LOW;
 
+	if (vep->bus_type == V4L2_MBUS_BT656) {
+		if (fwnode_property_present(fwnode, "pixel-mux"))
+			bus->pixmux = 1;
+		else
+			bus->pixmux = 0;
+
+		bus->num_channels = 0;
+		rval = fwnode_property_read_u8_array(fwnode, "channels",
+						     NULL, 0);
+		if (rval > 0) {
+			bus->num_channels = min_t(int,
+						  ARRAY_SIZE(bus->channels),
+						  rval);
+
+			fwnode_property_read_u8_array(fwnode, "channels",
+						      bus->channels,
+						      bus->num_channels);
+		}
+	}
+
 	bus->flags = flags;
 
 }

+ 2 - 0
drivers/staging/media/zoran/zoran.h

@@ -60,6 +60,8 @@ struct zoran_sync {
 #   define   V4L_MAX_FRAME   32
 #elif VIDEO_MAX_FRAME <= 64
 #   define   V4L_MAX_FRAME   64
+#elif VIDEO_MAX_FRAME <= 128
+#   define   V4L_MAX_FRAME   128
 #else
 #   error   "Too many video frame buffers to handle"
 #endif

+ 14 - 0
include/dt-bindings/pinctrl/dra.h

@@ -76,5 +76,19 @@
 /* DRA7 IODELAY configuration parameters */
 #define A_DELAY_PS(val)			((val) & 0xffff)
 #define G_DELAY_PS(val)			((val) & 0xffff)
+
+/* DRA72 VIP MUX selection parameters */
+#define VIP_VIN3A		(0x0 << 4)
+#define VIP_VIN5A		(0x1 << 4)
+#define VIP_VIN6A		(0x2 << 4)
+
+#define VIP_VIN4B		(0x0 << 3)
+
+#define VIP_VIN2A		(0x0 << 1)
+#define VIP_VIN4A		(0x1 << 1)
+
+#define VIP_VIN3B		(0x0 << 0)
+#define VIP_VIN2B		(0x1 << 0)
+
 #endif
 

+ 3 - 0
include/media/v4l2-fwnode.h

@@ -57,6 +57,9 @@ struct v4l2_fwnode_bus_parallel {
 	unsigned int flags;
 	unsigned char bus_width;
 	unsigned char data_shift;
+	unsigned char num_channels;
+	unsigned char pixmux;
+	unsigned char channels[16];
 };
 
 /**

+ 1 - 1
include/media/videobuf2-core.h

@@ -18,7 +18,7 @@
 #include <linux/dma-buf.h>
 #include <linux/bitops.h>
 
-#define VB2_MAX_FRAME	(32)
+#define VB2_MAX_FRAME	(128)
 #define VB2_MAX_PLANES	(8)
 
 /**

+ 1 - 1
include/uapi/linux/videodev2.h

@@ -70,7 +70,7 @@
  * Common stuff for both V4L1 and V4L2
  * Moved from videodev.h
  */
-#define VIDEO_MAX_FRAME               32
+#define VIDEO_MAX_FRAME               128
 #define VIDEO_MAX_PLANES               8
 
 /*

+ 25 - 0
ti_config_fragments/audio_display.cfg

@@ -89,6 +89,31 @@ CONFIG_TOUCHSCREEN_GOODIX=m
 
 CONFIG_HID_MULTITOUCH=m
 
+# V4L2
+
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_AM437X_VPFE=m
+CONFIG_VIDEO_TI_CAL=m
+CONFIG_VIDEO_TI_VIP=m
+
+CONFIG_V4L_MEM2MEM_DRIVERS=y
+CONFIG_VIDEO_TI_VPE=m
+
+CONFIG_MEDIA_SUBDRV_AUTOSELECT=n
+CONFIG_VIDEO_OV2659=m
+CONFIG_VIDEO_OV1063X=m
+CONFIG_VIDEO_MT9T11X=m
+CONFIG_VIDEO_OV490=m
+
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=m
+CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y
+
 # sound - START
 CONFIG_SND_ARM=n
 CONFIG_SND_SPI=n