Merge branch 'renesas-drivers-2016-10-18-v4.9-rc1/dts-rcar-gen3.rc10' into v4.9-rc1/rcar-3.4.x

* renesas-drivers-2016-10-18-v4.9-rc1/dts-rcar-gen3.rc10: (226 commits)
  arm64: dts: r8a7796-salvator-x: Disable sdr104 of SDHI
  arm64: dts: r8a7795-salvator-x: Disable sdr104 of SDHI
  arm64: dts: r8a7795-es1-salvator-x: Disable sdr104 of SDHI
  arm64: dts: r8a7796: Fix phy-mode to rgmii-txid for EthernetAVB
  arm64: dts: r8a7796: Delete the DRC module of VSP2 node.
  arm64: dts: r8a7796-salvator-x: Reserve Multimedia cma for MMNGR
  arm64: dts: r8a7796: Add QoS Driver node at Device Tree
  arm64: dts: r8a7796: Add power domains for IPMMU (PMB)
  arm64: dts: r8a7796: Support IPMMU (PMB) in IPMMU-VC0
  arm64: dts: r8a7796: Hook up SYS-DMAC/Audio-DMAC to IPMMU
  arm64: dts: r8a7796: Add IPMMU device nodes
  arm64: dts: r8a7796-salvator-x: Add ADSP device support
  arm64: dts: r8a7796: Add ADSP device node
  arm64: dts: r8a7796: Add iVDP1C and FCP and VCP4 device nodes for UVCS
  arm64: dts: r8a7796-salvator-x: Add VSP Manager support
  arm64: dts: r8a7796: Convert VSP Driver into VSP Manager
  arm64: dts: r8a7796-salvator-x: Add MMNGRBUF driver node
  arm64: dts: r8a7796-salvator-x: Add MMNGR support
  arm64: dts: r8a7796: Add GSX device node
  arm64: dts: r8a7796: Add INTC-EX device node
  arm64: dts: r8a7796: Add CAN support
  arm64: dts: r8a7796: Add CAN external clock support
  arm64: dts: r8a7796: Add MFIS Lock device node
  arm64: dts: r8a7796: Add MFIS device node
  arm64: dts: r8a7796-salvator-x: Add cpu-supply property in a57_0 node
  arm64: dts: r8a7796: Adjust VDD domain voltage value for CA53
  arm64: dts: r8a7796: Add OPPs table for cpu devices
  arm64: dts: r8a7796: Add the emergency node in thermal-zones
  arm64: dts: r8a7796: Create thermal zone to support IPA
  arm64: dts: r8a7796: Add thermal sensor device nodes
  ...
diff --git a/Documentation/devicetree/bindings/display/renesas,du.txt b/Documentation/devicetree/bindings/display/renesas,du.txt
index 0d30e42..8515658 100644
--- a/Documentation/devicetree/bindings/display/renesas,du.txt
+++ b/Documentation/devicetree/bindings/display/renesas,du.txt
@@ -9,6 +9,7 @@
     - "renesas,du-r8a7793" for R8A7793 (R-Car M2-N) compatible DU
     - "renesas,du-r8a7794" for R8A7794 (R-Car E2) compatible DU
     - "renesas,du-r8a7795" for R8A7795 (R-Car H3) compatible DU
+    - "renesas,du-r8a7796" for R8A7796 (R-Car M3) compatible DU
 
   - reg: A list of base address and length of each memory resource, one for
     each entry in the reg-names property.
@@ -50,6 +51,7 @@
  R8A7793 (M2-N)	DPAD		LVDS 0		-		-
  R8A7794 (E2)	DPAD 0		DPAD 1		-		-
  R8A7795 (H3)	DPAD		HDMI 0		HDMI 1		LVDS
+ R8A7796 (M3)	DPAD		HDMI		LVDS		-
 
 
 Example: R8A7790 (R-Car H2) DU
diff --git a/Documentation/devicetree/bindings/hwlock/rcar-hwspinlock.txt b/Documentation/devicetree/bindings/hwlock/rcar-hwspinlock.txt
new file mode 100644
index 0000000..133673d
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwlock/rcar-hwspinlock.txt
@@ -0,0 +1,24 @@
+DT bindings for the Renesas R-Car Hardware spinlock driver
+----------------------------------------------------------
+
+Required properties :
+- compatible : shall contain only one of the following:
+	- "renesas,mfis-lock-r8a7795", "renesas,mfis-lock";
+	- "renesas,mfis-lock-r8a7796", "renesas,mfis-lock";
+
+- reg : start address and length for MFIS lock registers.
+
+- clocks : clock phandle and specifier pair.
+
+- power-domains : the power domain the MFIS belongs to.
+
+
+Examples:
+
+mfis_lock: mfis-lock {
+	compatible =    "renesas,mfis-lock-r8a7795",
+			"renesas,mfis-lock";
+	reg = <0 0xe62600c0 0 0x0020>;
+	clocks = <&cpg CPG_MOD 213>;
+	power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+};
diff --git a/Documentation/devicetree/bindings/i2c/i2c-sh_mobile.txt b/Documentation/devicetree/bindings/i2c/i2c-sh_mobile.txt
index 214f94c..1a241b5 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-sh_mobile.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-sh_mobile.txt
@@ -11,6 +11,7 @@
 			- "renesas,iic-r8a7793" (R-Car M2-N)
 			- "renesas,iic-r8a7794" (R-Car E2)
 			- "renesas,iic-r8a7795" (R-Car H3)
+			- "renesas,iic-r8a7796" (R-Car M3)
 			- "renesas,iic-sh73a0" (SH-Mobile AG5)
 - reg             : address start and address range size of device
 - interrupts      : interrupt of device
diff --git a/Documentation/devicetree/bindings/media/rcar-csi2.txt b/Documentation/devicetree/bindings/media/rcar-csi2.txt
new file mode 100644
index 0000000..5d9a23f
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/rcar-csi2.txt
@@ -0,0 +1,80 @@
+Renesas R-Car MIPI CSI-2 driver (rcar-csi2)
+-------------------------------------------
+
+The rcar-csi2 device provides MIPI CSI-2 capabilities for the Renesas R-Car
+family of devices. It is to be used in conjunction with the rcar-vin driver which
+provides the video input capabilities.
+
+ - compatible: Must be one or more of the following
+   - "renesas,csi2-r8a7796" for the R8A7796 device
+   - "renesas,csi2-r8a7795" for the R8A7795 device
+   - "renesas,rcar-gen3-csi2" for a generic R-Car Gen3 compatible device.
+
+   When compatible with the generic version nodes must list the
+   SoC-specific version corresponding to the platform first
+   followed by the generic version.
+
+ - reg: the register base and size for the device registers
+ - interrupts: the interrupt for the device
+ - clocks: Reference to the parent clock
+
+The device node should contain two 'port' child nodes with one child 'endpoint'
+node, according to the bindings defined in Documentation/devicetree/bindings/
+media/video-interfaces.txt. Port 0 should connect the device that is the CSI-2
+producer. Port 1 should connect the R-Car VIN device (rcar-vin) consumer.
+
+ - ports:
+   - port@0: sub-node describing a endpoint to the CSI-2 source
+   - port@1: sub-node describing a endpoint to the VIN consumer
+
+Additionally, an alias named csiX will need to be created to specify
+which CSI-2 device this is.
+
+Example:
+
+/* SoC properties */
+
+	aliases {
+		csi40 = &csi40;
+	};
+
+	csi40: csi2@feaa0000 {
+		compatible = "renesas,csi2-r8a7795";
+		reg = <0 0xfeaa0000 0 0x10000>;
+		interrupts = <0 246 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cpg CPG_MOD 716>;
+		power-domains = <&cpg>;
+		status = "disabled";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@1 {
+				reg = <1>;
+				csi40_out: endpoint@1 {
+					remote-endpoint = <&vin0csi40>;
+				};
+			};
+		};
+	};
+
+/* Board properties */
+
+	&csi40 {
+		status = "okay";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				csi40_in: endpoint@0 {
+					clock-lanes = <0>;
+					data-lanes = <1 2 3 4>;
+					remote-endpoint = <&adv7482_1_out>;
+				};
+			};
+		};
+	};
diff --git a/Documentation/devicetree/bindings/media/rcar_vin.txt b/Documentation/devicetree/bindings/media/rcar_vin.txt
index 6a4e61c..c57eb39 100644
--- a/Documentation/devicetree/bindings/media/rcar_vin.txt
+++ b/Documentation/devicetree/bindings/media/rcar_vin.txt
@@ -2,10 +2,16 @@
 ------------------------------------------
 
 The rcar_vin device provides video input capabilities for the Renesas R-Car
-family of devices. The current blocks are always slaves and suppot one input
-channel which can be either RGB, YUYV or BT656.
+family of devices.
+
+On Gen2 the current blocks are always slaves and support one input
+channel which can be either RGB, YUYV or BT656
+
+On Gen3 the current blocks are always slaves and support multiple inputs
+channels which can be ether RGB, YUVU, BT656 or CSI-2.
 
  - compatible: Must be one or more of the following
+   - "renesas,vin-r8a7796" for the R8A7796 device
    - "renesas,vin-r8a7795" for the R8A7795 device
    - "renesas,vin-r8a7794" for the R8A7794 device
    - "renesas,vin-r8a7793" for the R8A7793 device
@@ -28,7 +34,30 @@
 Additionally, an alias named vinX will need to be created to specify
 which video input device this is.
 
-The per-board settings:
+On Gen3 additional ports can be specified to describe the CSI-2 group
+hierarchy. Port 0 are used to describe inputs (in per-board settings)
+and VIN group membership. Port 1 are used to describe CSI-2 endpoints.
+Port 2 are used to describe VIN endpoints which are part of the group.
+    - ports:
+        - port@0:
+            - reg 1: sub-node describing a endpoint connected to the VIN
+              group master.
+        - port@1: remote CSI-2 endpoints part of VIN group
+            - reg 0: sub-node describing a endpoint to CSI20
+            - reg 1: sub-node describing a endpoint to CSI21
+            - reg 3: sub-node describing a endpoint to CSI40
+            - reg 4: sub-node describing a endpoint to CSI41
+        - port@2: remote VIN endpoints part of VIN group
+            - reg 0: sub-node describing a endpoint to VIN0
+            - reg 1: sub-node describing a endpoint to VIN1
+            - reg 2: sub-node describing a endpoint to VIN2
+            - reg 3: sub-node describing a endpoint to VIN3
+            - reg 4: sub-node describing a endpoint to VIN4
+            - reg 5: sub-node describing a endpoint to VIN5
+            - reg 6: sub-node describing a endpoint to VIN6
+            - reg 7: sub-node describing a endpoint to VIN7
+
+The per-board settings Gen2:
  - port sub-node describing a single endpoint connected to the vin
    as described in video-interfaces.txt[1]. Only the first one will
    be considered as each vin interface has one input port.
@@ -36,9 +65,17 @@
    These settings are used to work out video input format and widths
    into the system.
 
+The per-board settings Gen3:
+-ports:
+    - port@0:
+        - reg 0: sub-node describing a endpoint connected to the VIN
+          private digital input as described in video-interfaces.txt[1].
 
-Device node example
--------------------
+   These settings are used to work out video input format and widths
+   into the system.
+
+Device node example Gen2
+------------------------
 
 	aliases {
 	       vin0 = &vin0;
@@ -52,8 +89,8 @@
                 status = "disabled";
         };
 
-Board setup example (vin1 composite video input)
-------------------------------------------------
+Board setup example Gen2 (vin1 composite video input)
+-----------------------------------------------------
 
 &i2c2   {
         status = "ok";
@@ -92,6 +129,174 @@
         };
 };
 
+Device node example Gen3
+------------------------
 
+aliases {
+       vin0 = &vin0;
+       vin4 = &vin4;
+};
+
+csi21: csi2@fea90000 {
+        ...
+
+        ports {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                port@1 {
+                        reg = <1>;
+
+                        csi21_out: endpoint@1 {
+                                remote-endpoint =  <&vin0csi21>;
+                        };
+                };
+        };
+};
+
+vin0: video@e6ef0000 {
+        compatible = "renesas,vin-r8a7795";
+        reg = <0 0xe6ef0000 0 0x1000>;
+        interrupts = <0 188 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&cpg CPG_MOD 811>;
+        power-domains = <&cpg>;
+        status = "disabled";
+
+        ports {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                port@0 {
+                        reg = <0>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+
+                        vin0csi: endpoint@1 {
+                                reg = <1>;
+                                remote-endpoint= <&vin0out0>;
+                        };
+
+                };
+                port@1 {
+                        reg = <1>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+
+                        vin0csi21: endpoint@1 {
+                                reg = <1>;
+                                remote-endpoint= <&csi21_out>;
+                        };
+                };
+                port@2 {
+                        reg = <2>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+
+                        vin0out0: endpoint@0 {
+                                reg = <0>;
+                                remote-endpoint = <&vin0csi>;
+                        };
+                        vin0out4: endpoint@4 {
+                                reg = <4>;
+                                remote-endpoint = <&vin4csi>;
+                        };
+                };
+        };
+};
+
+vin4: video@e6ef4000 {
+        compatible = "renesas,vin-r8a7795";
+        reg = <0 0xe6ef4000 0 0x1000>;
+        interrupts = <0 174 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&cpg CPG_MOD 807>;
+        power-domains = <&cpg>;
+        status = "disabled";
+
+        ports {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                port@0 {
+                        reg = <0>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+
+                        vin4csi: endpoint@1 {
+                                reg = <1>;
+                                remote-endpoint = <&vin0out4>;
+                        };
+                };
+        };
+};
+
+Board setup example Gen3
+- VIN0 and VIN4 part of CSI-2 group
+- VIN4 with local digital input
+-----------------------------------------------------
+
+&i2c2   {
+        ...
+
+        adv7482: composite-in@70 {
+                ...
+                port {
+                        adv7482_out: endpoint@1 {
+                                clock-lanes = <0>;
+                                data-lanes = <1>;
+                                remote-endpoint = <&csi21_in>;
+                        };
+                };
+        };
+
+        adv7612: composite-in@20 {
+                ...
+                port {
+                        adv7612_out: endpoint {
+                                bus-width = <8>;
+                                remote-endpoint = <&vin4ep0>;
+                        };
+                };
+        };
+};
+
+&csi21 {
+        status = "okay";
+
+        ...
+
+        ports {
+                port@0 {
+                        reg = <0>;
+
+                        csi21_in: endpoint@0 {
+                                clock-lanes = <0>;
+                                data-lanes = <1>;
+                                remote-endpoint = <&adv7482_out>;
+                        };
+                };
+        };
+};
+
+&vin0 {
+        status = "okay";
+};
+
+&vin4 {
+        status = "okay";
+
+        ports {
+                port@0 {
+                        reg = <0>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+
+                        vin4ep0: endpoint@0 {
+                                reg = <0>;
+                                remote-endpoint= <&adv7612_out>;
+                        };
+
+                };
+        };
+};
 
 [1] video-interfaces.txt common video media interface
diff --git a/Documentation/devicetree/bindings/pci/rcar-pci.txt b/Documentation/devicetree/bindings/pci/rcar-pci.txt
index 6cf9969..ca1698b 100644
--- a/Documentation/devicetree/bindings/pci/rcar-pci.txt
+++ b/Documentation/devicetree/bindings/pci/rcar-pci.txt
@@ -6,6 +6,7 @@
 	    "renesas,pcie-r8a7791" for the R8A7791 SoC;
 	    "renesas,pcie-r8a7793" for the R8A7793 SoC;
 	    "renesas,pcie-r8a7795" for the R8A7795 SoC;
+	    "renesas,pcie-r8a7796" for the R8A7796 SoC;
 	    "renesas,pcie-rcar-gen2" for a generic R-Car Gen2 compatible device.
 
 	    When compatible with the generic version, nodes must list the
diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt
index 1e4000d..27acba5 100644
--- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt
+++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt
@@ -31,8 +31,8 @@
     - "renesas,hscif-r8a7794" for R8A7794 (R-Car E2) HSCIF compatible UART.
     - "renesas,scif-r8a7795" for R8A7795 (R-Car H3) SCIF compatible UART.
     - "renesas,hscif-r8a7795" for R8A7795 (R-Car H3) HSCIF compatible UART.
-    - "renesas,scif-r8a7796" for R8A7796 (R-Car M3-W) SCIF compatible UART.
-    - "renesas,hscif-r8a7796" for R8A7796 (R-Car M3-W) HSCIF compatible UART.
+    - "renesas,scif-r8a7796" for R8A7796 (R-Car M3) SCIF compatible UART.
+    - "renesas,hscif-r8a7796" for R8A7796 (R-Car M3) HSCIF compatible UART.
     - "renesas,scifa-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFA compatible UART.
     - "renesas,scifb-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFB compatible UART.
     - "renesas,rcar-gen1-scif" for R-Car Gen1 SCIF compatible UART,
diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt
index aa005c1..c2b6251 100644
--- a/Documentation/devicetree/bindings/spi/sh-msiof.txt
+++ b/Documentation/devicetree/bindings/spi/sh-msiof.txt
@@ -10,6 +10,8 @@
 			 "renesas,msiof-r8a7792" (R-Car V2H)
 			 "renesas,msiof-r8a7793" (R-Car M2-N)
 			 "renesas,msiof-r8a7794" (R-Car E2)
+			 "renesas,msiof-r8a7795" (R-Car H3)
+			 "renesas,msiof-r8a7796" (R-Car M3)
 			 "renesas,msiof-sh73a0" (SH-Mobile AG5)
 - reg                  : A list of offsets and lengths of the register sets for
 			 the device.
diff --git a/Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt b/Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt
new file mode 100644
index 0000000..9b38109
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt
@@ -0,0 +1,84 @@
+* Renesas R-Car Gen3 Thermal
+
+Required properties:
+- compatible		: "renesas,thermal-<soctype>",
+			  Examples with soctypes are:
+			    - "renesas,thermal-r8a7795" (R-Car H3)
+			    - "renesas,thermal-r8a7796" (R-Car M3)
+- reg			: Address range of the thermal registers.
+- #thermal-sensor-cells : Please see ./thermal.txt
+
+Option properties:
+
+- interrupts		: use interrupt
+
+Example (non interrupt support):
+
+thermal: thermal@0xe6198000 {
+                        compatible = "renesas,thermal-r8a7795";
+                        reg = <0 0xe6198000 0 0x5c>;
+                };
+
+thermal-zones {
+	cpu_thermal: cpu-thermal {
+		polling-delay-passive	= <250>;
+		polling-delay		= <1000>;
+
+		thermal-sensors = <&thermal>;
+	};
+};
+
+Example (interrupt support):
+
+thermal: thermal@0xe6198000 {
+                        compatible = "renesas,thermal-r8a7795";
+                        reg = <0 0xe6198000 0 0x5c>;
+			interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
+			             <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
+			             <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
+                };
+
+thermal-zones {
+	cpu_thermal: cpu-thermal {
+		polling-delay-passive	= <250>;
+		polling-delay		= <0>;
+
+		thermal-sensors = <&thermal>;
+	};
+};
+
+* Emergency shutdown for R-CAR Gen3
+Emergency shutdown functionality provides the specific cooling mechanism
+for R-CAR Gen3. In case of high temperature(e.g over 100 degrees),
+it has the ability to rapidly cool down the system.
+
+Required property:
+- polling-delay:	The maximum number of milliseconds to wait between polls
+  Type: unsigned	when checking temperature for emergency shutdown.
+  Size: one cell
+
+- on-temperature:       This value indicates the emergency temperature and invokes
+  Type: unsigned	emergency shutdown functionality when exceeding this
+  Size: one cell	temperature.
+
+- off-temperature:	This value indicates the temperature to disable emergency
+  Type: unsigned	shutdown.
+  Size: one cell
+
+- status:		Should be "disabled" always.
+  Type: string
+
+- target_cpus:		This property indicates which CPU will be targeted for shutdown.
+  Type: phandle
+
+thermal-zones {
+	emergency {
+		polling-delay = <1000>;    /* milliseconds */
+		on-temperature = <110000>; /* millicelsius */
+		off-temperature = <95000>; /* millicelsius */
+		target_cpus = <&a57_1>,
+			      <&a57_2>,
+			      <&a57_3>;
+		status = "disabled";
+	};
+};
diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt
index 966885c..0b7d857 100644
--- a/Documentation/devicetree/bindings/usb/usb-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt
@@ -11,6 +11,7 @@
     - "renesas,xhci-r8a7791" for r8a7791 SoC
     - "renesas,xhci-r8a7793" for r8a7793 SoC
     - "renesas,xhci-r8a7795" for r8a7795 SoC
+    - "renesas,xhci-r8a7796" for r8a7796 SoC
     - "renesas,rcar-gen2-xhci" for a generic R-Car Gen2 compatible device
     - "renesas,rcar-gen3-xhci" for a generic R-Car Gen3 compatible device
     - "xhci-platform" (deprecated)
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 72f4eac..d9f4673 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -144,6 +144,7 @@
 	select PM_GENERIC_DOMAINS
 	select RENESAS_IRQC
 	select SOC_BUS
+	select RPMSG_VIRTIO
 	help
 	  This enables support for the ARMv8 based Renesas SoCs.
 
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index dab2cb0..81beecd 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -84,6 +84,12 @@
 CONFIG_CPU_IDLE=y
 CONFIG_ARM_CPUIDLE=y
 CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
 CONFIG_CPUFREQ_DT=y
 CONFIG_ARM_BIG_LITTLE_CPUFREQ=y
 CONFIG_ARM_SCPI_CPUFREQ=y
@@ -238,12 +244,14 @@
 CONFIG_I2C_UNIPHIER_F=y
 CONFIG_I2C_RCAR=y
 CONFIG_I2C_CROS_EC_TUNNEL=y
+CONFIG_I2C_SH_MOBILE=y
 CONFIG_SPI=y
 CONFIG_SPI_MESON_SPIFC=m
 CONFIG_SPI_ORION=y
 CONFIG_SPI_PL022=y
 CONFIG_SPI_QUP=y
-CONFIG_SPI_SPIDEV=m
+CONFIG_SPI_SH_MSIOF=y
+CONFIG_SPI_SPIDEV=y
 CONFIG_SPI_S3C64XX=y
 CONFIG_SPMI=y
 CONFIG_PINCTRL_SINGLE=y
@@ -260,6 +268,7 @@
 CONFIG_GPIO_PCA953X=y
 CONFIG_GPIO_PCA953X_IRQ=y
 CONFIG_GPIO_MAX77620=y
+CONFIG_POWER_AVS=y
 CONFIG_POWER_RESET_MSM=y
 CONFIG_BATTERY_BQ27XXX=y
 CONFIG_POWER_RESET_XGENE=y
@@ -268,10 +277,14 @@
 CONFIG_SENSORS_INA2XX=m
 CONFIG_SENSORS_ARM_SCPI=y
 CONFIG_THERMAL=y
+CONFIG_THERMAL_OF=y
+CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR=y
 CONFIG_THERMAL_EMULATION=y
 CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y
 CONFIG_CPU_THERMAL=y
 CONFIG_EXYNOS_THERMAL=y
+CONFIG_RCAR_GEN3_THERMAL=y
+CONFIG_RCAR_THERMAL_EMS_ENABLED=y
 CONFIG_WATCHDOG=y
 CONFIG_RENESAS_WDT=y
 CONFIG_S3C2410_WATCHDOG=y
@@ -284,6 +297,7 @@
 CONFIG_REGULATOR=y
 CONFIG_MFD_CROS_EC=y
 CONFIG_MFD_CROS_EC_I2C=y
+CONFIG_REGULATOR_BD9571MWV=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_GPIO=y
 CONFIG_REGULATOR_HI655X=y
@@ -292,12 +306,28 @@
 CONFIG_REGULATOR_QCOM_SMD_RPM=y
 CONFIG_REGULATOR_QCOM_SPMI=y
 CONFIG_REGULATOR_S2MPS11=y
-CONFIG_DRM=m
+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_V4L_MEM2MEM_DRIVERS=y
+CONFIG_VIDEO_RENESAS_FCP=y
+CONFIG_VIDEO_RENESAS_VSP1=y
+CONFIG_DRM=y
 CONFIG_DRM_NOUVEAU=m
 CONFIG_DRM_TEGRA=m
 CONFIG_DRM_PANEL_SIMPLE=m
 CONFIG_DRM_I2C_ADV7511=m
 CONFIG_DRM_HISI_KIRIN=m
+CONFIG_DRM_RCAR_DU=y
+CONFIG_DRM_RCAR_HDMI=y
+CONFIG_DRM_RCAR_LVDS=y
+CONFIG_DRM_RCAR_VSP=y
+CONFIG_VIDEO_RCAR_VIN=y
+CONFIG_VIDEO_RCAR_CSI2=y
+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set
+CONFIG_VIDEO_ADV7482=y
 CONFIG_FB=y
 CONFIG_FB_ARMCLCD=y
 CONFIG_BACKLIGHT_GENERIC=m
@@ -325,7 +355,7 @@
 CONFIG_USB_OHCI_EXYNOS=y
 CONFIG_USB_OHCI_HCD=y
 CONFIG_USB_OHCI_HCD_PLATFORM=y
-CONFIG_USB_RENESAS_USBHS=m
+CONFIG_USB_RENESAS_USBHS=y
 CONFIG_USB_STORAGE=y
 CONFIG_USB_DWC2=y
 CONFIG_USB_DWC3=y
@@ -337,7 +367,7 @@
 CONFIG_USB_MSM_OTG=y
 CONFIG_USB_ULPI=y
 CONFIG_USB_GADGET=y
-CONFIG_USB_RENESAS_USBHS_UDC=m
+CONFIG_USB_RENESAS_USBHS_UDC=y
 CONFIG_MMC=y
 CONFIG_MMC_BLOCK_MINORS=32
 CONFIG_MMC_ARMMMCI=y
@@ -386,6 +416,7 @@
 CONFIG_XEN_GRANT_DEV_ALLOC=y
 CONFIG_COMMON_CLK_SCPI=y
 CONFIG_COMMON_CLK_CS2000_CP=y
+CONFIG_COMMON_CLK_5P49V5923A=y
 CONFIG_COMMON_CLK_S2MPS11=y
 CONFIG_CLK_QORIQ=y
 CONFIG_COMMON_CLK_QCOM=y
@@ -413,6 +444,7 @@
 CONFIG_ACPI=y
 CONFIG_IIO=y
 CONFIG_EXYNOS_ADC=y
+CONFIG_PWM_RCAR=y
 CONFIG_PWM_SAMSUNG=y
 CONFIG_EXT2_FS=y
 CONFIG_EXT3_FS=y
diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c
index f72d601..59d2dc0 100644
--- a/drivers/ata/sata_rcar.c
+++ b/drivers/ata/sata_rcar.c
@@ -970,9 +970,41 @@ static int sata_rcar_resume(struct device *dev)
 	struct ata_host *host = dev_get_drvdata(dev);
 	struct sata_rcar_priv *priv = host->private_data;
 	void __iomem *base = priv->base;
+	u32 val;
 
 	clk_prepare_enable(priv->clk);
 
+	/* Re-use from sata_rcar_init_controller() */
+	/* reset and setup phy */
+	switch (priv->type) {
+	case RCAR_GEN1_SATA:
+		sata_rcar_gen1_phy_init(priv);
+		break;
+	case RCAR_GEN2_SATA:
+		sata_rcar_gen2_phy_init(priv);
+		break;
+	default:
+		dev_warn(host->dev, "SATA phy is not initialized\n");
+		break;
+	}
+
+	/* SATA-IP reset state */
+	val = ioread32(base + ATAPI_CONTROL1_REG);
+	val |= ATAPI_CONTROL1_RESET;
+	iowrite32(val, base + ATAPI_CONTROL1_REG);
+
+	/* ISM mode, PRD mode, DTEND flag at bit 0 */
+	val = ioread32(base + ATAPI_CONTROL1_REG);
+	val |= ATAPI_CONTROL1_ISM;
+	val |= ATAPI_CONTROL1_DESE;
+	val |= ATAPI_CONTROL1_DTA32M;
+	iowrite32(val, base + ATAPI_CONTROL1_REG);
+
+	/* Release the SATA-IP from the reset state */
+	val = ioread32(base + ATAPI_CONTROL1_REG);
+	val &= ~ATAPI_CONTROL1_RESET;
+	iowrite32(val, base + ATAPI_CONTROL1_REG);
+
 	/* ack and mask */
 	iowrite32(0, base + SATAINTSTAT_REG);
 	iowrite32(0x7ff, base + SATAINTMASK_REG);
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 6a8ac04..d4502a8 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -183,6 +183,13 @@
 	  Adapter driver so that any PWM output can be (mis)used as clock signal
 	  at 50% duty cycle.
 
+config COMMON_CLK_5P49V5923A
+	tristate "Clock driver for 5P49V5923A programmable clock generator"
+	depends on I2C
+	help
+	  If you say yes here you get support for the 5P49V5923A programmable
+	  clock generator.
+
 config COMMON_CLK_PXA
 	def_bool COMMON_CLK && ARCH_PXA
 	---help---
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 925081e..84fb169 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -17,6 +17,7 @@
 
 # hardware specific clock types
 # please keep this section sorted lexicographically by file path name
+obj-$(CONFIG_COMMON_CLK_5P49V5923A)	+= clk-5p49v5923a.o
 obj-$(CONFIG_MACH_ASM9260)		+= clk-asm9260.o
 obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN)	+= clk-axi-clkgen.o
 obj-$(CONFIG_ARCH_AXXIA)		+= clk-axm5516.o
diff --git a/drivers/clk/clk-5p49v5923a.c b/drivers/clk/clk-5p49v5923a.c
new file mode 100644
index 0000000..0cbe670
--- /dev/null
+++ b/drivers/clk/clk-5p49v5923a.c
@@ -0,0 +1,310 @@
+/*
+ * drivers/gpu/drm/i2c/5p49v5923a.c
+ *     This file is programmable clock generator driver.
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This file is based on the drivers/clk/clk-cs2000-cp.c
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/module.h>
+
+#define REF_CLK		1
+#define CLK_MAX		5
+
+#define INPUT_CLK	25000000
+
+#define C5P49_FB_INT_DIV_REG1	0x17
+#define C5P49_FB_INT_DIV_REG0	0x18
+
+/* offset address*/
+#define C5P49_DIV_FRAC_29_22	0x02
+#define C5P49_DIV_FRAC_21_14	0x03
+#define C5P49_DIV_FRAC_13_6	0x04
+#define C5P49_DIV_FRAC_5_0	0x05
+#define C5P49_DIV_INTEGER_11_4	0x0d
+#define C5P49_DIV_INTEGER_3_0	0x0e
+
+#define C5P49_CLK_OE_SHUTDOWN	0x68
+
+#define hw_to_priv(_hw)		container_of(_hw, struct clk_5p49_priv, hw)
+#define priv_to_client(priv)	(priv->client)
+#define priv_to_dev(priv)	(&(priv_to_client(priv)->dev))
+
+struct clk_5p49_priv {
+	struct		clk_hw hw;
+	struct		i2c_client *client;
+	struct		clk *clk_out;
+	unsigned long	index;
+	unsigned long	clk_rate;
+};
+
+static const struct of_device_id clk_5p49_of_match[] = {
+	{ .compatible = "idt,5p49v5923a", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, clk_5p49_of_match);
+
+static const struct i2c_device_id clk_5p49_id[] = {
+	{ "5p49v5923a",},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, clk_5p49_id);
+
+#define clk_5p49_read(priv, addr) \
+	i2c_smbus_read_byte_data(priv_to_client(priv), \
+	(addr + (0x10 * priv->index)))
+#define clk_5p49_write(priv, addr, val) \
+	i2c_smbus_write_byte_data(priv_to_client(priv), \
+	(addr + (0x10 * priv->index)), val)
+
+static int clk_5p49_set_rate(struct clk_hw *hw,
+			     unsigned long rate, unsigned long parent_rate)
+{
+	return 0;
+}
+
+static void clk_5p49_power(struct clk_hw *hw, bool power)
+{
+	struct clk_5p49_priv *priv = hw_to_priv(hw);
+	u8 reg;
+
+	if (power) {
+		reg = i2c_smbus_read_byte_data(priv->client,
+					C5P49_CLK_OE_SHUTDOWN);
+		reg |= (0x80 >> (priv->index - 1));
+		i2c_smbus_write_byte_data(priv->client,
+					C5P49_CLK_OE_SHUTDOWN, reg);
+	} else {
+		reg = i2c_smbus_read_byte_data(priv->client,
+					C5P49_CLK_OE_SHUTDOWN);
+		reg &= ~(0x80 >> (priv->index - 1));
+		i2c_smbus_write_byte_data(priv->client,
+					C5P49_CLK_OE_SHUTDOWN, reg);
+	}
+}
+
+static int clk_5p49_enable(struct clk_hw *hw)
+{
+	clk_5p49_power(hw, true);
+
+	return 0;
+}
+
+static void clk_5p49_disable(struct clk_hw *hw)
+{
+	clk_5p49_power(hw, false);
+}
+
+static unsigned long clk_5p49_recalc_rate(struct clk_hw *hw,
+					unsigned long parent_rate)
+{
+	struct clk_5p49_priv *priv = hw_to_priv(hw);
+
+	return priv->clk_rate;
+}
+
+static int clk_5p49_div_calculation(struct clk_hw *hw, unsigned long rate)
+{
+	struct clk_5p49_priv *priv = hw_to_priv(hw);
+	int integ_div, frac_div, div, vco_div, vco_clk;
+	u32 shift_1kHz = 1000;
+	u8 frac_0, frac_1, frac_2, frac_3;
+
+	vco_div = ((i2c_smbus_read_byte_data(priv->client,
+			C5P49_FB_INT_DIV_REG0) & 0xF0) >> 4)
+			+ (i2c_smbus_read_byte_data(priv->client,
+			C5P49_FB_INT_DIV_REG1) << 4);
+
+	clk_5p49_power(hw, false);
+
+	vco_clk = INPUT_CLK * vco_div / shift_1kHz;
+	dev_dbg(&priv->client->dev, "vco clock:%d kHz\n", vco_clk);
+
+	vco_clk = (vco_clk / 2);
+	rate = rate / shift_1kHz;
+
+	integ_div = (vco_clk / rate);
+	div = ((vco_clk * shift_1kHz) / rate);
+	frac_div = div - (integ_div * shift_1kHz);
+
+	if (frac_div > 0x3fffffff)
+		return -EINVAL;
+
+	clk_5p49_write(priv, C5P49_DIV_INTEGER_11_4,
+			((0x0ff0 & (u16)integ_div) >> 4));
+	clk_5p49_write(priv, C5P49_DIV_INTEGER_3_0,
+			((0x000f & (u16)integ_div) << 4));
+
+	/* spread = 0.01% */
+	frac_div = frac_div - ((div / (100 * 100 / 1)) / 2);
+	frac_div = ((0x1000000 / shift_1kHz) * frac_div);
+	dev_dbg(&priv->client->dev,
+		"integer:0x%x, fraction:0x%x\n",
+		integ_div, frac_div);
+
+	frac_0 = (frac_div & 0x3fc00000) >> 22;
+	frac_1 = (frac_div & 0x003fc000) >> 14;
+	frac_2 = (frac_div & 0x00003fc0) >> 6;
+	frac_3 = (frac_div & 0x0000003f) << 2;
+
+	clk_5p49_write(priv, C5P49_DIV_FRAC_29_22, frac_0);
+	clk_5p49_write(priv, C5P49_DIV_FRAC_21_14, frac_1);
+	clk_5p49_write(priv, C5P49_DIV_FRAC_13_6,  frac_2);
+	clk_5p49_write(priv, C5P49_DIV_FRAC_5_0,   frac_3);
+
+	clk_5p49_power(hw, true);
+
+	return 0;
+}
+
+static long clk_5p49_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *parent_rate)
+{
+	struct clk_5p49_priv *priv = hw_to_priv(hw);
+	int ret;
+
+	priv->clk_rate = 0;
+
+	ret = clk_5p49_div_calculation(hw, rate);
+	if (ret < 0)
+		return ret;
+
+	priv->clk_rate = rate;
+
+	return 0;
+}
+
+static u8 clk_5p49_get_parent(struct clk_hw *hw)
+{
+	return 0;
+}
+
+static const struct clk_ops clk_5p49_ops = {
+	.get_parent	= clk_5p49_get_parent,
+	.set_rate	= clk_5p49_set_rate,
+	.prepare	= clk_5p49_enable,
+	.unprepare	= clk_5p49_disable,
+	.recalc_rate	= clk_5p49_recalc_rate,
+	.round_rate	= clk_5p49_round_rate,
+};
+
+static int clk_5p49_clk_register(struct clk_5p49_priv *priv,
+				 struct device_node *np)
+{
+	struct clk_init_data init;
+	struct clk *clk;
+	const char *name;
+	static const char *parent_names[REF_CLK];
+	int ret = 0;
+
+	parent_names[0]	= __clk_get_name(of_clk_get(np, 0));
+	name = np->name;
+
+	init.name		= name;
+	init.ops		= &clk_5p49_ops;
+	init.flags		= CLK_IS_BASIC | CLK_SET_RATE_PARENT;
+	init.parent_names	= parent_names;
+	init.num_parents	= ARRAY_SIZE(parent_names);
+
+	priv->hw.init = &init;
+
+	clk = clk_register(NULL, &priv->hw);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	ret = of_clk_add_provider(np, of_clk_src_simple_get, clk);
+	if (ret < 0) {
+		clk_unregister(clk);
+		return ret;
+	}
+
+	priv->clk_out = clk;
+
+	return 0;
+}
+
+static int clk_5p49_probe(struct i2c_client *client,
+			  const struct i2c_device_id *id)
+{
+	struct clk_5p49_priv *priv = NULL;
+	struct device *dev = &client->dev;
+	struct device_node *np = dev->of_node, *ch_np;
+	int ret, i, ch = 1;	/* ch = 0 reserved.*/
+	u32 probe_cnt = 0;
+
+	for (i = ch; i < CLK_MAX; i++) {
+		char name[20];
+
+		sprintf(name, "5p49v5923a_clk%u", i);
+		ch_np = of_get_child_by_name(np, name);
+		if (!ch_np)
+			continue;
+
+		priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+		if (!priv)
+			return -ENOMEM;
+
+		priv->client = client;
+		priv->index = i + 1;
+		i2c_set_clientdata(client, priv);
+
+		ret = clk_5p49_clk_register(priv, ch_np);
+		if (ret < 0)
+			return ret;
+		probe_cnt++;
+	}
+
+	if (probe_cnt == 0) {
+		dev_err(dev, "Device tree error.\n");
+		return -EINVAL;
+	}
+
+	dev_info(dev, "Rev.0x%x, probed\n",
+		i2c_smbus_read_byte_data(priv->client, 0x01));
+
+	return 0;
+}
+
+static int clk_5p49_remove(struct i2c_client *client)
+{
+	struct clk_5p49_priv *priv = i2c_get_clientdata(client);
+	struct device *dev = &client->dev;
+	struct device_node *np = dev->of_node;
+
+	of_clk_del_provider(np);
+
+	clk_unregister(priv->clk_out);
+
+	return 0;
+}
+
+static struct i2c_driver clk_5p49_driver = {
+	.driver = {
+		.name = "5p49v5923a",
+		.of_match_table = clk_5p49_of_match,
+	},
+	.probe		= clk_5p49_probe,
+	.remove		= clk_5p49_remove,
+	.id_table	= clk_5p49_id,
+};
+
+module_i2c_driver(clk_5p49_driver);
+
+MODULE_DESCRIPTION("5p49v5923a programmable clock generator driver");
+MODULE_AUTHOR("Koji Matsuoka <koji.matsuoka.xm@renesas.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c
index 021f3da..0a04b05 100644
--- a/drivers/clk/clk-cs2000-cp.c
+++ b/drivers/clk/clk-cs2000-cp.c
@@ -489,9 +489,42 @@ static int cs2000_probe(struct i2c_client *client,
 	return ret;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int cs2000_suspend(struct device *dev)
+{
+	/* Empty function for now */
+	return 0;
+}
+
+static int cs2000_resume(struct device *dev)
+{
+	struct cs2000_priv *priv = dev_get_drvdata(dev);
+	int rate;
+	int ret;
+
+	/* Setting is referred from cs2000_clk_register */
+	rate = clk_get_rate(priv->ref_clk);
+	ret = __cs2000_set_rate(priv, 0, rate, rate);
+
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static const struct dev_pm_ops cs2000_pm_ops = {
+	.suspend = cs2000_suspend,
+	.resume_early = cs2000_resume,
+};
+#define DEV_PM_OPS (&cs2000_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
 static struct i2c_driver cs2000_driver = {
 	.driver = {
 		.name = "cs2000-cp",
+		.pm	= DEV_PM_OPS,
 		.of_match_table = cs2000_of_match,
 	},
 	.probe		= cs2000_probe,
diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c
index 2d325c7..982d76d 100644
--- a/drivers/clk/renesas/rcar-gen3-cpg.c
+++ b/drivers/clk/renesas/rcar-gen3-cpg.c
@@ -21,6 +21,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/sys_soc.h>
+#include <linux/soc/renesas/rcar_prr.h>
 
 #include "renesas-cpg-mssr.h"
 #include "rcar-gen3-cpg.h"
@@ -39,9 +40,154 @@ static const struct soc_device_attribute r8a7796es10[] __initconst = {
 #define CPG_PLL2CR		0x002c
 #define CPG_PLL4CR		0x01f4
 
-/** Modify for Z-clock
- * -----------------------------------------------------------------------------
- * Z Clock & Z2 Clock
+/* Implementation for customized clocks (Z-clk, Z2-clk, PLL0-clk) for CPUFreq */
+#define CPG_PLLECR     0x00D0
+#define CPG_PLLECR_PLL0ST (1 << 8)
+
+
+static const struct soc_device_attribute r8a7795es10[] = {
+	{ .soc_id = "r8a7795", .revision = "ES1.0" },
+	{ /* sentinel */ }
+};
+
+/* Define for PLL0 clk driver */
+#define CPG_PLL0CR_STC_MASK             0x7f000000
+#define CPG_PLL0CR_STC_SHIFT            24
+
+#ifdef CONFIG_RCAR_Z_CLK_MAX_THRESHOLD
+#define Z_CLK_MAX_THRESHOLD     CONFIG_RCAR_Z_CLK_MAX_THRESHOLD
+#else
+#define Z_CLK_MAX_THRESHOLD             1500000000
+#endif
+
+struct cpg_pll0_clk {
+	struct clk_hw hw;
+	void __iomem *reg;
+	void __iomem *pllecr_reg;
+};
+
+#define to_pll0_clk(_hw)   container_of(_hw, struct cpg_pll0_clk, hw)
+
+static int cpg_pll0_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long prate)
+{
+	struct cpg_pll0_clk *pll0_clk = to_pll0_clk(hw);
+	unsigned int stc_val;
+	u32 val;
+	int i;
+
+	/* Start clock issue W/A (for H3 WS1.0) */
+	if (soc_device_match(r8a7795es10))
+		prate *= 2; /* PLL0 output multiplied by 2 */
+	/* End clock issue W/A */
+
+	stc_val = DIV_ROUND_CLOSEST(rate, prate);
+	stc_val = clamp(stc_val, 90U, 120U);/*Lowest value is 1.5G (stc == 90)*/
+	pr_debug("%s(): prate: %lu, rate: %lu, pll0-mult: %d\n",
+		__func__, prate, rate, stc_val);
+
+	stc_val -= 1;
+	val = clk_readl(pll0_clk->reg);
+	val &= ~CPG_PLL0CR_STC_MASK;
+	val |= stc_val << CPG_PLL0CR_STC_SHIFT;
+	clk_writel(val, pll0_clk->reg);
+
+	i = 0;
+	while (!(clk_readl(pll0_clk->pllecr_reg) & CPG_PLLECR_PLL0ST)) {
+		cpu_relax();
+		i++;
+	}
+
+	if (i > 1000)
+		pr_warn("%s(): PLL0: long settled time: %d\n", __func__, i);
+
+	return 0;
+}
+
+static long cpg_pll0_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *parent_rate)
+{
+	unsigned long prate = *parent_rate;
+	unsigned int mult;
+
+	if (rate < Z_CLK_MAX_THRESHOLD)
+		rate = Z_CLK_MAX_THRESHOLD; /* Set lowest value: 1.5GHz */
+
+	/* Start clock issue W/A (for H3 WS1.0) */
+	if (soc_device_match(r8a7795es10))
+		prate *= 2; /* PLL0 output multiplied by 2 */
+	/* End clock issue W/A */
+
+	mult = DIV_ROUND_CLOSEST(rate, prate);
+	mult = clamp(mult, 90U, 120U); /* 1.5G => (stc == 90)*/
+
+	rate = prate * mult;
+
+	/* Round to closest value at 100MHz unit */
+	rate = 100000000 * DIV_ROUND_CLOSEST(rate, 100000000);
+	pr_debug("%s(): output rate: %lu, parent_rate: %lu, pll0-mult: %d\n",
+		__func__, rate, prate, mult);
+	return rate;
+}
+
+static unsigned long cpg_pll0_clk_recalc_rate(struct clk_hw *hw,
+					unsigned long parent_rate)
+{
+	struct cpg_pll0_clk *pll0_clk = to_pll0_clk(hw);
+	unsigned int val;
+	unsigned long rate;
+
+	val = (clk_readl(pll0_clk->reg) & CPG_PLL0CR_STC_MASK)
+		>> CPG_PLL0CR_STC_SHIFT;
+
+	rate = (u64)parent_rate * (val + 1);
+
+	/* Start clock issue W/A (for H3 WS1.0) */
+	if (soc_device_match(r8a7795es10))
+		rate *= 2; /* PLL0 output multiplied by 2 */
+	/* End clock issue W/A */
+
+	/* Round to closest value at 100MHz unit */
+	rate = 100000000 * DIV_ROUND_CLOSEST(rate, 100000000);
+	return rate;
+}
+
+static const struct clk_ops cpg_pll0_clk_ops = {
+	.recalc_rate = cpg_pll0_clk_recalc_rate,
+	.round_rate = cpg_pll0_clk_round_rate,
+	.set_rate = cpg_pll0_clk_set_rate,
+};
+
+static struct clk * __init cpg_pll0_clk_register(const char *name,
+				const char *parent_name,
+				void __iomem *cpg_base)
+{
+	struct clk_init_data init;
+	struct cpg_pll0_clk *pll0_clk;
+	struct clk *clk;
+
+	pll0_clk = kzalloc(sizeof(*pll0_clk), GFP_KERNEL);
+	if (!pll0_clk)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &cpg_pll0_clk_ops;
+	init.flags = 0;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+
+	pll0_clk->reg = cpg_base + CPG_PLL0CR;
+	pll0_clk->pllecr_reg = cpg_base + CPG_PLLECR;
+	pll0_clk->hw.init = &init;
+
+	clk = clk_register(NULL, &pll0_clk->hw);
+	if (IS_ERR(clk))
+		kfree(pll0_clk);
+
+	return clk;
+}
+
+/* Modify for Z-clock and Z2-clock
  *
  * Traits of this clock:
  * prepare - clk_prepare only ensures that parents are prepared
@@ -56,7 +202,7 @@ static const struct soc_device_attribute r8a7796es10[] __initconst = {
 #define CPG_FRQCRC_ZFC_SHIFT		8
 #define CPG_FRQCRC_Z2FC_MASK		0x1f
 
-
+/* Z clk driver code */
 struct cpg_z_clk {
 	struct clk_hw hw;
 	void __iomem *reg;
@@ -79,24 +225,7 @@ static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw,
 
 	rate = div_u64((u64)parent_rate * mult + 16, 32);
 	/* Round to closest value at 100MHz unit */
-	rate = 100000000*DIV_ROUND_CLOSEST(rate, 100000000);
-	return rate;
-}
-
-static unsigned long cpg_z2_clk_recalc_rate(struct clk_hw *hw,
-					   unsigned long parent_rate)
-{
-	struct cpg_z_clk *zclk = to_z_clk(hw);
-	unsigned int mult;
-	unsigned int val;
-	unsigned long rate;
-
-	val = (clk_readl(zclk->reg) & CPG_FRQCRC_Z2FC_MASK);
-	mult = 32 - val;
-
-	rate = div_u64((u64)parent_rate * mult + 16, 32);
-	/* Round to closest value at 100MHz unit */
-	rate = 100000000*DIV_ROUND_CLOSEST(rate, 100000000);
+	rate = 100000000 * DIV_ROUND_CLOSEST(rate, 100000000);
 	return rate;
 }
 
@@ -109,10 +238,25 @@ static long cpg_z_clk_round_rate(struct clk_hw *hw, unsigned long rate,
 	if (!prate)
 		prate = 1;
 
-	mult = div_u64((u64)rate * 32 + prate/2, prate);
+	if (rate <= Z_CLK_MAX_THRESHOLD) { /* Focus on changing z-clock */
+		prate = Z_CLK_MAX_THRESHOLD; /* Set parent to: 1.5GHz */
+		mult = div_u64((u64)rate * 32 + prate/2, prate);
+	} else {
+		/* Focus on changing parent. Fix z-clock divider is 32/32 */
+		mult = 32;
+	}
+
 	mult = clamp(mult, 1U, 32U);
 
-	return *parent_rate / 32 * mult;
+	/* Re-calculate the parent_rate to propagate new rate for it */
+	prate = div_u64((u64)rate * 32 + mult/2, mult);
+	prate = 100000000 * DIV_ROUND_CLOSEST(prate, 100000000);
+	rate = 100000000 * DIV_ROUND_CLOSEST(prate / 32 * mult, 100000000);
+	pr_debug("%s():z-clk mult:%d, re-calculated prate:%lu, return: %lu\n",
+		__func__, mult, prate, rate);
+	*parent_rate = prate;
+
+	return rate;
 }
 
 static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -123,9 +267,16 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
 	u32 val, kick;
 	unsigned int i;
 
-	mult = div_u64((u64)rate * 32 + parent_rate/2, parent_rate);
+	if (rate <= Z_CLK_MAX_THRESHOLD) { /* Focus on changing z-clock */
+		parent_rate = Z_CLK_MAX_THRESHOLD; /* Set parent to: 1.5GHz */
+		mult = div_u64((u64)rate * 32 + parent_rate/2, parent_rate);
+	} else {
+		mult = 32;
+	}
 	mult = clamp(mult, 1U, 32U);
 
+	pr_debug("%s(): rate: %lu, set prate to: %lu, z-clk mult: %d\n",
+		__func__, rate, parent_rate, mult);
 	if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
 		return -EBUSY;
 
@@ -161,25 +312,12 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
 	return -ETIMEDOUT;
 }
 
-static int cpg_z2_clk_set_rate(struct clk_hw *hw, unsigned long rate,
-			      unsigned long parent_rate)
-{
-	pr_info("Do not support Z2 clock changing\n");
-	return 0;
-}
-
 static const struct clk_ops cpg_z_clk_ops = {
 	.recalc_rate = cpg_z_clk_recalc_rate,
 	.round_rate = cpg_z_clk_round_rate,
 	.set_rate = cpg_z_clk_set_rate,
 };
 
-static const struct clk_ops cpg_z2_clk_ops = {
-	.recalc_rate = cpg_z2_clk_recalc_rate,
-	.round_rate = cpg_z_clk_round_rate,
-	.set_rate = cpg_z2_clk_set_rate,
-};
-
 static struct clk * __init cpg_z_clk_register(const char *name,
 					      const char *parent_name,
 					      void __iomem *reg)
@@ -194,7 +332,7 @@ static struct clk * __init cpg_z_clk_register(const char *name,
 
 	init.name = name;
 	init.ops = &cpg_z_clk_ops;
-	init.flags = 0;
+	init.flags = CLK_SET_RATE_PARENT;
 	init.parent_names = &parent_name;
 	init.num_parents = 1;
 
@@ -209,6 +347,49 @@ static struct clk * __init cpg_z_clk_register(const char *name,
 	return clk;
 }
 
+/* Z2 clk driver code */
+static unsigned long cpg_z2_clk_recalc_rate(struct clk_hw *hw,
+					   unsigned long parent_rate)
+{
+	struct cpg_z_clk *zclk = to_z_clk(hw);
+	unsigned int mult;
+	unsigned int val;
+	unsigned long rate;
+
+	val = (clk_readl(zclk->reg) & CPG_FRQCRC_Z2FC_MASK);
+	mult = 32 - val;
+
+	rate = div_u64((u64)parent_rate * mult + 16, 32);
+	/* Round to closest value at 100MHz unit */
+	rate = 100000000 * DIV_ROUND_CLOSEST(rate, 100000000);
+	return rate;
+}
+
+static long cpg_z2_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *parent_rate)
+{
+	unsigned long prate  = *parent_rate;
+	unsigned int mult;
+
+	mult = div_u64((u64)rate * 32 + prate/2, prate);
+	mult = clamp(mult, 1U, 32U);
+
+	return prate / 32 * mult;
+}
+
+static int cpg_z2_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+			      unsigned long parent_rate)
+{
+	pr_info("Do not support Z2 clock changing\n");
+	return 0;
+}
+
+static const struct clk_ops cpg_z2_clk_ops = {
+	.recalc_rate = cpg_z2_clk_recalc_rate,
+	.round_rate = cpg_z2_clk_round_rate,
+	.set_rate = cpg_z2_clk_set_rate,
+};
+
 static struct clk * __init cpg_z2_clk_register(const char *name,
 					      const char *parent_name,
 					      void __iomem *reg)
@@ -237,7 +418,7 @@ static struct clk * __init cpg_z2_clk_register(const char *name,
 
 	return clk;
 }
-/** End of modifying for Z-clock */
+/** End of modifying for Z-clock, Z2-clock and PLL0-clock */
 
 /*
  * SDn Clock
@@ -479,15 +660,12 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
 
 	case CLK_TYPE_GEN3_PLL0:
 		/*
-		 * PLL0 is a configurable multiplier clock. Register it as a
-		 * fixed factor clock for now as there's no generic multiplier
-		 * clock implementation and we currently have no need to change
-		 * the multiplier value.
+		 * The PLL0 is implemented as customized clock,
+		 * it changes the multiplier when cpufreq changes between
+		 * normal and override mode.
 		 */
-		value = readl(base + CPG_PLL0CR);
-		mult = (((value >> 24) & 0x7f) + 1) * 2;
-		break;
-
+		return cpg_pll0_clk_register(core->name,
+				__clk_get_name(parent), base);
 	case CLK_TYPE_GEN3_PLL1:
 		mult = cpg_pll_config->pll1_mult;
 		break;
@@ -500,7 +678,11 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
 		 * the multiplier value.
 		 */
 		value = readl(base + CPG_PLL2CR);
-		mult = (((value >> 24) & 0x7f) + 1) * 2;
+		mult = ((value >> 24) & 0x7f) + 1;
+		/* Start clock issue W/A (for H3 WS1.0) */
+		if (soc_device_match(r8a7795es10))
+			mult *= 2; /* PLL2 output multiplied by 2 */
+		/* End clock issue W/A */
 		break;
 
 	case CLK_TYPE_GEN3_PLL3:
@@ -516,6 +698,10 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
 		 */
 		value = readl(base + CPG_PLL4CR);
 		mult = (((value >> 24) & 0x7f) + 1) * 2;
+		/* Start clock issue W/A (for H3 WS1.0) */
+		if (soc_device_match(r8a7795es10))
+			mult *= 2; /* PLL4 output multiplied by 2 */
+		/* End clock issue W/A */
 		break;
 
 	case CLK_TYPE_GEN3_SD:
@@ -581,5 +767,6 @@ int __init rcar_gen3_cpg_init(const struct rcar_gen3_cpg_pll_config *config,
 {
 	cpg_pll_config = config;
 	cpg_clk_extalr = clk_extalr;
+
 	return 0;
 }
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c
index 75b0d89..db75fc4 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.c
+++ b/drivers/clk/renesas/renesas-cpg-mssr.c
@@ -26,6 +26,7 @@
 #include <linux/pm_clock.h>
 #include <linux/pm_domain.h>
 #include <linux/slab.h>
+#include <linux/soc/renesas/s2ram_ddr_backup.h>
 
 #include <dt-bindings/clock/renesas-cpg-mssr.h>
 
@@ -38,6 +39,59 @@
 #define WARN_DEBUG(x)	do { } while (0)
 #endif
 
+#ifdef CONFIG_RCAR_DDR_BACKUP
+static struct hw_register cpg_ip_regs[] = {
+	{"CPGWPCR",	0x904, 32, 0},
+	{"CPGWPR",	0x900, 32, 0},
+	{"FRQCRB",	0x004, 32, 0},
+	{"FRQCRC",	0x0E0, 32, 0},
+	{"PLLECR",	0x0D0, 32, 0},
+	{"PLL0CR",	0x0D8, 32, 0},
+	{"PLL2CR",	0x02C, 32, 0},
+	{"PLL4CR",	0x1F4, 32, 0},
+	{"PLL0STPCR",	0x0F0, 32, 0},
+	{"PLL2STPCR",	0x0F8, 32, 0},
+	{"PLL3STPCR",	0x0FC, 32, 0},
+	{"PLL4STPCR",	0x1F8, 32, 0},
+	{"SD0CKCR",	0x074, 32, 0},
+	{"SD1CKCR",	0x078, 32, 0},
+	{"SD2CKCR",	0x268, 32, 0},
+	{"SD3CKCR",	0x26C, 32, 0},
+	{"RPCCKCR",	0x038, 32, 0},
+	{"CANFDCKCR",	0x244, 32, 0},
+	{"MSOCKCR",	0x014, 32, 0},
+	{"HDMICKCR",	0x250, 32, 0},
+	{"CSI0CKCR",	0x00C, 32, 0},
+	{"CSIREFCKCR",	0X034, 32, 0},
+	{"DVFSCR0",	0x058, 32, 0},
+	{"DVFSCR1",	0x05C, 32, 0},
+	{"FSAPBR",	0x700, 32, 0},
+	{"FSCLKCSR",	0x704, 32, 0},
+	{"FSCNTCHKH0",	0x710, 32, 0},
+	{"FSCNTCHKH1",	0x714, 32, 0},
+	{"FSCNTCHKH2",	0x718, 32, 0},
+	{"FSCNTCHKH3",	0x71C, 32, 0},
+	{"FSCNTCHKH4",	0x720, 32, 0},
+	{"FSCNTCHKH5",	0x724, 32, 0},
+	{"FSCNTCHKH6",	0x728, 32, 0},
+	{"FSCNTCHKL0",	0x730, 32, 0},
+	{"FSCNTCHKL1",	0x734, 32, 0},
+	{"FSCNTCHKL2",	0x738, 32, 0},
+	{"FSCNTCHKL3",	0x73C, 32, 0},
+	{"FSCNTCHKL4",	0x740, 32, 0},
+	{"FSCNTCHKL5",	0x744, 32, 0},
+	{"FSCNTCHKL6",	0x748, 32, 0},
+	{"FSSEQCHKCSR",	0xAF4, 32, 0},
+};
+
+static struct rcar_ip cpg_ip = {
+	.ip_name   = "CPG",
+	.base_addr = 0xE6150000,
+	.size      = 0xAF8,
+	.reg_count = ARRAY_SIZE(cpg_ip_regs),
+	.ip_reg    = cpg_ip_regs,
+};
+#endif /* CONFIG_RCAR_DDR_BACKUP */
 
 /*
  * Module Standby and Software Reset register offets.
@@ -574,6 +628,7 @@ static int __init cpg_mssr_probe(struct platform_device *pdev)
 	priv->num_core_clks = info->num_total_core_clks;
 	priv->num_mod_clks = info->num_hw_mod_clks;
 	priv->last_dt_core_clk = info->last_dt_core_clk;
+	platform_set_drvdata(pdev, priv);
 
 	for (i = 0; i < nclks; i++)
 		clks[i] = ERR_PTR(-ENOENT);
@@ -602,9 +657,46 @@ static int __init cpg_mssr_probe(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int cpg_mssr_suspend(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	struct cpg_mssr_priv *priv;
+
+	pr_debug("%s\n", __func__);
+
+	priv = dev_get_drvdata(dev);
+
+	if (!cpg_ip.virt_addr)
+		cpg_ip.virt_addr = priv->base;
+
+	ret = rcar_handle_registers(&cpg_ip, DO_BACKUP);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+	return ret;
+}
+
+static int cpg_mssr_resume(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	pr_debug("%s\n", __func__);
+	ret = rcar_handle_registers(&cpg_ip, DO_RESTORE);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(cpg_mssr_pm_ops,
+			cpg_mssr_suspend, cpg_mssr_resume);
+#define DEV_PM_OPS (&cpg_mssr_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
 static struct platform_driver cpg_mssr_driver = {
 	.driver		= {
 		.name	= "renesas-cpg-mssr",
+		.pm	= DEV_PM_OPS,
 		.of_match_table = cpg_mssr_match,
 	},
 };
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
index 7126762..29f76a4 100644
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
@@ -57,6 +57,8 @@ static const struct of_device_id machines[] __initconst = {
 	{ .compatible = "renesas,r8a7792", },
 	{ .compatible = "renesas,r8a7793", },
 	{ .compatible = "renesas,r8a7794", },
+	{ .compatible = "renesas,r8a7795", },
+	{ .compatible = "renesas,r8a7796", },
 	{ .compatible = "renesas,sh73a0", },
 
 	{ .compatible = "rockchip,rk2928", },
diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c
index f440d38..ca1b3f3 100644
--- a/drivers/cpuidle/cpuidle-arm.c
+++ b/drivers/cpuidle/cpuidle-arm.c
@@ -12,6 +12,7 @@
 #define pr_fmt(fmt) "CPUidle arm: " fmt
 
 #include <linux/cpuidle.h>
+#include <linux/cpufeature.h>
 #include <linux/cpumask.h>
 #include <linux/cpu_pm.h>
 #include <linux/kernel.h>
@@ -19,7 +20,9 @@
 #include <linux/of.h>
 #include <linux/slab.h>
 
+#include <asm/cpu.h>
 #include <asm/cpuidle.h>
+#include <asm/cputype.h>
 
 #include "dt_idle_states.h"
 
@@ -64,6 +67,30 @@ static struct cpuidle_driver arm_idle_driver = {
 	}
 };
 
+#if IS_ENABLED(CONFIG_ARM64)
+static int __init arm_idle_driver_init(struct cpuidle_driver *drv, int part_id)
+{
+	struct cpumask *cpumask;
+	int cpu;
+
+	cpumask = kzalloc(cpumask_size(), GFP_KERNEL);
+	if (!cpumask)
+		return -ENOMEM;
+
+	for_each_possible_cpu(cpu) {
+		struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, cpu);
+		u32 midr = cpuinfo->reg_midr;
+
+		if (MIDR_PARTNUM(midr) == part_id)
+			cpumask_set_cpu(cpu, cpumask);
+	}
+
+	drv->cpumask = cpumask;
+
+	return 0;
+}
+#endif
+
 static const struct of_device_id arm_idle_state_match[] __initconst = {
 	{ .compatible = "arm,idle-state",
 	  .data = arm_enter_idle_state },
@@ -83,6 +110,13 @@ static int __init arm_idle_init(void)
 	struct cpuidle_driver *drv = &arm_idle_driver;
 	struct cpuidle_device *dev;
 
+#if IS_ENABLED(CONFIG_ARM64)
+	/* Support CPUIdle for Cortex-A57 only */
+	ret = arm_idle_driver_init(drv, ARM_CPU_PART_CORTEX_A57);
+	if (ret)
+		return ret;
+#endif
+
 	/*
 	 * Initialize idle states data, starting at index 1.
 	 * This driver is DT only, if no DT idle states are detected (ret == 0)
@@ -103,7 +137,7 @@ static int __init arm_idle_init(void)
 	 * Call arch CPU operations in order to initialize
 	 * idle states suspend back-end specific data
 	 */
-	for_each_possible_cpu(cpu) {
+	for_each_cpu(cpu, drv->cpumask) {
 		ret = arm_cpuidle_init(cpu);
 
 		/*
diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig
index 6e0685f..55584f1 100644
--- a/drivers/dma/sh/Kconfig
+++ b/drivers/dma/sh/Kconfig
@@ -40,7 +40,7 @@
 endif
 
 config RCAR_DMAC
-	tristate "Renesas R-Car Gen2 DMA Controller"
+	tristate "Renesas R-Car Gen2/3 DMA Controller"
 	depends on ARCH_RENESAS || COMPILE_TEST
 	select RENESAS_DMA
 	help
diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c
index 2e441d0..e9d79d7 100644
--- a/drivers/dma/sh/rcar-dmac.c
+++ b/drivers/dma/sh/rcar-dmac.c
@@ -333,6 +333,14 @@ static bool rcar_dmac_chan_is_busy(struct rcar_dmac_chan *chan)
 	return !!(chcr & (RCAR_DMACHCR_DE | RCAR_DMACHCR_TE));
 }
 
+/* Transfer completed but not yet handled */
+static bool rcar_dmac_last_tx_complete(struct rcar_dmac_chan *chan)
+{
+	u32 chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR);
+
+	return (chcr & RCAR_DMACHCR_TE) == RCAR_DMACHCR_TE;
+}
+
 static void rcar_dmac_chan_start_xfer(struct rcar_dmac_chan *chan)
 {
 	struct rcar_dmac_desc *desc = chan->desc.running;
@@ -735,14 +743,38 @@ static int rcar_dmac_fill_hwdesc(struct rcar_dmac_chan *chan,
 /* -----------------------------------------------------------------------------
  * Stop and reset
  */
-
-static void rcar_dmac_chan_halt(struct rcar_dmac_chan *chan)
+#define NR_READS_TO_WAIT 5 /* number of times to check if DE = 0 */
+static inline int rcar_dmac_wait_stop(struct rcar_dmac_chan *chan)
 {
-	u32 chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR);
+	unsigned int i = 0;
 
+	do {
+		u32 chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR);
+
+		if (!(chcr & RCAR_DMACHCR_DE))
+			return 0;
+		cpu_relax();
+		dev_dbg(chan->chan.device->dev, "DMA xfer couldn't be stopped");
+	} while (++i < NR_READS_TO_WAIT);
+
+	return -EBUSY;
+}
+
+/* Called with chan lock held */
+static int rcar_dmac_chan_halt(struct rcar_dmac_chan *chan)
+{
+	u32 chcr;
+	int ret;
+
+	chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR);
 	chcr &= ~(RCAR_DMACHCR_DSE | RCAR_DMACHCR_DSIE | RCAR_DMACHCR_IE |
 		  RCAR_DMACHCR_TE | RCAR_DMACHCR_DE);
 	rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr);
+	ret = rcar_dmac_wait_stop(chan);
+
+	WARN_ON(ret < 0);
+
+	return ret;
 }
 
 static void rcar_dmac_chan_reinit(struct rcar_dmac_chan *chan)
@@ -769,6 +801,26 @@ static void rcar_dmac_chan_reinit(struct rcar_dmac_chan *chan)
 	}
 }
 
+static int rcar_dmac_chan_pause(struct dma_chan *chan)
+{
+	u32 chcr;
+	int ret;
+	unsigned long flags;
+	struct rcar_dmac_chan *rchan = to_rcar_dmac_chan(chan);
+
+	spin_lock_irqsave(&rchan->lock, flags);
+
+	chcr = rcar_dmac_chan_read(rchan, RCAR_DMACHCR);
+	chcr &= ~RCAR_DMACHCR_DE;
+	rcar_dmac_chan_write(rchan, RCAR_DMACHCR, chcr);
+	ret = rcar_dmac_wait_stop(rchan);
+
+	spin_unlock_irqrestore(&rchan->lock, flags);
+
+	WARN_ON(ret < 0);
+	return ret;
+}
+
 static void rcar_dmac_stop(struct rcar_dmac *dmac)
 {
 	rcar_dmac_write(dmac, RCAR_DMAOR, 0);
@@ -1295,6 +1347,10 @@ static enum dma_status rcar_dmac_tx_status(struct dma_chan *chan,
 	unsigned long flags;
 	unsigned int residue;
 
+	/* Interrupt not yet serviced */
+	if (rcar_dmac_last_tx_complete(rchan))
+		return DMA_COMPLETE;
+
 	status = dma_cookie_status(chan, cookie, txstate);
 	if (status == DMA_COMPLETE || !txstate)
 		return status;
@@ -1582,16 +1638,33 @@ static struct dma_chan *rcar_dmac_of_xlate(struct of_phandle_args *dma_spec,
 #ifdef CONFIG_PM_SLEEP
 static int rcar_dmac_sleep_suspend(struct device *dev)
 {
-	/*
-	 * TODO: Wait for the current transfer to complete and stop the device.
-	 */
+	struct rcar_dmac *dmac = dev_get_drvdata(dev);
+	int i;
+
+	for (i = 0; i < dmac->n_channels; ++i) {
+		if (!dmac->channels[i].iomem)
+			continue;
+
+		spin_lock(&dmac->channels[i].lock);
+		pm_runtime_get_sync(dev);
+		rcar_dmac_chan_halt(&dmac->channels[i]);
+		pm_runtime_put(dev);
+		spin_unlock(&dmac->channels[i].lock);
+	}
+
 	return 0;
 }
 
 static int rcar_dmac_sleep_resume(struct device *dev)
 {
-	/* TODO: Resume transfers, if any. */
-	return 0;
+	struct rcar_dmac *dmac = dev_get_drvdata(dev);
+	int ret;
+
+	pm_runtime_get_sync(dev);
+	ret = rcar_dmac_init(dmac);
+	pm_runtime_put(dev);
+
+	return ret;
 }
 #endif
 
@@ -1819,6 +1892,7 @@ static int rcar_dmac_probe(struct platform_device *pdev)
 	engine->device_prep_slave_sg = rcar_dmac_prep_slave_sg;
 	engine->device_prep_dma_cyclic = rcar_dmac_prep_dma_cyclic;
 	engine->device_config = rcar_dmac_device_config;
+	engine->device_pause = rcar_dmac_chan_pause;
 	engine->device_terminate_all = rcar_dmac_chan_terminate_all;
 	engine->device_tx_status = rcar_dmac_tx_status;
 	engine->device_issue_pending = rcar_dmac_issue_pending;
diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c
index 06ecdc3..52bc399 100644
--- a/drivers/dma/sh/usb-dmac.c
+++ b/drivers/dma/sh/usb-dmac.c
@@ -699,7 +699,20 @@ static int usb_dmac_runtime_resume(struct device *dev)
 }
 #endif /* CONFIG_PM */
 
+#ifdef CONFIG_PM_SLEEP
+static int usb_dmac_sleep_suspend(struct device *dev)
+{
+	return usb_dmac_runtime_suspend(dev);
+}
+
+static int usb_dmac_sleep_resume(struct device *dev)
+{
+	return usb_dmac_runtime_resume(dev);
+}
+#endif
+
 static const struct dev_pm_ops usb_dmac_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(usb_dmac_sleep_suspend, usb_dmac_sleep_resume)
 	SET_RUNTIME_PM_OPS(usb_dmac_runtime_suspend, usb_dmac_runtime_resume,
 			   NULL)
 };
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index 8263429..178f553 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -537,6 +537,13 @@ static int __init psci_probe(void)
 	psci_0_2_set_functions();
 
 	psci_init_migrate();
+	/*
+	 * psci_init_migrate() might fail to get needed information due to
+	 * incomplete firmware support.
+	 * FIXME: Let's assume that Trusted OS services (e.g DDR training)
+	 * always run in CPU0.
+	 */
+	resident_cpu = 0;
 
 	if (PSCI_VERSION_MAJOR(ver) >= 1) {
 		psci_init_cpu_suspend();
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
index 2be48f5..ece6fb6 100644
--- a/drivers/gpio/gpio-rcar.c
+++ b/drivers/gpio/gpio-rcar.c
@@ -29,6 +29,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/spinlock.h>
 #include <linux/slab.h>
+#include <linux/soc/renesas/s2ram_ddr_backup.h>
 
 struct gpio_rcar_priv {
 	void __iomem *base;
@@ -57,6 +58,244 @@ struct gpio_rcar_priv {
 
 #define RCAR_MAX_GPIO_PER_BANK		32
 
+#ifdef CONFIG_RCAR_DDR_BACKUP
+static struct hw_register gpio0_ip_regs[] = {
+	{"IOINTSEL",    0x0000, 32, 0},
+	{"INOUTSEL",    0x0004, 32, 0},
+	{"OUTDT",       0x0008, 32, 0},
+	{"POSNEG",      0x0020, 32, 0},
+	{"EDGLEVEL",    0x0024, 32, 0},
+	{"FILONOFF",    0x0028, 32, 0},
+	{"OUTDTSEL",    0x0040, 32, 0},
+	{"OUTDTH",      0x0044, 32, 0},
+	{"OUTDTL",      0x0048, 32, 0},
+	{"BOTHEDGE",    0x004C, 32, 0},
+	{"INTCLR",      0x0014, 32, 0},
+	{"INTMSK",      0x0018, 32, 0},
+	{"MSKCLR",      0x001C, 32, 0},
+	{"INTMSKS",     0x0038, 32, 0},
+	{"MSKCLRS",     0x003C, 32, 0},
+};
+
+static struct rcar_ip gpio0_ip = {
+	.ip_name   = "GPIO0",
+	.base_addr = 0xE6050000,
+	.size      = 0x50,
+	.reg_count = ARRAY_SIZE(gpio0_ip_regs),
+	.ip_reg    = gpio0_ip_regs,
+};
+
+static struct hw_register gpio1_ip_regs[] = {
+	{"IOINTSEL",    0x0000, 32, 0},
+	{"INOUTSEL",    0x0004, 32, 0},
+	{"OUTDT",       0x0008, 32, 0},
+	{"POSNEG",      0x0020, 32, 0},
+	{"EDGLEVEL",    0x0024, 32, 0},
+	{"FILONOFF",    0x0028, 32, 0},
+	{"OUTDTSEL",    0x0040, 32, 0},
+	{"OUTDTH",      0x0044, 32, 0},
+	{"OUTDTL",      0x0048, 32, 0},
+	{"BOTHEDGE",    0x004C, 32, 0},
+	{"INTCLR",      0x0014, 32, 0},
+	{"INTMSK",      0x0018, 32, 0},
+	{"MSKCLR",      0x001C, 32, 0},
+	{"INTMSKS",     0x0038, 32, 0},
+	{"MSKCLRS",     0x003C, 32, 0},
+};
+
+static struct rcar_ip gpio1_ip = {
+	.ip_name   = "GPIO1",
+	.base_addr = 0xE6051000,
+	.size      = 0x50,
+	.reg_count = ARRAY_SIZE(gpio1_ip_regs),
+	.ip_reg    = gpio1_ip_regs,
+};
+
+static struct hw_register gpio2_ip_regs[] = {
+	{"IOINTSEL",    0x0000, 32, 0},
+	{"INOUTSEL",    0x0004, 32, 0},
+	{"OUTDT",       0x0008, 32, 0},
+	{"POSNEG",      0x0020, 32, 0},
+	{"EDGLEVEL",    0x0024, 32, 0},
+	{"FILONOFF",    0x0028, 32, 0},
+	{"OUTDTSEL",    0x0040, 32, 0},
+	{"OUTDTH",      0x0044, 32, 0},
+	{"OUTDTL",      0x0048, 32, 0},
+	{"BOTHEDGE",    0x004C, 32, 0},
+	{"INTCLR",      0x0014, 32, 0},
+	{"INTMSK",      0x0018, 32, 0},
+	{"MSKCLR",      0x001C, 32, 0},
+	{"INTMSKS",     0x0038, 32, 0},
+	{"MSKCLRS",     0x003C, 32, 0},
+};
+
+static struct rcar_ip gpio2_ip = {
+	.ip_name   = "GPIO2",
+	.base_addr = 0xE6052000,
+	.size      = 0x50,
+	.reg_count = ARRAY_SIZE(gpio2_ip_regs),
+	.ip_reg    = gpio2_ip_regs,
+};
+
+static struct hw_register gpio3_ip_regs[] = {
+	{"IOINTSEL",    0x0000, 32, 0},
+	{"INOUTSEL",    0x0004, 32, 0},
+	{"OUTDT",       0x0008, 32, 0},
+	{"POSNEG",      0x0020, 32, 0},
+	{"EDGLEVEL",    0x0024, 32, 0},
+	{"FILONOFF",    0x0028, 32, 0},
+	{"OUTDTSEL",    0x0040, 32, 0},
+	{"OUTDTH",      0x0044, 32, 0},
+	{"OUTDTL",      0x0048, 32, 0},
+	{"BOTHEDGE",    0x004C, 32, 0},
+	{"INTCLR",      0x0014, 32, 0},
+	{"INTMSK",      0x0018, 32, 0},
+	{"MSKCLR",      0x001C, 32, 0},
+	{"INTMSKS",     0x0038, 32, 0},
+	{"MSKCLRS",     0x003C, 32, 0},
+};
+
+static struct rcar_ip gpio3_ip = {
+	.ip_name   = "GPIO3",
+	.base_addr = 0xE6053000,
+	.size      = 0x50,
+	.reg_count = ARRAY_SIZE(gpio3_ip_regs),
+	.ip_reg    = gpio3_ip_regs,
+};
+
+static struct hw_register gpio4_ip_regs[] = {
+	{"IOINTSEL",    0x0000, 32, 0},
+	{"INOUTSEL",    0x0004, 32, 0},
+	{"OUTDT",       0x0008, 32, 0},
+	{"POSNEG",      0x0020, 32, 0},
+	{"EDGLEVEL",    0x0024, 32, 0},
+	{"FILONOFF",    0x0028, 32, 0},
+	{"OUTDTSEL",    0x0040, 32, 0},
+	{"OUTDTH",      0x0044, 32, 0},
+	{"OUTDTL",      0x0048, 32, 0},
+	{"BOTHEDGE",    0x004C, 32, 0},
+	{"INTCLR",      0x0014, 32, 0},
+	{"INTMSK",      0x0018, 32, 0},
+	{"MSKCLR",      0x001C, 32, 0},
+	{"INTMSKS",     0x0038, 32, 0},
+	{"MSKCLRS",     0x003C, 32, 0},
+};
+
+static struct rcar_ip gpio4_ip = {
+	.ip_name   = "GPIO4",
+	.base_addr = 0xE6054000,
+	.size      = 0x50,
+	.reg_count = ARRAY_SIZE(gpio4_ip_regs),
+	.ip_reg    = gpio4_ip_regs,
+};
+
+static struct hw_register gpio5_ip_regs[] = {
+	{"IOINTSEL",    0x0000, 32, 0},
+	{"INOUTSEL",    0x0004, 32, 0},
+	{"OUTDT",       0x0008, 32, 0},
+	{"POSNEG",      0x0020, 32, 0},
+	{"EDGLEVEL",    0x0024, 32, 0},
+	{"FILONOFF",    0x0028, 32, 0},
+	{"OUTDTSEL",    0x0040, 32, 0},
+	{"OUTDTH",      0x0044, 32, 0},
+	{"OUTDTL",      0x0048, 32, 0},
+	{"BOTHEDGE",    0x004C, 32, 0},
+	{"INTCLR",      0x0014, 32, 0},
+	{"INTMSK",      0x0018, 32, 0},
+	{"MSKCLR",      0x001C, 32, 0},
+	{"INTMSKS",     0x0038, 32, 0},
+	{"MSKCLRS",     0x003C, 32, 0},
+};
+
+static struct rcar_ip gpio5_ip = {
+	.ip_name   = "GPIO5",
+	.base_addr = 0xE6055000,
+	.size      = 0x50,
+	.reg_count = ARRAY_SIZE(gpio5_ip_regs),
+	.ip_reg    = gpio5_ip_regs,
+};
+
+static struct hw_register gpio6_ip_regs[] = {
+	{"IOINTSEL",    0x0000, 32, 0},
+	{"INOUTSEL",    0x0004, 32, 0},
+	{"OUTDT",       0x0008, 32, 0},
+	{"POSNEG",      0x0020, 32, 0},
+	{"EDGLEVEL",    0x0024, 32, 0},
+	{"FILONOFF",    0x0028, 32, 0},
+	{"OUTDTSEL",    0x0040, 32, 0},
+	{"OUTDTH",      0x0044, 32, 0},
+	{"OUTDTL",      0x0048, 32, 0},
+	{"BOTHEDGE",    0x004C, 32, 0},
+	{"INTCLR",      0x0014, 32, 0},
+	{"INTMSK",      0x0018, 32, 0},
+	{"MSKCLR",      0x001C, 32, 0},
+	{"INTMSKS",     0x0038, 32, 0},
+	{"MSKCLRS",     0x003C, 32, 0},
+};
+
+static struct rcar_ip gpio6_ip = {
+	.ip_name   = "GPIO6",
+	.base_addr = 0xE6055400,
+	.size      = 0x50,
+	.reg_count = ARRAY_SIZE(gpio6_ip_regs),
+	.ip_reg    = gpio6_ip_regs,
+};
+
+static struct hw_register gpio7_ip_regs[] = {
+	{"IOINTSEL",    0x0000, 32, 0},
+	{"INOUTSEL",    0x0004, 32, 0},
+	{"OUTDT",       0x0008, 32, 0},
+	{"POSNEG",      0x0020, 32, 0},
+	{"EDGLEVEL",    0x0024, 32, 0},
+	{"FILONOFF",    0x0028, 32, 0},
+	{"OUTDTSEL",    0x0040, 32, 0},
+	{"OUTDTH",      0x0044, 32, 0},
+	{"OUTDTL",      0x0048, 32, 0},
+	{"BOTHEDGE",    0x004C, 32, 0},
+	{"INTCLR",      0x0014, 32, 0},
+	{"INTMSK",      0x0018, 32, 0},
+	{"MSKCLR",      0x001C, 32, 0},
+	{"INTMSKS",     0x0038, 32, 0},
+	{"MSKCLRS",     0x003C, 32, 0},
+};
+
+static struct rcar_ip gpio7_ip = {
+	.ip_name   = "GPIO7",
+	.base_addr = 0xE6055800,
+	.size      = 0x50,
+	.reg_count = ARRAY_SIZE(gpio7_ip_regs),
+	.ip_reg    = gpio7_ip_regs,
+};
+
+static struct rcar_ip *ip_info_tbl[] = {
+	&gpio0_ip,
+	&gpio1_ip,
+	&gpio2_ip,
+	&gpio3_ip,
+	&gpio4_ip,
+	&gpio5_ip,
+	&gpio6_ip,
+	&gpio7_ip,
+};
+
+static int gpio_rcar_find_index(struct device_node *nd)
+{
+	u32 addr;
+	int i;
+	int num_ip = ARRAY_SIZE(ip_info_tbl);
+
+	for (i = 0; i < num_ip; i++) {
+		of_property_read_u32_index(nd, "reg", 1, &addr);
+		if (addr == (u32)ip_info_tbl[i]->base_addr) {
+			pr_debug("base addr: 0x%08X index %d\n", addr, i);
+			break;
+		}
+	}
+
+	return ((i < num_ip) ? i : -1);
+}
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+
 static inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, int offs)
 {
 	return ioread32(p->base + offs);
@@ -510,11 +749,64 @@ static int gpio_rcar_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int gpio_rcar_suspend(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	struct rcar_ip *gpio_ip;
+	int index =  gpio_rcar_find_index(dev->of_node);
+	struct gpio_rcar_priv *p = dev_get_drvdata(dev);
+
+	pr_debug("%s\n", __func__);
+	if (index < 0) {
+		pr_err("%s: Failed to find GPIO index\n", __func__);
+		return index;
+	}
+
+	gpio_ip = ip_info_tbl[index];
+
+	if (!gpio_ip->virt_addr)
+		gpio_ip->virt_addr = p->base;
+
+	ret = rcar_handle_registers(gpio_ip, DO_BACKUP);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+	return ret;
+}
+
+static int gpio_rcar_resume(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	struct rcar_ip *gpio_ip;
+	int index =  gpio_rcar_find_index(dev->of_node);
+
+	pr_debug("%s\n", __func__);
+	if (index < 0) {
+		pr_err("%s: Failed to find GPIO index\n", __func__);
+		return index;
+	}
+
+	gpio_ip = ip_info_tbl[index];
+
+	ret = rcar_handle_registers(gpio_ip, DO_RESTORE);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops,
+			gpio_rcar_suspend, gpio_rcar_resume);
+#define DEV_PM_OPS (&gpio_rcar_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
 static struct platform_driver gpio_rcar_device_driver = {
 	.probe		= gpio_rcar_probe,
 	.remove		= gpio_rcar_remove,
 	.driver		= {
 		.name	= "gpio_rcar",
+		.pm	= DEV_PM_OPS,
 		.of_match_table = of_match_ptr(gpio_rcar_of_table),
 	}
 };
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index ab7023e..f30477e 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -1,14 +1,16 @@
 /*
+ * DesignWare High-Definition Multimedia Interface (HDMI) driver
+ *
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
+ * Copyright (C) 2013-2015 Mentor Graphics Inc.
  * Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski at gmx.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
- * Designware High-Definition Multimedia Interface (HDMI) driver
- *
- * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
  */
 #include <linux/module.h>
 #include <linux/irq.h>
@@ -20,6 +22,7 @@
 #include <linux/of_device.h>
 #include <linux/spinlock.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_of.h>
 #include <drm/drmP.h>
 #include <drm/drm_atomic_helper.h>
@@ -31,6 +34,26 @@
 #include "dw-hdmi.h"
 #include "dw-hdmi-audio.h"
 
+/* HDMI_IH_I2CM_STAT0 and HDMI_IH_MUTE_I2CM_STAT0 register bits */
+#define HDMI_IH_I2CM_STAT0_ERROR		BIT(0)
+#define HDMI_IH_I2CM_STAT0_DONE			BIT(1)
+
+/* HDMI_I2CM_OPERATION register bits */
+#define HDMI_I2CM_OPERATION_READ		BIT(0)
+#define HDMI_I2CM_OPERATION_READ_EXT		BIT(1)
+#define HDMI_I2CM_OPERATION_WRITE		BIT(4)
+
+/* HDMI_I2CM_INT register bits */
+#define HDMI_I2CM_INT_DONE_MASK			BIT(2)
+#define HDMI_I2CM_INT_DONE_POL			BIT(3)
+
+/* HDMI_I2CM_CTLINT register bits */
+#define HDMI_I2CM_CTLINT_ARB_MASK		BIT(2)
+#define HDMI_I2CM_CTLINT_ARB_POL		BIT(3)
+#define HDMI_I2CM_CTLINT_NAC_MASK		BIT(6)
+#define HDMI_I2CM_CTLINT_NAC_POL		BIT(7)
+
+
 #define HDMI_EDID_LEN		512
 
 #define RGB			0
@@ -39,6 +62,8 @@
 #define YCBCR422_8BITS		3
 #define XVYCC444		4
 
+#define TIMEOUT			200 /* time for hot plug detection */
+
 enum hdmi_datamap {
 	RGB444_8B = 0x01,
 	RGB444_10B = 0x03,
@@ -101,6 +126,17 @@ struct hdmi_data_info {
 	struct hdmi_vmode video_mode;
 };
 
+struct dw_hdmi_i2c {
+	struct i2c_adapter	adap;
+
+	spinlock_t		lock;
+	struct completion	cmp;
+	u8			stat;
+
+	u8			slave_reg;
+	bool			is_regaddr;
+};
+
 struct dw_hdmi {
 	struct drm_connector connector;
 	struct drm_encoder *encoder;
@@ -112,6 +148,8 @@ struct dw_hdmi {
 	struct clk *isfr_clk;
 	struct clk *iahb_clk;
 
+	struct dw_hdmi_i2c *i2c;
+
 	struct hdmi_data_info hdmi_data;
 	const struct dw_hdmi_plat_data *plat_data;
 
@@ -142,6 +180,11 @@ struct dw_hdmi {
 	unsigned int audio_n;
 	bool audio_enable;
 
+	bool interlaced;
+	bool isfr_use;
+	bool iahb_use;
+	int num;
+
 	void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
 	u8 (*read)(struct dw_hdmi *hdmi, int offset);
 };
@@ -198,6 +241,247 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg,
 	hdmi_modb(hdmi, data << shift, mask, reg);
 }
 
+static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hdmi->i2c->lock, flags);
+
+	if (hdmi->dev_type == RCAR_HDMI) {
+		/* Set Standard Mode speed */
+		hdmi_writeb(hdmi, 0x03, HDMI_I2CM_DIV);
+	} else {
+		/* Set Fast Mode speed */
+		hdmi_writeb(hdmi, 0x0b, HDMI_I2CM_DIV);
+	}
+
+	/* Software reset */
+	hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ);
+
+	/* Set done, not acknowledged and arbitration interrupt polarities */
+	hdmi_writeb(hdmi, HDMI_I2CM_INT_DONE_POL, HDMI_I2CM_INT);
+	hdmi_writeb(hdmi, HDMI_I2CM_CTLINT_NAC_POL | HDMI_I2CM_CTLINT_ARB_POL,
+		    HDMI_I2CM_CTLINT);
+
+	/* Clear DONE and ERROR interrupts */
+	hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
+		    HDMI_IH_I2CM_STAT0);
+
+	/* Mute DONE and ERROR interrupts */
+	hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
+		    HDMI_IH_MUTE_I2CM_STAT0);
+
+	spin_unlock_irqrestore(&hdmi->i2c->lock, flags);
+}
+
+static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi,
+			    unsigned char *buf, int length)
+{
+	int stat;
+	unsigned long flags;
+	struct dw_hdmi_i2c *i2c = hdmi->i2c;
+
+	spin_lock_irqsave(&i2c->lock, flags);
+
+	if (!i2c->is_regaddr) {
+		dev_dbg(hdmi->dev, "set read register address to 0\n");
+		i2c->slave_reg = 0x00;
+		i2c->is_regaddr = true;
+	}
+
+	while (length--) {
+		hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS);
+		hdmi_writeb(hdmi,
+			    HDMI_I2CM_OPERATION_READ, HDMI_I2CM_OPERATION);
+		i2c->stat = 0;
+
+		spin_unlock_irqrestore(&i2c->lock, flags);
+
+		stat = wait_for_completion_interruptible_timeout(&i2c->cmp,
+								 HZ / 10);
+		if (!stat)
+			return -ETIMEDOUT;
+		if (stat < 0)
+			return stat;
+
+		spin_lock_irqsave(&i2c->lock, flags);
+
+		/* Check for error condition on the bus */
+		if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR) {
+			spin_unlock_irqrestore(&i2c->lock, flags);
+			return -EIO;
+		}
+
+		*buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI);
+	}
+
+	spin_unlock_irqrestore(&i2c->lock, flags);
+
+	return 0;
+}
+
+static int dw_hdmi_i2c_write(struct dw_hdmi *hdmi,
+				  unsigned char *buf, int length)
+{
+	int stat;
+	unsigned long flags;
+	struct dw_hdmi_i2c *i2c = hdmi->i2c;
+
+	spin_lock_irqsave(&i2c->lock, flags);
+
+	if (!i2c->is_regaddr) {
+		if (length) {
+			/* Use the first write byte as register address */
+			i2c->slave_reg = buf[0];
+			length--;
+			buf++;
+		} else {
+			dev_dbg(hdmi->dev, "set write register address to 0\n");
+			i2c->slave_reg = 0x00;
+		}
+		i2c->is_regaddr = true;
+	}
+
+	while (length--) {
+		hdmi_writeb(hdmi, *buf++, HDMI_I2CM_DATAO);
+		hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS);
+		hdmi_writeb(hdmi,
+			    HDMI_I2CM_OPERATION_WRITE, HDMI_I2CM_OPERATION);
+		i2c->stat = 0;
+
+		spin_unlock_irqrestore(&i2c->lock, flags);
+
+		stat = wait_for_completion_interruptible_timeout(&i2c->cmp,
+								 HZ / 10);
+		if (!stat)
+			return -ETIMEDOUT;
+		if (stat < 0)
+			return stat;
+
+		spin_lock_irqsave(&i2c->lock, flags);
+
+		/* Check for error condition on the bus */
+		if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR) {
+			spin_unlock_irqrestore(&i2c->lock, flags);
+			return -EIO;
+		}
+	}
+
+	spin_unlock_irqrestore(&i2c->lock, flags);
+
+	return 0;
+}
+
+static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap,
+			    struct i2c_msg *msgs, int num)
+{
+	struct dw_hdmi *hdmi = i2c_get_adapdata(adap);
+	struct dw_hdmi_i2c *i2c = hdmi->i2c;
+
+	int i, ret;
+	u8 addr;
+	unsigned long flags;
+
+	dev_dbg(hdmi->dev, "xfer: num: %d, addr: 0x%x\n", num, msgs[0].addr);
+
+	spin_lock_irqsave(&i2c->lock, flags);
+
+	hdmi_writeb(hdmi, 0x00, HDMI_IH_MUTE_I2CM_STAT0);
+
+	/* Set slave device address from the first transaction */
+	addr = msgs[0].addr;
+	hdmi_writeb(hdmi, addr, HDMI_I2CM_SLAVE);
+
+	/* Set slave device register address on transfer */
+	i2c->is_regaddr = false;
+
+	spin_unlock_irqrestore(&i2c->lock, flags);
+
+	for (i = 0; i < num; i++) {
+		dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: 0x%x\n",
+			i + 1, num, msgs[i].len, msgs[i].flags);
+
+		if (msgs[i].addr != addr) {
+			dev_warn(hdmi->dev,
+				 "unsupported transaction, changed slave address\n");
+			ret = -EOPNOTSUPP;
+			break;
+		}
+
+		if (msgs[i].len == 0) {
+			dev_dbg(hdmi->dev,
+				 "unsupported transaction %d/%d, no data\n",
+				 i + 1, num);
+			ret = -EOPNOTSUPP;
+			break;
+		}
+
+		if (msgs[i].flags & I2C_M_RD)
+			ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, msgs[i].len);
+		else
+			ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, msgs[i].len);
+
+		if (ret < 0)
+			break;
+	}
+
+	if (!ret)
+		ret = num;
+
+	spin_lock_irqsave(&i2c->lock, flags);
+
+	/* Mute DONE and ERROR interrupts */
+	hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
+		    HDMI_IH_MUTE_I2CM_STAT0);
+
+	spin_unlock_irqrestore(&i2c->lock, flags);
+
+	return ret;
+}
+
+static u32 dw_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm dw_hdmi_algorithm = {
+	.master_xfer	= dw_hdmi_i2c_xfer,
+	.functionality	= dw_hdmi_i2c_func,
+};
+
+static struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi *hdmi)
+{
+	struct i2c_adapter *adap;
+	int ret;
+
+	hdmi->i2c = devm_kzalloc(hdmi->dev, sizeof(*hdmi->i2c), GFP_KERNEL);
+	if (!hdmi->i2c)
+		return ERR_PTR(-ENOMEM);
+
+	spin_lock_init(&hdmi->i2c->lock);
+	init_completion(&hdmi->i2c->cmp);
+
+	adap = &hdmi->i2c->adap;
+	adap->class = I2C_CLASS_DDC;
+	adap->owner = THIS_MODULE;
+	adap->dev.parent = hdmi->dev;
+	adap->algo = &dw_hdmi_algorithm;
+	strlcpy(adap->name, "DesignWare HDMI", sizeof(adap->name));
+	i2c_set_adapdata(adap, hdmi);
+
+	ret = i2c_add_adapter(adap);
+	if (ret) {
+		dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
+		devm_kfree(hdmi->dev, hdmi->i2c);
+		hdmi->i2c = NULL;
+		return ERR_PTR(ret);
+	}
+
+	dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
+
+	return adap;
+}
+
 static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
 			   unsigned int n)
 {
@@ -739,6 +1023,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 	const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
 	const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
 	const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
+	const struct dw_hdmi_multi_div *multi_div = pdata->multi_div;
 
 	if (prep)
 		return -EINVAL;
@@ -774,6 +1059,13 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 		    phy_config->mpixelclock)
 			break;
 
+	if (hdmi->dev_type == RCAR_HDMI) {
+		for (; multi_div->mpixelclock != (~0UL); multi_div++)
+			if (hdmi->hdmi_data.video_mode.mpixelclock <=
+			    multi_div->mpixelclock)
+				break;
+	}
+
 	if (mpll_config->mpixelclock == ~0UL ||
 	    curr_ctrl->mpixelclock == ~0UL ||
 	    phy_config->mpixelclock == ~0UL) {
@@ -808,21 +1100,27 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 	hdmi_phy_test_clear(hdmi, 0);
 
 	hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].cpce, 0x06);
-	hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].gmp, 0x15);
+	if (hdmi->dev_type != RCAR_HDMI)
+		hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].gmp, 0x15);
 
 	/* CURRCTRL */
 	hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[res_idx], 0x10);
 
-	hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);  /* PLLPHBYCTRL */
-	hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
+	if (hdmi->dev_type == RCAR_HDMI)
+		hdmi_phy_i2c_write(hdmi, multi_div->multi[res_idx], 0x11);
 
-	hdmi_phy_i2c_write(hdmi, phy_config->term, 0x19);  /* TXTERM */
-	hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, 0x09); /* CKSYMTXCTRL */
-	hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, 0x0E); /* VLEVCTRL */
+	if (hdmi->dev_type != RCAR_HDMI) {
+		hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);  /* PLLPHBYCTRL */
+		hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
 
-	/* REMOVE CLK TERM */
-	hdmi_phy_i2c_write(hdmi, 0x8000, 0x05);  /* CKCALCTRL */
-
+		hdmi_phy_i2c_write(hdmi, phy_config->term, 0x19);  /* TXTERM */
+		hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr,
+						 0x09); /* CKSYMTXCTRL */
+		hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr,
+						 0x0E); /* VLEVCTRL */
+		/* REMOVE CLK TERM */
+		hdmi_phy_i2c_write(hdmi, 0x8000, 0x05);  /* CKCALCTRL */
+	}
 	dw_hdmi_phy_enable_powerdown(hdmi, false);
 
 	/* toggle TMDS enable */
@@ -833,7 +1131,8 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 	dw_hdmi_phy_gen2_txpwron(hdmi, 1);
 	dw_hdmi_phy_gen2_pddq(hdmi, 0);
 
-	if (hdmi->dev_type == RK3288_HDMI)
+	if ((hdmi->dev_type == RK3288_HDMI) ||
+		(hdmi->dev_type == RCAR_HDMI))
 		dw_hdmi_phy_enable_spare(hdmi, 1);
 
 	/*Wait for PHY PLL lock */
@@ -1400,17 +1699,60 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
 	hdmi->disabled = true;
 	dw_hdmi_update_power(hdmi);
 	dw_hdmi_update_phy_mask(hdmi);
+
+	if (hdmi->dev_type == RCAR_HDMI) {
+		hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+		hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE,
+					 HDMI_PHY_POL0);
+	}
 	mutex_unlock(&hdmi->mutex);
 }
 
 static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
+	u8 intr_stat, i;
 
 	mutex_lock(&hdmi->mutex);
+
+	if (hdmi->dev_type == RCAR_HDMI) {
+		/* Reinitialization for resume */
+		initialize_hdmi_ih_mutes(hdmi);
+		hdmi_init_clk_regenerator(hdmi);
+		hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE,
+					HDMI_PHY_POL0);
+		hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD |
+					HDMI_IH_PHY_STAT0_RX_SENSE,
+					HDMI_IH_PHY_STAT0);
+		hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD |
+					HDMI_IH_PHY_STAT0_RX_SENSE),
+					HDMI_IH_MUTE_PHY_STAT0);
+		dw_hdmi_fb_registered(hdmi);
+		hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD |
+					HDMI_IH_PHY_STAT0_RX_SENSE),
+					HDMI_IH_MUTE_PHY_STAT0);
+		dw_hdmi_i2c_init(hdmi);
+	}
+
 	hdmi->disabled = false;
 	dw_hdmi_update_power(hdmi);
 	dw_hdmi_update_phy_mask(hdmi);
+
+	if (hdmi->dev_type == RCAR_HDMI) {
+		/* Wait for hot plug detection */
+		for (i = 0; i < TIMEOUT; i++) {
+			intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
+			if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
+				dev_dbg(hdmi->dev,
+					"HPD is occurred after %dms\n", i);
+				break;
+			}
+			mdelay(1);
+		}
+		if (i == TIMEOUT)
+			dev_warn(hdmi->dev, "HPD is not occurred\n");
+	}
+
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1512,16 +1854,47 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
 	.mode_set = dw_hdmi_bridge_mode_set,
 };
 
+static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi)
+{
+	struct dw_hdmi_i2c *i2c = hdmi->i2c;
+	unsigned long flags;
+
+	spin_lock_irqsave(&i2c->lock, flags);
+
+	i2c->stat = hdmi_readb(hdmi, HDMI_IH_I2CM_STAT0);
+	if (!i2c->stat) {
+		spin_unlock_irqrestore(&i2c->lock, flags);
+		return IRQ_NONE;
+	}
+
+	hdmi_writeb(hdmi, i2c->stat, HDMI_IH_I2CM_STAT0);
+	complete(&i2c->cmp);
+
+	dev_dbg(hdmi->dev, "i2cm_stat: 0x%02x, addr: 0x%02x, reg: 0x%02x\n",
+		i2c->stat, hdmi_readb(hdmi, HDMI_I2CM_SLAVE),
+		hdmi_readb(hdmi, HDMI_I2CM_ADDRESS));
+
+	spin_unlock_irqrestore(&i2c->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
 static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
 {
 	struct dw_hdmi *hdmi = dev_id;
 	u8 intr_stat;
+	irqreturn_t ret = IRQ_NONE;
+
+	if (hdmi->i2c)
+		ret = dw_hdmi_i2c_irq(hdmi);
 
 	intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
-	if (intr_stat)
+	if (intr_stat) {
 		hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+		return IRQ_WAKE_THREAD;
+	}
 
-	return intr_stat ? IRQ_WAKE_THREAD : IRQ_NONE;
+	return ret;
 }
 
 static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
@@ -1618,6 +1991,9 @@ static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi)
 	encoder->bridge = bridge;
 	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
 
+	if (hdmi->interlaced)
+		hdmi->connector.interlace_allowed = true;
+
 	drm_connector_helper_add(&hdmi->connector,
 				 &dw_hdmi_connector_helper_funcs);
 
@@ -1679,6 +2055,11 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 		return -EINVAL;
 	}
 
+	if (of_property_read_u32(np, "interlaced", &val) == 0)
+		hdmi->interlaced = val;
+	else
+		hdmi->interlaced = false;
+
 	ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
 	if (ddc_node) {
 		hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
@@ -1696,30 +2077,59 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	if (IS_ERR(hdmi->regs))
 		return PTR_ERR(hdmi->regs);
 
-	hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
-	if (IS_ERR(hdmi->isfr_clk)) {
-		ret = PTR_ERR(hdmi->isfr_clk);
-		dev_err(hdmi->dev, "Unable to get HDMI isfr clk: %d\n", ret);
-		return ret;
+	if (of_property_read_u32(np, "clock-isfr", &val) == 0)
+		hdmi->isfr_use = val;
+	else
+		hdmi->isfr_use = true;
+
+	if (hdmi->isfr_use) {
+		if (of_property_read_u32(np, "hdmi-num", &val) == 0)
+			hdmi->num = val;
+		else
+			hdmi->num = -1;
+
+		if (hdmi->num > 1) {
+			char name[7];
+
+			sprintf(name, "isfr.%u", hdmi->plat_data->index);
+			hdmi->isfr_clk = devm_clk_get(hdmi->dev, name);
+		} else
+			hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
+		if (IS_ERR(hdmi->isfr_clk)) {
+			ret = PTR_ERR(hdmi->isfr_clk);
+			dev_err(hdmi->dev,
+				 "Unable to get HDMI isfr clk: %d\n", ret);
+			return ret;
+		}
+
+		ret = clk_prepare_enable(hdmi->isfr_clk);
+		if (ret) {
+			dev_err(hdmi->dev,
+				 "Cannot enable HDMI isfr clock: %d\n", ret);
+			return ret;
+		}
 	}
 
-	ret = clk_prepare_enable(hdmi->isfr_clk);
-	if (ret) {
-		dev_err(hdmi->dev, "Cannot enable HDMI isfr clock: %d\n", ret);
-		return ret;
-	}
+	if (of_property_read_u32(np, "clock-iahb", &val) == 0)
+		hdmi->iahb_use = val;
+	else
+		hdmi->iahb_use = true;
 
-	hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
-	if (IS_ERR(hdmi->iahb_clk)) {
-		ret = PTR_ERR(hdmi->iahb_clk);
-		dev_err(hdmi->dev, "Unable to get HDMI iahb clk: %d\n", ret);
-		goto err_isfr;
-	}
+	if (hdmi->iahb_use) {
+		hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
+		if (IS_ERR(hdmi->iahb_clk)) {
+			ret = PTR_ERR(hdmi->iahb_clk);
+			dev_err(hdmi->dev,
+				 "Unable to get HDMI iahb clk: %d\n", ret);
+			goto err_isfr;
+		}
 
-	ret = clk_prepare_enable(hdmi->iahb_clk);
-	if (ret) {
-		dev_err(hdmi->dev, "Cannot enable HDMI iahb clock: %d\n", ret);
-		goto err_isfr;
+		ret = clk_prepare_enable(hdmi->iahb_clk);
+		if (ret) {
+			dev_err(hdmi->dev,
+				 "Cannot enable HDMI iahb clock: %d\n", ret);
+			goto err_isfr;
+		}
 	}
 
 	/* Product and revision IDs */
@@ -1744,6 +2154,13 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	 */
 	hdmi_init_clk_regenerator(hdmi);
 
+	/* If DDC bus is not specified, try to register HDMI I2C bus */
+	if (!hdmi->ddc) {
+		hdmi->ddc = dw_hdmi_i2c_adapter(hdmi);
+		if (IS_ERR(hdmi->ddc))
+			hdmi->ddc = NULL;
+	}
+
 	/*
 	 * Configure registers related to HDMI interrupt
 	 * generation before registering IRQ.
@@ -1784,14 +2201,23 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 		hdmi->audio = platform_device_register_full(&pdevinfo);
 	}
 
+	/* Unmute I2CM interrupts and reset HDMI DDC I2C master controller */
+	if (hdmi->i2c)
+		dw_hdmi_i2c_init(hdmi);
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
 
 err_iahb:
-	clk_disable_unprepare(hdmi->iahb_clk);
+	if (hdmi->i2c)
+		i2c_del_adapter(&hdmi->i2c->adap);
+
+	if (hdmi->iahb_use)
+		clk_disable_unprepare(hdmi->iahb_clk);
 err_isfr:
-	clk_disable_unprepare(hdmi->isfr_clk);
+	if (hdmi->isfr_use)
+		clk_disable_unprepare(hdmi->isfr_clk);
 
 	return ret;
 }
@@ -1807,15 +2233,23 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
-	clk_disable_unprepare(hdmi->iahb_clk);
-	clk_disable_unprepare(hdmi->isfr_clk);
-	i2c_put_adapter(hdmi->ddc);
+	if (hdmi->iahb_use)
+		clk_disable_unprepare(hdmi->iahb_clk);
+
+	if (hdmi->isfr_use)
+		clk_disable_unprepare(hdmi->isfr_clk);
+
+	if (hdmi->i2c)
+		i2c_del_adapter(&hdmi->i2c->adap);
+	else
+		i2c_put_adapter(hdmi->ddc);
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
 
 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
 MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
+MODULE_AUTHOR("Vladimir Zapolskiy <vladimir_zapolskiy at mentor.com>");
 MODULE_DESCRIPTION("DW HDMI transmitter driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:dw-hdmi");
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index 4c2fd05..1249314 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -14,12 +14,15 @@
 config DRM_RCAR_HDMI
 	bool "R-Car DU HDMI Encoder Support"
 	depends on DRM_RCAR_DU
+	depends on OF
+	select DRM_DW_HDMI
 	help
 	  Enable support for external HDMI encoders.
 
 config DRM_RCAR_LVDS
 	bool "R-Car DU LVDS Encoder Support"
 	depends on DRM_RCAR_DU
+	select GPIOLIB
 	help
 	  Enable support for the R-Car Display Unit embedded LVDS encoders.
 
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
index d3b4465..4666821 100644
--- a/drivers/gpu/drm/rcar-du/Makefile
+++ b/drivers/gpu/drm/rcar-du/Makefile
@@ -3,13 +3,13 @@
 		 rcar_du_encoder.o \
 		 rcar_du_group.o \
 		 rcar_du_kms.o \
-		 rcar_du_lvdscon.o \
 		 rcar_du_plane.o \
 		 rcar_du_vgacon.o
 
 rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI)	+= rcar_du_hdmienc.o
 
-rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS)	+= rcar_du_lvdsenc.o
+rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS)	+= rcar_du_lvdsenc.o \
+					   rcar_du_lvdscon.o
 
 rcar-du-drm-$(CONFIG_DRM_RCAR_VSP)	+= rcar_du_vsp.o
 
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 7316fc7..428e078 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -1,7 +1,7 @@
 /*
  * rcar_du_crtc.c  --  R-Car Display Unit CRTCs
  *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -13,6 +13,7 @@
 
 #include <linux/clk.h>
 #include <linux/mutex.h>
+#include <linux/soc/renesas/rcar_prr.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
@@ -69,7 +70,7 @@ static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg,
 	rcar_du_write(rcdu, rcrtc->mmio_offset + reg, (value & ~clr) | set);
 }
 
-static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc)
+int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc)
 {
 	int ret;
 
@@ -94,7 +95,7 @@ static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc)
 	return ret;
 }
 
-static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
+void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
 {
 	rcar_du_group_put(rcrtc->group);
 
@@ -102,18 +103,92 @@ static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
 	clk_disable_unprepare(rcrtc->clock);
 }
 
+void rcar_du_crtc_vbk_check(struct rcar_du_crtc *rcrtc)
+{
+	u32 i, timeout = 100, loop = 2;
+	u32 status;
+
+	/* Check next VBK flag */
+	for (i = 0; i < timeout; i++) {
+		status = rcar_du_crtc_read(rcrtc, DSSR);
+		if (status & DSSR_VBK) {
+
+			rcar_du_crtc_write(rcrtc, DSRCR, DSRCR_VBCL);
+			if (--loop == 0)
+				break;
+		}
+		mdelay(1);
+	}
+}
+
 /* -----------------------------------------------------------------------------
  * Hardware Setup
  */
 
+static void rcar_du_dpll_divider(struct dpll_info *dpll, unsigned int extclk,
+				 unsigned int mode_clock)
+{
+	unsigned long dpllclk;
+	unsigned long diff;
+	unsigned long n, m, fdpll;
+	bool match_flag = false;
+	bool clk_diff_set = true;
+
+	for (n = 39; n < 120; n++) {
+		for (m = 0; m < 4; m++) {
+			for (fdpll = 1; fdpll < 32; fdpll++) {
+				if (RCAR_PRR_IS_PRODUCT(H3) &&
+					(RCAR_PRR_CHK_CUT(H3, WS11) <= 0))
+					/* 1/2 (FRQSEL=1) for duty rate 50% */
+					dpllclk = extclk * (n + 1) / (m + 1)
+						 / (fdpll + 1) / 2;
+				else
+					dpllclk = extclk * (n + 1) / (m + 1)
+						 / (fdpll + 1);
+				if (dpllclk >= 400000000)
+					continue;
+
+				diff = abs((long)dpllclk - (long)mode_clock);
+				if (clk_diff_set ||
+					((diff == 0) || (dpll->diff > diff))) {
+					dpll->diff = diff;
+					dpll->n = n;
+					dpll->m = m;
+					dpll->fdpll = fdpll;
+					dpll->dpllclk = dpllclk;
+
+					if (clk_diff_set)
+						clk_diff_set = false;
+
+					if (diff == 0) {
+						match_flag = true;
+						break;
+					}
+				}
+			}
+			if (match_flag)
+				break;
+		}
+		if (match_flag)
+			break;
+	}
+}
+
 static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
 {
 	const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
+	struct rcar_du_device *rcdu = rcrtc->group->dev;
 	unsigned long mode_clock = mode->clock * 1000;
 	unsigned long clk;
 	u32 value;
 	u32 escr;
 	u32 div;
+	u32 dpll_reg = 0;
+	struct dpll_info *dpll;
+
+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
+	if (dpll == NULL)
+		return;
 
 	/* Compute the clock divisor and select the internal or external dot
 	 * clock based on the requested frequency.
@@ -129,7 +204,17 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
 		unsigned long rate;
 		u32 extdiv;
 
+		clk_set_rate(rcrtc->extclock, mode_clock);
 		extclk = clk_get_rate(rcrtc->extclock);
+
+		if (rcdu->info->dpll_ch & (0x01 << rcrtc->index)) {
+			rcar_du_dpll_divider(dpll, extclk, mode_clock);
+			extclk = dpll->dpllclk;
+			dev_dbg(rcrtc->group->dev->dev,
+				"dpllclk:%d, fdpll:%d, n:%d, m:%d, diff:%d\n",
+				 dpll->dpllclk, dpll->fdpll, dpll->n, dpll->m,
+				 dpll->diff);
+		}
 		extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock);
 		extdiv = clamp(extdiv, 1U, 64U) - 1;
 
@@ -140,17 +225,44 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
 		    abs((long)rate - (long)mode_clock)) {
 			dev_dbg(rcrtc->group->dev->dev,
 				"crtc%u: using external clock\n", rcrtc->index);
-			escr = extdiv | ESCR_DCLKSEL_DCLKIN;
+			if (rcdu->info->dpll_ch & (0x01 << rcrtc->index)) {
+				if (RCAR_PRR_IS_PRODUCT(H3) &&
+					(RCAR_PRR_CHK_CUT(H3, WS11) <= 0))
+					escr = ESCR_DCLKSEL_DCLKIN | 0x01;
+				else
+					escr = ESCR_DCLKSEL_DCLKIN;
+				dpll_reg =  DPLLCR_CODE | DPLLCR_M(dpll->m) |
+					DPLLCR_FDPLL(dpll->fdpll) |
+					DPLLCR_CLKE | DPLLCR_N(dpll->n) |
+					DPLLCR_STBY;
+
+				if (rcrtc->index == DU_CH_1)
+					dpll_reg |= (DPLLCR_PLCS1 |
+						DPLLCR_INCS_DPLL01_DOTCLKIN13);
+				if (rcrtc->index == DU_CH_2) {
+					dpll_reg |= (DPLLCR_PLCS0 |
+						DPLLCR_INCS_DPLL01_DOTCLKIN02);
+					if (RCAR_PRR_IS_PRODUCT(H3) &&
+					   (RCAR_PRR_CHK_CUT(H3, WS11) <= 0))
+						dpll_reg |= (0x01 << 21);
+				}
+				rcar_du_group_write(rcrtc->group, DPLLCR,
+								  dpll_reg);
+			} else
+				escr = extdiv | ESCR_DCLKSEL_DCLKIN;
 		}
 	}
 
+	kfree(dpll);
+
 	rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR,
 			    escr);
 	rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);
 
 	/* Signal polarities */
-	value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL)
-	      | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL)
+	value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0)
+	      | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0)
+	      | ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? DSMR_ODEV : 0)
 	      | DSMR_DIPM_DISP | DSMR_CSPM;
 	rcar_du_crtc_write(rcrtc, DSMR, value);
 
@@ -172,7 +284,7 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
 					mode->crtc_vsync_start - 1);
 	rcar_du_crtc_write(rcrtc, VCR,  mode->crtc_vtotal - 1);
 
-	rcar_du_crtc_write(rcrtc, DESR,  mode->htotal - mode->hsync_start);
+	rcar_du_crtc_write(rcrtc, DESR,  mode->htotal - mode->hsync_start - 1);
 	rcar_du_crtc_write(rcrtc, DEWR,  mode->hdisplay);
 }
 
@@ -282,12 +394,12 @@ static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc)
 		rcrtc->group->dptsr_planes = dptsr_planes;
 
 		if (rcrtc->group->used_crtcs)
-			rcar_du_group_restart(rcrtc->group);
+			rcar_du_group_restart(rcrtc->group, rcrtc);
 	}
 
 	/* Restart the group if plane sources have changed. */
 	if (rcrtc->group->need_restart)
-		rcar_du_group_restart(rcrtc->group);
+		rcar_du_group_restart(rcrtc->group, rcrtc);
 
 	mutex_unlock(&rcrtc->group->lock);
 
@@ -380,7 +492,7 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
 			     (interlaced ? DSYSR_SCM_INT_VIDEO : 0) |
 			     DSYSR_TVM_MASTER);
 
-	rcar_du_group_start_stop(rcrtc->group, true);
+	rcar_du_group_start_stop(rcrtc->group, true, rcrtc);
 
 	/* Enable the VSP compositor. */
 	if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
@@ -428,17 +540,32 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
 	 */
 	rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_SWITCH);
 
-	rcar_du_group_start_stop(rcrtc->group, false);
+	rcar_du_group_start_stop(rcrtc->group, false, rcrtc);
+
+	rcrtc->started = false;
+}
+
+void rcar_du_crtc_remove_suspend(struct rcar_du_crtc *rcrtc)
+{
+	if (!rcrtc->started)
+		return;
+
+	rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, 0);
+
+	rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_SWITCH);
+
+	rcar_du_group_start_stop(rcrtc->group, false, rcrtc);
+
+	rcar_du_crtc_put(rcrtc);
 
 	rcrtc->started = false;
 }
 
 void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc)
 {
-	if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
-		rcar_du_vsp_disable(rcrtc);
-
+	rcrtc->suspend = true;
 	rcar_du_crtc_stop(rcrtc);
+	rcrtc->suspend = false;
 	rcar_du_crtc_put(rcrtc);
 }
 
@@ -453,9 +580,7 @@ void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc)
 	rcar_du_crtc_start(rcrtc);
 
 	/* Commit the planes state. */
-	if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) {
-		rcar_du_vsp_enable(rcrtc);
-	} else {
+	if (!rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) {
 		for (i = 0; i < rcrtc->group->num_planes; ++i) {
 			struct rcar_du_plane *plane = &rcrtc->group->planes[i];
 
@@ -582,6 +707,18 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
 	int irq;
 	int ret;
 
+	if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSPDL_SOURCE)) {
+		if (index == rcdu->info->vspdl_pair_ch)
+			rcrtc->lif_index = 1;
+		else
+			rcrtc->lif_index = 0;
+
+		if ((rcrtc->lif_index == 1) && (rcrtc->vsp->num_brs == 0))
+			return 0;
+	} else {
+		rcrtc->lif_index = 0;
+	}
+
 	/* Get the CRTC clock and the optional external clock. */
 	if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) {
 		sprintf(clk_name, "du.%u", index);
@@ -610,10 +747,19 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
 	rcrtc->group = rgrp;
 	rcrtc->mmio_offset = mmio_offsets[index];
 	rcrtc->index = index;
+	rcrtc->lvds_ch = -1;
+	rcrtc->suspend = false;
 
-	if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE))
-		primary = &rcrtc->vsp->planes[0].plane;
-	else
+	if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) {
+		unsigned int primary_num;
+
+		primary_num = rcrtc->vsp->num_planes - rcrtc->vsp->num_brs;
+
+		if (rcrtc->lif_index == 1)
+			primary = &rcrtc->vsp->planes[primary_num].plane;
+		else
+			primary = &rcrtc->vsp->planes[0].plane;
+	} else
 		primary = &rgrp->planes[index % 2].plane;
 
 	ret = drm_crtc_init_with_planes(rcdu->ddev, crtc, primary,
@@ -648,6 +794,12 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
 		return ret;
 	}
 
+	ret = RCAR_PRR_INIT();
+	if (ret) {
+		dev_dbg(rcdu->dev, "product register init fail.\n");
+		return ret;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
index 6f08b7e..fdd9f46 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -1,7 +1,7 @@
 /*
  * rcar_du_crtc.h  --  R-Car Display Unit CRTCs
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -52,6 +52,18 @@ struct rcar_du_crtc {
 
 	struct rcar_du_group *group;
 	struct rcar_du_vsp *vsp;
+
+	int lvds_ch;
+	bool suspend;
+	unsigned int lif_index;
+};
+
+struct dpll_info {
+	unsigned int dpllclk;
+	unsigned int diff;
+	unsigned int fdpll;
+	unsigned int n;
+	unsigned int m;
 };
 
 #define to_rcar_crtc(c)	container_of(c, struct rcar_du_crtc, crtc)
@@ -61,6 +73,8 @@ enum rcar_du_output {
 	RCAR_DU_OUTPUT_DPAD1,
 	RCAR_DU_OUTPUT_LVDS0,
 	RCAR_DU_OUTPUT_LVDS1,
+	RCAR_DU_OUTPUT_HDMI0,
+	RCAR_DU_OUTPUT_HDMI1,
 	RCAR_DU_OUTPUT_TCON,
 	RCAR_DU_OUTPUT_MAX,
 };
@@ -68,9 +82,13 @@ enum rcar_du_output {
 int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index);
 void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable);
 void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc);
+void rcar_du_crtc_remove_suspend(struct rcar_du_crtc *rcrtc);
 void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc);
 
 void rcar_du_crtc_route_output(struct drm_crtc *crtc,
 			       enum rcar_du_output output);
+void rcar_du_crtc_vbk_check(struct rcar_du_crtc *rcrtc);
+int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc);
+void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc);
 
 #endif /* __RCAR_DU_CRTC_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 73c971e..13d2ccf 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -1,7 +1,7 @@
 /*
  * rcar_du_drv.c  --  R-Car Display Unit DRM driver
  *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -20,16 +20,27 @@
 #include <linux/pm.h>
 #include <linux/slab.h>
 #include <linux/wait.h>
+#include <linux/sys_soc.h>
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder_slave.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
+#include <drm/rcar_du_drm.h>
+
+#include <media/vsp1.h>
 
 #include "rcar_du_crtc.h"
+#include "rcar_du_encoder.h"
 #include "rcar_du_drv.h"
 #include "rcar_du_kms.h"
 #include "rcar_du_regs.h"
+#include "rcar_du_vsp.h"
+#include "rcar_du_hdmienc.h"
+#include "rcar_du_lvdsenc.h"
 
 /* -----------------------------------------------------------------------------
  * Device Information
@@ -108,6 +119,7 @@ static const struct rcar_du_device_info rcar_du_r8a7791_info = {
 		},
 	},
 	.num_lvds = 1,
+	.vsp_num = 4,
 };
 
 static const struct rcar_du_device_info rcar_du_r8a7794_info = {
@@ -133,21 +145,32 @@ static const struct rcar_du_device_info rcar_du_r8a7794_info = {
 	.num_lvds = 0,
 };
 
-static const struct rcar_du_device_info rcar_du_r8a7795_info = {
+static const struct rcar_du_device_info rcar_du_r8a7795_es1x_info = {
 	.gen = 3,
 	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
 		  | RCAR_DU_FEATURE_EXT_CTRL_REGS
-		  | RCAR_DU_FEATURE_VSP1_SOURCE,
+		  | RCAR_DU_FEATURE_VSP1_SOURCE
+		  | RCAR_DU_FEATURE_DIDSR2_REG,
 	.num_crtcs = 4,
 	.routes = {
-		/* R8A7795 has one RGB output, one LVDS output and two
-		 * (currently unsupported) HDMI outputs.
+		/* R8A7795 has one RGB output, two HDMI outputs and one
+		 * LVDS output.
 		 */
 		[RCAR_DU_OUTPUT_DPAD0] = {
 			.possible_crtcs = BIT(3),
 			.encoder_type = DRM_MODE_ENCODER_NONE,
 			.port = 0,
 		},
+		[RCAR_DU_OUTPUT_HDMI0] = {
+			.possible_crtcs = BIT(1),
+			.encoder_type = RCAR_DU_ENCODER_HDMI,
+			.port = 1,
+		},
+		[RCAR_DU_OUTPUT_HDMI1] = {
+			.possible_crtcs = BIT(2),
+			.encoder_type = RCAR_DU_ENCODER_HDMI,
+			.port = 2,
+		},
 		[RCAR_DU_OUTPUT_LVDS0] = {
 			.possible_crtcs = BIT(0),
 			.encoder_type = DRM_MODE_ENCODER_LVDS,
@@ -155,6 +178,90 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = {
 		},
 	},
 	.num_lvds = 1,
+	.dpll_ch =  BIT(1) | BIT(2),
+	.vsp_num = 5,
+};
+
+static const struct rcar_du_device_info rcar_du_r8a7795_info = {
+	.gen = 3,
+	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
+		  | RCAR_DU_FEATURE_EXT_CTRL_REGS
+		  | RCAR_DU_FEATURE_VSP1_SOURCE
+		  | RCAR_DU_FEATURE_DIDSR2_REG
+		  | RCAR_DU_FEATURE_VSPDL_SOURCE,
+	.num_crtcs = 4,
+	.routes = {
+		/* R8A7795 has one RGB output, two HDMI outputs and one
+		 * LVDS output.
+		 */
+		[RCAR_DU_OUTPUT_DPAD0] = {
+			.possible_crtcs = BIT(3),
+			.encoder_type = DRM_MODE_ENCODER_NONE,
+			.port = 0,
+		},
+		[RCAR_DU_OUTPUT_HDMI0] = {
+			.possible_crtcs = BIT(1),
+			.encoder_type = RCAR_DU_ENCODER_HDMI,
+			.port = 1,
+		},
+		[RCAR_DU_OUTPUT_HDMI1] = {
+			.possible_crtcs = BIT(2),
+			.encoder_type = RCAR_DU_ENCODER_HDMI,
+			.port = 2,
+		},
+		[RCAR_DU_OUTPUT_LVDS0] = {
+			.possible_crtcs = BIT(0),
+			.encoder_type = DRM_MODE_ENCODER_LVDS,
+			.port = 3,
+		},
+	},
+	.num_lvds = 1,
+	.dpll_ch =  BIT(1) | BIT(2),
+	.vsp_num = 5,
+	.vspdl_pair_ch = DU_CH_3,
+};
+
+static const struct rcar_du_device_info rcar_du_r8a7796_info = {
+	.gen = 3,
+	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
+		  | RCAR_DU_FEATURE_EXT_CTRL_REGS
+		  | RCAR_DU_FEATURE_VSP1_SOURCE,
+	.num_crtcs = 3,
+	.routes = {
+		/* R8A7796 has one RGB output, one LVDS output and one
+		 * HDMI outputs.
+		 */
+		[RCAR_DU_OUTPUT_DPAD0] = {
+			.possible_crtcs = BIT(2),
+			.encoder_type = DRM_MODE_ENCODER_NONE,
+			.port = 0,
+		},
+		[RCAR_DU_OUTPUT_HDMI0] = {
+			.possible_crtcs = BIT(1),
+			.encoder_type = RCAR_DU_ENCODER_HDMI,
+			.port = 1,
+		},
+		[RCAR_DU_OUTPUT_LVDS0] = {
+			.possible_crtcs = BIT(0),
+			.encoder_type = DRM_MODE_ENCODER_LVDS,
+			.port = 2,
+		},
+	},
+	.num_lvds = 1,
+	.dpll_ch =  BIT(1),
+	.vsp_num = 5,
+};
+
+/* H3 WS1.0  */
+static const struct soc_device_attribute r8a7795es10[] = {
+	{ .soc_id = "r8a7795", .revision = "ES1.0" },
+	{ },
+};
+
+/* H3 WS1.1  */
+static const struct soc_device_attribute r8a7795es11[] = {
+	{ .soc_id = "r8a7795", .revision = "ES1.1" },
+	{ },
 };
 
 static const struct of_device_id rcar_du_of_table[] = {
@@ -164,6 +271,7 @@ static const struct of_device_id rcar_du_of_table[] = {
 	{ .compatible = "renesas,du-r8a7793", .data = &rcar_du_r8a7791_info },
 	{ .compatible = "renesas,du-r8a7794", .data = &rcar_du_r8a7794_info },
 	{ .compatible = "renesas,du-r8a7795", .data = &rcar_du_r8a7795_info },
+	{ .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info },
 	{ }
 };
 
@@ -177,7 +285,8 @@ static void rcar_du_lastclose(struct drm_device *dev)
 {
 	struct rcar_du_device *rcdu = dev->dev_private;
 
-	drm_fbdev_cma_restore_mode(rcdu->fbdev);
+	if (dev->irq_enabled)
+		drm_fbdev_cma_restore_mode(rcdu->fbdev);
 }
 
 static int rcar_du_enable_vblank(struct drm_device *dev, unsigned int pipe)
@@ -196,6 +305,13 @@ static void rcar_du_disable_vblank(struct drm_device *dev, unsigned int pipe)
 	rcar_du_crtc_enable_vblank(&rcdu->crtcs[pipe], false);
 }
 
+static const struct drm_ioctl_desc rcar_du_ioctls[] = {
+	DRM_IOCTL_DEF_DRV(DRM_RCAR_DU_SET_VMUTE, rcar_du_set_vmute,
+		DRM_UNLOCKED | DRM_CONTROL_ALLOW),
+	DRM_IOCTL_DEF_DRV(DRM_RCAR_DU_SCRSHOT, rcar_du_vsp_write_back,
+		DRM_UNLOCKED | DRM_CONTROL_ALLOW),
+};
+
 static const struct file_operations rcar_du_fops = {
 	.owner		= THIS_MODULE,
 	.open		= drm_open,
@@ -237,30 +353,85 @@ static struct drm_driver rcar_du_driver = {
 	.date			= "20130110",
 	.major			= 1,
 	.minor			= 0,
+	.ioctls			= rcar_du_ioctls,
+	.num_ioctls		= ARRAY_SIZE(rcar_du_ioctls),
 };
 
 /* -----------------------------------------------------------------------------
  * Power management
  */
-
 #ifdef CONFIG_PM_SLEEP
 static int rcar_du_pm_suspend(struct device *dev)
 {
 	struct rcar_du_device *rcdu = dev_get_drvdata(dev);
-
+	int i;
+#if IS_ENABLED(CONFIG_DRM_RCAR_HDMI)
+	struct drm_encoder *encoder;
+#endif
 	drm_kms_helper_poll_disable(rcdu->ddev);
-	/* TODO Suspend the CRTC */
+#if IS_ENABLED(CONFIG_DRM_RCAR_HDMI)
+	list_for_each_entry(encoder,
+		 &rcdu->ddev->mode_config.encoder_list, head) {
+		if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS)
+			rcar_du_hdmienc_suspend(encoder);
+	}
+#endif
 
+#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
+	for (i = 0; i < rcdu->info->num_lvds; ++i) {
+		if (rcdu->lvds[i])
+			rcar_du_lvdsenc_stop_suspend(rcdu->lvds[i]);
+	}
+#endif
+	for (i = 0; i < rcdu->num_crtcs; ++i) {
+		if (rcdu->crtcs[i].started) {
+			rcar_du_crtc_suspend(&rcdu->crtcs[i]);
+			clk_set_rate(rcdu->crtcs[i].extclock, 0);
+			rcdu->crtcs[i].group->used_crtcs = 0;
+		}
+	}
 	return 0;
 }
 
 static int rcar_du_pm_resume(struct device *dev)
 {
 	struct rcar_du_device *rcdu = dev_get_drvdata(dev);
+	struct drm_encoder *encoder;
+	int i;
 
-	/* TODO Resume the CRTC */
+	encoder = NULL;
 
+	for (i = 0; i < rcdu->num_crtcs; ++i)
+		rcar_du_crtc_resume(&rcdu->crtcs[i]);
+
+#if IS_ENABLED(CONFIG_DRM_RCAR_HDMI)
+	list_for_each_entry(encoder,
+		&rcdu->ddev->mode_config.encoder_list, head) {
+		if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS)
+			rcar_du_hdmienc_resume(encoder);
+	}
+#endif
+#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
+	for (i = 0; i < rcdu->num_crtcs; ++i) {
+		if (rcdu->crtcs[i].lvds_ch >= 0)
+			rcar_du_lvdsenc_start(
+				rcdu->lvds[rcdu->crtcs[i].lvds_ch],
+				&rcdu->crtcs[i]);
+	}
+#endif
+
+	for (i = 0; i < rcdu->num_crtcs; ++i) {
+		struct drm_crtc *crtc = &rcdu->crtcs[i].crtc;
+
+		if (rcdu->crtcs[i].started) {
+			rcar_du_group_restart(rcdu->crtcs[i].group,
+						&rcdu->crtcs[i]);
+			rcar_du_async_commit(rcdu->ddev, crtc);
+		}
+	}
 	drm_kms_helper_poll_enable(rcdu->ddev);
+	drm_fbdev_cma_hotplug_event(rcdu->fbdev);
+
 	return 0;
 }
 #endif
@@ -272,20 +443,53 @@ static const struct dev_pm_ops rcar_du_pm_ops = {
 /* -----------------------------------------------------------------------------
  * Platform driver
  */
+static void rcar_du_remove_suspend(struct rcar_du_device *rcdu)
+{
+	int i;
+#if IS_ENABLED(CONFIG_DRM_RCAR_HDMI)
+	struct drm_encoder *encoder;
+
+	list_for_each_entry(encoder,
+		&rcdu->ddev->mode_config.encoder_list, head) {
+		if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS)
+			rcar_du_hdmienc_disable(encoder);
+	}
+#endif
+
+#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
+	for (i = 0; i < rcdu->info->num_lvds; ++i) {
+		if (rcdu->lvds[i])
+			rcar_du_lvdsenc_stop_suspend(rcdu->lvds[i]);
+	}
+#endif
+	for (i = 0; i < rcdu->num_crtcs; ++i) {
+		if (rcdu->crtcs[i].started)
+			rcar_du_crtc_remove_suspend(&rcdu->crtcs[i]);
+	}
+}
 
 static int rcar_du_remove(struct platform_device *pdev)
 {
 	struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
 	struct drm_device *ddev = rcdu->ddev;
+	int i;
+
+	ddev->irq_enabled = 0;
+
+	for (i = 0; i < rcdu->num_crtcs; ++i) {
+		if (rcdu->crtcs[i].started)
+			drm_crtc_vblank_off(&rcdu->crtcs[i].crtc);
+	}
 
 	drm_dev_unregister(ddev);
 
+	rcar_du_remove_suspend(rcdu);
+
 	if (rcdu->fbdev)
 		drm_fbdev_cma_fini(rcdu->fbdev);
 
 	drm_kms_helper_poll_fini(ddev);
 	drm_mode_config_cleanup(ddev);
-	drm_vblank_cleanup(ddev);
 
 	drm_dev_unref(ddev);
 
@@ -315,6 +519,9 @@ static int rcar_du_probe(struct platform_device *pdev)
 	rcdu->dev = &pdev->dev;
 	rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data;
 
+	if (soc_device_match(r8a7795es10) || soc_device_match(r8a7795es11))
+		rcdu->info = &rcar_du_r8a7795_es1x_info;
+
 	ddev = drm_dev_alloc(&rcar_du_driver, &pdev->dev);
 	if (IS_ERR(ddev))
 		return PTR_ERR(ddev);
@@ -322,8 +529,6 @@ static int rcar_du_probe(struct platform_device *pdev)
 	rcdu->ddev = ddev;
 	ddev->dev_private = rcdu;
 
-	platform_set_drvdata(pdev, rcdu);
-
 	/* I/O resources */
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
@@ -342,6 +547,7 @@ static int rcar_du_probe(struct platform_device *pdev)
 	/* DRM/KMS objects */
 	ret = rcar_du_modeset_init(rcdu);
 	if (ret < 0) {
+		platform_set_drvdata(pdev, rcdu);
 		if (ret != -EPROBE_DEFER)
 			dev_err(&pdev->dev,
 				"failed to initialize DRM/KMS (%d)\n", ret);
@@ -357,6 +563,8 @@ static int rcar_du_probe(struct platform_device *pdev)
 	if (ret)
 		goto error;
 
+	platform_set_drvdata(pdev, rcdu);
+
 	DRM_INFO("Device %s probed\n", dev_name(&pdev->dev));
 
 	return 0;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
index c843c31..9108f4b 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -1,7 +1,7 @@
 /*
  * rcar_du_drv.h  --  R-Car Display Unit DRM driver
  *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -31,6 +31,8 @@ struct rcar_du_lvdsenc;
 #define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK	(1 << 0)	/* Per-CRTC IRQ and clock */
 #define RCAR_DU_FEATURE_EXT_CTRL_REGS	(1 << 1)	/* Has extended control registers */
 #define RCAR_DU_FEATURE_VSP1_SOURCE	(1 << 2)	/* Has inputs from VSP1 */
+#define RCAR_DU_FEATURE_DIDSR2_REG	(1 << 3)	/* Has DIDSR2 register */
+#define RCAR_DU_FEATURE_VSPDL_SOURCE	(1 << 4)	/* Has VSPDL from VSP2 */
 
 #define RCAR_DU_QUIRK_ALIGN_128B	(1 << 0)	/* Align pitches to 128 bytes */
 #define RCAR_DU_QUIRK_LVDS_LANES	(1 << 1)	/* LVDS lanes 1 and 3 inverted */
@@ -67,12 +69,15 @@ struct rcar_du_device_info {
 	unsigned int num_crtcs;
 	struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
 	unsigned int num_lvds;
+	unsigned int dpll_ch;
+	unsigned int vsp_num;
+	unsigned int vspdl_pair_ch;
 };
 
 #define RCAR_DU_MAX_CRTCS		4
 #define RCAR_DU_MAX_GROUPS		DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
 #define RCAR_DU_MAX_LVDS		2
-#define RCAR_DU_MAX_VSPS		4
+#define RCAR_DU_MAX_VSPS		5
 
 struct rcar_du_device {
 	struct device *dev;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
index ab8645c..6eaba60 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
@@ -1,7 +1,7 @@
 /*
  * rcar_du_encoder.c  --  R-Car Display Unit Encoder
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -106,7 +106,8 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
 			 enum rcar_du_encoder_type type,
 			 enum rcar_du_output output,
 			 struct device_node *enc_node,
-			 struct device_node *con_node)
+			 struct device_node *con_node,
+			 const char *device_name)
 {
 	struct rcar_du_encoder *renc;
 	struct drm_encoder *encoder;
@@ -150,8 +151,12 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
 		break;
 	}
 
+	renc->device_name = device_name;
+
 	if (type == RCAR_DU_ENCODER_HDMI) {
 		ret = rcar_du_hdmienc_init(rcdu, renc, enc_node);
+		if (of_device_is_compatible(enc_node, "rockchip,rcar-dw-hdmi"))
+			goto done;
 		if (ret < 0)
 			goto done;
 	} else {
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
index 7fc10a9..26d589b 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
@@ -1,7 +1,7 @@
 /*
  * rcar_du_encoder.h  --  R-Car Display Unit Encoder
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -33,6 +33,7 @@ struct rcar_du_encoder {
 	enum rcar_du_output output;
 	struct rcar_du_hdmienc *hdmi;
 	struct rcar_du_lvdsenc *lvds;
+	const char *device_name;
 };
 
 #define to_rcar_encoder(e) \
@@ -52,6 +53,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
 			 enum rcar_du_encoder_type type,
 			 enum rcar_du_output output,
 			 struct device_node *enc_node,
-			 struct device_node *con_node);
+			 struct device_node *con_node,
+			 const char *device_name);
 
 #endif /* __RCAR_DU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c
index 33b2fc5..9eaed53 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_group.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c
@@ -1,7 +1,7 @@
 /*
  * rcar_du_group.c  --  R-Car Display Unit Channels Pair
  *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -108,13 +108,30 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)
 		/* Configure input dot clock routing. We currently hardcode the
 		 * configuration to routing DOTCLKINn to DUn.
 		 */
-		rcar_du_group_write(rgrp, DIDSR, DIDSR_CODE |
+		if (rcdu->info->gen < 3) {
+			rcar_du_group_write(rgrp, DIDSR, DIDSR_CODE |
 				    DIDSR_LCDS_DCLKIN(2) |
 				    DIDSR_LCDS_DCLKIN(1) |
 				    DIDSR_LCDS_DCLKIN(0) |
 				    DIDSR_PDCS_CLK(2, 0) |
 				    DIDSR_PDCS_CLK(1, 0) |
 				    DIDSR_PDCS_CLK(0, 0));
+		} else {
+			if (rgrp->index == 0) {
+				rcar_du_group_write(rgrp,
+				    DIDSR, DIDSR_CODE |
+				    DIDSR_LCDS0_DCLKIN |
+				    DIDSR_PDCS_CLK(1, 0) |
+				    DIDSR_PDCS_CLK(0, 0));
+			} else if ((rgrp->index == 1) &&
+				rcar_du_has(rgrp->dev,
+				RCAR_DU_FEATURE_DIDSR2_REG)) {
+				rcar_du_group_write(rgrp,
+				    DIDSR, DIDSR_CODE |
+				    DIDSR_PDCS_CLK(1, 0) |
+				    DIDSR_PDCS_CLK(0, 0));
+			}
+		}
 	}
 
 	if (rcdu->info->gen >= 3)
@@ -164,14 +181,30 @@ void rcar_du_group_put(struct rcar_du_group *rgrp)
 	--rgrp->use_count;
 }
 
-static void __rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start)
+static void __rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start,
+				       struct rcar_du_crtc *rcrtc)
 {
+	struct rcar_du_device *rcdu = rgrp->dev;
+
+	if (!start) {
+		rcar_du_group_write(rgrp, DSYSR,
+			(rcar_du_group_read(rgrp, DSYSR) &
+			~(DSYSR_DRES | DSYSR_DEN)));
+
+		/* Wait until access to VSP stops after setting both
+		 * the DEN and DRES bits in DSYSRm.
+		 */
+		if (rcdu->info->gen >= 3)
+			rcar_du_crtc_vbk_check(rcrtc);
+	}
+
 	rcar_du_group_write(rgrp, DSYSR,
 		(rcar_du_group_read(rgrp, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) |
 		(start ? DSYSR_DEN : DSYSR_DRES));
 }
 
-void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start)
+void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start,
+			      struct rcar_du_crtc *rcrtc)
 {
 	/* Many of the configuration bits are only updated when the display
 	 * reset (DRES) bit in DSYSR is set to 1, disabling *both* CRTCs. Some
@@ -186,20 +219,21 @@ void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start)
 	 */
 	if (start) {
 		if (rgrp->used_crtcs++ != 0)
-			__rcar_du_group_start_stop(rgrp, false);
-		__rcar_du_group_start_stop(rgrp, true);
+			__rcar_du_group_start_stop(rgrp, false, rcrtc);
+		__rcar_du_group_start_stop(rgrp, true, rcrtc);
 	} else {
 		if (--rgrp->used_crtcs == 0)
-			__rcar_du_group_start_stop(rgrp, false);
+			__rcar_du_group_start_stop(rgrp, false, rcrtc);
 	}
 }
 
-void rcar_du_group_restart(struct rcar_du_group *rgrp)
+void rcar_du_group_restart(struct rcar_du_group *rgrp,
+			   struct rcar_du_crtc *rcrtc)
 {
 	rgrp->need_restart = false;
 
-	__rcar_du_group_start_stop(rgrp, false);
-	__rcar_du_group_start_stop(rgrp, true);
+	__rcar_du_group_start_stop(rgrp, false, rcrtc);
+	__rcar_du_group_start_stop(rgrp, true, rcrtc);
 }
 
 int rcar_du_set_dpad0_vsp1_routing(struct rcar_du_device *rcdu)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.h b/drivers/gpu/drm/rcar-du/rcar_du_group.h
index 5e3adc6..e7391c0 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_group.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_group.h
@@ -1,7 +1,7 @@
 /*
  * rcar_du_group.c  --  R-Car Display Unit Planes and CRTCs Group
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -56,8 +56,10 @@ void rcar_du_group_write(struct rcar_du_group *rgrp, u32 reg, u32 data);
 
 int rcar_du_group_get(struct rcar_du_group *rgrp);
 void rcar_du_group_put(struct rcar_du_group *rgrp);
-void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start);
-void rcar_du_group_restart(struct rcar_du_group *rgrp);
+void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start,
+			      struct rcar_du_crtc *rcrtc);
+void rcar_du_group_restart(struct rcar_du_group *rgrp,
+			   struct rcar_du_crtc *rcrtc);
 int rcar_du_group_set_routing(struct rcar_du_group *rgrp);
 
 int rcar_du_set_dpad0_vsp1_routing(struct rcar_du_device *rcdu);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
index e03004f..1e75e39 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
@@ -1,7 +1,7 @@
 /*
  * R-Car Display Unit HDMI Encoder
  *
- * Copyright (C) 2014 Renesas Electronics Corporation
+ * Copyright (C) 2014-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -13,10 +13,13 @@
 
 #include <linux/slab.h>
 
+#include <drm/bridge/dw_hdmi.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 
+#include <linux/of_platform.h>
+
 #include "rcar_du_drv.h"
 #include "rcar_du_encoder.h"
 #include "rcar_du_hdmienc.h"
@@ -24,14 +27,43 @@
 
 struct rcar_du_hdmienc {
 	struct rcar_du_encoder *renc;
+	struct device *dev;
 	bool enabled;
+	unsigned int index;
 };
 
 #define to_rcar_hdmienc(e)	(to_rcar_encoder(e)->hdmi)
 
-static void rcar_du_hdmienc_disable(struct drm_encoder *encoder)
+void rcar_du_hdmienc_suspend(struct drm_encoder *encoder)
+{
+	const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs;
+
+	if ((bfuncs) && (bfuncs->disable))
+		bfuncs->disable(encoder->bridge);
+}
+
+#define SMSTPCR7 0xE615014C
+
+void rcar_du_hdmienc_resume(struct drm_encoder *encoder)
+{
+	void __iomem *smstpcr = ioremap_nocache(SMSTPCR7, 4);
+	const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs;
+	u32 temp;
+
+	temp = readl(smstpcr);
+	writel(temp & ~(0x3 << 28), smstpcr);
+
+	if ((bfuncs) && (bfuncs->enable))
+		bfuncs->enable(encoder->bridge);
+}
+
+void rcar_du_hdmienc_disable(struct drm_encoder *encoder)
 {
 	struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
+	const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs;
+
+	if ((bfuncs) && (bfuncs->disable))
+		bfuncs->disable(encoder->bridge);
 
 	if (hdmienc->renc->lvds)
 		rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc,
@@ -40,13 +72,16 @@ static void rcar_du_hdmienc_disable(struct drm_encoder *encoder)
 	hdmienc->enabled = false;
 }
 
-static void rcar_du_hdmienc_enable(struct drm_encoder *encoder)
+void rcar_du_hdmienc_enable(struct drm_encoder *encoder)
 {
 	struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
+	const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs;
 
 	if (hdmienc->renc->lvds)
 		rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc,
 				       true);
+	if ((bfuncs) && (bfuncs->enable))
+		bfuncs->enable(encoder->bridge);
 
 	hdmienc->enabled = true;
 }
@@ -56,21 +91,30 @@ static int rcar_du_hdmienc_atomic_check(struct drm_encoder *encoder,
 					struct drm_connector_state *conn_state)
 {
 	struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
+	const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs;
 	struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
+	const struct drm_display_mode *mode = &crtc_state->mode;
+	int ret = 0;
 
 	if (hdmienc->renc->lvds)
 		rcar_du_lvdsenc_atomic_check(hdmienc->renc->lvds,
 					     adjusted_mode);
 
-	return 0;
+	if ((bfuncs) && (bfuncs->mode_fixup))
+		ret = bfuncs->mode_fixup(encoder->bridge, mode,
+				 adjusted_mode) ? 0 : -EINVAL;
+	return ret;
 }
 
-
 static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder,
 				     struct drm_display_mode *mode,
 				     struct drm_display_mode *adjusted_mode)
 {
 	struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
+	const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs;
+
+	if ((bfuncs) && (bfuncs->mode_set))
+		bfuncs->mode_set(encoder->bridge, mode, adjusted_mode);
 
 	rcar_du_crtc_route_output(encoder->crtc, hdmienc->renc->output);
 }
@@ -96,22 +140,199 @@ static const struct drm_encoder_funcs encoder_funcs = {
 	.destroy = rcar_du_hdmienc_cleanup,
 };
 
+static const struct dw_hdmi_mpll_config rcar_du_hdmienc_mpll_cfg[] = {
+	{
+		44900000, {
+			{ 0x0003, 0x0000},
+			{ 0x0003, 0x0000},
+			{ 0x0003, 0x0000}
+		},
+	}, {
+		90000000, {
+			{ 0x0002, 0x0000},
+			{ 0x0002, 0x0000},
+			{ 0x0002, 0x0000}
+		},
+	}, {
+		182750000, {
+			{ 0x0001, 0x0000},
+			{ 0x0001, 0x0000},
+			{ 0x0001, 0x0000}
+		},
+	}, {
+		297000000, {
+			{ 0x0000, 0x0000},
+			{ 0x0000, 0x0000},
+			{ 0x0000, 0x0000}
+		},
+	}, {
+		~0UL, {
+			{ 0xFFFF, 0xFFFF },
+			{ 0xFFFF, 0xFFFF },
+			{ 0xFFFF, 0xFFFF },
+		},
+	}
+};
+static const struct dw_hdmi_curr_ctrl rcar_du_hdmienc_cur_ctr[] = {
+	/*	pixelclk    bpp8    bpp10   bpp12 */
+	{
+		35500000,  { 0x0344, 0x0000, 0x0000 },
+	}, {
+		44900000,  { 0x0285, 0x0000, 0x0000 },
+	}, {
+		71000000,  { 0x1184, 0x0000, 0x0000 },
+	}, {
+		90000000,  { 0x1144, 0x0000, 0x0000 },
+	}, {
+		140250000, { 0x20c4, 0x0000, 0x0000 },
+	}, {
+		182750000, { 0x2084, 0x0000, 0x0000 },
+	}, {
+		297000000, { 0x0084, 0x0000, 0x0000 },
+	}, {
+		~0UL,	   { 0x0000, 0x0000, 0x0000 },
+	}
+};
+
+static const struct dw_hdmi_multi_div rcar_du_hdmienc_multi_div[] = {
+	/*	pixelclk    bpp8    bpp10   bpp12 */
+	{
+		35500000,  { 0x0328, 0x0000, 0x0000 },
+	}, {
+		44900000,  { 0x0128, 0x0000, 0x0000 },
+	}, {
+		71000000,  { 0x0314, 0x0000, 0x0000 },
+	}, {
+		90000000,  { 0x0114, 0x0000, 0x0000 },
+	}, {
+		140250000, { 0x030a, 0x0000, 0x0000 },
+	}, {
+		182750000, { 0x010a, 0x0000, 0x0000 },
+	}, {
+		281250000, { 0x0305, 0x0000, 0x0000 },
+	}, {
+		297000000, { 0x0105, 0x0000, 0x0000 },
+	}, {
+		~0UL,	   { 0x0000, 0x0000, 0x0000 },
+	}
+};
+
+static const struct dw_hdmi_phy_config rcar_du_hdmienc_phy_config[] = {
+	/*pixelclk   symbol   term   vlev*/
+	{ 74250000,  0x8009, 0x0004, 0x0272},
+	{ 148500000, 0x802b, 0x0004, 0x028d},
+	{ 297000000, 0x8039, 0x0005, 0x028d},
+	{ ~0UL,      0x0000, 0x0000, 0x0000}
+};
+
+static enum drm_mode_status
+rcar_du_hdmienc_mode_valid(struct drm_connector *connector,
+			    struct drm_display_mode *mode)
+{
+	if ((mode->hdisplay > 3840) || (mode->vdisplay > 2160))
+		return MODE_BAD;
+
+	if (((mode->hdisplay == 3840) && (mode->vdisplay == 2160))
+		&& (mode->vrefresh > 30))
+		return MODE_BAD;
+
+	if (mode->clock > 297000)
+		return MODE_BAD;
+
+	return MODE_OK;
+}
+
+static const struct dw_hdmi_plat_data rcar_du_hdmienc_hdmi0_drv_data = {
+	.mode_valid = rcar_du_hdmienc_mode_valid,
+	.mpll_cfg   = rcar_du_hdmienc_mpll_cfg,
+	.cur_ctr    = rcar_du_hdmienc_cur_ctr,
+	.multi_div  = rcar_du_hdmienc_multi_div,
+	.phy_config = rcar_du_hdmienc_phy_config,
+	.dev_type   = RCAR_HDMI,
+	.index	    = 0,
+};
+
+static const struct dw_hdmi_plat_data rcar_du_hdmienc_hdmi1_drv_data = {
+	.mode_valid = rcar_du_hdmienc_mode_valid,
+	.mpll_cfg   = rcar_du_hdmienc_mpll_cfg,
+	.cur_ctr    = rcar_du_hdmienc_cur_ctr,
+	.multi_div  = rcar_du_hdmienc_multi_div,
+	.phy_config = rcar_du_hdmienc_phy_config,
+	.dev_type   = RCAR_HDMI,
+	.index	    = 1,
+};
+
+static const struct of_device_id rcar_du_hdmienc_dt_ids[] = {
+	{
+		.data = &rcar_du_hdmienc_hdmi0_drv_data
+	},
+	{
+		.data = &rcar_du_hdmienc_hdmi1_drv_data
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, rcar_du_hdmienc_dt_ids);
+
 int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
 			 struct rcar_du_encoder *renc, struct device_node *np)
 {
 	struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
-	struct drm_bridge *bridge;
 	struct rcar_du_hdmienc *hdmienc;
-	int ret;
+	struct resource *iores;
+	struct platform_device *pdev;
+	const struct dw_hdmi_plat_data *plat_data;
+	int ret, irq;
+	bool dw_hdmi_use = false;
+	struct drm_bridge *bridge = NULL;
 
 	hdmienc = devm_kzalloc(rcdu->dev, sizeof(*hdmienc), GFP_KERNEL);
 	if (hdmienc == NULL)
 		return -ENOMEM;
 
-	/* Locate drm bridge from the hdmi encoder DT node */
-	bridge = of_drm_find_bridge(np);
-	if (!bridge)
-		return -EPROBE_DEFER;
+	if (strcmp(renc->device_name, "rockchip,rcar-dw-hdmi") == 0)
+		dw_hdmi_use = true;
+
+	if (dw_hdmi_use) {
+		if (renc->output == RCAR_DU_OUTPUT_HDMI0)
+			hdmienc->index = 0;
+		else if (renc->output == RCAR_DU_OUTPUT_HDMI1)
+			hdmienc->index = 1;
+		else
+			return -EINVAL;
+
+		np = of_parse_phandle(rcdu->dev->of_node, "hdmi",
+						 hdmienc->index);
+		if (!np) {
+			dev_err(rcdu->dev, "hdmi node not found\n");
+			return -ENXIO;
+		}
+
+		pdev = of_find_device_by_node(np);
+		of_node_put(np);
+		if (!pdev)
+			return -ENXIO;
+
+		plat_data = rcar_du_hdmienc_dt_ids[hdmienc->index].data;
+		hdmienc->dev = &pdev->dev;
+
+		irq = platform_get_irq(pdev, 0);
+		if (irq < 0)
+			return irq;
+
+		iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		if (!iores)
+			return -ENXIO;
+
+	} else {
+		/* Locate the DRM bridge from the HDMI encoder DT node. */
+		bridge = of_drm_find_bridge(np);
+		if (!bridge) {
+			dev_dbg(rcdu->dev,
+				"can't get bridge for %s, deferring probe\n",
+				of_node_full_name(np));
+			return -EPROBE_DEFER;
+		}
+	}
 
 	ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
 			       DRM_MODE_ENCODER_TMDS, NULL);
@@ -124,13 +345,21 @@ int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
 	hdmienc->renc = renc;
 
 	/* Link drm_bridge to encoder */
-	bridge->encoder = encoder;
-	encoder->bridge = bridge;
+	if (bridge) {
+		bridge->encoder = encoder;
+		encoder->bridge = bridge;
+	}
 
-	ret = drm_bridge_attach(rcdu->ddev, bridge);
-	if (ret) {
-		drm_encoder_cleanup(encoder);
-		return ret;
+	if (dw_hdmi_use)
+		ret = dw_hdmi_bind(rcdu->dev, NULL, rcdu->ddev, encoder,
+				iores, irq, plat_data);
+
+	if (bridge) {
+		ret = drm_bridge_attach(rcdu->ddev, bridge);
+		if (ret) {
+			drm_encoder_cleanup(encoder);
+			return ret;
+		}
 	}
 
 	return 0;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h
index 2ff0128..14f5c33 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h
@@ -1,7 +1,7 @@
 /*
  * R-Car Display Unit HDMI Encoder
  *
- * Copyright (C) 2014 Renesas Electronics Corporation
+ * Copyright (C) 2014-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -23,6 +23,10 @@ struct rcar_du_encoder;
 #if IS_ENABLED(CONFIG_DRM_RCAR_HDMI)
 int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
 			 struct rcar_du_encoder *renc, struct device_node *np);
+void rcar_du_hdmienc_disable(struct drm_encoder *encoder);
+void rcar_du_hdmienc_enable(struct drm_encoder *encoder);
+void rcar_du_hdmienc_suspend(struct drm_encoder *encoder);
+void rcar_du_hdmienc_resume(struct drm_encoder *encoder);
 #else
 static inline int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
 				       struct rcar_du_encoder *renc,
@@ -30,6 +34,18 @@ static inline int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
 {
 	return -ENOSYS;
 }
+static inline void rcar_du_hdmienc_disable(struct drm_encoder *encoder)
+{
+}
+static inline void rcar_du_hdmienc_enable(struct drm_encoder *encoder)
+{
+}
+static inline void rcar_du_hdmienc_suspend(struct drm_encoder *encoder)
+{
+}
+static inline void rcar_du_hdmienc_resume(struct drm_encoder *encoder)
+{
+}
 #endif
 
 #endif /* __RCAR_DU_HDMIENC_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index bd9c3bb..d1583a9 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -1,7 +1,7 @@
 /*
  * rcar_du_kms.c  --  R-Car Display Unit Mode Setting
  *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -95,6 +95,48 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
 		.planes = 2,
 		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
 		.edf = PnDDCR4_EDF_NONE,
+	}, {
+		.fourcc = DRM_FORMAT_NV61,
+		.bpp = 16,
+		.planes = 2,
+		.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+		.edf = PnDDCR4_EDF_NONE,
+	},
+};
+
+static const struct rcar_du_format_info rcar_vsp_format_infos[] = {
+	{
+		.fourcc = DRM_FORMAT_RGB332,
+		.bpp = 8,
+		.planes = 1,
+	}, {
+		.fourcc = DRM_FORMAT_ARGB4444,
+		.bpp = 16,
+		.planes = 1,
+	}, {
+		.fourcc = DRM_FORMAT_XRGB4444,
+		.bpp = 16,
+		.planes = 1,
+	}, {
+		.fourcc = DRM_FORMAT_BGR888,
+		.bpp = 24,
+		.planes = 1,
+	}, {
+		.fourcc = DRM_FORMAT_RGB888,
+		.bpp = 24,
+		.planes = 1,
+	}, {
+		.fourcc = DRM_FORMAT_BGRA8888,
+		.bpp = 32,
+		.planes = 1,
+	}, {
+		.fourcc = DRM_FORMAT_BGRX8888,
+		.bpp = 32,
+		.planes = 1,
+	}, {
+		.fourcc = DRM_FORMAT_YVYU,
+		.bpp = 16,
+		.planes = 1,
 	},
 	/* The following formats are not supported on Gen2 and thus have no
 	 * associated .pnmr or .edf settings.
@@ -142,6 +184,18 @@ const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc)
 	return NULL;
 }
 
+const struct rcar_du_format_info *rcar_vsp_format_info(u32 fourcc)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(rcar_vsp_format_infos); ++i) {
+		if (rcar_vsp_format_infos[i].fourcc == fourcc)
+			return &rcar_vsp_format_infos[i];
+	}
+
+	return NULL;
+}
+
 /* -----------------------------------------------------------------------------
  * Frame buffer
  */
@@ -178,6 +232,15 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
 	unsigned int i;
 
 	format = rcar_du_format_info(mode_cmd->pixel_format);
+
+	if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE) &&
+		(format == NULL)) {
+		format = rcar_vsp_format_info(mode_cmd->pixel_format);
+	}
+
+	if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE) && (format != NULL))
+		goto done;
+
 	if (format == NULL) {
 		dev_dbg(dev->dev, "unsupported pixel format %08x\n",
 			mode_cmd->pixel_format);
@@ -210,7 +273,7 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
 			return ERR_PTR(-EINVAL);
 		}
 	}
-
+done:
 	return drm_fb_cma_create(dev, file_priv, mode_cmd);
 }
 
@@ -279,7 +342,6 @@ static void rcar_du_atomic_work(struct work_struct *work)
 {
 	struct rcar_du_commit *commit =
 		container_of(work, struct rcar_du_commit, work);
-
 	rcar_du_atomic_complete(commit);
 }
 
@@ -342,6 +404,34 @@ static int rcar_du_atomic_commit(struct drm_device *dev,
 	return ret;
 }
 
+int rcar_du_async_commit(struct drm_device *dev, struct drm_crtc *crtc)
+{
+	int ret;
+	struct drm_atomic_state *state;
+	struct drm_crtc_state *crtc_state;
+
+	state = drm_atomic_state_alloc(dev);
+	if (!state)
+		return -ENOMEM;
+
+	crtc_state = drm_atomic_helper_crtc_duplicate_state(crtc);
+	if (!crtc_state)
+		return -ENOMEM;
+
+	state->crtcs->state = crtc_state;
+	state->crtcs->ptr = crtc;
+	crtc_state->state = state;
+	crtc_state->active = true;
+
+	ret = drm_atomic_commit(state);
+	if (ret != 0) {
+		drm_atomic_helper_crtc_destroy_state(crtc, crtc_state);
+		return ret;
+	}
+
+	return 0;
+}
+
 /* -----------------------------------------------------------------------------
  * Initialization
  */
@@ -363,6 +453,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
 	} encoders[] = {
 		{ "adi,adv7123", RCAR_DU_ENCODER_VGA },
 		{ "adi,adv7511w", RCAR_DU_ENCODER_HDMI },
+		{ "rockchip,rcar-dw-hdmi", RCAR_DU_ENCODER_HDMI },
 		{ "thine,thc63lvdm83d", RCAR_DU_ENCODER_LVDS },
 	};
 
@@ -373,6 +464,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
 	struct device_node *entity_ep_node;
 	struct device_node *entity;
 	int ret;
+	const char *enc_name = NULL;
 
 	/*
 	 * Locate the connected entity and infer its type from the number of
@@ -424,6 +516,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
 			if (of_device_is_compatible(encoder,
 						    encoders[i].compatible)) {
 				enc_type = encoders[i].type;
+				enc_name = encoders[i].compatible;
 				break;
 			}
 		}
@@ -444,7 +537,8 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
 		connector = entity;
 	}
 
-	ret = rcar_du_encoder_init(rcdu, enc_type, output, encoder, connector);
+	ret = rcar_du_encoder_init(rcdu, enc_type, output, encoder, connector,
+					enc_name);
 	of_node_put(encoder);
 	of_node_put(connector);
 
@@ -549,8 +643,8 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
 
 	dev->mode_config.min_width = 0;
 	dev->mode_config.min_height = 0;
-	dev->mode_config.max_width = 4095;
-	dev->mode_config.max_height = 2047;
+	dev->mode_config.max_width = 4096;
+	dev->mode_config.max_height = 2160;
 	dev->mode_config.funcs = &rcar_du_mode_config_funcs;
 
 	rcdu->num_crtcs = rcdu->info->num_crtcs;
@@ -591,15 +685,26 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
 	/* Initialize the compositors. */
 	if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) {
 		for (i = 0; i < rcdu->num_crtcs; ++i) {
-			struct rcar_du_vsp *vsp = &rcdu->vsps[i];
+			struct rcar_du_vsp *vsp;
+			int vsp_index = i;
+			bool init = true;
 
-			vsp->index = i;
+			if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSPDL_SOURCE) &&
+					(i == rcdu->info->vspdl_pair_ch)) {
+				vsp_index = 0;
+				init = false;
+			}
+
+			vsp = &rcdu->vsps[vsp_index];
+			vsp->index = vsp_index;
 			vsp->dev = rcdu;
 			rcdu->crtcs[i].vsp = vsp;
 
-			ret = rcar_du_vsp_init(vsp);
-			if (ret < 0)
-				return ret;
+			if (init) {
+				ret = rcar_du_vsp_init(vsp);
+				if (ret < 0)
+					return ret;
+			}
 		}
 	}
 
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
index 07951d5..ccccc71 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
@@ -1,7 +1,7 @@
 /*
  * rcar_du_kms.h  --  R-Car Display Unit Mode Setting
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -30,10 +30,12 @@ struct rcar_du_format_info {
 };
 
 const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc);
+const struct rcar_du_format_info *rcar_vsp_format_info(u32 fourcc);
 
 int rcar_du_modeset_init(struct rcar_du_device *rcdu);
 
 int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev,
 			struct drm_mode_create_dumb *args);
+int rcar_du_async_commit(struct drm_device *dev, struct drm_crtc *crtc);
 
 #endif /* __RCAR_DU_KMS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
index d4881ee..07f9e3e 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
@@ -1,7 +1,7 @@
 /*
  * rcar_du_lvdscon.h  --  R-Car Display Unit LVDS Connector
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -17,8 +17,17 @@
 struct rcar_du_device;
 struct rcar_du_encoder;
 
+#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
 int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
 				struct rcar_du_encoder *renc,
 				struct device_node *np);
+#else
+static inline int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
+				struct rcar_du_encoder *renc,
+				struct device_node *np)
+{
+	return -ENOSYS;
+}
+#endif
 
 #endif /* __RCAR_DU_LVDSCON_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
index 116740a..08ee27e 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
@@ -1,7 +1,7 @@
 /*
  * rcar_du_lvdsenc.c  --  R-Car Display Unit LVDS Encoder
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -14,6 +14,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/io.h>
+#include <linux/of_gpio.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
@@ -32,6 +33,7 @@ struct rcar_du_lvdsenc {
 
 	enum rcar_lvds_input input;
 	enum rcar_lvds_mode mode;
+	int gpio_pd;
 };
 
 static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data)
@@ -105,6 +107,13 @@ static void rcar_du_lvdsenc_start_gen3(struct rcar_du_lvdsenc *lvds,
 
 	rcar_lvds_write(lvds, LVDPLLCR, pllcr);
 
+	/* Turn all the channels on. */
+	rcar_lvds_write(lvds, LVDCR1,
+			LVDCR1_CHSTBY_GEN3(3) | LVDCR1_CHSTBY_GEN3(2) |
+			LVDCR1_CHSTBY_GEN3(1) | LVDCR1_CHSTBY_GEN3(0) |
+			LVDCR1_CLKSTBY_GEN3);
+
+
 	/* Turn the PLL on, set it to LVDS normal mode, wait for the startup
 	 * delay and turn the output on.
 	 */
@@ -118,23 +127,29 @@ static void rcar_du_lvdsenc_start_gen3(struct rcar_du_lvdsenc *lvds,
 
 	lvdcr0 |= LVDCR0_LVRES;
 	rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-
-	/* Turn all the channels on. */
-	rcar_lvds_write(lvds, LVDCR1,
-			LVDCR1_CHSTBY_GEN3(3) | LVDCR1_CHSTBY_GEN3(2) |
-			LVDCR1_CHSTBY_GEN3(1) | LVDCR1_CHSTBY_GEN3(0) |
-			LVDCR1_CLKSTBY_GEN3);
 }
 
-static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
+int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
 				 struct rcar_du_crtc *rcrtc)
 {
 	u32 lvdhcr;
 	int ret;
+	void __iomem *srstclr7_reg;
+	u32 srstclr7_lvds = 0;
 
 	if (lvds->enabled)
 		return 0;
 
+	/* software reset release */
+	if (lvds->index == 0)
+		srstclr7_lvds |= SRCR7_LVDS;
+	srstclr7_reg = ioremap_nocache(SRSTCLR7, 0x04);
+	writel_relaxed(srstclr7_lvds, srstclr7_reg);
+	iounmap(srstclr7_reg);
+
+	if (gpio_is_valid(lvds->gpio_pd))
+		gpio_set_value(lvds->gpio_pd, 1);
+
 	ret = clk_prepare_enable(lvds->clock);
 	if (ret < 0)
 		return ret;
@@ -165,15 +180,19 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
 	else
 		rcar_du_lvdsenc_start_gen3(lvds, rcrtc);
 
+	rcrtc->lvds_ch = lvds->index;
 	lvds->enabled = true;
 
 	return 0;
 }
 
-static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds)
+int rcar_du_lvdsenc_stop_suspend(struct rcar_du_lvdsenc *lvds)
 {
+	void __iomem *srcr7_reg;
+	u32 srcr7_lvds = 0;
+
 	if (!lvds->enabled)
-		return;
+		return -1;
 
 	rcar_lvds_write(lvds, LVDCR0, 0);
 	rcar_lvds_write(lvds, LVDCR1, 0);
@@ -181,6 +200,36 @@ static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds)
 	clk_disable_unprepare(lvds->clock);
 
 	lvds->enabled = false;
+
+	if (gpio_is_valid(lvds->gpio_pd))
+		gpio_set_value(lvds->gpio_pd, 0);
+
+	if (lvds->index == 0)
+		srcr7_lvds |= SRCR7_LVDS;
+	srcr7_reg = ioremap_nocache(SRCR7, 0x04);
+	writel_relaxed(readl_relaxed(srcr7_reg) | srcr7_lvds, srcr7_reg);
+	iounmap(srcr7_reg);
+
+	return 0;
+}
+
+static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds)
+{
+	int ret;
+	unsigned int i;
+
+	if (!lvds->enabled)
+		return;
+
+	ret = rcar_du_lvdsenc_stop_suspend(lvds);
+	if (ret < 0)
+		return;
+
+	for (i = 0; i < lvds->dev->num_crtcs; ++i)
+		if (lvds->index == lvds->dev->crtcs[i].lvds_ch)
+			lvds->dev->crtcs[i].lvds_ch = -1;
+
+	return;
 }
 
 int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, struct drm_crtc *crtc,
@@ -245,6 +294,7 @@ int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu)
 	struct rcar_du_lvdsenc *lvds;
 	unsigned int i;
 	int ret;
+	char name[16];
 
 	for (i = 0; i < rcdu->info->num_lvds; ++i) {
 		lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
@@ -257,12 +307,20 @@ int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu)
 		lvds->index = i;
 		lvds->input = i ? RCAR_LVDS_INPUT_DU1 : RCAR_LVDS_INPUT_DU0;
 		lvds->enabled = false;
+		/* Get optional backlight GPIO */
+		lvds->gpio_pd = of_get_named_gpio(rcdu->dev->of_node,
+						 "backlight-gpios", 0);
 
 		ret = rcar_du_lvdsenc_get_resources(lvds, pdev);
 		if (ret < 0)
 			return ret;
 
 		rcdu->lvds[i] = lvds;
+
+		sprintf(name, "lvds%u", i);
+		if (gpio_is_valid(lvds->gpio_pd))
+			devm_gpio_request_one(&pdev->dev, lvds->gpio_pd,
+					GPIOF_OUT_INIT_LOW, name);
 	}
 
 	return 0;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
index 7218ac8..e18e632 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
@@ -1,7 +1,7 @@
 /*
  * rcar_du_lvdsenc.h  --  R-Car Display Unit LVDS Encoder
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -41,6 +41,9 @@ int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds,
 			   struct drm_crtc *crtc, bool enable);
 void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
 				  struct drm_display_mode *mode);
+int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
+				 struct rcar_du_crtc *rcrtc);
+int rcar_du_lvdsenc_stop_suspend(struct rcar_du_lvdsenc *lvds);
 #else
 static inline int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu)
 {
@@ -59,6 +62,15 @@ static inline void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
 						struct drm_display_mode *mode)
 {
 }
+static inline int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
+					struct rcar_du_crtc *rcrtc)
+{
+	return 0;
+}
+static inline int rcar_du_lvdsenc_stop_suspend(struct rcar_du_lvdsenc *lvds)
+{
+	return 0;
+}
 #endif
 
 #endif /* __RCAR_DU_LVDSENC_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
index 8b91dd3..c1de338 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
@@ -1,7 +1,7 @@
 /*
  * rcar_du_plane.h  --  R-Car Display Unit Planes
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -34,6 +34,11 @@ enum rcar_du_plane_source {
 	RCAR_DU_PLANE_VSPD1,
 };
 
+#define DU_CH_0		0
+#define DU_CH_1		1
+#define DU_CH_2		2
+#define DU_CH_3		3
+
 struct rcar_du_plane {
 	struct drm_plane plane;
 	struct rcar_du_group *group;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
index fedb016..3c3c798 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
@@ -1,7 +1,7 @@
 /*
  * rcar_du_regs.h  --  R-Car Display Unit Registers Definitions
  *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -255,6 +255,8 @@
 
 #define DIDSR			0x20028
 #define DIDSR_CODE		(0x7790 << 16)
+#define DIDSR_LCDS0_DCLKIN	(0 << 9)
+#define DIDSR_LCDS0_LVDSIF	(1 << 9)
 #define DIDSR_LCDS_DCLKIN(n)	(0 << (8 + (n) * 2))
 #define DIDSR_LCDS_LVDS0(n)	(2 << (8 + (n) * 2))
 #define DIDSR_LCDS_LVDS1(n)	(3 << (8 + (n) * 2))
@@ -277,6 +279,25 @@
 #define DEFR10_TSEL_H3_TCON1	(0 << 1) /* DEFR102 register only (DU2/DU3) */
 #define DEFR10_DEFE10		(1 << 0)
 
+#define DPLLCR			0x20044
+#define DPLLCR_CODE		(0x95 << 24)
+#define DPLLCR_PLCS1		(1 << 23)
+#define DPLLCR_PLCS0		(1 << 20)
+#define DPLLCR_CLKE		(1 << 18)
+#define DPLLCR_FDPLL(n)		((n) << 12)	/* n=0 Setting prohibited */
+/* H'00 to H'26, H'78 to H'7F: Setting prohibited.*/
+#define DPLLCR_N(n)		((n) << 5)
+#define DPLLCR_M(n)		((n) << 3)
+#define DPLLCR_STBY		(1 << 2)
+#define DPLLCR_INCS_DPLL01_DOTCLKIN02	(0 << 0)
+#define DPLLCR_INCS_DPLL01_DOTCLKIN13	(1 << 1)
+
+#define DPLLC2R			0x20048
+#define DPLLC2R_CODE		(0x95 << 24)
+#define DPLLC2R_SELC		(1 << 12)
+#define DPLLC2R_M(n)		((n) << 8)
+#define DPLLC2R_FDPLL(n)	((n) << 0)	/* n=0 Setting prohibited */
+
 /* -----------------------------------------------------------------------------
  * Display Timing Generation Registers
  */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
index 8d6125c..e81dfbe 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
@@ -1,7 +1,7 @@
 /*
  * rcar_du_vgacon.c  --  R-Car Display Unit VGA Connector
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -23,7 +23,7 @@
 
 static int rcar_du_vga_connector_get_modes(struct drm_connector *connector)
 {
-	return 0;
+	return drm_add_modes_noedid(connector, 1024, 768);
 }
 
 static const struct drm_connector_helper_funcs connector_helper_funcs = {
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
index 851c2e7..c7024e5 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
@@ -1,7 +1,7 @@
 /*
- * rcar_du_vsp.h  --  R-Car Display Unit VSP-Based Compositor
+ * rcar_du_vsp.c  --  R-Car Display Unit VSP-Based Compositor
  *
- * Copyright (C) 2015 Renesas Electronics Corporation
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -12,12 +12,14 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_plane_helper.h>
+#include <drm/rcar_du_drm.h>
 
 #include <linux/dma-mapping.h>
 #include <linux/of_platform.h>
@@ -68,22 +70,23 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
 	 */
 	crtc->group->need_restart = true;
 
-	vsp1_du_setup_lif(crtc->vsp->vsp, mode->hdisplay, mode->vdisplay);
+	vsp1_du_setup_lif(crtc->vsp->vsp, mode->hdisplay, mode->vdisplay,
+			  crtc->lif_index, 0);
 }
 
 void rcar_du_vsp_disable(struct rcar_du_crtc *crtc)
 {
-	vsp1_du_setup_lif(crtc->vsp->vsp, 0, 0);
+	vsp1_du_setup_lif(crtc->vsp->vsp, 0, 0, 0, crtc->suspend);
 }
 
 void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc)
 {
-	vsp1_du_atomic_begin(crtc->vsp->vsp);
+	vsp1_du_atomic_begin(crtc->vsp->vsp, crtc->lif_index);
 }
 
 void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc)
 {
-	vsp1_du_atomic_flush(crtc->vsp->vsp);
+	vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->lif_index);
 }
 
 /* Keep the two tables in sync. */
@@ -145,6 +148,28 @@ static const u32 formats_v4l2[] = {
 	V4L2_PIX_FMT_YVU444M,
 };
 
+static const u32 formats_xlate[][2] = {
+	{ DRM_FORMAT_RGB332, V4L2_PIX_FMT_RGB332 },
+	{ DRM_FORMAT_ARGB4444, V4L2_PIX_FMT_ARGB444 },
+	{ DRM_FORMAT_XRGB4444, V4L2_PIX_FMT_XRGB444 },
+	{ DRM_FORMAT_ARGB1555, V4L2_PIX_FMT_ARGB555 },
+	{ DRM_FORMAT_XRGB1555, V4L2_PIX_FMT_XRGB555 },
+	{ DRM_FORMAT_RGB565, V4L2_PIX_FMT_RGB565 },
+	{ DRM_FORMAT_BGR888, V4L2_PIX_FMT_RGB24 },
+	{ DRM_FORMAT_RGB888, V4L2_PIX_FMT_BGR24 },
+	{ DRM_FORMAT_BGRA8888, V4L2_PIX_FMT_ARGB32 },
+	{ DRM_FORMAT_BGRX8888, V4L2_PIX_FMT_XRGB32 },
+	{ DRM_FORMAT_ARGB8888, V4L2_PIX_FMT_ABGR32 },
+	{ DRM_FORMAT_XRGB8888, V4L2_PIX_FMT_XBGR32 },
+	{ DRM_FORMAT_UYVY, V4L2_PIX_FMT_UYVY },
+	{ DRM_FORMAT_YUYV, V4L2_PIX_FMT_YUYV },
+	{ DRM_FORMAT_YVYU, V4L2_PIX_FMT_YVYU },
+	{ DRM_FORMAT_NV12, V4L2_PIX_FMT_NV12M },
+	{ DRM_FORMAT_NV21, V4L2_PIX_FMT_NV21M },
+	{ DRM_FORMAT_NV16, V4L2_PIX_FMT_NV16M },
+	{ DRM_FORMAT_NV61, V4L2_PIX_FMT_NV61M },
+};
+
 static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
 {
 	struct rcar_du_vsp_plane_state *state =
@@ -158,6 +183,12 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
 	};
 	unsigned int i;
 
+	if (plane->plane.state->crtc->mode.flags
+				 & DRM_MODE_FLAG_INTERLACE)
+		cfg.interlaced = true;
+	else
+		cfg.interlaced = false;
+
 	cfg.src.left = state->state.src_x >> 16;
 	cfg.src.top = state->state.src_y >> 16;
 	cfg.src.width = state->state.src_w >> 16;
@@ -249,6 +280,7 @@ static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
 	struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
 	struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
 	struct rcar_du_device *rcdu = rplane->vsp->dev;
+	int hdisplay, vdisplay;
 
 	if (!state->fb || !state->crtc) {
 		rstate->format = NULL;
@@ -261,7 +293,25 @@ static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
 		return -EINVAL;
 	}
 
+	hdisplay = state->crtc->mode.hdisplay;
+	vdisplay = state->crtc->mode.vdisplay;
+
+	if ((state->plane->type == DRM_PLANE_TYPE_OVERLAY) &&
+		(((state->crtc_w + state->crtc_x) > hdisplay) ||
+		((state->crtc_h + state->crtc_y) > vdisplay))) {
+		dev_err(rcdu->dev,
+			"%s: specify (%dx%d) + (%d, %d) < (%dx%d).\n",
+			__func__, state->crtc_w, state->crtc_h, state->crtc_x,
+			state->crtc_y, hdisplay, vdisplay);
+		return -EINVAL;
+	}
+
 	rstate->format = rcar_du_format_info(state->fb->pixel_format);
+
+	if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE) &&
+			 (rstate->format == NULL))
+		rstate->format = rcar_vsp_format_info(state->fb->pixel_format);
+
 	if (rstate->format == NULL) {
 		dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
 			state->fb->pixel_format);
@@ -366,6 +416,107 @@ static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane,
 	return 0;
 }
 
+int rcar_du_vsp_write_back(struct drm_device *dev, void *data,
+			   struct drm_file *file_priv)
+{
+	int ret;
+	struct rcar_du_screen_shot *sh = (struct rcar_du_screen_shot *)data;
+	struct drm_mode_object *obj;
+	struct drm_crtc *crtc;
+	struct rcar_du_crtc *rcrtc;
+	struct rcar_du_device *rcdu;
+	const struct drm_display_mode *mode;
+	u32 pixelformat, bpp;
+	unsigned int pitch;
+	dma_addr_t mem[3];
+
+	obj = drm_mode_object_find(dev, sh->crtc_id, DRM_MODE_OBJECT_CRTC);
+	if (!obj)
+		return -EINVAL;
+
+	crtc = obj_to_crtc(obj);
+	rcrtc = to_rcar_crtc(crtc);
+	rcdu = rcrtc->group->dev;
+	mode = &rcrtc->crtc.state->adjusted_mode;
+
+	switch (sh->fmt) {
+	case DRM_FORMAT_RGB565:
+		bpp = 16;
+		pixelformat = V4L2_PIX_FMT_RGB565;
+		break;
+	case DRM_FORMAT_ARGB1555:
+		bpp = 16;
+		pixelformat = V4L2_PIX_FMT_ARGB555;
+		break;
+	case DRM_FORMAT_ARGB8888:
+		bpp = 32;
+		pixelformat = V4L2_PIX_FMT_ABGR32;
+		break;
+	default:
+		dev_err(rcdu->dev, "specified format is not supported.\n");
+		return -EINVAL;
+	}
+
+	pitch = mode->hdisplay * bpp / 8;
+
+	mem[0] = sh->buff;
+	mem[1] = 0;
+	mem[2] = 0;
+
+	if ((sh->width != (mode->hdisplay)) ||
+		(sh->height != (mode->vdisplay)))
+		return -EINVAL;
+
+	if ((pitch * mode->vdisplay) > sh->buff_len)
+		return -EINVAL;
+
+	vsp1_du_setup_wb(rcrtc->vsp->vsp, pixelformat, pitch, mem,
+						rcrtc->lif_index);
+	vsp1_du_wait_wb(rcrtc->vsp->vsp, 2, rcrtc->lif_index);
+
+	ret = rcar_du_async_commit(dev, crtc);
+	if (ret != 0)
+		return ret;
+
+	vsp1_du_wait_wb(rcrtc->vsp->vsp, 1, rcrtc->lif_index);
+
+	ret = rcar_du_async_commit(dev, crtc);
+	if (ret != 0)
+		return ret;
+
+	vsp1_du_wait_wb(rcrtc->vsp->vsp, 0, rcrtc->lif_index);
+
+	return ret;
+}
+
+int rcar_du_set_vmute(struct drm_device *dev, void *data,
+		struct drm_file *file_priv)
+{
+	struct rcar_du_vmute *vmute =
+		(struct rcar_du_vmute *)data;
+	struct drm_mode_object *obj;
+	struct drm_crtc *crtc;
+	struct rcar_du_crtc *rcrtc;
+	int ret = 0;
+
+	dev_dbg(dev->dev, "CRTC[%d], display:%s\n",
+		vmute->crtc_id, vmute->on ? "off":"on");
+
+	obj = drm_mode_object_find(dev, vmute->crtc_id,
+					DRM_MODE_OBJECT_CRTC);
+	if (!obj)
+		return -EINVAL;
+
+	crtc = obj_to_crtc(obj);
+	rcrtc = to_rcar_crtc(crtc);
+
+	vsp1_du_if_set_mute(rcrtc->vsp->vsp, vmute->on, rcrtc->lif_index);
+
+	ret = rcar_du_async_commit(dev, crtc);
+
+	return ret;
+}
+
 static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
 	.update_plane = drm_atomic_helper_update_plane,
 	.disable_plane = drm_atomic_helper_disable_plane,
@@ -378,6 +529,28 @@ static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
 	.atomic_get_property = rcar_du_vsp_plane_atomic_get_property,
 };
 
+static const uint32_t formats[] = {
+	DRM_FORMAT_RGB332,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_XRGB1555,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_BGRX8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV21,
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV61,
+};
+
 int rcar_du_vsp_init(struct rcar_du_vsp *vsp)
 {
 	struct rcar_du_device *rcdu = vsp->dev;
@@ -385,6 +558,7 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp)
 	struct device_node *np;
 	unsigned int i;
 	int ret;
+	unsigned long possible_crtcs, share_num;
 
 	/* Find the VSP device and initialize it. */
 	np = of_parse_phandle(rcdu->dev->of_node, "vsps", vsp->index);
@@ -393,6 +567,8 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp)
 		return -ENXIO;
 	}
 
+	of_property_read_u32(np, "renesas,#brs", &vsp->num_brs);
+
 	pdev = of_find_device_by_node(np);
 	of_node_put(np);
 	if (!pdev)
@@ -407,23 +583,41 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp)
 	 /* The VSP2D (Gen3) has 5 RPFs, but the VSP1D (Gen2) is limited to
 	  * 4 RPFs.
 	  */
-	vsp->num_planes = rcdu->info->gen >= 3 ? 5 : 4;
+	vsp->num_planes = rcdu->info->vsp_num;
 
 	vsp->planes = devm_kcalloc(rcdu->dev, vsp->num_planes,
 				   sizeof(*vsp->planes), GFP_KERNEL);
 	if (!vsp->planes)
 		return -ENOMEM;
 
+	share_num = vsp->num_planes - vsp->num_brs;
+
 	for (i = 0; i < vsp->num_planes; ++i) {
-		enum drm_plane_type type = i ? DRM_PLANE_TYPE_OVERLAY
-					 : DRM_PLANE_TYPE_PRIMARY;
 		struct rcar_du_vsp_plane *plane = &vsp->planes[i];
+		enum drm_plane_type type;
+
+		if ((rcar_du_has(rcdu, RCAR_DU_FEATURE_VSPDL_SOURCE)) &&
+			(vsp->index == 0)) {
+
+			if (i < share_num)
+				possible_crtcs = 1 << vsp->index;
+			else
+				possible_crtcs =
+					 1 << rcdu->info->vspdl_pair_ch;
+
+			type = (i == 0) || (i == share_num) ?
+			       DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
+		} else {
+			possible_crtcs = 1 << vsp->index;
+			type = i ? DRM_PLANE_TYPE_OVERLAY
+					 : DRM_PLANE_TYPE_PRIMARY;
+		}
 
 		plane->vsp = vsp;
 		plane->index = i;
 
 		ret = drm_universal_plane_init(rcdu->ddev, &plane->plane,
-					       1 << vsp->index,
+					       possible_crtcs,
 					       &rcar_du_vsp_plane_funcs,
 					       formats_kms,
 					       ARRAY_SIZE(formats_kms), type,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
index bbb4161..3fd9cef 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
@@ -1,7 +1,7 @@
 /*
  * rcar_du_vsp.h  --  R-Car Display Unit VSP-Based Compositor
  *
- * Copyright (C) 2015 Renesas Electronics Corporation
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -32,6 +32,7 @@ struct rcar_du_vsp {
 	struct rcar_du_device *dev;
 	struct rcar_du_vsp_plane *planes;
 	unsigned int num_planes;
+	unsigned int num_brs;
 };
 
 static inline struct rcar_du_vsp_plane *to_rcar_vsp_plane(struct drm_plane *p)
@@ -69,12 +70,20 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc);
 void rcar_du_vsp_disable(struct rcar_du_crtc *crtc);
 void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc);
 void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc);
+int rcar_du_set_vmute(struct drm_device *dev, void *data,
+			struct drm_file *file_priv);
+int rcar_du_vsp_write_back(struct drm_device *dev, void *data,
+			struct drm_file *file_priv);
 #else
 static inline int rcar_du_vsp_init(struct rcar_du_vsp *vsp) { return 0; };
 static inline void rcar_du_vsp_enable(struct rcar_du_crtc *crtc) { };
 static inline void rcar_du_vsp_disable(struct rcar_du_crtc *crtc) { };
 static inline void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc) { };
 static inline void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc) { };
+static inline int rcar_du_set_vmute(struct drm_device *dev, void *data,
+			struct drm_file *file_priv) { return 0; };
+static inline int rcar_du_vsp_write_back(struct drm_device *dev, void *data,
+			struct drm_file *file_priv) { return 0; };
 #endif
 
 #endif /* __RCAR_DU_VSP_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
index d7d294b..3c019d1 100644
--- a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
+++ b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
@@ -1,7 +1,7 @@
 /*
  * rcar_lvds_regs.h  --  R-Car LVDS Interface Registers Definitions
  *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -76,4 +76,8 @@
 #define LVDCHCR_CHSEL_CH(n, c)		((((c) - (n)) & 3) << ((n) * 4))
 #define LVDCHCR_CHSEL_MASK(n)		(3 << ((n) * 4))
 
+#define SRCR7		0xE61501CC
+#define SRSTCLR7	0xE615095C
+#define SRCR7_LVDS	(1 << 27)
+
 #endif /* __RCAR_LVDS_REGS_H__ */
diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig
index 73a4016..f0c0a35 100644
--- a/drivers/hwspinlock/Kconfig
+++ b/drivers/hwspinlock/Kconfig
@@ -53,4 +53,17 @@
 
 	  If unsure, say N.
 
+config HWSPINLOCK_RCAR
+	bool "R-Car Hardware Spinlock functionality"
+	depends on ARCH_RENESAS
+	select HWSPINLOCK
+	default y
+	help
+	  Say y here to support the R-Car Hardware Spinlock functionality, which
+	  provides a synchronisation mechanism for the various processor on the
+	  SoC.
+	  This function is implemented with MFIS device.
+
+	  If unsure, say N.
+
 endmenu
diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile
index 6b59cb5a..4ee4001 100644
--- a/drivers/hwspinlock/Makefile
+++ b/drivers/hwspinlock/Makefile
@@ -6,4 +6,5 @@
 obj-$(CONFIG_HWSPINLOCK_OMAP)		+= omap_hwspinlock.o
 obj-$(CONFIG_HWSPINLOCK_QCOM)		+= qcom_hwspinlock.o
 obj-$(CONFIG_HWSPINLOCK_SIRF)		+= sirf_hwspinlock.o
+obj-$(CONFIG_HWSPINLOCK_RCAR)		+= rcar_hwspinlock.o
 obj-$(CONFIG_HSEM_U8500)		+= u8500_hsem.o
diff --git a/drivers/hwspinlock/rcar_hwspinlock.c b/drivers/hwspinlock/rcar_hwspinlock.c
new file mode 100644
index 0000000..35ba8c1
--- /dev/null
+++ b/drivers/hwspinlock/rcar_hwspinlock.c
@@ -0,0 +1,156 @@
+/*
+ * rcar_hwspinlock.c
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/hwspinlock.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include "hwspinlock_internal.h"
+
+#define RCAR_HWSPINLOCK_NUM	(8)
+
+static int rcar_hwspinlock_trylock(struct hwspinlock *lock)
+{
+	void __iomem *addr = lock->priv;
+
+	return !ioread32(addr);
+}
+
+static void rcar_hwspinlock_unlock(struct hwspinlock *lock)
+{
+	void __iomem *addr = lock->priv;
+
+	iowrite32(0, addr);
+}
+
+static const struct hwspinlock_ops rcar_hwspinlock_ops = {
+	.trylock	= rcar_hwspinlock_trylock,
+	.unlock		= rcar_hwspinlock_unlock,
+};
+
+static const struct of_device_id rcar_hwspinlock_of_match[] = {
+	{ .compatible = "renesas,mfis-lock" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, rcar_hwspinlock_of_match);
+
+static int rcar_hwspinlock_probe(struct platform_device *pdev)
+{
+	int				idx;
+	int				ret = 0;
+	u32 __iomem			*addr;
+	struct hwspinlock_device	*bank;
+	struct hwspinlock		*lock;
+	struct resource			*res = NULL;
+	struct clk			*clock;
+
+	/* enable MFIS clock */
+	clock = of_clk_get(pdev->dev.of_node, 0);
+	if (!clock) {
+		dev_err(&pdev->dev, "Failed to get clock.\n");
+		ret = PTR_ERR(clock);
+		goto out;
+	}
+	clk_prepare_enable(clock);
+
+	pm_runtime_enable(&pdev->dev);
+
+	/* map MFIS register */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	addr = (u32 __iomem *)devm_ioremap_nocache(&pdev->dev,
+					res->start, resource_size(res));
+	if (IS_ERR(addr)) {
+		dev_err(&pdev->dev, "Failed to remap MFIS Lock register.\n");
+		ret = PTR_ERR(addr);
+		goto clk_disable;
+	}
+
+	/* create hwspinlock control info */
+	bank = devm_kzalloc(&pdev->dev,
+			sizeof(*bank) + sizeof(*lock) * RCAR_HWSPINLOCK_NUM,
+			GFP_KERNEL);
+	if (!bank) {
+		dev_err(&pdev->dev, "Failed to allocate memory.\n");
+		ret = PTR_ERR(bank);
+		goto clk_disable;
+	}
+
+	for (idx = 0; idx < RCAR_HWSPINLOCK_NUM; idx++) {
+		lock = &bank->lock[idx];
+		lock->priv = &addr[idx];
+	}
+	platform_set_drvdata(pdev, bank);
+
+	/* register hwspinlock */
+	ret = hwspin_lock_register(bank, &pdev->dev, &rcar_hwspinlock_ops,
+				   0, RCAR_HWSPINLOCK_NUM);
+	if (!ret)
+		goto out;
+
+clk_disable:
+	if (clock)
+		clk_disable_unprepare(clock);
+
+out:
+	return ret;
+}
+
+static int rcar_hwspinlock_remove(struct platform_device *pdev)
+{
+	int		ret;
+	struct clk	*clock = NULL;
+
+	ret = hwspin_lock_unregister(platform_get_drvdata(pdev));
+	if (ret) {
+		dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
+		return ret;
+	}
+
+	pm_runtime_disable(&pdev->dev);
+	clock = of_clk_get(pdev->dev.of_node, 0);
+	if (clock)
+		clk_disable_unprepare(clock);
+
+	return 0;
+}
+
+static struct platform_driver rcar_hwspinlock_driver = {
+	.probe		= rcar_hwspinlock_probe,
+	.remove		= rcar_hwspinlock_remove,
+	.driver		= {
+		.name	= "rcar_hwspinlock",
+		.of_match_table = rcar_hwspinlock_of_match,
+	},
+};
+
+static int __init rcar_hwspinlock_init(void)
+{
+	return platform_driver_register(&rcar_hwspinlock_driver);
+}
+core_initcall(rcar_hwspinlock_init);
+
+static void __exit rcar_hwspinlock_exit(void)
+{
+	platform_driver_unregister(&rcar_hwspinlock_driver);
+}
+module_exit(rcar_hwspinlock_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index 726615e..95c5085 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -33,6 +33,7 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
+#include <linux/soc/renesas/s2ram_ddr_backup.h>
 
 /* register offsets */
 #define ICSCR	0x00	/* slave ctrl */
@@ -147,6 +148,240 @@ struct rcar_i2c_priv {
 
 #define LOOP_TIMEOUT	1024
 
+#ifdef CONFIG_RCAR_DDR_BACKUP
+static struct hw_register i2c0_ip_regs[] = {
+	{"ICSCR",   0x00, 32, 0},
+	{"ICMCR",   0x04, 32, 0},
+	{"ICSIER",  0x10, 32, 0},
+	{"ICMIER",  0x14, 32, 0},
+	{"ICCCR",   0x18, 32, 0},
+	{"ICSAR",   0x1C, 32, 0},
+	{"ICMAR",   0x20, 32, 0},
+	{"ICCCR2",  0x28, 32, 0},
+	{"ICMPR",   0x2C, 32, 0},
+	{"ICHPR",   0x30, 32, 0},
+	{"ICLPR",   0x34, 32, 0},
+	{"ICDMAER", 0x3C, 32, 0},
+	{"ICFBSCR", 0x38, 32, 0},
+};
+
+static struct rcar_ip i2c0_ip = {
+	.ip_name = "I2C0",
+	.base_addr = 0xE6500000,
+	.size = 0x40,
+	.reg_count = ARRAY_SIZE(i2c0_ip_regs),
+	.ip_reg = i2c0_ip_regs,
+};
+
+static struct hw_register i2c1_ip_regs[] = {
+	{"ICSCR",   0x00, 32, 0},
+	{"ICMCR",   0x04, 32, 0},
+	{"ICSIER",  0x10, 32, 0},
+	{"ICMIER",  0x14, 32, 0},
+	{"ICCCR",   0x18, 32, 0},
+	{"ICSAR",   0x1C, 32, 0},
+	{"ICMAR",   0x20, 32, 0},
+	{"ICCCR2",  0x28, 32, 0},
+	{"ICMPR",   0x2C, 32, 0},
+	{"ICHPR",   0x30, 32, 0},
+	{"ICLPR",   0x34, 32, 0},
+	{"ICDMAER", 0x3C, 32, 0},
+	{"ICFBSCR", 0x38, 32, 0},
+};
+
+static struct rcar_ip i2c1_ip = {
+	.ip_name = "I2C1",
+	.base_addr = 0xE6508000,
+	.size = 0x40,
+	.reg_count = ARRAY_SIZE(i2c1_ip_regs),
+	.ip_reg = i2c1_ip_regs,
+};
+
+static struct hw_register i2c2_ip_regs[] = {
+	{"ICSCR",   0x00, 32, 0},
+	{"ICMCR",   0x04, 32, 0},
+	{"ICSIER",  0x10, 32, 0},
+	{"ICMIER",  0x14, 32, 0},
+	{"ICCCR",   0x18, 32, 0},
+	{"ICSAR",   0x1C, 32, 0},
+	{"ICMAR",   0x20, 32, 0},
+	{"ICCCR2",  0x28, 32, 0},
+	{"ICMPR",   0x2C, 32, 0},
+	{"ICHPR",   0x30, 32, 0},
+	{"ICLPR",   0x34, 32, 0},
+	{"ICDMAER", 0x3C, 32, 0},
+	{"ICFBSCR", 0x38, 32, 0},
+};
+
+static struct rcar_ip i2c2_ip = {
+	.ip_name = "I2C2",
+	.base_addr = 0xE6510000,
+	.size = 0x40,
+	.reg_count = ARRAY_SIZE(i2c2_ip_regs),
+	.ip_reg = i2c2_ip_regs,
+};
+
+static struct hw_register i2c3_ip_regs[] = {
+	{"ICSCR",   0x00, 32, 0},
+	{"ICMCR",   0x04, 32, 0},
+	{"ICSIER",  0x10, 32, 0},
+	{"ICMIER",  0x14, 32, 0},
+	{"ICCCR",   0x18, 32, 0},
+	{"ICSAR",   0x1C, 32, 0},
+	{"ICMAR",   0x20, 32, 0},
+	{"ICCCR2",  0x28, 32, 0},
+	{"ICMPR",   0x2C, 32, 0},
+	{"ICHPR",   0x30, 32, 0},
+	{"ICLPR",   0x34, 32, 0},
+	{"ICDMAER", 0x3C, 32, 0},
+	{"ICFBSCR", 0x38, 32, 0},
+};
+
+static struct rcar_ip i2c3_ip = {
+	.ip_name = "I2C3",
+	.base_addr = 0xE66D0000,
+	.size = 0x40,
+	.reg_count = ARRAY_SIZE(i2c3_ip_regs),
+	.ip_reg = i2c3_ip_regs,
+};
+
+static struct hw_register i2c4_ip_regs[] = {
+	{"ICSCR",   0x00, 32, 0},
+	{"ICMCR",   0x04, 32, 0},
+	{"ICSIER",  0x10, 32, 0},
+	{"ICMIER",  0x14, 32, 0},
+	{"ICCCR",   0x18, 32, 0},
+	{"ICSAR",   0x1C, 32, 0},
+	{"ICMAR",   0x20, 32, 0},
+	{"ICCCR2",  0x28, 32, 0},
+	{"ICMPR",   0x2C, 32, 0},
+	{"ICHPR",   0x30, 32, 0},
+	{"ICLPR",   0x34, 32, 0},
+	{"ICDMAER", 0x3C, 32, 0},
+	{"ICFBSCR", 0x38, 32, 0},
+};
+
+static struct rcar_ip i2c4_ip = {
+	.ip_name = "I2C4",
+	.base_addr = 0xE66D8000,
+	.size = 0x40,
+	.reg_count = ARRAY_SIZE(i2c4_ip_regs),
+	.ip_reg = i2c4_ip_regs,
+};
+
+static struct hw_register i2c5_ip_regs[] = {
+	{"ICSCR",   0x00, 32, 0},
+	{"ICMCR",   0x04, 32, 0},
+	{"ICSIER",  0x10, 32, 0},
+	{"ICMIER",  0x14, 32, 0},
+	{"ICCCR",   0x18, 32, 0},
+	{"ICSAR",   0x1C, 32, 0},
+	{"ICMAR",   0x20, 32, 0},
+	{"ICCCR2",  0x28, 32, 0},
+	{"ICMPR",   0x2C, 32, 0},
+	{"ICHPR",   0x30, 32, 0},
+	{"ICLPR",   0x34, 32, 0},
+	{"ICDMAER", 0x3C, 32, 0},
+	{"ICFBSCR", 0x38, 32, 0},
+};
+
+static struct rcar_ip i2c5_ip = {
+	.ip_name = "I2C5",
+	.base_addr = 0xE66E0000,
+	.size = 0x40,
+	.reg_count = ARRAY_SIZE(i2c5_ip_regs),
+	.ip_reg = i2c5_ip_regs,
+};
+
+static struct hw_register i2c6_ip_regs[] = {
+	{"ICSCR",   0x00, 32, 0},
+	{"ICMCR",   0x04, 32, 0},
+	{"ICSIER",  0x10, 32, 0},
+	{"ICMIER",  0x14, 32, 0},
+	{"ICCCR",   0x18, 32, 0},
+	{"ICSAR",   0x1C, 32, 0},
+	{"ICMAR",   0x20, 32, 0},
+	{"ICCCR2",  0x28, 32, 0},
+	{"ICMPR",   0x2C, 32, 0},
+	{"ICHPR",   0x30, 32, 0},
+	{"ICLPR",   0x34, 32, 0},
+	{"ICDMAER", 0x3C, 32, 0},
+	{"ICFBSCR", 0x38, 32, 0},
+};
+
+static struct rcar_ip i2c6_ip = {
+	.ip_name = "I2C6",
+	.base_addr = 0xE66E8000,
+	.size = 0x40,
+	.reg_count = ARRAY_SIZE(i2c6_ip_regs),
+	.ip_reg = i2c6_ip_regs,
+};
+
+struct i2c_ip_info {
+	const char *name;
+	struct rcar_ip *ip;
+};
+
+static struct i2c_ip_info ip_info_tbl[] = {
+	{"e6500000.i2c", &i2c0_ip},
+	{"e6508000.i2c", &i2c1_ip},
+	{"e6510000.i2c", &i2c2_ip},
+	{"e66d0000.i2c", &i2c3_ip},
+	{"e66d8000.i2c", &i2c4_ip},
+	{"e66e0000.i2c", &i2c5_ip},
+	{"e66e8000.i2c", &i2c6_ip},
+	{NULL, NULL},
+};
+
+static struct rcar_ip *rcar_i2c_get_ip(const char *name)
+{
+	struct i2c_ip_info *ip_info = ip_info_tbl;
+	struct rcar_ip *ip = NULL;
+
+	while (ip_info->name) {
+		if (!strcmp(ip_info->name, name)) {
+			ip = ip_info->ip;
+			break;
+		}
+		ip_info++;
+	}
+
+	return ip;
+}
+
+static int rcar_i2c_save_regs(struct platform_device *pdev)
+{
+	struct rcar_ip *ip = rcar_i2c_get_ip(pdev->name);
+	int ret = -ENODEV;
+
+	if (ip) {
+		struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
+
+		if (!ip->virt_addr)
+			ip->virt_addr = priv->io;
+
+		ret = rcar_handle_registers(ip, DO_BACKUP);
+		pr_debug("%s: Backup %s register\n", __func__, ip->ip_name);
+	} else
+		pr_err("%s: Failed to find i2c-rcar\n", __func__);
+
+	return ret;
+}
+
+static int rcar_i2c_restore_regs(struct platform_device *pdev)
+{
+	struct rcar_ip *ip = rcar_i2c_get_ip(pdev->name);
+	int ret = -ENODEV;
+
+	if (ip) {
+		ret = rcar_handle_registers(ip, DO_RESTORE);
+		pr_debug("%s: Restore %s register\n", __func__, ip->ip_name);
+	} else
+		pr_err("%s: Failed to find i2c-rcar\n", __func__);
+
+	return ret;
+}
+#endif /* CONFIG_RCAR_DDR_BACKUP */
 
 static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
 {
@@ -904,9 +1139,47 @@ static int rcar_i2c_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int rcar_i2c_suspend(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	struct platform_device *pdev = to_platform_device(dev);
+
+	pm_runtime_get_sync(dev);
+	ret = rcar_i2c_save_regs(pdev);
+	pm_runtime_put(dev);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+	return ret;
+}
+
+static int rcar_i2c_resume(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	struct platform_device *pdev = to_platform_device(dev);
+
+	pm_runtime_get_sync(dev);
+	ret = rcar_i2c_restore_regs(pdev);
+	pm_runtime_put(dev);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+	return ret;
+}
+
+static const struct dev_pm_ops rcar_i2c_pm_ops = {
+	.suspend = rcar_i2c_suspend,
+	.resume_early = rcar_i2c_resume,
+};
+
+#define DEV_PM_OPS (&rcar_i2c_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
 static struct platform_driver rcar_i2c_driver = {
 	.driver	= {
 		.name	= "i2c-rcar",
+		.pm	= DEV_PM_OPS,
 		.of_match_table = rcar_i2c_dt_ids,
 	},
 	.probe		= rcar_i2c_probe,
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index 192f36f0..df33426 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -34,6 +34,7 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
+#include <linux/soc/renesas/s2ram_ddr_backup.h>
 
 /* Transmit operation:                                                      */
 /*                                                                          */
@@ -153,6 +154,26 @@ struct sh_mobile_dt_config {
 	void (*setup)(struct sh_mobile_i2c_data *pd);
 };
 
+#ifdef CONFIG_RCAR_DDR_BACKUP
+static struct hw_register i2c_dvfs_ip_regs[] = {
+	{"ICDR",   0x00, 8, 0},
+	{"ICCR",   0x04, 8, 0},
+	{"ICIC",   0x0C, 8, 0},
+	{"ICCL",   0x10, 8, 0},
+	{"ICCH",   0x14, 8, 0},
+	{"ICTC",   0x28, 8, 0},
+	{"ICVCON", 0x6C, 8, 0},
+};
+
+static struct rcar_ip i2c_dvfs_ip = {
+	.ip_name = "I2C_DVFS",
+	.base_addr = 0xE60B0000,
+	.size = 0x70,
+	.reg_count = ARRAY_SIZE(i2c_dvfs_ip_regs),
+	.ip_reg = i2c_dvfs_ip_regs,
+};
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+
 #define IIC_FLAG_HAS_ICIC67	(1 << 0)
 
 #define STANDARD_MODE		100000
@@ -836,6 +857,7 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = {
 	{ .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config },
 	{ .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config },
 	{ .compatible = "renesas,iic-r8a7795", .data = &fast_clock_dt_config },
+	{ .compatible = "renesas,iic-r8a7796", .data = &fast_clock_dt_config },
 	{ .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config },
 	{},
 };
@@ -1011,9 +1033,42 @@ static int sh_mobile_i2c_runtime_nop(struct device *dev)
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int sh_mobile_i2c_suspend(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	struct sh_mobile_i2c_data *i2c_data = dev_get_drvdata(dev);
+
+	if (!i2c_dvfs_ip.virt_addr)
+		i2c_dvfs_ip.virt_addr = i2c_data->reg;
+	pm_runtime_get_sync(dev);
+	ret = rcar_handle_registers(&i2c_dvfs_ip, DO_BACKUP);
+	pm_runtime_put(dev);
+#endif /* CONFIG_RCAR_DDR_BACKUP  */
+	return ret;
+}
+
+static int sh_mobile_i2c_resume(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	pm_runtime_get_sync(dev);
+	ret = rcar_handle_registers(&i2c_dvfs_ip, DO_RESTORE);
+	pm_runtime_put(dev);
+#endif /* CONFIG_RCAR_DDR_BACKUP  */
+	return ret;
+}
+
+#else
+#define sh_mobile_i2c_suspend	NULL
+#define sh_mobile_i2c_resume	NULL
+#endif /* CONFIG_PM_SLEEP */
+
 static const struct dev_pm_ops sh_mobile_i2c_dev_pm_ops = {
-	.runtime_suspend = sh_mobile_i2c_runtime_nop,
-	.runtime_resume = sh_mobile_i2c_runtime_nop,
+	SET_SYSTEM_SLEEP_PM_OPS(sh_mobile_i2c_suspend, sh_mobile_i2c_resume)
+	SET_RUNTIME_PM_OPS(sh_mobile_i2c_runtime_nop, sh_mobile_i2c_runtime_nop,
+			NULL)
 };
 
 static struct platform_driver sh_mobile_i2c_driver = {
@@ -1030,7 +1085,7 @@ static int __init sh_mobile_i2c_adap_init(void)
 {
 	return platform_driver_register(&sh_mobile_i2c_driver);
 }
-subsys_initcall(sh_mobile_i2c_adap_init);
+subsys_initcall_sync(sh_mobile_i2c_adap_init);
 
 static void __exit sh_mobile_i2c_adap_exit(void)
 {
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c
index 2b92116..2f516ea 100644
--- a/drivers/iommu/ipmmu-vmsa.c
+++ b/drivers/iommu/ipmmu-vmsa.c
@@ -41,6 +41,14 @@ struct ipmmu_features {
 	bool twobit_imttbcr_sl0;
 };
 
+#ifdef CONFIG_RCAR_DDR_BACKUP
+struct hw_register {
+	char *reg_name;
+	unsigned int reg_offset;
+	unsigned int reg_data;
+};
+#endif
+
 struct ipmmu_vmsa_device {
 	struct device *dev;
 	void __iomem *base;
@@ -52,6 +60,9 @@ struct ipmmu_vmsa_device {
 	spinlock_t lock;			/* Protects ctx and domains[] */
 	DECLARE_BITMAP(ctx, IPMMU_CTX_MAX);
 	struct ipmmu_vmsa_domain *domains[IPMMU_CTX_MAX];
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	struct hw_register *reg_backup[IPMMU_CTX_MAX];
+#endif
 
 	struct dma_iommu_mapping *mapping;
 };
@@ -74,6 +85,10 @@ struct ipmmu_vmsa_archdata {
 	unsigned int num_utlbs;
 	struct device *dev;
 	struct list_head list;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	unsigned int *utlbs_val;
+	unsigned int *asids_val;
+#endif
 };
 
 static DEFINE_SPINLOCK(ipmmu_devices_lock);
@@ -215,6 +230,7 @@ static void set_archdata(struct device *dev, struct ipmmu_vmsa_archdata *p)
 #define IMPMBD(n)			(0x02c0 + ((n) * 4))
 
 #define IMUCTR(n)			(0x0300 + ((n) * 16))
+#define IMUCTR2(n)			(0x0600 + ((n) * 16))
 #define IMUCTR_FIXADDEN			(1 << 31)
 #define IMUCTR_FIXADD_MASK		(0xff << 16)
 #define IMUCTR_FIXADD_SHIFT		16
@@ -225,11 +241,114 @@ static void set_archdata(struct device *dev, struct ipmmu_vmsa_archdata *p)
 #define IMUCTR_MMUEN			(1 << 0)
 
 #define IMUASID(n)			(0x0308 + ((n) * 16))
+#define IMUASID2(n)			(0x0608 + ((n) * 16))
 #define IMUASID_ASID8_MASK		(0xff << 8)
 #define IMUASID_ASID8_SHIFT		8
 #define IMUASID_ASID0_MASK		(0xff << 0)
 #define IMUASID_ASID0_SHIFT		0
 
+#ifdef CONFIG_RCAR_DDR_BACKUP
+#define HW_REGISTER_BACKUP_SIZE		ARRAY_SIZE(root_pgtable0_reg)
+static struct hw_register root_pgtable0_reg[] = {
+	{"IMTTLBR0",	IMTTLBR0,	0},
+	{"IMTTUBR0",	IMTTUBR0,	0},
+	{"IMTTBCR",	IMTTBCR,	0},
+	{"IMTTLBR1",	IMTTLBR1,	0},
+	{"IMTTUBR1",	IMTTUBR1,	0},
+	{"IMMAIR0",	IMMAIR0,	0},
+	{"IMMAIR1",	IMMAIR1,	0},
+	{"IMCTR",	IMCTR,		0},
+};
+
+static struct hw_register root_pgtable1_reg[] = {
+	{"IMTTLBR0",	IMTTLBR0,	0},
+	{"IMTTUBR0",	IMTTUBR0,	0},
+	{"IMTTBCR",	IMTTBCR,	0},
+	{"IMTTLBR1",	IMTTLBR1,	0},
+	{"IMTTUBR1",	IMTTUBR1,	0},
+	{"IMMAIR0",	IMMAIR0,	0},
+	{"IMMAIR1",	IMMAIR1,	0},
+	{"IMCTR",	IMCTR,		0},
+};
+
+static struct hw_register root_pgtable2_reg[] = {
+	{"IMTTLBR0",	IMTTLBR0,	0},
+	{"IMTTUBR0",	IMTTUBR0,	0},
+	{"IMTTBCR",	IMTTBCR,	0},
+	{"IMTTLBR1",	IMTTLBR1,	0},
+	{"IMTTUBR1",	IMTTUBR1,	0},
+	{"IMMAIR0",	IMMAIR0,	0},
+	{"IMMAIR1",	IMMAIR1,	0},
+	{"IMCTR",	IMCTR,		0},
+};
+
+static struct hw_register root_pgtable3_reg[] = {
+	{"IMTTLBR0",	IMTTLBR0,	0},
+	{"IMTTUBR0",	IMTTUBR0,	0},
+	{"IMTTBCR",	IMTTBCR,	0},
+	{"IMTTLBR1",	IMTTLBR1,	0},
+	{"IMTTUBR1",	IMTTUBR1,	0},
+	{"IMMAIR0",	IMMAIR0,	0},
+	{"IMMAIR1",	IMMAIR1,	0},
+	{"IMCTR",	IMCTR,		0},
+};
+
+static struct hw_register root_pgtable4_reg[] = {
+	{"IMTTLBR0",	IMTTLBR0,	0},
+	{"IMTTUBR0",	IMTTUBR0,	0},
+	{"IMTTBCR",	IMTTBCR,	0},
+	{"IMTTLBR1",	IMTTLBR1,	0},
+	{"IMTTUBR1",	IMTTUBR1,	0},
+	{"IMMAIR0",	IMMAIR0,	0},
+	{"IMMAIR1",	IMMAIR1,	0},
+	{"IMCTR",	IMCTR,		0},
+};
+
+static struct hw_register root_pgtable5_reg[] = {
+	{"IMTTLBR0",	IMTTLBR0,	0},
+	{"IMTTUBR0",	IMTTUBR0,	0},
+	{"IMTTBCR",	IMTTBCR,	0},
+	{"IMTTLBR1",	IMTTLBR1,	0},
+	{"IMTTUBR1",	IMTTUBR1,	0},
+	{"IMMAIR0",	IMMAIR0,	0},
+	{"IMMAIR1",	IMMAIR1,	0},
+	{"IMCTR",	IMCTR,		0},
+};
+
+static struct hw_register root_pgtable6_reg[] = {
+	{"IMTTLBR0",	IMTTLBR0,	0},
+	{"IMTTUBR0",	IMTTUBR0,	0},
+	{"IMTTBCR",	IMTTBCR,	0},
+	{"IMTTLBR1",	IMTTLBR1,	0},
+	{"IMTTUBR1",	IMTTUBR1,	0},
+	{"IMMAIR0",	IMMAIR0,	0},
+	{"IMMAIR1",	IMMAIR1,	0},
+	{"IMCTR",	IMCTR,		0},
+};
+
+static struct hw_register root_pgtable7_reg[] = {
+	{"IMTTLBR0",	IMTTLBR0,	0},
+	{"IMTTUBR0",	IMTTUBR0,	0},
+	{"IMTTBCR",	IMTTBCR,	0},
+	{"IMTTLBR1",	IMTTLBR1,	0},
+	{"IMTTUBR1",	IMTTUBR1,	0},
+	{"IMMAIR0",	IMMAIR0,	0},
+	{"IMMAIR1",	IMMAIR1,	0},
+	{"IMCTR",	IMCTR,		0},
+};
+
+static struct hw_register *root_pgtable[IPMMU_CTX_MAX] = {
+	root_pgtable0_reg,
+	root_pgtable1_reg,
+	root_pgtable2_reg,
+	root_pgtable3_reg,
+	root_pgtable4_reg,
+	root_pgtable5_reg,
+	root_pgtable6_reg,
+	root_pgtable7_reg,
+};
+
+#endif
 /* -----------------------------------------------------------------------------
  * Root device handling
  */
@@ -334,6 +453,7 @@ static void ipmmu_utlb_enable(struct ipmmu_vmsa_domain *domain,
 			      unsigned int utlb)
 {
 	struct ipmmu_vmsa_device *mmu = domain->mmu;
+	unsigned int offset;
 
 	/*
 	 * TODO: Reference-count the microTLB as several bus masters can be
@@ -341,9 +461,12 @@ static void ipmmu_utlb_enable(struct ipmmu_vmsa_domain *domain,
 	 */
 
 	/* TODO: What should we set the ASID to ? */
-	ipmmu_write(mmu, IMUASID(utlb), 0);
+	offset = (utlb < 32) ? IMUASID(utlb) : IMUASID2(utlb - 32);
+	ipmmu_write(mmu, offset, 0);
+
 	/* TODO: Do we need to flush the microTLB ? */
-	ipmmu_write(mmu, IMUCTR(utlb),
+	offset = (utlb < 32) ? IMUCTR(utlb) : IMUCTR2(utlb - 32);
+	ipmmu_write(mmu, offset,
 		    IMUCTR_TTSEL_MMU(domain->context_id) | IMUCTR_FLUSH |
 		    IMUCTR_MMUEN);
 }
@@ -355,8 +478,10 @@ static void ipmmu_utlb_disable(struct ipmmu_vmsa_domain *domain,
 			       unsigned int utlb)
 {
 	struct ipmmu_vmsa_device *mmu = domain->mmu;
+	unsigned int offset;
 
-	ipmmu_write(mmu, IMUCTR(utlb), 0);
+	offset = (utlb < 32) ? IMUCTR(utlb) : IMUCTR2(utlb - 32);
+	ipmmu_write(mmu, offset, 0);
 }
 
 static void ipmmu_tlb_flush_all(void *cookie)
@@ -445,6 +570,9 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain)
 	}
 
 	domain->context_id = ret;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	domain->root->reg_backup[ret] = root_pgtable[ret];
+#endif
 
 	/* TTBR0 */
 	ttbr = domain->cfg.arm_lpae_s1_cfg.ttbr[0];
@@ -516,6 +644,11 @@ static void ipmmu_domain_destroy_context(struct ipmmu_vmsa_domain *domain)
 	 */
 	ipmmu_ctx_write2(domain, IMCTR, IMCTR_FLUSH);
 	ipmmu_tlb_sync(domain);
+
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	domain->root->reg_backup[domain->context_id] = NULL;
+#endif
+
 	ipmmu_domain_free_context(domain->root, domain->context_id);
 }
 
@@ -625,6 +758,95 @@ static void ipmmu_domain_free(struct iommu_domain *io_domain)
 	kfree(domain);
 }
 
+/* Hack to identity map address ranges needed by the DMAC hardware */
+static struct {
+	phys_addr_t base;
+	size_t size;
+} dmac_workaround[] = {
+	{
+		.base = 0xe6e60000, /* SCIF0 */
+		.size = SZ_4K,
+	},
+	{
+		.base = 0xe6e68000, /* SCIF1 */
+		.size = SZ_4K,
+	},
+	/* DMA transfer of SCIF2 is not supported */
+	{
+		.base = 0xe6c50000, /* SCIF3 */
+		.size = SZ_4K,
+	},
+	{
+		.base = 0xe6c40000, /* SCIF4 */
+		.size = SZ_4K,
+	},
+	{
+		.base = 0xe6f30000, /* SCIF5 */
+		.size = SZ_4K,
+	},
+	{
+		.base = 0xe6540000, /* HSCIF0 */
+		.size = SZ_4K,
+	},
+	{
+		.base = 0xe6550000, /* HSCIF1 */
+		.size = SZ_4K,
+	},
+	{
+		.base = 0xe6560000, /* HSCIF2 */
+		.size = SZ_4K,
+	},
+	{
+		.base = 0xe66a0000, /* HSCIF3 */
+		.size = SZ_4K,
+	},
+	{
+		.base = 0xe66b0000, /* HSCIF4 */
+		.size = SZ_4K,
+	},
+	{
+		.base = 0xe6e90000, /* MSIOF0 */
+		.size = SZ_4K,
+	},
+	{
+		.base = 0xe6ea0000, /* MSIOF1 */
+		.size = SZ_4K,
+	},
+	{
+		.base = 0xe6c00000, /* MSIOF2 */
+		.size = SZ_4K,
+	},
+	{
+		.base = 0xe6c10000, /* MSIOF3 */
+		.size = SZ_4K,
+	},
+};
+
+static void ipmmu_workaround(struct iommu_domain *io_domain,
+			     struct device *dev)
+{
+	int k;
+
+	/* only apply workaround for DMA controllers */
+	if (!strstr(dev_name(dev), "dma-controller"))
+		return;
+
+	dev_info(dev, "Adding iommu workaround\n");
+
+	for (k = 0; k < ARRAY_SIZE(dmac_workaround); k++) {
+		phys_addr_t phys_addr;
+
+		phys_addr = iommu_iova_to_phys(io_domain,
+					       dmac_workaround[k].base);
+		if (phys_addr)
+			continue;
+
+		iommu_map(io_domain, dmac_workaround[k].base,
+			  dmac_workaround[k].base, dmac_workaround[k].size,
+			  IOMMU_READ | IOMMU_WRITE);
+	}
+}
+
 static int ipmmu_attach_device(struct iommu_domain *io_domain,
 			       struct device *dev)
 {
@@ -678,6 +900,8 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain,
 	if (ret < 0)
 		return ret;
 
+	ipmmu_workaround(io_domain, dev);
+
 	for (i = 0; i < archdata->num_utlbs; ++i)
 		ipmmu_utlb_enable(domain, archdata->utlbs[i]);
 
@@ -794,6 +1018,9 @@ static int ipmmu_init_platform_device(struct device *dev)
 	struct ipmmu_vmsa_archdata *archdata;
 	struct ipmmu_vmsa_device *mmu;
 	unsigned int *utlbs;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	unsigned int *utlbs_val, *asids_val;
+#endif
 	unsigned int i;
 	int num_utlbs;
 	int ret = -ENODEV;
@@ -809,6 +1036,15 @@ static int ipmmu_init_platform_device(struct device *dev)
 	if (!utlbs)
 		return -ENOMEM;
 
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	utlbs_val = kcalloc(num_utlbs, sizeof(*utlbs_val), GFP_KERNEL);
+	if (!utlbs_val)
+		return -ENOMEM;
+	asids_val = kcalloc(num_utlbs, sizeof(*asids_val), GFP_KERNEL);
+	if (!asids_val)
+		return -ENOMEM;
+#endif
+
 	spin_lock(&ipmmu_devices_lock);
 
 	list_for_each_entry(mmu, &ipmmu_devices, list) {
@@ -842,6 +1078,10 @@ static int ipmmu_init_platform_device(struct device *dev)
 
 	archdata->mmu = mmu;
 	archdata->utlbs = utlbs;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	archdata->utlbs_val = utlbs_val;
+	archdata->asids_val = asids_val;
+#endif
 	archdata->num_utlbs = num_utlbs;
 	archdata->dev = dev;
 	set_archdata(dev, archdata);
@@ -940,6 +1180,10 @@ static void ipmmu_remove_device(struct device *dev)
 	iommu_group_remove_device(dev);
 
 	kfree(archdata->utlbs);
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	kfree(archdata->utlbs_val);
+	kfree(archdata->asids_val);
+#endif
 	kfree(archdata);
 
 	set_archdata(dev, NULL);
@@ -1230,9 +1474,195 @@ static int ipmmu_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_RCAR_DDR_BACKUP
+static int ipmmu_utlbs_backup(struct ipmmu_vmsa_device *mmu)
+{
+	unsigned int i;
+	struct ipmmu_vmsa_device *slave_mmu = NULL;
+	struct ipmmu_vmsa_archdata *slave_dev = NULL;
+
+	pr_debug("%s: Handle UTLB backup\n", dev_name(mmu->dev));
+
+	spin_lock(&ipmmu_slave_devices_lock);
+
+	list_for_each_entry(slave_dev, &ipmmu_slave_devices, list) {
+		slave_mmu = slave_dev->mmu;
+
+		if (slave_mmu != mmu)
+			continue;
+
+		for (i = 0; i < slave_dev->num_utlbs; ++i) {
+			slave_dev->utlbs_val[i] =
+				ipmmu_read(slave_mmu,
+					IMUCTR(slave_dev->utlbs[i]));
+			slave_dev->asids_val[i] =
+				ipmmu_read(slave_mmu,
+					IMUASID(slave_dev->utlbs[i]));
+			pr_debug("%d: Backup UTLB[%d]: 0x%x, ASID[%d]: %d\n",
+				i, slave_dev->utlbs[i], slave_dev->utlbs_val[i],
+				slave_dev->utlbs[i],
+				slave_dev->asids_val[i]);
+		}
+	}
+
+	spin_unlock(&ipmmu_slave_devices_lock);
+
+	return 0;
+}
+
+static int ipmmu_utlbs_restore(struct ipmmu_vmsa_device *mmu)
+{
+	unsigned int i;
+	struct ipmmu_vmsa_device *slave_mmu = NULL;
+	struct ipmmu_vmsa_archdata *slave_dev = NULL;
+
+	pr_debug("%s: Handle UTLB restore\n", dev_name(mmu->dev));
+
+	spin_lock(&ipmmu_slave_devices_lock);
+
+	list_for_each_entry(slave_dev, &ipmmu_slave_devices, list) {
+		slave_mmu = slave_dev->mmu;
+
+		if (slave_mmu != mmu)
+			continue;
+
+		for (i = 0; i < slave_dev->num_utlbs; ++i) {
+			ipmmu_write(slave_mmu, IMUASID(slave_dev->utlbs[i]),
+					slave_dev->asids_val[i]);
+			ipmmu_write(slave_mmu,
+				IMUCTR(slave_dev->utlbs[i]),
+				(slave_dev->utlbs_val[i] | IMUCTR_FLUSH));
+			pr_debug("%d: Restore UTLB[%d]: 0x%x, ASID[%d]: %d\n",
+				i, slave_dev->utlbs[i],
+				ipmmu_read(slave_mmu,
+					IMUCTR(slave_dev->utlbs[i])),
+				slave_dev->utlbs[i],
+				ipmmu_read(slave_mmu,
+				IMUASID(slave_dev->utlbs[i])));
+		}
+	}
+
+	spin_unlock(&ipmmu_slave_devices_lock);
+
+	return 0;
+}
+
+static int ipmmu_domain_backup_context(struct ipmmu_vmsa_domain *domain)
+{
+	struct ipmmu_vmsa_device *mmu = domain->root;
+	struct hw_register *reg = mmu->reg_backup[domain->context_id];
+	unsigned int i;
+
+	pr_info("%s: Handle domain context backup\n", dev_name(mmu->dev));
+
+	for (i = 0; i < HW_REGISTER_BACKUP_SIZE; i++) {
+		reg[i].reg_data = ipmmu_ctx_read(domain, reg[i].reg_offset);
+
+		pr_info("%s: reg_data 0x%x, reg_offset 0x%x\n",
+				reg[i].reg_name,
+				reg[i].reg_data,
+				reg[i].reg_offset);
+	}
+
+	return 0;
+}
+
+static int ipmmu_domain_restore_context(struct ipmmu_vmsa_domain *domain)
+{
+	struct ipmmu_vmsa_device *mmu = domain->root;
+	struct hw_register *reg = mmu->reg_backup[domain->context_id];
+	unsigned int i;
+
+	pr_info("%s: Handle domain context restore\n", dev_name(mmu->dev));
+
+	for (i = 0; i < HW_REGISTER_BACKUP_SIZE; i++) {
+		if (reg[i].reg_offset != IMCTR) {
+			ipmmu_ctx_write(domain,
+				reg[i].reg_offset,
+				reg[i].reg_data);
+
+			pr_info("%s: reg_data 0x%x, reg_offset 0x%x\n",
+				reg[i].reg_name,
+				ipmmu_ctx_read(domain, reg[i].reg_offset),
+				reg[i].reg_offset);
+
+		} else {
+			ipmmu_ctx_write2(domain,
+				reg[i].reg_offset,
+				reg[i].reg_data | IMCTR_FLUSH);
+
+			pr_info("%s: reg_data 0x%x, reg_offset 0x%x\n",
+				reg[i].reg_name,
+				ipmmu_ctx_read(domain,
+					reg[i].reg_offset),
+				reg[i].reg_offset);
+		}
+	}
+
+	return 0;
+}
+#endif
+
+static int ipmmu_suspend(struct device *dev)
+{
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	int ctx;
+	unsigned int i;
+	struct ipmmu_vmsa_device *mmu = dev_get_drvdata(dev);
+
+	pr_debug("%s: %s\n", __func__, dev_name(dev));
+
+	/* Only backup UTLB in IPMMU cache devices*/
+	if (!ipmmu_is_root(mmu))
+		ipmmu_utlbs_backup(mmu);
+
+	ctx = find_first_zero_bit(mmu->ctx, mmu->num_ctx);
+
+	for (i = 0; i < ctx; i++) {
+		pr_info("Handle ctx %d\n", i);
+		ipmmu_domain_backup_context(mmu->domains[i]);
+	}
+#endif
+
+	return 0;
+}
+
+static int ipmmu_resume(struct device *dev)
+{
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	int ctx;
+	unsigned int i;
+	struct ipmmu_vmsa_device *mmu = dev_get_drvdata(dev);
+
+	pr_debug("%s: %s\n", __func__, dev_name(dev));
+
+	ctx = find_first_zero_bit(mmu->ctx, mmu->num_ctx);
+
+	for (i = 0; i < ctx; i++) {
+		pr_info("Handle ctx %d\n", i);
+		ipmmu_domain_restore_context(mmu->domains[i]);
+	}
+
+	/* Only backup UTLB in IPMMU cache devices*/
+	if (!ipmmu_is_root(mmu))
+		ipmmu_utlbs_restore(mmu);
+#endif
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ipmmu_pm_ops,
+			ipmmu_suspend, ipmmu_resume);
+#define DEV_PM_OPS (&ipmmu_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
 static struct platform_driver ipmmu_driver = {
 	.driver = {
 		.name = "ipmmu-vmsa",
+		.pm	= DEV_PM_OPS,
 		.of_match_table = of_match_ptr(ipmmu_of_ids),
 	},
 	.probe = ipmmu_probe,
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 2669b4b..51f89a7 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -204,6 +204,16 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called adv7183.
 
+config VIDEO_ADV7482
+	tristate "Analog Devices ADV7482 decoder"
+	depends on VIDEO_V4L2 && I2C
+	---help---
+	  V4l2 subdevice driver for the Analog Devices
+	  ADV7482 video decoder.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adv7482.
+
 config VIDEO_ADV7604
 	tristate "Analog Devices ADV7604 decoder"
 	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 92773b2..0140e60 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -26,6 +26,7 @@
 obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
 obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
 obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o
+obj-$(CONFIG_VIDEO_ADV7482) += adv7482.o
 obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o
 obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o
 obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o
diff --git a/drivers/media/i2c/adv7482.c b/drivers/media/i2c/adv7482.c
new file mode 100644
index 0000000..f5a9311
--- /dev/null
+++ b/drivers/media/i2c/adv7482.c
@@ -0,0 +1,2414 @@
+/*
+ * drivers/media/i2c/adv7482.c
+ *     This file is Analog Devices ADV7482 HDMI receiver driver.
+ *
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-of.h>
+#include <linux/mutex.h>
+#include <media/soc_camera.h>
+
+#include <linux/delay.h>  /* for msleep() */
+
+#define DRIVER_NAME "adv7482"
+
+/****************************************/
+/* ADV7482 I2C slave address definition */
+/****************************************/
+#define ADV7482_I2C_IO			0x70	/* IO Map */
+#define ADV7482_I2C_DPLL		0x26	/* DPLL Map */
+#define ADV7482_I2C_CP			0x22	/* CP Map */
+#define ADV7482_I2C_HDMI		0x34	/* HDMI Map */
+#define ADV7482_I2C_EDID		0x36	/* EDID Map */
+#define ADV7482_I2C_REPEATER		0x32	/* HDMI RX Repeater Map */
+#define ADV7482_I2C_INFOFRAME		0x31	/* HDMI RX InfoFrame Map */
+#define ADV7482_I2C_CEC			0x41	/* CEC Map */
+#define ADV7482_I2C_SDP			0x79	/* SDP Map */
+#define ADV7482_I2C_TXB			0x48	/* CSI-TXB Map */
+#define ADV7482_I2C_TXA			0x4A	/* CSI-TXA Map */
+#define ADV7482_I2C_WAIT		0xFE	/* Wait x mesec */
+#define ADV7482_I2C_EOR			0xFF	/* End Mark */
+
+/****************************************/
+/* ADV7482 IO register definition       */
+/****************************************/
+
+/* Revision */
+#define ADV7482_IO_RD_INFO1_REG	0xDF	/* chip version register */
+#define ADV7482_IO_RD_INFO2_REG	0xE0	/* chip version register */
+
+#define ADV7482_IO_CP_DATAPATH_REG	0x03	/* datapath ctrl */
+#define ADV7482_IO_CP_COLORSPACE_REG	0x04
+#define ADV7482_IO_CP_VID_STD_REG	0x05	/* Video Standard */
+
+/* Power Management */
+#define ADV7482_IO_PWR_MAN_REG	0x0C	/* Power management register */
+#define ADV7482_IO_PWR_ON		0xE0	/* Power on */
+#define ADV7482_IO_PWR_OFF		0x00	/* Power down */
+
+#define ADV7482_HDMI_DDC_PWRDN	0x73	/* Power DDC pads control register */
+#define ADV7482_HDMI_DDC_PWR_ON		0x00	/* Power on */
+#define ADV7482_HDMI_DDC_PWR_OFF	0x01	/* Power down */
+
+#define ADV7482_IO_CP_VID_STD_480I	0x40
+#define ADV7482_IO_CP_VID_STD_576I	0x41
+#define ADV7482_IO_CP_VID_STD_480P	0x4A
+#define ADV7482_IO_CP_VID_STD_576P	0x4B
+#define ADV7482_IO_CP_VID_STD_720P	0x53
+#define ADV7482_IO_CP_VID_STD_1080I	0x54
+#define ADV7482_IO_CP_VID_STD_1080P	0x5E
+#define ADV7482_IO_CP_VID_STD_SVGA56	0x80
+#define ADV7482_IO_CP_VID_STD_SVGA60	0x81
+#define ADV7482_IO_CP_VID_STD_SVGA72	0x82
+#define ADV7482_IO_CP_VID_STD_SVGA75	0x83
+#define ADV7482_IO_CP_VID_STD_SVGA85	0x84
+#define ADV7482_IO_CP_VID_STD_SXGA60	0x85
+#define ADV7482_IO_CP_VID_STD_SXGA75	0x86
+#define ADV7482_IO_CP_VID_STD_VGA60	0x88
+#define ADV7482_IO_CP_VID_STD_VGA72	0x89
+#define ADV7482_IO_CP_VID_STD_VGA75	0x8A
+#define ADV7482_IO_CP_VID_STD_VGA85	0x8B
+#define ADV7482_IO_CP_VID_STD_XGA60	0x8C
+#define ADV7482_IO_CP_VID_STD_XGA70	0x8D
+#define ADV7482_IO_CP_VID_STD_XGA75	0x8E
+#define ADV7482_IO_CP_VID_STD_XGA85	0x8F
+#define ADV7482_IO_CP_VID_STD_UXGA60	0x96
+
+#define ADV7482_IO_CSI4_EN_ENABLE       0x80
+#define ADV7482_IO_CSI4_EN_DISABLE      0x00
+
+#define ADV7482_IO_CSI1_EN_ENABLE       0x40
+#define ADV7482_IO_CSI1_EN_DISABLE      0x00
+
+/****************************************/
+/* ADV7482 CP register definition       */
+/****************************************/
+
+/* Contrast Control */
+#define ADV7482_CP_CON_REG	0x3a	/* Contrast (unsigned) */
+#define ADV7482_CP_CON_MIN	0	/* Minimum contrast */
+#define ADV7482_CP_CON_DEF	128	/* Default */
+#define ADV7482_CP_CON_MAX	255	/* Maximum contrast */
+
+/* Saturation Control */
+#define ADV7482_CP_SAT_REG	0x3b	/* Saturation (unsigned) */
+#define ADV7482_CP_SAT_MIN	0	/* Minimum saturation */
+#define ADV7482_CP_SAT_DEF	128	/* Default */
+#define ADV7482_CP_SAT_MAX	255	/* Maximum saturation */
+
+/* Brightness Control */
+#define ADV7482_CP_BRI_REG	0x3c	/* Brightness (signed) */
+#define ADV7482_CP_BRI_MIN	-128	/* Luma is -512d */
+#define ADV7482_CP_BRI_DEF	0	/* Luma is 0 */
+#define ADV7482_CP_BRI_MAX	127	/* Luma is 508d */
+
+/* Hue Control */
+#define ADV7482_CP_HUE_REG	0x3d	/* Hue (unsigned) */
+#define ADV7482_CP_HUE_MIN	0	/* -90 degree */
+#define ADV7482_CP_HUE_DEF	0	/* -90 degree */
+#define ADV7482_CP_HUE_MAX	255	/* +90 degree */
+
+/* Video adjustment register */
+#define ADV7482_CP_VID_ADJ_REG		0x3e
+/* Video adjustment mask */
+#define ADV7482_CP_VID_ADJ_MASK		0x7F
+/* Enable color controls */
+#define ADV7482_CP_VID_ADJ_ENABLE	0x80
+
+/****************************************/
+/* ADV7482 HDMI register definition     */
+/****************************************/
+
+/* HDMI status register */
+#define ADV7482_HDMI_STATUS1_REG		0x07
+/* VERT_FILTER_LOCKED flag */
+#define ADV7482_HDMI_VF_LOCKED_FLG		0x80
+/* DE_REGEN_FILTER_LOCKED flag */
+#define ADV7482_HDMI_DERF_LOCKED_FLG		0x20
+/* LINE_WIDTH[12:8] mask */
+#define ADV7482_HDMI_LWIDTH_MSBS_MASK		0x1F
+
+/* LINE_WIDTH[7:0] register */
+#define ADV7482_HDMI_LWIDTH_REG			0x08
+
+/* FIELD0_HEIGHT[12:8] register */
+#define ADV7482_HDMI_F0HEIGHT_MSBS_REG		0x09
+/* FIELD0_HEIGHT[12:8] mask */
+#define ADV7482_HDMI_F0HEIGHT_MSBS_MASK		0x1F
+
+/* FIELD0_HEIGHT[7:0] register */
+#define ADV7482_HDMI_F0HEIGHT_LSBS_REG		0x0A
+
+/* HDMI status register */
+#define ADV7482_HDMI_STATUS2_REG		0x0B
+/* DEEP_COLOR_MODE[1:0] mask */
+#define ADV7482_HDMI_DCM_MASK			0xC0
+/* HDMI_INTERLACED flag */
+#define ADV7482_HDMI_IP_FLAG			0x20
+/* FIELD1_HEIGHT[12:8] mask */
+#define ADV7482_HDMI_F1HEIGHT_MSBS_MASK		0x1F
+
+/* FIELD1_HEIGHT[7:0] register */
+#define ADV7482_HDMI_F1HEIGHT_REG		0x0C
+
+struct adv7482_sdp_main_info {
+	u8			status_reg_10;
+};
+
+/****************************************/
+/* ADV7482 SDP register definition      */
+/****************************************/
+
+#define ADV7482_SDP_MAIN_MAP	0x00
+#define ADV7482_SDP_SUB_MAP1	0x20
+#define ADV7482_SDP_SUB_MAP2	0x40
+
+#define ADV7482_SDP_NO_RO_MAIN_MAP	0x00
+#define ADV7482_SDP_RO_MAIN_MAP		0x01
+#define ADV7482_SDP_RO_SUB_MAP1		0x02
+#define ADV7482_SDP_RO_SUB_MAP2		0x03
+
+#define ADV7482_SDP_MAIN_MAP_RW \
+			(ADV7482_SDP_MAIN_MAP | ADV7482_SDP_NO_RO_MAIN_MAP)
+
+#define ADV7482_SDP_STD_AD_PAL_BG_NTSC_J_SECAM		0x0
+#define ADV7482_SDP_STD_AD_PAL_BG_NTSC_J_SECAM_PED	0x1
+#define ADV7482_SDP_STD_AD_PAL_N_NTSC_J_SECAM		0x2
+#define ADV7482_SDP_STD_AD_PAL_N_NTSC_M_SECAM		0x3
+#define ADV7482_SDP_STD_NTSC_J				0x4
+#define ADV7482_SDP_STD_NTSC_M				0x5
+#define ADV7482_SDP_STD_PAL60				0x6
+#define ADV7482_SDP_STD_NTSC_443			0x7
+#define ADV7482_SDP_STD_PAL_BG				0x8
+#define ADV7482_SDP_STD_PAL_N				0x9
+#define ADV7482_SDP_STD_PAL_M				0xa
+#define ADV7482_SDP_STD_PAL_M_PED			0xb
+#define ADV7482_SDP_STD_PAL_COMB_N			0xc
+#define ADV7482_SDP_STD_PAL_COMB_N_PED			0xd
+#define ADV7482_SDP_STD_PAL_SECAM			0xe
+#define ADV7482_SDP_STD_PAL_SECAM_PED			0xf
+
+#define ADV7482_SDP_REG_INPUT_CONTROL		0x00
+#define ADV7482_SDP_INPUT_CONTROL_INSEL_MASK	0x0f
+
+#define ADV7482_SDP_REG_INPUT_VIDSEL		0x02
+
+#define ADV7482_SDP_REG_CTRL			0x0e
+
+#define ADV7482_SDP_REG_PWR_MAN		0x0f
+#define ADV7482_SDP_PWR_MAN_ON		0x00
+#define ADV7482_SDP_PWR_MAN_OFF		0x20
+#define ADV7482_SDP_PWR_MAN_RES		0x80
+
+/* Contrast */
+#define ADV7482_SDP_REG_CON		0x08	/*Unsigned */
+#define ADV7482_SDP_CON_MIN		0
+#define ADV7482_SDP_CON_DEF		128
+#define ADV7482_SDP_CON_MAX		255
+/* Brightness*/
+#define ADV7482_SDP_REG_BRI		0x0a	/*Signed */
+#define ADV7482_SDP_BRI_MIN		-128
+#define ADV7482_SDP_BRI_DEF		0
+#define ADV7482_SDP_BRI_MAX		127
+/* Hue */
+#define ADV7482_SDP_REG_HUE		0x0b	/*Signed, inverted */
+#define ADV7482_SDP_HUE_MIN		-127
+#define ADV7482_SDP_HUE_DEF		0
+#define ADV7482_SDP_HUE_MAX		128
+
+/* Saturation */
+#define ADV7482_SDP_REG_SD_SAT_CB	0xe3
+#define ADV7482_SDP_REG_SD_SAT_CR	0xe4
+#define ADV7482_SDP_SAT_MIN		0
+#define ADV7482_SDP_SAT_DEF		128
+#define ADV7482_SDP_SAT_MAX		255
+
+#define ADV7482_SDP_INPUT_CVBS_AIN1		0x00
+#define ADV7482_SDP_INPUT_CVBS_AIN2		0x01
+#define ADV7482_SDP_INPUT_CVBS_AIN3		0x02
+#define ADV7482_SDP_INPUT_CVBS_AIN4		0x03
+#define ADV7482_SDP_INPUT_CVBS_AIN5		0x04
+#define ADV7482_SDP_INPUT_CVBS_AIN6		0x05
+#define ADV7482_SDP_INPUT_CVBS_AIN7		0x06
+#define ADV7482_SDP_INPUT_CVBS_AIN8		0x07
+#define ADV7482_SDP_INPUT_SVIDEO_AIN1_AIN2	0x08
+#define ADV7482_SDP_INPUT_SVIDEO_AIN3_AIN4	0x09
+#define ADV7482_SDP_INPUT_SVIDEO_AIN5_AIN6	0x0a
+#define ADV7482_SDP_INPUT_SVIDEO_AIN7_AIN8	0x0b
+#define ADV7482_SDP_INPUT_YPRPB_AIN1_AIN2_AIN3	0x0c
+#define ADV7482_SDP_INPUT_YPRPB_AIN4_AIN5_AIN6	0x0d
+#define ADV7482_SDP_INPUT_DIFF_CVBS_AIN1_AIN2	0x0e
+#define ADV7482_SDP_INPUT_DIFF_CVBS_AIN3_AIN4	0x0f
+#define ADV7482_SDP_INPUT_DIFF_CVBS_AIN5_AIN6	0x10
+#define ADV7482_SDP_INPUT_DIFF_CVBS_AIN7_AIN8	0x11
+
+#define ADV7482_SDP_R_REG_10			0x10
+#define ADV7482_SDP_R_REG_10_IN_LOCK		0x01
+#define ADV7482_SDP_R_REG_10_FSC_LOCK		0x04
+
+#define ADV7482_SDP_R_REG_10_AUTOD_MASK		0x70
+#define ADV7482_SDP_R_REG_10_AUTOD_NTSM_M_J	0x00
+#define ADV7482_SDP_R_REG_10_AUTOD_NTSC_4_43	0x10
+#define ADV7482_SDP_R_REG_10_AUTOD_PAL_M	0x20
+#define ADV7482_SDP_R_REG_10_AUTOD_PAL_60	0x30
+#define ADV7482_SDP_R_REG_10_AUTOD_PAL_B_G	0x40
+#define ADV7482_SDP_R_REG_10_AUTOD_SECAM	0x50
+#define ADV7482_SDP_R_REG_10_AUTOD_PAL_COMB	0x60
+#define ADV7482_SDP_R_REG_10_AUTOD_SECAM_525	0x70
+
+#define ADV7482_MAX_WIDTH		1920
+#define ADV7482_MAX_HEIGHT		1080
+
+/****************************************/
+/* ADV7482 structure definition         */
+/****************************************/
+
+/* Structure for register values */
+struct adv7482_reg_value {
+	u8 addr;				/* i2c slave address */
+	u8 reg;					/* sub (register) address */
+	u8 value;				/* register value */
+};
+
+#define END_REGISTER_TABLE \
+{ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+
+/* Register default values */
+
+static const struct adv7482_reg_value adv7482_sw_reset[] = {
+
+	{ADV7482_I2C_IO, 0xFF, 0xFF},	/* SW reset */
+	{ADV7482_I2C_WAIT, 0x00, 0x05},	/* delay 5 */
+	{ADV7482_I2C_IO, 0x01, 0x76},	/* ADI Required Write */
+	{ADV7482_I2C_IO, 0xF2, 0x01},	/* Enable I2C Read Auto-Increment */
+
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_set_slave_address[] = {
+
+	/* I2C Slave Address settings */
+	{ADV7482_I2C_IO, 0xF3, ADV7482_I2C_DPLL * 2},	/* DPLL Map */
+	{ADV7482_I2C_IO, 0xF4, ADV7482_I2C_CP * 2},	/* CP Map */
+	{ADV7482_I2C_IO, 0xF5, ADV7482_I2C_HDMI * 2},	/* HDMI Map */
+	{ADV7482_I2C_IO, 0xF6, ADV7482_I2C_EDID * 2},	/* EDID Map */
+	{ADV7482_I2C_IO, 0xF7, ADV7482_I2C_REPEATER * 2},
+						/* HDMI RX Repeater Map */
+	{ADV7482_I2C_IO, 0xF8, ADV7482_I2C_INFOFRAME * 2},
+						/* HDMI RX InfoFrame Map */
+	{ADV7482_I2C_IO, 0xFA, ADV7482_I2C_CEC * 2},	/* CEC Map */
+	{ADV7482_I2C_IO, 0xFB, ADV7482_I2C_SDP * 2},	/* SDP Map */
+	{ADV7482_I2C_IO, 0xFC, ADV7482_I2C_TXB * 2},	/* CSI-TXB Map */
+	{ADV7482_I2C_IO, 0xFD, ADV7482_I2C_TXA * 2},	/* CSI-TXA Map */
+
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+/* Supported Formats For Script Below */
+/* - 01-29 HDMI to MIPI TxA CSI 4-Lane - RGB888: */
+static const struct adv7482_reg_value adv7482_init_txa_4lane[] = {
+	/* I2C Slave Address settings */
+	{ADV7482_I2C_IO, 0xF3, ADV7482_I2C_DPLL * 2},	/* DPLL Map */
+	{ADV7482_I2C_IO, 0xF4, ADV7482_I2C_CP * 2},	/* CP Map */
+	{ADV7482_I2C_IO, 0xF5, ADV7482_I2C_HDMI * 2},	/* HDMI Map */
+	{ADV7482_I2C_IO, 0xF6, ADV7482_I2C_EDID * 2},	/* EDID Map */
+	{ADV7482_I2C_IO, 0xF7, ADV7482_I2C_REPEATER * 2},
+						/* HDMI RX Repeater Map */
+	{ADV7482_I2C_IO, 0xF8, ADV7482_I2C_INFOFRAME * 2},
+						/* HDMI RX InfoFrame Map */
+	{ADV7482_I2C_IO, 0xFA, ADV7482_I2C_CEC * 2},	/* CEC Map */
+	{ADV7482_I2C_IO, 0xFB, ADV7482_I2C_SDP * 2},	/* SDP Map */
+	{ADV7482_I2C_IO, 0xFC, ADV7482_I2C_TXB * 2},	/* CSI-TXB Map */
+	{ADV7482_I2C_IO, 0xFD, ADV7482_I2C_TXA * 2},	/* CSI-TXA Map */
+
+	/* Disable chip powerdown & Enable HDMI Rx block */
+	{ADV7482_I2C_IO, 0x00, 0x40},
+
+	{ADV7482_I2C_REPEATER, 0x40, 0x83},	/* Enable HDCP 1.1 */
+
+	{ADV7482_I2C_HDMI, 0x00, 0x08},	/* Foreground Channel = A */
+	{ADV7482_I2C_HDMI, 0x98, 0xFF},	/* ADI Required Write */
+	{ADV7482_I2C_HDMI, 0x99, 0xA3},	/* ADI Required Write */
+	{ADV7482_I2C_HDMI, 0x9A, 0x00},	/* ADI Required Write */
+	{ADV7482_I2C_HDMI, 0x9B, 0x0A},	/* ADI Required Write */
+	{ADV7482_I2C_HDMI, 0x9D, 0x40},	/* ADI Required Write */
+	{ADV7482_I2C_HDMI, 0xCB, 0x09},	/* ADI Required Write */
+	{ADV7482_I2C_HDMI, 0x3D, 0x10},	/* ADI Required Write */
+	{ADV7482_I2C_HDMI, 0x3E, 0x7B},	/* ADI Required Write */
+	{ADV7482_I2C_HDMI, 0x3F, 0x5E},	/* ADI Required Write */
+	{ADV7482_I2C_HDMI, 0x4E, 0xFE},	/* ADI Required Write */
+	{ADV7482_I2C_HDMI, 0x4F, 0x18},	/* ADI Required Write */
+	{ADV7482_I2C_HDMI, 0x57, 0xA3},	/* ADI Required Write */
+	{ADV7482_I2C_HDMI, 0x58, 0x04},	/* ADI Required Write */
+	{ADV7482_I2C_HDMI, 0x85, 0x10},	/* ADI Required Write */
+
+	{ADV7482_I2C_HDMI, 0x83, 0x00},	/* Enable All Terminations */
+	{ADV7482_I2C_HDMI, 0xA3, 0x01},	/* ADI Required Write */
+	{ADV7482_I2C_HDMI, 0xBE, 0x00},	/* ADI Required Write */
+
+	{ADV7482_I2C_HDMI, 0x6C, 0x01},	/* HPA Manual Enable */
+	{ADV7482_I2C_HDMI, 0xF8, 0x01},	/* HPA Asserted */
+	{ADV7482_I2C_HDMI, 0x0F, 0x00},	/* Audio Mute Speed Set to Fastest */
+					/* (Smallest Step Size) */
+
+	/* EDID */
+	/* Header information(0-19th byte) */
+	{ADV7482_I2C_EDID, 0x00, 0x00},	/* Fixed header pattern */
+	{ADV7482_I2C_EDID, 0x01, 0xFF},
+	{ADV7482_I2C_EDID, 0x02, 0xFF},
+	{ADV7482_I2C_EDID, 0x03, 0xFF},
+	{ADV7482_I2C_EDID, 0x04, 0xFF},
+	{ADV7482_I2C_EDID, 0x05, 0xFF},
+	{ADV7482_I2C_EDID, 0x06, 0xFF},
+	{ADV7482_I2C_EDID, 0x07, 0x00},
+	{ADV7482_I2C_EDID, 0x08, 0x00},	/* Manufacturer ID */
+	{ADV7482_I2C_EDID, 0x09, 0x00},
+	{ADV7482_I2C_EDID, 0x0A, 0x00},	/* Manufacturer product code */
+	{ADV7482_I2C_EDID, 0x0B, 0x00},
+	{ADV7482_I2C_EDID, 0x0C, 0x00},	/* Serial number */
+	{ADV7482_I2C_EDID, 0x0D, 0x00},
+	{ADV7482_I2C_EDID, 0x0E, 0x00},
+	{ADV7482_I2C_EDID, 0x0F, 0x00},
+	{ADV7482_I2C_EDID, 0x10, 0x01},	/* Week of manufacture */
+	{ADV7482_I2C_EDID, 0x11, 0x0C},	/* Year of manufacture */
+	{ADV7482_I2C_EDID, 0x12, 0x01},	/* EDID version */
+	{ADV7482_I2C_EDID, 0x13, 0x03},
+	/* Basic display parameters(20-24th byte) */
+	{ADV7482_I2C_EDID, 0x14, 0x80},	/* Video input parameters bitmap */
+	{ADV7482_I2C_EDID, 0x15, 0x50},	/* Maximum horizontal image size */
+	{ADV7482_I2C_EDID, 0x16, 0x2D},	/* Maximum vertical image size */
+	{ADV7482_I2C_EDID, 0x17, 0x78},	/* Display gamma */
+	{ADV7482_I2C_EDID, 0x18, 0x0A},	/* Supported features bitmap */
+	/* Chromaticity coordinates(25-34th byte) */
+	{ADV7482_I2C_EDID, 0x19, 0x0D},
+	{ADV7482_I2C_EDID, 0x1A, 0xC9},
+	{ADV7482_I2C_EDID, 0x1B, 0xA0},
+	{ADV7482_I2C_EDID, 0x1C, 0x57},
+	{ADV7482_I2C_EDID, 0x1D, 0x47},
+	{ADV7482_I2C_EDID, 0x1E, 0x98},
+	{ADV7482_I2C_EDID, 0x1F, 0x27},
+	{ADV7482_I2C_EDID, 0x20, 0x12},
+	{ADV7482_I2C_EDID, 0x21, 0x48},
+	{ADV7482_I2C_EDID, 0x22, 0x4C},
+	/* Established timing bitmap(35-37th byte) */
+	{ADV7482_I2C_EDID, 0x23, 0x20},
+	{ADV7482_I2C_EDID, 0x24, 0x00},
+	{ADV7482_I2C_EDID, 0x25, 0x00},
+	/* Standard timing information(38-53th byte) */
+	/* Because they are unused, in this field, all values are 0101h. */
+	{ADV7482_I2C_EDID, 0x26, 0x01},
+	{ADV7482_I2C_EDID, 0x27, 0x01},
+	{ADV7482_I2C_EDID, 0x28, 0x01},
+	{ADV7482_I2C_EDID, 0x29, 0x01},
+	{ADV7482_I2C_EDID, 0x2A, 0x01},
+	{ADV7482_I2C_EDID, 0x2B, 0x01},
+	{ADV7482_I2C_EDID, 0x2C, 0x01},
+	{ADV7482_I2C_EDID, 0x2D, 0x01},
+	{ADV7482_I2C_EDID, 0x2E, 0x01},
+	{ADV7482_I2C_EDID, 0x2F, 0x01},
+	{ADV7482_I2C_EDID, 0x30, 0x01},
+	{ADV7482_I2C_EDID, 0x31, 0x01},
+	{ADV7482_I2C_EDID, 0x32, 0x01},
+	{ADV7482_I2C_EDID, 0x33, 0x01},
+	{ADV7482_I2C_EDID, 0x34, 0x01},
+	{ADV7482_I2C_EDID, 0x35, 0x01},
+	/* Descriptor blocks of Descriptor 1(54-71th byte) */
+	{ADV7482_I2C_EDID, 0x36, 0x02},
+	{ADV7482_I2C_EDID, 0x37, 0x3A},
+	{ADV7482_I2C_EDID, 0x38, 0x80},
+	{ADV7482_I2C_EDID, 0x39, 0x18},
+	{ADV7482_I2C_EDID, 0x3A, 0x71},
+	{ADV7482_I2C_EDID, 0x3B, 0x38},
+	{ADV7482_I2C_EDID, 0x3C, 0x2D},
+	{ADV7482_I2C_EDID, 0x3D, 0x40},
+	{ADV7482_I2C_EDID, 0x3E, 0x58},
+	{ADV7482_I2C_EDID, 0x3F, 0x2C},
+	{ADV7482_I2C_EDID, 0x40, 0x45},
+	{ADV7482_I2C_EDID, 0x41, 0x00},
+	{ADV7482_I2C_EDID, 0x42, 0xDC},
+	{ADV7482_I2C_EDID, 0x43, 0x0B},
+	{ADV7482_I2C_EDID, 0x44, 0x11},
+	{ADV7482_I2C_EDID, 0x45, 0x00},
+	{ADV7482_I2C_EDID, 0x46, 0x00},
+	{ADV7482_I2C_EDID, 0x47, 0x1E},
+	/* Descriptor blocks of Descriptor 2(72-89th byte) */
+	{ADV7482_I2C_EDID, 0x48, 0x8C},
+	{ADV7482_I2C_EDID, 0x49, 0x0A},
+	{ADV7482_I2C_EDID, 0x4A, 0xD0},
+	{ADV7482_I2C_EDID, 0x4B, 0x8A},
+	{ADV7482_I2C_EDID, 0x4C, 0x20},
+	{ADV7482_I2C_EDID, 0x4D, 0xE0},
+	{ADV7482_I2C_EDID, 0x4E, 0x2D},
+	{ADV7482_I2C_EDID, 0x4F, 0x10},
+	{ADV7482_I2C_EDID, 0x50, 0x10},
+	{ADV7482_I2C_EDID, 0x51, 0x3E},
+	{ADV7482_I2C_EDID, 0x52, 0x96},
+	{ADV7482_I2C_EDID, 0x53, 0x00},
+	{ADV7482_I2C_EDID, 0x54, 0x58},
+	{ADV7482_I2C_EDID, 0x55, 0xC2},
+	{ADV7482_I2C_EDID, 0x56, 0x21},
+	{ADV7482_I2C_EDID, 0x57, 0x00},
+	{ADV7482_I2C_EDID, 0x58, 0x00},
+	{ADV7482_I2C_EDID, 0x59, 0x18},
+	/* Descriptor blocks of Descriptor 3(90-107th byte) */
+	{ADV7482_I2C_EDID, 0x5A, 0x00},
+	{ADV7482_I2C_EDID, 0x5B, 0x00},
+	{ADV7482_I2C_EDID, 0x5C, 0x00},
+	{ADV7482_I2C_EDID, 0x5D, 0x7C},
+	{ADV7482_I2C_EDID, 0x5E, 0x00},
+	{ADV7482_I2C_EDID, 0x5F, 0x4D},
+	{ADV7482_I2C_EDID, 0x60, 0x59},
+	{ADV7482_I2C_EDID, 0x61, 0x20},
+	{ADV7482_I2C_EDID, 0x62, 0x48},
+	{ADV7482_I2C_EDID, 0x63, 0x44},
+	{ADV7482_I2C_EDID, 0x64, 0x54},
+	{ADV7482_I2C_EDID, 0x65, 0x56},
+	{ADV7482_I2C_EDID, 0x66, 0x0A},
+	{ADV7482_I2C_EDID, 0x67, 0x20},
+	{ADV7482_I2C_EDID, 0x68, 0x20},
+	{ADV7482_I2C_EDID, 0x69, 0x20},
+	{ADV7482_I2C_EDID, 0x6A, 0x20},
+	{ADV7482_I2C_EDID, 0x6B, 0x20},
+	/* Descriptor blocks of Descriptor 4(108-125th byte) */
+	{ADV7482_I2C_EDID, 0x6C, 0x00},
+	{ADV7482_I2C_EDID, 0x6D, 0x00},
+	{ADV7482_I2C_EDID, 0x6E, 0x00},
+	{ADV7482_I2C_EDID, 0x6F, 0x00},
+	{ADV7482_I2C_EDID, 0x70, 0x00},
+	{ADV7482_I2C_EDID, 0x71, 0x00},
+	{ADV7482_I2C_EDID, 0x72, 0x00},
+	{ADV7482_I2C_EDID, 0x73, 0x0A},
+	{ADV7482_I2C_EDID, 0x74, 0x2E},
+	{ADV7482_I2C_EDID, 0x75, 0x06},
+	{ADV7482_I2C_EDID, 0x76, 0x00},
+	{ADV7482_I2C_EDID, 0x77, 0x0A},
+	{ADV7482_I2C_EDID, 0x78, 0x20},
+	{ADV7482_I2C_EDID, 0x79, 0x20},
+	{ADV7482_I2C_EDID, 0x7A, 0x20},
+	{ADV7482_I2C_EDID, 0x7B, 0x20},
+	{ADV7482_I2C_EDID, 0x7C, 0x20},
+	{ADV7482_I2C_EDID, 0x7D, 0x20},
+	/* Number of extensions to follow(126th byte) */
+	{ADV7482_I2C_EDID, 0x7E, 0x01},	/* Extension enable */
+	/* Checksum(127th byte) */
+	{ADV7482_I2C_EDID, 0x7F, 0x75},
+
+	/* CEA EDID Timing Extension Version 3 */
+	{ADV7482_I2C_EDID, 0x80, 0x02},	/* CEA 861 Externsion Block */
+	{ADV7482_I2C_EDID, 0x81, 0x03},
+	{ADV7482_I2C_EDID, 0x82, 0x1E},
+	{ADV7482_I2C_EDID, 0x83, 0x40},
+	{ADV7482_I2C_EDID, 0x84, 0x83},
+	{ADV7482_I2C_EDID, 0x85, 0x7F},
+	{ADV7482_I2C_EDID, 0x86, 0x40},
+	{ADV7482_I2C_EDID, 0x87, 0x05},
+	{ADV7482_I2C_EDID, 0x88, 0x40},
+	{ADV7482_I2C_EDID, 0x89, 0x48},
+	{ADV7482_I2C_EDID, 0x8A, 0x10},	/* 1920x1080p @ 59.94/60 Hz */
+	{ADV7482_I2C_EDID, 0x8B, 0x05},	/* 1920x1080i @ 59.94/60 Hz */
+	{ADV7482_I2C_EDID, 0x8C, 0x04},	/* 1280x720p @ 59.94/60 Hz */
+	{ADV7482_I2C_EDID, 0x8D, 0x01},	/* 640x480p @ 59.94/60 Hz */
+	{ADV7482_I2C_EDID, 0x8E, 0x02},	/* 720x480p @ 59.94/60 Hz */
+	{ADV7482_I2C_EDID, 0x8F, 0x06},	/* 720(1440)x480i @ 59.94/60 Hz */
+	{ADV7482_I2C_EDID, 0x90, 0x15},	/* 720(1440)x576i @ 50 Hz */
+	{ADV7482_I2C_EDID, 0x91, 0x11},	/* 720x576p @ 50 Hz */
+	{ADV7482_I2C_EDID, 0x92, 0x00},
+	{ADV7482_I2C_EDID, 0x93, 0x00},
+	{ADV7482_I2C_EDID, 0x94, 0x00},
+	{ADV7482_I2C_EDID, 0x95, 0x00},
+	{ADV7482_I2C_EDID, 0x96, 0x00},
+	{ADV7482_I2C_EDID, 0x97, 0x00},
+	{ADV7482_I2C_EDID, 0x98, 0x00},
+	{ADV7482_I2C_EDID, 0x99, 0x00},
+	{ADV7482_I2C_EDID, 0x9A, 0x00},
+	{ADV7482_I2C_EDID, 0x9B, 0x00},
+	{ADV7482_I2C_EDID, 0x9C, 0x00},
+	{ADV7482_I2C_EDID, 0x9D, 0x00},
+	{ADV7482_I2C_EDID, 0x9E, 0x00},
+	{ADV7482_I2C_EDID, 0x9F, 0x00},
+	{ADV7482_I2C_EDID, 0xA0, 0x00},
+	{ADV7482_I2C_EDID, 0xA1, 0x00},
+	{ADV7482_I2C_EDID, 0xA2, 0x00},
+	{ADV7482_I2C_EDID, 0xA3, 0x00},
+	{ADV7482_I2C_EDID, 0xA4, 0x00},
+	{ADV7482_I2C_EDID, 0xA5, 0x00},
+	{ADV7482_I2C_EDID, 0xA6, 0x00},
+	{ADV7482_I2C_EDID, 0xA7, 0x00},
+	{ADV7482_I2C_EDID, 0xA8, 0x00},
+	{ADV7482_I2C_EDID, 0xA9, 0x00},
+	{ADV7482_I2C_EDID, 0xAA, 0x00},
+	{ADV7482_I2C_EDID, 0xAB, 0x00},
+	{ADV7482_I2C_EDID, 0xAC, 0x00},
+	{ADV7482_I2C_EDID, 0xAD, 0x00},
+	{ADV7482_I2C_EDID, 0xAE, 0x00},
+	{ADV7482_I2C_EDID, 0xAF, 0x00},
+	{ADV7482_I2C_EDID, 0xB0, 0x00},
+	{ADV7482_I2C_EDID, 0xB1, 0x00},
+	{ADV7482_I2C_EDID, 0xB2, 0x00},
+	{ADV7482_I2C_EDID, 0xB3, 0x00},
+	{ADV7482_I2C_EDID, 0xB4, 0x00},
+	{ADV7482_I2C_EDID, 0xB5, 0x00},
+	{ADV7482_I2C_EDID, 0xB6, 0x00},
+	{ADV7482_I2C_EDID, 0xB7, 0x00},
+	{ADV7482_I2C_EDID, 0xB8, 0x00},
+	{ADV7482_I2C_EDID, 0xB9, 0x00},
+	{ADV7482_I2C_EDID, 0xBA, 0x20},
+	{ADV7482_I2C_EDID, 0xBB, 0x20},
+	{ADV7482_I2C_EDID, 0xBC, 0x20},
+	{ADV7482_I2C_EDID, 0xBD, 0x20},
+	{ADV7482_I2C_EDID, 0xBE, 0x20},
+	{ADV7482_I2C_EDID, 0xBF, 0x20},
+	{ADV7482_I2C_EDID, 0xC0, 0x20},
+	{ADV7482_I2C_EDID, 0xC1, 0x20},
+	{ADV7482_I2C_EDID, 0xC2, 0x20},
+	{ADV7482_I2C_EDID, 0xC3, 0x20},
+	{ADV7482_I2C_EDID, 0xC4, 0x20},
+	{ADV7482_I2C_EDID, 0xC5, 0x20},
+	{ADV7482_I2C_EDID, 0xC6, 0x00},
+	{ADV7482_I2C_EDID, 0xC7, 0x00},
+	{ADV7482_I2C_EDID, 0xC8, 0x00},
+	{ADV7482_I2C_EDID, 0xC9, 0xFF},
+	{ADV7482_I2C_EDID, 0xCA, 0x00},
+	{ADV7482_I2C_EDID, 0xCB, 0x0A},
+	{ADV7482_I2C_EDID, 0xCC, 0x20},
+	{ADV7482_I2C_EDID, 0xCD, 0x20},
+	{ADV7482_I2C_EDID, 0xCE, 0x20},
+	{ADV7482_I2C_EDID, 0xCF, 0x20},
+	{ADV7482_I2C_EDID, 0xD0, 0x20},
+	{ADV7482_I2C_EDID, 0xD1, 0x20},
+	{ADV7482_I2C_EDID, 0xD2, 0x20},
+	{ADV7482_I2C_EDID, 0xD3, 0x20},
+	{ADV7482_I2C_EDID, 0xD4, 0x20},
+	{ADV7482_I2C_EDID, 0xD5, 0x20},
+	{ADV7482_I2C_EDID, 0xD6, 0x20},
+	{ADV7482_I2C_EDID, 0xD7, 0x20},
+	{ADV7482_I2C_EDID, 0xD8, 0x00},
+	{ADV7482_I2C_EDID, 0xD9, 0x00},
+	{ADV7482_I2C_EDID, 0xDA, 0x00},
+	{ADV7482_I2C_EDID, 0xDB, 0x1B},
+	{ADV7482_I2C_EDID, 0xDC, 0x00},
+	{ADV7482_I2C_EDID, 0xDD, 0x0A},
+	{ADV7482_I2C_EDID, 0xDE, 0x20},
+	{ADV7482_I2C_EDID, 0xDF, 0x20},
+	{ADV7482_I2C_EDID, 0xE0, 0x20},
+	{ADV7482_I2C_EDID, 0xE1, 0x20},
+	{ADV7482_I2C_EDID, 0xE2, 0x20},
+	{ADV7482_I2C_EDID, 0xE3, 0x20},
+	{ADV7482_I2C_EDID, 0xE4, 0x20},
+	{ADV7482_I2C_EDID, 0xE5, 0x20},
+	{ADV7482_I2C_EDID, 0xE6, 0x20},
+	{ADV7482_I2C_EDID, 0xE7, 0x20},
+	{ADV7482_I2C_EDID, 0xE8, 0x20},
+	{ADV7482_I2C_EDID, 0xE9, 0x20},
+	{ADV7482_I2C_EDID, 0xEA, 0x00},
+	{ADV7482_I2C_EDID, 0xEB, 0x00},
+	{ADV7482_I2C_EDID, 0xEC, 0x00},
+	{ADV7482_I2C_EDID, 0xED, 0x00},
+	{ADV7482_I2C_EDID, 0xEE, 0x00},
+	{ADV7482_I2C_EDID, 0xEF, 0x00},
+	{ADV7482_I2C_EDID, 0xF0, 0x00},
+	{ADV7482_I2C_EDID, 0xF1, 0x00},
+	{ADV7482_I2C_EDID, 0xF2, 0x00},
+	{ADV7482_I2C_EDID, 0xF3, 0x00},
+	{ADV7482_I2C_EDID, 0xF4, 0x00},
+	{ADV7482_I2C_EDID, 0xF5, 0x00},
+	{ADV7482_I2C_EDID, 0xF6, 0x00},
+	{ADV7482_I2C_EDID, 0xF7, 0x00},
+	{ADV7482_I2C_EDID, 0xF8, 0x00},
+	{ADV7482_I2C_EDID, 0xF9, 0x00},
+	{ADV7482_I2C_EDID, 0xFA, 0x00},
+	{ADV7482_I2C_EDID, 0xFB, 0x00},
+	{ADV7482_I2C_EDID, 0xFC, 0x00},
+	{ADV7482_I2C_EDID, 0xFD, 0x00},
+	{ADV7482_I2C_EDID, 0xFE, 0x00},
+	{ADV7482_I2C_EDID, 0xFF, 0xD8},	/* Set the Most Significant Bit */
+
+	{ADV7482_I2C_REPEATER, 0x70, 0x10},
+					/* EDID image, in 128-byte blocks. */
+	{ADV7482_I2C_REPEATER, 0x74, 0x01},
+				/* Manual enable active for E-EDID on Port A */
+
+	{ADV7482_I2C_IO, 0x04, 0x02},	/* RGB Out of CP */
+	{ADV7482_I2C_IO, 0x12, 0xF0},
+		/* CSC Depends on ip Packets - SDR 444 */
+	{ADV7482_I2C_IO, 0x17, 0x80},
+		/* Luma & Chroma Values Can Reach 254d */
+	{ADV7482_I2C_IO, 0x03, 0x86},	/* CP-Insert_AV_Code */
+
+	{ADV7482_I2C_CP, 0x7C, 0x00},	/* ADI Required Write */
+
+	{ADV7482_I2C_IO, 0x0C, 0xE0},	/* Enable LLC_DLL & Double LLC Timing */
+	{ADV7482_I2C_IO, 0x0E, 0xDD},	/* LLC/PIX/SPI PINS TRISTATED AUD */
+					/* Outputs Enabled */
+	{ADV7482_I2C_IO, 0x10, 0xA0},	/* Enable 4-lane CSI Tx & Pixel Port */
+
+	{ADV7482_I2C_TXA, 0x00, 0x84},	/* Enable 4-lane MIPI */
+	{ADV7482_I2C_TXA, 0x00, 0xA4},	/* Set Auto DPHY Timing */
+	{ADV7482_I2C_TXA, 0xDB, 0x10},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0xD6, 0x07},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0xC4, 0x0A},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0x71, 0x33},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0x72, 0x11},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0xF0, 0x00},	/* i2c_dphy_pwdn - 1'b0 */
+
+	{ADV7482_I2C_TXA, 0x31, 0x82},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0x1E, 0x40},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0xDA, 0x01},	/* i2c_mipi_pll_en - 1'b1 */
+	{ADV7482_I2C_WAIT, 0x00, 0x02},	/* delay 2 */
+	{ADV7482_I2C_TXA, 0x00, 0x24 },	/* Power-up CSI-TX */
+	{ADV7482_I2C_WAIT, 0x00, 0x01},	/* delay 1 */
+	{ADV7482_I2C_TXA, 0xC1, 0x2B},	/* ADI Required Write */
+	{ADV7482_I2C_WAIT, 0x00, 0x01},	/* delay 1 */
+	{ADV7482_I2C_TXA, 0x31, 0x80},	/* ADI Required Write */
+
+	/* for debug */
+#ifdef REL_DGB_FORCE_TO_SEND_COLORBAR
+	{ADV7482_I2C_CP, 0x37, 0x81},	/* Output Colorbars Pattern */
+#endif
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+/* 02-01 Analog CVBS to MIPI TX-B CSI 1-Lane - */
+/* Autodetect CVBS Single Ended In Ain 1 - MIPI Out */
+static const struct adv7482_reg_value adv7482_init_txb_1lane[] = {
+
+	{ADV7482_I2C_IO, 0x00, 0x30},
+		/* Disable chip powerdown - powerdown Rx */
+	{ADV7482_I2C_IO, 0xF2, 0x01},	/* Enable I2C Read Auto-Increment */
+
+	/* I2C Slave Address settings */
+	{ADV7482_I2C_IO, 0xF3, ADV7482_I2C_DPLL * 2},	/* DPLL Map */
+	{ADV7482_I2C_IO, 0xF4, ADV7482_I2C_CP * 2},	/* CP Map */
+	{ADV7482_I2C_IO, 0xF5, ADV7482_I2C_HDMI * 2},	/* HDMI Map */
+	{ADV7482_I2C_IO, 0xF6, ADV7482_I2C_EDID * 2},	/* EDID Map */
+	{ADV7482_I2C_IO, 0xF7, ADV7482_I2C_REPEATER * 2},
+						/* HDMI RX Repeater Map */
+	{ADV7482_I2C_IO, 0xF8, ADV7482_I2C_INFOFRAME * 2},
+						/* HDMI RX InfoFrame Map */
+	{ADV7482_I2C_IO, 0xFA, ADV7482_I2C_CEC * 2},	/* CEC Map */
+	{ADV7482_I2C_IO, 0xFB, ADV7482_I2C_SDP * 2},	/* SDP Map */
+	{ADV7482_I2C_IO, 0xFC, ADV7482_I2C_TXB * 2},	/* CSI-TXB Map */
+	{ADV7482_I2C_IO, 0xFD, ADV7482_I2C_TXA * 2},	/* CSI-TXA Map */
+
+	{ADV7482_I2C_IO, 0x0E, 0xFF},	/* LLC/PIX/AUD/SPI PINS TRISTATED */
+
+	{ADV7482_I2C_SDP, ADV7482_SDP_REG_PWR_MAN, ADV7482_SDP_PWR_MAN_ON},
+		/* Exit Power Down Mode */
+	{ADV7482_I2C_SDP, 0x52, 0xCD},	/* ADI Required Write */
+	{ADV7482_I2C_SDP, ADV7482_SDP_REG_INPUT_CONTROL,
+		ADV7482_SDP_INPUT_CVBS_AIN8},	/* INSEL = CVBS in on Ain 8 */
+	{ADV7482_I2C_SDP, ADV7482_SDP_REG_CTRL, 0x80},
+		/* ADI Required Write */
+	{ADV7482_I2C_SDP, 0x9C, 0x00},	/* ADI Required Write */
+	{ADV7482_I2C_SDP, 0x9C, 0xFF},	/* ADI Required Write */
+	{ADV7482_I2C_SDP, ADV7482_SDP_REG_CTRL, ADV7482_SDP_MAIN_MAP_RW},
+		/* ADI Required Write */
+
+	/* ADI recommended writes for improved video quality */
+	{ADV7482_I2C_SDP, 0x80, 0x51},	/* ADI Required Write */
+	{ADV7482_I2C_SDP, 0x81, 0x51},	/* ADI Required Write */
+	{ADV7482_I2C_SDP, 0x82, 0x68},	/* ADI Required Write */
+
+	{ADV7482_I2C_SDP, 0x03, 0x42},
+		/* Tri-S Output Drivers, PwrDwn 656 pads */
+	{ADV7482_I2C_SDP, 0x04, 0xB5},	/* ITU-R BT.656-4 compatible */
+	{ADV7482_I2C_SDP, 0x13, 0x00},	/* ADI Required Write */
+
+	{ADV7482_I2C_SDP, 0x17, 0x41},	/* Select SH1 */
+	{ADV7482_I2C_SDP, 0x31, 0x12},	/* ADI Required Write */
+	{ADV7482_I2C_SDP, 0xE6, 0x4F},
+		/* Set V bit end position manually in NTSC mode */
+
+#ifdef REL_DGB_FORCE_TO_SEND_COLORBAR
+	{ADV7482_I2C_SDP, 0x0C, 0x01},	/* ColorBar */
+	{ADV7482_I2C_SDP, 0x14, 0x01},	/* ColorBar */
+#endif
+	/* Enable 1-Lane MIPI Tx, */
+	/* enable pixel output and route SD through Pixel port */
+	{ADV7482_I2C_IO, 0x10, 0x70},
+
+	{ADV7482_I2C_TXB, 0x00, 0x81},	/* Enable 1-lane MIPI */
+	{ADV7482_I2C_TXB, 0x00, 0xA1},	/* Set Auto DPHY Timing */
+#if 0 /* If CVBS input only is used, please enable this code. */
+	{ADV7482_I2C_TXA, 0xF0, 0x00},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0xD6, 0x07},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0xC0, 0x3C},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0xC3, 0x3C},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0xC6, 0x3C},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0xC9, 0x3C},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0xCC, 0x3C},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0xD5, 0x03},	/* ADI Required Write */
+#endif
+	{ADV7482_I2C_TXB, 0xD2, 0x40},	/* ADI Required Write */
+	{ADV7482_I2C_TXB, 0xC4, 0x0A},	/* ADI Required Write */
+	{ADV7482_I2C_TXB, 0x71, 0x33},	/* ADI Required Write */
+	{ADV7482_I2C_TXB, 0x72, 0x11},	/* ADI Required Write */
+	{ADV7482_I2C_TXB, 0xF0, 0x00},	/* i2c_dphy_pwdn - 1'b0 */
+	{ADV7482_I2C_TXB, 0x31, 0x82},	/* ADI Required Write */
+	{ADV7482_I2C_TXB, 0x1E, 0x40},	/* ADI Required Write */
+	{ADV7482_I2C_TXB, 0xDA, 0x01},	/* i2c_mipi_pll_en - 1'b1 */
+
+	{ADV7482_I2C_WAIT, 0x00, 0x02},	/* delay 2 */
+	{ADV7482_I2C_TXB, 0x00, 0x21 },	/* Power-up CSI-TX */
+	{ADV7482_I2C_WAIT, 0x00, 0x01},	/* delay 1 */
+	{ADV7482_I2C_TXB, 0xC1, 0x2B},	/* ADI Required Write */
+	{ADV7482_I2C_WAIT, 0x00, 0x01},	/* delay 1 */
+	{ADV7482_I2C_TXB, 0x31, 0x80},	/* ADI Required Write */
+
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_power_up_txa_4lane[] = {
+
+	{ADV7482_I2C_TXA, 0x00, 0x84},	/* Enable 4-lane MIPI */
+	{ADV7482_I2C_TXA, 0x00, 0xA4},	/* Set Auto DPHY Timing */
+
+	{ADV7482_I2C_TXA, 0x31, 0x82},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0x1E, 0x40},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0xDA, 0x01},	/* i2c_mipi_pll_en - 1'b1 */
+	{ADV7482_I2C_WAIT, 0x00, 0x02},	/* delay 2 */
+	{ADV7482_I2C_TXA, 0x00, 0x24 },	/* Power-up CSI-TX */
+	{ADV7482_I2C_WAIT, 0x00, 0x01},	/* delay 1 */
+	{ADV7482_I2C_TXA, 0xC1, 0x2B},	/* ADI Required Write */
+	{ADV7482_I2C_WAIT, 0x00, 0x01},	/* delay 1 */
+	{ADV7482_I2C_TXA, 0x31, 0x80},	/* ADI Required Write */
+
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_power_down_txa_4lane[] = {
+
+	{ADV7482_I2C_TXA, 0x31, 0x82},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0x1E, 0x00},	/* ADI Required Write */
+	{ADV7482_I2C_TXA, 0x00, 0x84},	/* Enable 4-lane MIPI */
+	{ADV7482_I2C_TXA, 0xDA, 0x01},	/* i2c_mipi_pll_en - 1'b1 */
+	{ADV7482_I2C_TXA, 0xC1, 0x3B},	/* ADI Required Write */
+
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_power_up_txb_1lane[] = {
+
+	{ADV7482_I2C_TXB, 0x00, 0x81},	/* Enable 1-lane MIPI */
+	{ADV7482_I2C_TXB, 0x00, 0xA1},	/* Set Auto DPHY Timing */
+
+	{ADV7482_I2C_TXB, 0x31, 0x82},	/* ADI Required Write */
+	{ADV7482_I2C_TXB, 0x1E, 0x40},	/* ADI Required Write */
+	{ADV7482_I2C_TXB, 0xDA, 0x01},	/* i2c_mipi_pll_en - 1'b1 */
+	{ADV7482_I2C_WAIT, 0x00, 0x02},	/* delay 2 */
+	{ADV7482_I2C_TXB, 0x00, 0x21 },	/* Power-up CSI-TX */
+	{ADV7482_I2C_WAIT, 0x00, 0x01},	/* delay 1 */
+	{ADV7482_I2C_TXB, 0xC1, 0x2B},	/* ADI Required Write */
+	{ADV7482_I2C_WAIT, 0x00, 0x01},	/* delay 1 */
+	{ADV7482_I2C_TXB, 0x31, 0x80},	/* ADI Required Write */
+
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_power_down_txb_1lane[] = {
+
+	{ADV7482_I2C_TXB, 0x31, 0x82},	/* ADI Required Write */
+	{ADV7482_I2C_TXB, 0x1E, 0x00},	/* ADI Required Write */
+	{ADV7482_I2C_TXB, 0x00, 0x81},	/* Enable 4-lane MIPI */
+	{ADV7482_I2C_TXB, 0xDA, 0x01},	/* i2c_mipi_pll_en - 1'b1 */
+	{ADV7482_I2C_TXB, 0xC1, 0x3B},	/* ADI Required Write */
+
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_power_up_hdmi_rx[] = {
+
+	/* Disable chip powerdown & Enable HDMI Rx block */
+	{ADV7482_I2C_IO, 0x00, 0x40},
+
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_power_down_hdmi_rx[] = {
+
+	{ADV7482_I2C_IO, 0x00, 0x30},	/* Disable chip powerdown */
+
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_enable_csi4_csi1[] = {
+
+	{ADV7482_I2C_IO, 0x10, 0xE0},	/* Enable 4-lane CSI Tx & Pixel Port */
+
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_set_virtual_channel0[] = {
+
+	{ADV7482_I2C_TXB, 0x0D, 0x00},	/* Set virtual channel 0 */
+	{ADV7482_I2C_TXA, 0x0D, 0x00},	/* Set virtual channel 0 */
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_set_virtual_channel1[] = {
+
+	{ADV7482_I2C_TXB, 0x0D, 0x40},	/* Set virtual channel 1 */
+	{ADV7482_I2C_TXA, 0x0D, 0x40},	/* Set virtual channel 1 */
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_set_virtual_channel2[] = {
+
+	{ADV7482_I2C_TXB, 0x0D, 0x80},	/* Set virtual channel 2 */
+	{ADV7482_I2C_TXA, 0x0D, 0x80},	/* Set virtual channel 2 */
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_set_virtual_channel3[] = {
+
+	{ADV7482_I2C_TXB, 0x0D, 0xC0},	/* Set virtual channel 3 */
+	{ADV7482_I2C_TXA, 0x0D, 0xC0},	/* Set virtual channel 3 */
+	{ADV7482_I2C_EOR, 0xFF, 0xFF}	/* End of register table */
+};
+
+enum decoder_input_interface {
+	DECODER_INPUT_INTERFACE_RGB888,
+	DECODER_INPUT_INTERFACE_YCBCR422,
+};
+
+/**
+ * struct adv7482_link_config - Describes adv7482 hardware configuration
+ * @input_colorspace:		The input colorspace (RGB, YUV444, YUV422)
+ */
+struct adv7482_link_config {
+	enum decoder_input_interface	input_interface;
+	struct adv7482_reg_value	*regs;
+
+	struct adv7482_reg_value	*power_up;
+	struct adv7482_reg_value	*power_down;
+
+	int (*init_device)(void *);
+	int (*init_controls)(void *);
+	int (*s_power)(void *);
+	int (*s_ctrl)(void *);
+	int (*enum_mbus_code)(void *);
+	int (*set_pad_format)(void *);
+	int (*get_pad_format)(void *);
+	int (*s_std)(void *);
+	int (*querystd)(void *);
+	int (*g_input_status)(void *);
+	int (*s_routing)(void *);
+	int (*g_mbus_config)(void *);
+
+	struct device	*dev;
+	bool		sw_reset;
+	bool		hdmi_in;
+	bool		sdp_in;
+	int		vc_ch;
+};
+
+struct adv7482_state {
+	struct v4l2_ctrl_handler		ctrl_hdl;
+	struct v4l2_subdev			sd;
+	struct media_pad			pad;
+	 /* mutual excl. when accessing chip */
+	struct mutex				mutex;
+	int					irq;
+	v4l2_std_id				curr_norm;
+	bool					streaming;
+	const struct adv7482_color_format	*cfmt;
+	u32					width;
+	u32					height;
+
+	struct i2c_client			*client;
+	unsigned int				register_page;
+	struct i2c_client			*csi_client;
+	enum v4l2_field				field;
+
+	struct device				*dev;
+	struct adv7482_link_config		mipi_csi2_link[2];
+};
+
+/*****************************************************************************/
+/*  Private functions                                                        */
+/*****************************************************************************/
+
+#define to_adv7482_sd(_ctrl) (&container_of(_ctrl->handler,		\
+					    struct adv7482_state,	\
+					    ctrl_hdl)->sd)
+
+static inline struct adv7482_state *to_state(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct adv7482_state, sd);
+}
+
+/*
+ * adv7482_write_registers() - Write adv7482 device registers
+ * @client: pointer to i2c_client structure
+ * @regs: pointer to adv7482_reg_value structure
+ *
+ * Write the specified adv7482 register's values.
+ */
+static int adv7482_write_registers(struct i2c_client *client,
+					const struct adv7482_reg_value *regs)
+{
+	struct i2c_msg msg;
+	u8 data_buf[2];
+	int ret = -EINVAL;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	msg.flags = 0;
+	msg.len = 2;
+	msg.buf = &data_buf[0];
+
+	while (regs->addr != ADV7482_I2C_EOR) {
+
+		if (regs->addr == ADV7482_I2C_WAIT)
+			msleep(regs->value);
+		else {
+			msg.addr = regs->addr;
+			data_buf[0] = regs->reg;
+			data_buf[1] = regs->value;
+
+			ret = i2c_transfer(client->adapter, &msg, 1);
+			if (ret < 0)
+				break;
+		}
+		regs++;
+	}
+
+	return (ret < 0) ? ret : 0;
+}
+
+/*
+ * adv7482_write_register() - Write adv7482 device register
+ * @client: pointer to i2c_client structure
+ * @addr: i2c slave address of adv7482 device
+ * @reg: adv7482 device register address
+ * @value: the value to be written
+ *
+ * Write the specified adv7482 register's value.
+ */
+static int adv7482_write_register(struct i2c_client *client,
+		u8 addr, u8 reg, u8 value)
+{
+	struct adv7482_reg_value regs[2];
+	int ret;
+
+	regs[0].addr = addr;
+	regs[0].reg = reg;
+	regs[0].value = value;
+	regs[1].addr = ADV7482_I2C_EOR;
+	regs[1].reg = 0xFF;
+	regs[1].value = 0xFF;
+
+	ret = adv7482_write_registers(client, regs);
+
+	return ret;
+}
+
+/*
+ * adv7482_read_register() - Read adv7482 device register
+ * @client: pointer to i2c_client structure
+ * @addr: i2c slave address of adv7482 device
+ * @reg: adv7482 device register address
+ * @value: pointer to the value
+ *
+ * Read the specified adv7482 register's value.
+ */
+static int adv7482_read_register(struct i2c_client *client,
+		u8 addr, u8 reg, u8 *value)
+{
+	struct i2c_msg msg[2];
+	u8 reg_buf, data_buf;
+	int ret;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	msg[0].addr = addr;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	msg[0].buf = &reg_buf;
+	msg[1].addr = addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = 1;
+	msg[1].buf = &data_buf;
+
+	reg_buf = reg;
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret < 0)
+		return ret;
+
+	*value = data_buf;
+
+	return (ret < 0) ? ret : 0;
+}
+
+static int adv7482_read_sdp_main_info(struct i2c_client *client,
+		struct adv7482_sdp_main_info *info)
+{
+	int ret;
+	u8 value;
+
+	ret = adv7482_write_register(client, ADV7482_I2C_SDP,
+				ADV7482_SDP_REG_CTRL, ADV7482_SDP_RO_MAIN_MAP);
+	if (ret < 0)
+		return ret;
+
+	/* status_reg_10 */
+	ret = adv7482_read_register(client, ADV7482_I2C_SDP,
+			ADV7482_SDP_R_REG_10, &value);
+	if (ret < 0)
+		return ret;
+
+	info->status_reg_10 = value;
+
+	return ret;
+}
+
+static int adv7482_set_video_standard(struct adv7482_state *state,
+				      unsigned int std)
+{
+	int ret;
+	u8 value;
+
+	ret = adv7482_read_register(state->client, ADV7482_I2C_SDP,
+				ADV7482_SDP_REG_INPUT_VIDSEL, &value);
+	if (ret < 0)
+		return ret;
+
+	value &= 0xf;
+	value |= std << 4;
+
+	return adv7482_write_register(state->client, ADV7482_I2C_SDP,
+				ADV7482_SDP_REG_INPUT_VIDSEL, value);
+}
+
+static v4l2_std_id adv7482_std_to_v4l2(u8 status_reg_10)
+{
+	/* in case V4L2_IN_ST_NO_SIGNAL */
+	if (!(status_reg_10 & ADV7482_SDP_R_REG_10_IN_LOCK))
+		return V4L2_STD_UNKNOWN;
+
+	switch (status_reg_10 & ADV7482_SDP_R_REG_10_AUTOD_MASK) {
+	case ADV7482_SDP_R_REG_10_AUTOD_NTSM_M_J:
+		return V4L2_STD_NTSC;
+	case ADV7482_SDP_R_REG_10_AUTOD_NTSC_4_43:
+		return V4L2_STD_NTSC_443;
+	case ADV7482_SDP_R_REG_10_AUTOD_PAL_M:
+		return V4L2_STD_PAL_M;
+	case ADV7482_SDP_R_REG_10_AUTOD_PAL_60:
+		return V4L2_STD_PAL_60;
+	case ADV7482_SDP_R_REG_10_AUTOD_PAL_B_G:
+		return V4L2_STD_PAL;
+	case ADV7482_SDP_R_REG_10_AUTOD_SECAM:
+		return V4L2_STD_SECAM;
+	case ADV7482_SDP_R_REG_10_AUTOD_PAL_COMB:
+		return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
+	case ADV7482_SDP_R_REG_10_AUTOD_SECAM_525:
+		return V4L2_STD_SECAM;
+	default:
+		return V4L2_STD_UNKNOWN;
+	}
+}
+
+static int v4l2_std_to_adv7482(v4l2_std_id std)
+{
+	if (std == V4L2_STD_PAL_60)
+		return ADV7482_SDP_STD_PAL60;
+	if (std == V4L2_STD_NTSC_443)
+		return ADV7482_SDP_STD_NTSC_443;
+	if (std == V4L2_STD_PAL_N)
+		return ADV7482_SDP_STD_PAL_N;
+	if (std == V4L2_STD_PAL_M)
+		return ADV7482_SDP_STD_PAL_M;
+	if (std == V4L2_STD_PAL_Nc)
+		return ADV7482_SDP_STD_PAL_COMB_N;
+	if (std & V4L2_STD_PAL)
+		return ADV7482_SDP_STD_PAL_BG;
+	if (std & V4L2_STD_NTSC)
+		return ADV7482_SDP_STD_NTSC_M;
+	if (std & V4L2_STD_SECAM)
+		return ADV7482_SDP_STD_PAL_SECAM;
+
+	return -EINVAL;
+}
+
+static u32 adv7482_status_to_v4l2(u8 status_reg_10)
+{
+	if (!(status_reg_10 & ADV7482_SDP_R_REG_10_IN_LOCK))
+		return V4L2_IN_ST_NO_SIGNAL;
+
+	return 0;
+}
+
+static int __adv7482_status(struct adv7482_state *state, u32 *status,
+			    v4l2_std_id *std)
+{
+	int ret;
+	u8 status_reg_10;
+	struct adv7482_sdp_main_info sdp_info;
+
+	ret = adv7482_read_sdp_main_info(state->client, &sdp_info);
+	if (ret < 0)
+		return ret;
+
+	status_reg_10 = sdp_info.status_reg_10;
+
+	if (status)
+		*status = adv7482_status_to_v4l2(status_reg_10);
+	if (std)
+		*std = adv7482_std_to_v4l2(status_reg_10);
+
+	return 0;
+}
+
+/*
+ * adv7482_get_vid_info() - Get video information
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @progressive: progressive or interlace
+ * @width: line width
+ * @height: lines per frame
+ *
+ * Get video information.
+ */
+static int adv7482_get_vid_info(struct v4l2_subdev *sd, u8 *progressive,
+				u32 *width, u32 *height, u8 *signal)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u8 hdmi_int;
+	u8 msb;
+	u8 lsb;
+	int ret;
+
+	if (signal)
+		*signal = 0;
+
+	/* decide line width */
+	ret = adv7482_read_register(client, ADV7482_I2C_HDMI,
+			ADV7482_HDMI_STATUS1_REG, &msb);
+	if (ret < 0)
+		return ret;
+
+	if (!(msb & ADV7482_HDMI_VF_LOCKED_FLG) ||
+	    !(msb & ADV7482_HDMI_DERF_LOCKED_FLG))
+		return -EIO;
+
+	if (signal)
+		*signal = 1;
+
+	/* decide interlaced or progressive */
+	ret = adv7482_read_register(client, ADV7482_I2C_HDMI,
+			ADV7482_HDMI_STATUS2_REG, &hdmi_int);
+	if (ret < 0)
+		return ret;
+
+	*progressive =  1;
+	if ((hdmi_int & ADV7482_HDMI_IP_FLAG) != 0)
+		*progressive =  0;
+
+	ret = adv7482_read_register(client, ADV7482_I2C_HDMI,
+			ADV7482_HDMI_LWIDTH_REG, &lsb);
+	if (ret < 0)
+		return ret;
+
+	*width = (u32)(ADV7482_HDMI_LWIDTH_MSBS_MASK & msb);
+	*width = (lsb | (*width << 8));
+
+	/* decide lines per frame */
+	ret = adv7482_read_register(client, ADV7482_I2C_HDMI,
+			ADV7482_HDMI_F0HEIGHT_MSBS_REG, &msb);
+	if (ret < 0)
+		return ret;
+
+	ret = adv7482_read_register(client, ADV7482_I2C_HDMI,
+			ADV7482_HDMI_F0HEIGHT_LSBS_REG, &lsb);
+	if (ret < 0)
+		return ret;
+
+	*height = (u32)(ADV7482_HDMI_F0HEIGHT_MSBS_MASK & msb);
+	*height = (lsb | (*height << 8));
+	if (!(*progressive))
+		*height = *height * 2;
+
+	if (*width == 0 || *height == 0)
+		return -EIO;
+
+	return 0;
+}
+
+static int adv7482_set_vid_info(struct v4l2_subdev *sd)
+{
+	struct adv7482_state *state = to_state(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	u8 progressive;
+	u8 signal;
+	u8 val = ADV7482_IO_CP_VID_STD_480P;
+	u32 width;
+	u32 height;
+	int ret;
+
+	/* Get video information */
+	ret = adv7482_get_vid_info(sd, &progressive, &width, &height, &signal);
+	if (ret < 0) {
+		width		= ADV7482_MAX_WIDTH;
+		height		= ADV7482_MAX_HEIGHT;
+		progressive	= 1;
+	} else {
+		if ((width == 640) &&
+			(height == 480) && (progressive == 1)) {
+			val = ADV7482_IO_CP_VID_STD_VGA60;
+			dev_info(state->dev,
+				 "Changed active resolution to 640x480p\n");
+		} else if ((width == 720) &&
+			(height == 480) && (progressive == 1)) {
+			val = ADV7482_IO_CP_VID_STD_480P;
+			dev_info(state->dev,
+				 "Changed active resolution to 720x480p\n");
+		} else if ((width == 720) &&
+			(height == 576) && (progressive == 1)) {
+			val = ADV7482_IO_CP_VID_STD_576P;
+			dev_info(state->dev,
+				 "Changed active resolution to 720x576p\n");
+		} else if ((width == 1280) &&
+			(height == 720) && (progressive == 1)) {
+			val = ADV7482_IO_CP_VID_STD_720P;
+			dev_info(state->dev,
+				 "Changed active resolution to 1280x720p\n");
+		} else if ((width == 1920) &&
+			(height == 1080) && (progressive == 1)) {
+			val = ADV7482_IO_CP_VID_STD_1080P;
+			dev_info(state->dev,
+				 "Changed active resolution to 1920x1080p\n");
+		} else if ((width == 1920) &&
+			(height == 1080) && (progressive == 0)) {
+			val = ADV7482_IO_CP_VID_STD_1080I;
+			dev_info(state->dev,
+				 "Changed active resolution to 1920x1080i\n");
+		} else {
+			dev_err(state->dev,
+				 "Not support resolution %dx%d%c\n",
+				  width, height, (progressive) ? 'p' : 'i');
+			return -EINVAL;
+		}
+	}
+
+	/*
+	 * The resolution of 720p, 1080i and 1080p is Hsync width of 40 pixel
+	 * clock cycles. These resolutions must be shifted horizontally to
+	 * the left in active video mode.
+	 */
+	if ((val == ADV7482_IO_CP_VID_STD_1080I) ||
+		(val == ADV7482_IO_CP_VID_STD_1080P)) {
+		ret = adv7482_write_register(client,
+					ADV7482_I2C_CP, 0x8B, 0x43);
+		ret = adv7482_write_register(client,
+					ADV7482_I2C_CP, 0x8C, 0xD4);
+		ret = adv7482_write_register(client,
+					ADV7482_I2C_CP, 0x8B, 0x4F);
+		ret = adv7482_write_register(client,
+					ADV7482_I2C_CP, 0x8D, 0xD4);
+	} else if (val == ADV7482_IO_CP_VID_STD_720P) {
+		ret = adv7482_write_register(client,
+					ADV7482_I2C_CP, 0x8B, 0x43);
+		ret = adv7482_write_register(client,
+					ADV7482_I2C_CP, 0x8C, 0xD8);
+		ret = adv7482_write_register(client,
+					ADV7482_I2C_CP, 0x8B, 0x4F);
+		ret = adv7482_write_register(client,
+					ADV7482_I2C_CP, 0x8D, 0xD8);
+	} else {
+		ret = adv7482_write_register(client,
+					ADV7482_I2C_CP, 0x8B, 0x40);
+		ret = adv7482_write_register(client,
+					ADV7482_I2C_CP, 0x8C, 0x00);
+		ret = adv7482_write_register(client,
+					ADV7482_I2C_CP, 0x8B, 0x40);
+		ret = adv7482_write_register(client,
+					ADV7482_I2C_CP, 0x8D, 0x00);
+	}
+
+	ret = adv7482_write_register(client, ADV7482_I2C_IO,
+				ADV7482_IO_CP_VID_STD_REG, val);
+
+	return ret;
+}
+
+/*****************************************************************************/
+/*  V4L2 decoder i/f handler for v4l2_subdev_core_ops                        */
+/*****************************************************************************/
+
+/*
+ * adv7482_querystd() - V4L2 decoder i/f handler for querystd
+ * @sd: ptr to v4l2_subdev struct
+ * @std: standard input video id
+ *
+ * Obtains the video standard input id
+ */
+static int adv7482_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+	struct adv7482_state *state = to_state(sd);
+	struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+	int err = mutex_lock_interruptible(&state->mutex);
+
+	if (err)
+		return err;
+
+	if (config->input_interface != DECODER_INPUT_INTERFACE_YCBCR422) {
+		*std = V4L2_STD_ATSC;
+		goto unlock;
+	}
+
+	if (state->streaming) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	err = adv7482_set_video_standard(state,
+				ADV7482_SDP_STD_AD_PAL_BG_NTSC_J_SECAM);
+	if (err)
+		goto unlock;
+
+	msleep(100);
+	err = __adv7482_status(state, NULL, std);
+	if (err)
+		goto unlock;
+
+	err = v4l2_std_to_adv7482(state->curr_norm);
+	if (err < 0)
+		goto unlock;
+
+	err = adv7482_set_video_standard(state, err);
+
+unlock:
+	mutex_unlock(&state->mutex);
+
+	return err;
+}
+
+/*
+ * adv7482_g_input_status() - V4L2 decoder i/f handler for g_input_status
+ * @sd: ptr to v4l2_subdev struct
+ * @status: video input status flag
+ *
+ * Obtains the video input status flags.
+ */
+static int adv7482_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct adv7482_state *state = to_state(sd);
+	struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+	u8 status1 = 0;
+	int ret = mutex_lock_interruptible(&state->mutex);
+
+	if (ret)
+		return ret;
+
+	if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422)
+		ret = __adv7482_status(state, status, NULL);
+	else {
+		ret = adv7482_read_register(client, ADV7482_I2C_HDMI,
+				ADV7482_HDMI_STATUS1_REG, &status1);
+		if (ret < 0)
+			goto out;
+
+		if (!(status1 & ADV7482_HDMI_VF_LOCKED_FLG))
+			*status = V4L2_IN_ST_NO_SIGNAL;
+		else if (!(status1 & ADV7482_HDMI_DERF_LOCKED_FLG))
+			*status = V4L2_IN_ST_NO_SIGNAL;
+		else
+			*status = 0;
+	}
+
+	ret = 0;
+out:
+	mutex_unlock(&state->mutex);
+	return ret;
+}
+
+static int adv7482_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct adv7482_state *state = to_state(sd);
+	struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+
+	if (code->index != 0)
+		return -EINVAL;
+
+	if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422)
+		code->code = MEDIA_BUS_FMT_YUYV8_2X8;
+	else
+		code->code = MEDIA_BUS_FMT_RGB888_1X24;
+
+	return 0;
+}
+
+static int adv7482_mbus_fmt(struct v4l2_subdev *sd,
+			    struct v4l2_mbus_framefmt *fmt)
+{
+	struct adv7482_state *state = to_state(sd);
+
+	u8 progressive;
+	u8 signal;
+	u32 width;
+	u32 height;
+	int ret;
+	struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+	u8 status_reg_10;
+	struct adv7482_sdp_main_info sdp_info;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422) {
+		ret = __adv7482_status(state, NULL, &state->curr_norm);
+		if (ret < 0)
+			return ret;
+
+		fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
+		fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+		fmt->width  = 720;
+		fmt->height = state->curr_norm & V4L2_STD_525_60
+							 ? 480 : 576;
+
+		/* Get video information */
+		ret = adv7482_read_sdp_main_info(client, &sdp_info);
+		if (ret < 0)
+			return ret;
+
+		status_reg_10 = sdp_info.status_reg_10;
+
+		if ((status_reg_10 & ADV7482_SDP_R_REG_10_IN_LOCK) &&
+			(status_reg_10 & ADV7482_SDP_R_REG_10_FSC_LOCK)
+			&& (((status_reg_10 &
+			ADV7482_SDP_R_REG_10_AUTOD_PAL_M) ==
+			ADV7482_SDP_R_REG_10_AUTOD_PAL_M) ||
+			((status_reg_10 &
+			ADV7482_SDP_R_REG_10_AUTOD_PAL_60) ==
+			ADV7482_SDP_R_REG_10_AUTOD_PAL_60) ||
+			((status_reg_10 &
+			ADV7482_SDP_R_REG_10_AUTOD_PAL_B_G) ==
+			ADV7482_SDP_R_REG_10_AUTOD_PAL_B_G) ||
+			((status_reg_10 &
+			ADV7482_SDP_R_REG_10_AUTOD_PAL_COMB) ==
+			ADV7482_SDP_R_REG_10_AUTOD_PAL_COMB)))
+			dev_info(state->dev,
+			   "Detected the PAL video input signal\n");
+		else if ((status_reg_10 & ADV7482_SDP_R_REG_10_IN_LOCK)
+			&& (status_reg_10 &
+			ADV7482_SDP_R_REG_10_FSC_LOCK) &&
+			(((status_reg_10 &
+			ADV7482_SDP_R_REG_10_AUTOD_NTSC_4_43) ==
+			ADV7482_SDP_R_REG_10_AUTOD_NTSC_4_43) ||
+			((status_reg_10 &
+			ADV7482_SDP_R_REG_10_AUTOD_MASK) ==
+			ADV7482_SDP_R_REG_10_AUTOD_NTSM_M_J)))
+			dev_info(state->dev,
+			   "Detected the NTSC video input signal\n");
+		else
+			dev_info(state->dev,
+			   "Not detect any video input signal\n");
+
+		state->width = fmt->width;
+		state->height = fmt->height;
+		state->field = V4L2_FIELD_INTERLACED;
+
+	} else {
+		fmt->code = MEDIA_BUS_FMT_RGB888_1X24;
+		fmt->colorspace = V4L2_COLORSPACE_SRGB;
+
+		/* Get video information */
+		ret = adv7482_get_vid_info(sd, &progressive,
+			&width, &height, &signal);
+		if (ret < 0) {
+			width		= ADV7482_MAX_WIDTH;
+			height		= ADV7482_MAX_HEIGHT;
+			progressive	= 1;
+		}
+
+		if (signal)
+			dev_info(state->dev,
+			 "Detected the HDMI video input signal (%dx%d%c)\n",
+			  width, height, (progressive) ? 'p' : 'i');
+		else
+			dev_info(state->dev,
+				"Not detect any video input signal\n");
+
+		state->width = width;
+		state->height = height;
+		state->field =
+		      (progressive) ? V4L2_FIELD_NONE : V4L2_FIELD_INTERLACED;
+
+		fmt->width = state->width;
+		fmt->height = state->height;
+	}
+
+	return 0;
+}
+
+static int adv7482_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
+{
+	*norm = V4L2_STD_ALL;
+	return 0;
+}
+
+static int adv7482_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
+{
+	struct adv7482_state *state = to_state(sd);
+
+	*norm = state->curr_norm;
+
+	return 0;
+}
+
+static int adv7482_program_std(struct adv7482_state *state)
+{
+	struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+	int ret;
+
+	if (config->input_interface != DECODER_INPUT_INTERFACE_YCBCR422)
+		return -EINVAL;
+
+	ret = v4l2_std_to_adv7482(state->curr_norm);
+	if (ret < 0)
+		return ret;
+
+	ret = adv7482_set_video_standard(state, ret);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int adv7482_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+	struct adv7482_state *state = to_state(sd);
+	struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+	int ret = mutex_lock_interruptible(&state->mutex);
+
+	if (ret)
+		return ret;
+
+	if (config->input_interface != DECODER_INPUT_INTERFACE_YCBCR422) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Make sure we can support this std */
+	ret = v4l2_std_to_adv7482(std);
+	if (ret < 0)
+		goto out;
+
+	state->curr_norm = std;
+
+	ret = adv7482_program_std(state);
+out:
+	mutex_unlock(&state->mutex);
+	return ret;
+}
+
+static int adv7482_set_field_mode(struct adv7482_state *state)
+{
+	/* FIXME */
+
+	return 0;
+}
+
+static int adv7482_set_power(struct adv7482_state *state, bool on)
+{
+	u8 val;
+	int ret;
+	struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+
+	if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422) {
+		ret = adv7482_read_register(state->client, ADV7482_I2C_TXB,
+				0x1E, &val);
+		if (ret < 0)
+			return ret;
+
+		if (on && ((val & 0x40) == 0)) {
+			/* Power up */
+			ret = adv7482_write_registers(state->client,
+					adv7482_power_up_txb_1lane);
+			if (ret < 0)
+				goto fail;
+		} else {
+			/* Power down */
+			ret = adv7482_write_registers(state->client,
+					adv7482_power_down_txb_1lane);
+			if (ret < 0)
+				goto fail;
+		}
+	} else {
+		/* set active resolution */
+		ret = adv7482_set_vid_info(&state->sd);
+		ret = adv7482_read_register(state->client, ADV7482_I2C_TXA,
+				0x1E, &val);
+		if (ret < 0)
+			return ret;
+
+		if (on && ((val & 0x40) == 0)) {
+			/* Power up */
+			ret = adv7482_write_registers(state->client,
+					adv7482_power_up_txa_4lane);
+			if (ret < 0)
+				goto fail;
+		} else {
+			/* Power down */
+			ret = adv7482_write_registers(state->client,
+					adv7482_power_down_txa_4lane);
+			if (ret < 0)
+				goto fail;
+		}
+	}
+
+	return 0;
+fail:
+	pr_info("%s: Failed set power operation, ret = %d\n", __func__, ret);
+	return ret;
+}
+
+static int adv7482_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct adv7482_state *state = to_state(sd);
+	int ret;
+
+	ret = mutex_lock_interruptible(&state->mutex);
+	if (ret)
+		return ret;
+
+	ret = adv7482_set_power(state, enable);
+	if (ret)
+		goto out;
+
+	state->streaming = enable;
+
+out:
+	mutex_unlock(&state->mutex);
+	return ret;
+}
+
+static int adv7482_get_pad_format(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_format *format)
+{
+	struct adv7482_state *state = to_state(sd);
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		format->format = *v4l2_subdev_get_try_format(sd, cfg, 0);
+	else {
+		adv7482_mbus_fmt(sd, &format->format);
+		format->format.field = state->field;
+	}
+
+	return 0;
+}
+
+static int adv7482_set_pad_format(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_format *format)
+{
+	struct adv7482_state *state = to_state(sd);
+	struct v4l2_mbus_framefmt *framefmt;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		framefmt = &format->format;
+		if (state->field != format->format.field) {
+			state->field = format->format.field;
+			adv7482_set_field_mode(state);
+		}
+	} else {
+		adv7482_mbus_fmt(sd, &format->format);
+
+		if (format->format.field == V4L2_FIELD_ANY)
+			format->format.field = state->field;
+
+		return 0;
+	}
+
+	return adv7482_mbus_fmt(sd, framefmt);
+}
+
+/*
+ * adv7482_g_mbus_config() - V4L2 decoder i/f handler for g_mbus_config
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @cfg: pointer to V4L2 mbus_config structure
+ *
+ * Get mbus configuration.
+ */
+static int adv7482_g_mbus_config(struct v4l2_subdev *sd,
+					struct v4l2_mbus_config *cfg)
+{
+	struct adv7482_state *state = to_state(sd);
+	struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+
+	if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422)
+		cfg->flags = V4L2_MBUS_CSI2_1_LANE |
+				V4L2_MBUS_CSI2_CHANNEL_0 |
+				V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	else
+		cfg->flags = V4L2_MBUS_CSI2_LANES |
+				V4L2_MBUS_CSI2_CHANNELS |
+				V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+	cfg->type = V4L2_MBUS_CSI2;
+
+	return 0;
+}
+
+/*****************************************************************************/
+/*  V4L2 decoder i/f handler for v4l2_ctrl_ops                               */
+/*****************************************************************************/
+static int adv7482_cp_s_ctrl(struct v4l2_ctrl *ctrl, struct i2c_client *client)
+{
+	u8 val;
+	int ret;
+
+	/* Enable video adjustment first */
+	ret = adv7482_read_register(client, ADV7482_I2C_CP,
+			ADV7482_CP_VID_ADJ_REG, &val);
+	if (ret < 0)
+		return ret;
+	val |= ADV7482_CP_VID_ADJ_ENABLE;
+	ret = adv7482_write_register(client, ADV7482_I2C_CP,
+			ADV7482_CP_VID_ADJ_REG, val);
+	if (ret < 0)
+		return ret;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		if ((ctrl->val < ADV7482_CP_BRI_MIN) ||
+					(ctrl->val > ADV7482_CP_BRI_MAX))
+			ret = -ERANGE;
+		else
+			ret = adv7482_write_register(client, ADV7482_I2C_CP,
+					ADV7482_CP_BRI_REG, ctrl->val);
+		break;
+	case V4L2_CID_HUE:
+		if ((ctrl->val < ADV7482_CP_HUE_MIN) ||
+					(ctrl->val > ADV7482_CP_HUE_MAX))
+			ret = -ERANGE;
+		else
+			ret = adv7482_write_register(client, ADV7482_I2C_CP,
+					ADV7482_CP_HUE_REG, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		if ((ctrl->val < ADV7482_CP_CON_MIN) ||
+					(ctrl->val > ADV7482_CP_CON_MAX))
+			ret = -ERANGE;
+		else
+			ret = adv7482_write_register(client, ADV7482_I2C_CP,
+					ADV7482_CP_CON_REG, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		if ((ctrl->val < ADV7482_CP_SAT_MIN) ||
+					(ctrl->val > ADV7482_CP_SAT_MAX))
+			ret = -ERANGE;
+		else
+			ret = adv7482_write_register(client, ADV7482_I2C_CP,
+					ADV7482_CP_SAT_REG, ctrl->val);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int adv7482_sdp_s_ctrl(struct v4l2_ctrl *ctrl, struct i2c_client *client)
+{
+	int ret;
+
+	ret = adv7482_write_register(client, ADV7482_I2C_SDP,
+			ADV7482_SDP_REG_CTRL, ADV7482_SDP_MAIN_MAP_RW);
+	if (ret < 0)
+		return ret;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		if ((ctrl->val < ADV7482_SDP_BRI_MIN) ||
+					(ctrl->val > ADV7482_SDP_BRI_MAX))
+			ret = -ERANGE;
+		else
+			ret = adv7482_write_register(client, ADV7482_I2C_SDP,
+					ADV7482_SDP_REG_BRI, ctrl->val);
+		break;
+	case V4L2_CID_HUE:
+		if ((ctrl->val < ADV7482_SDP_HUE_MIN) ||
+					(ctrl->val > ADV7482_SDP_HUE_MAX))
+			ret = -ERANGE;
+		else
+			/*Hue is inverted according to HSL chart */
+			ret = adv7482_write_register(client, ADV7482_I2C_SDP,
+					ADV7482_SDP_REG_HUE, -ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		if ((ctrl->val < ADV7482_SDP_CON_MIN) ||
+					(ctrl->val > ADV7482_SDP_CON_MAX))
+			ret = -ERANGE;
+		else
+			ret = adv7482_write_register(client, ADV7482_I2C_SDP,
+					ADV7482_SDP_REG_CON, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		/*
+		 *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
+		 *Let's not confuse the user, everybody understands saturation
+		 */
+		if ((ctrl->val < ADV7482_SDP_SAT_MIN) ||
+					(ctrl->val > ADV7482_SDP_SAT_MAX))
+			ret = -ERANGE;
+		else {
+			ret = adv7482_write_register(client, ADV7482_I2C_SDP,
+					ADV7482_SDP_REG_SD_SAT_CB, ctrl->val);
+			if (ret < 0)
+				break;
+
+			ret = adv7482_write_register(client, ADV7482_I2C_SDP,
+					ADV7482_SDP_REG_SD_SAT_CR, ctrl->val);
+		}
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/*
+ * adv7482_s_ctrl() - V4L2 decoder i/f handler for s_ctrl
+ * @ctrl: pointer to standard V4L2 control structure
+ *
+ * Set a control in ADV7482 decoder device.
+ */
+static int adv7482_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_adv7482_sd(ctrl);
+	struct adv7482_state *state = to_state(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret = mutex_lock_interruptible(&state->mutex);
+	struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+
+	if (ret)
+		return ret;
+
+	if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422)
+		ret = adv7482_sdp_s_ctrl(ctrl, client);
+	else
+		ret = adv7482_cp_s_ctrl(ctrl, client);
+
+	mutex_unlock(&state->mutex);
+
+	return ret;
+}
+
+static const struct v4l2_subdev_video_ops adv7482_video_ops = {
+	.g_std		= adv7482_g_std,
+	.s_std		= adv7482_s_std,
+	.querystd	= adv7482_querystd,
+	.g_input_status = adv7482_g_input_status,
+	.g_tvnorms	= adv7482_g_tvnorms,
+	.g_mbus_config	= adv7482_g_mbus_config,
+	.s_stream	= adv7482_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops adv7482_pad_ops = {
+	.enum_mbus_code = adv7482_enum_mbus_code,
+	.set_fmt = adv7482_set_pad_format,
+	.get_fmt = adv7482_get_pad_format,
+};
+
+static const struct v4l2_subdev_ops adv7482_ops = {
+	.video = &adv7482_video_ops,
+	.pad = &adv7482_pad_ops,
+};
+
+static const struct v4l2_ctrl_ops adv7482_ctrl_ops = {
+	.s_ctrl = adv7482_s_ctrl,
+};
+
+/*
+ * adv7482_init_controls() - Init controls
+ * @state: pointer to private state structure
+ *
+ * Init ADV7482 supported control handler.
+ */
+static int adv7482_cp_init_controls(struct adv7482_state *state)
+{
+	v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
+
+	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+			  V4L2_CID_BRIGHTNESS, ADV7482_CP_BRI_MIN,
+			  ADV7482_CP_BRI_MAX, 1, ADV7482_CP_BRI_DEF);
+	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+			  V4L2_CID_CONTRAST, ADV7482_CP_CON_MIN,
+			  ADV7482_CP_CON_MAX, 1, ADV7482_CP_CON_DEF);
+	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+			  V4L2_CID_SATURATION, ADV7482_CP_SAT_MIN,
+			  ADV7482_CP_SAT_MAX, 1, ADV7482_CP_SAT_DEF);
+	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+			  V4L2_CID_HUE, ADV7482_CP_HUE_MIN,
+			  ADV7482_CP_HUE_MAX, 1, ADV7482_CP_HUE_DEF);
+	state->sd.ctrl_handler = &state->ctrl_hdl;
+	if (state->ctrl_hdl.error) {
+		int err = state->ctrl_hdl.error;
+
+		v4l2_ctrl_handler_free(&state->ctrl_hdl);
+		return err;
+	}
+	v4l2_ctrl_handler_setup(&state->ctrl_hdl);
+
+	return 0;
+}
+
+static int adv7482_sdp_init_controls(struct adv7482_state *state)
+{
+	v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
+
+	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+			  V4L2_CID_BRIGHTNESS, ADV7482_SDP_BRI_MIN,
+			  ADV7482_SDP_BRI_MAX, 1, ADV7482_SDP_BRI_DEF);
+	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+			  V4L2_CID_CONTRAST, ADV7482_SDP_CON_MIN,
+			  ADV7482_SDP_CON_MAX, 1, ADV7482_SDP_CON_DEF);
+	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+			  V4L2_CID_SATURATION, ADV7482_SDP_SAT_MIN,
+			  ADV7482_SDP_SAT_MAX, 1, ADV7482_SDP_SAT_DEF);
+	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+			  V4L2_CID_HUE, ADV7482_SDP_HUE_MIN,
+			  ADV7482_SDP_HUE_MAX, 1, ADV7482_SDP_HUE_DEF);
+
+	state->sd.ctrl_handler = &state->ctrl_hdl;
+	if (state->ctrl_hdl.error) {
+		int err = state->ctrl_hdl.error;
+
+		v4l2_ctrl_handler_free(&state->ctrl_hdl);
+		return err;
+	}
+	v4l2_ctrl_handler_setup(&state->ctrl_hdl);
+
+	return 0;
+}
+
+static void adv7482_exit_controls(struct adv7482_state *state)
+{
+	v4l2_ctrl_handler_free(&state->ctrl_hdl);
+}
+
+/*****************************************************************************/
+/*  i2c driver interface handlers                                            */
+/*****************************************************************************/
+
+static int adv7482_parse_dt(struct device_node *np,
+			    struct adv7482_link_config *config)
+{
+	struct v4l2_of_endpoint bus_cfg;
+	struct device_node *endpoint;
+	const char *str;
+	int ret;
+	int ch;
+
+	/* Parse the endpoint. */
+	endpoint = of_graph_get_next_endpoint(np, NULL);
+	if (!endpoint)
+		return -EINVAL;
+
+	v4l2_of_parse_endpoint(endpoint, &bus_cfg);
+	of_node_put(endpoint);
+
+	/* check input-interface */
+	ret = of_property_read_string(np, "adi,input-interface", &str);
+	if (ret < 0)
+		return ret;
+
+	if (!strcmp(str, "rgb888")) {
+		config->input_interface = DECODER_INPUT_INTERFACE_RGB888;
+		config->regs =
+		(struct adv7482_reg_value *)adv7482_init_txa_4lane;
+		config->power_up =
+		(struct adv7482_reg_value *)adv7482_power_up_txa_4lane;
+		config->power_down =
+		(struct adv7482_reg_value *)adv7482_power_down_txa_4lane;
+		config->init_controls =
+		(int (*)(void *))adv7482_cp_init_controls;
+	} else {
+		config->input_interface = DECODER_INPUT_INTERFACE_YCBCR422;
+		config->regs =
+		(struct adv7482_reg_value *)adv7482_init_txb_1lane;
+		config->power_up =
+		(struct adv7482_reg_value *)adv7482_power_up_txb_1lane;
+		config->power_down =
+		(struct adv7482_reg_value *)adv7482_power_down_txb_1lane;
+		config->init_controls =
+		(int (*)(void *))adv7482_sdp_init_controls;
+	}
+
+	ret = of_property_read_string(np, "adi,input-hdmi", &str);
+	if (ret < 0)
+		return ret;
+
+	if (!strcmp(str, "on"))
+		config->hdmi_in = 1;
+	else
+		config->hdmi_in = 0;
+
+	ret = of_property_read_string(np, "adi,input-sdp", &str);
+	if (ret < 0)
+		return ret;
+
+	if (!strcmp(str, "on"))
+		config->sdp_in = 1;
+	else
+		config->sdp_in = 0;
+
+	ret = of_property_read_string(np, "adi,sw-reset", &str);
+	if (ret < 0)
+		return ret;
+
+	if (!strcmp(str, "on"))
+		config->sw_reset = 1;
+	else
+		config->sw_reset = 0;
+
+	ret = of_property_read_u32(np, "adi,virtual-channel", &ch);
+	if (ret < 0)
+		return ret;
+
+	if ((ch < 0) || (ch > 3))
+		return -EINVAL;
+
+	config->vc_ch = ch;
+
+	config->init_device    = NULL;
+	config->s_power        = NULL;
+	config->s_ctrl         = NULL;
+	config->enum_mbus_code = NULL;
+	config->set_pad_format = NULL;
+	config->get_pad_format = NULL;
+	config->s_std          = NULL;
+	config->querystd       = NULL;
+	config->g_input_status = NULL;
+	config->s_routing      = NULL;
+	config->g_mbus_config  = NULL;
+
+	return 0;
+}
+
+/*
+ * adv7482_probe - Probe a ADV7482 device
+ * @client: pointer to i2c_client structure
+ * @id: pointer to i2c_device_id structure
+ *
+ * Initialize the ADV7482 device
+ */
+static int adv7482_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct adv7482_state *state;
+	struct adv7482_link_config link_config;
+	struct device *dev = &client->dev;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	/* Check if the adapter supports the needed features */
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return -EIO;
+
+	v4l_info(client, "chip found @ 0x%02x (%s)\n",
+			client->addr << 1, client->adapter->name);
+
+	state = kzalloc(sizeof(struct adv7482_state), GFP_KERNEL);
+	if (state == NULL) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	state->client = client;
+	state->irq = client->irq;
+
+	ret = adv7482_parse_dt(dev->of_node, &link_config);
+	if (ret) {
+		dev_err(&client->dev, "adv7482 parse error\n");
+		return ret;
+	}
+
+	state->mipi_csi2_link[0].input_interface = link_config.input_interface;
+
+	mutex_init(&state->mutex);
+	state->curr_norm = V4L2_STD_NTSC;
+	sd = &state->sd;
+	state->width = ADV7482_MAX_WIDTH;
+	state->height = ADV7482_MAX_HEIGHT;
+	state->field = V4L2_FIELD_NONE;
+
+	v4l2_i2c_subdev_init(sd, client, &adv7482_ops);
+
+	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	state->dev		= dev;
+	state->mipi_csi2_link[0].dev = dev;
+
+	/* SW reset ADV7482 to its default values */
+	if (link_config.sw_reset) {
+		ret = adv7482_write_registers(client, adv7482_sw_reset);
+		if (ret < 0)
+			goto err_unreg_subdev;
+
+		/* check rd_info */
+		{
+			u8 msb;
+			u8 lsb;
+
+			ret = adv7482_read_register(client, ADV7482_I2C_IO,
+					ADV7482_IO_RD_INFO1_REG, &lsb);
+			if (ret < 0)
+				return ret;
+
+			ret = adv7482_read_register(client, ADV7482_I2C_IO,
+					ADV7482_IO_RD_INFO2_REG, &msb);
+			if (ret < 0)
+				return ret;
+
+			v4l_info(client, "adv7482 revision is %02x%02x\n",
+					lsb, msb);
+		}
+	}
+
+	if (link_config.hdmi_in) {
+		ret = adv7482_write_registers(client,
+				adv7482_init_txa_4lane);
+		if (ret < 0)
+			goto err_unreg_subdev;
+
+		/* Power down */
+		ret = adv7482_write_registers(client,
+				adv7482_power_down_txa_4lane);
+		if (ret < 0)
+			goto err_unreg_subdev;
+
+		v4l_info(client, "adv7482 txa power down\n");
+	} else
+		v4l_info(client, "adv7482 hdmi_in is disabled.\n");
+
+	/* Initializes ADV7482 to its default values */
+	if (link_config.sdp_in) {
+		ret = adv7482_write_registers(client,
+						adv7482_init_txb_1lane);
+		if (ret < 0)
+			goto err_unreg_subdev;
+
+		/* Power down */
+		ret = adv7482_write_registers(client,
+						adv7482_power_down_txb_1lane);
+		if (ret < 0)
+			goto err_unreg_subdev;
+
+		v4l_info(client, "adv7482 txb power down\n");
+	} else
+		v4l_info(client, "adv7482 sdp_in is disabled.\n");
+
+	if (link_config.sdp_in && link_config.hdmi_in) {
+		/* Power up hdmi rx */
+		ret = adv7482_write_registers(client,
+						adv7482_power_up_hdmi_rx);
+		if (ret < 0)
+			goto err_unreg_subdev;
+
+		/* Enable csi4 and sci1 */
+		ret = adv7482_write_registers(client,
+						adv7482_enable_csi4_csi1);
+		if (ret < 0)
+			goto err_unreg_subdev;
+
+		v4l_info(client, "adv7482 enable csi1 and csi4\n");
+	}
+
+	if (link_config.input_interface == DECODER_INPUT_INTERFACE_YCBCR422)
+		ret = adv7482_sdp_init_controls(state);
+	else
+		ret = adv7482_cp_init_controls(state);
+	if (ret)
+		goto err_unreg_subdev;
+
+	/* Setting virtual channel for ADV7482 */
+	if (link_config.vc_ch == 0)
+		ret = adv7482_write_registers(client,
+					adv7482_set_virtual_channel0);
+	else if (link_config.vc_ch == 1)
+		ret = adv7482_write_registers(client,
+					adv7482_set_virtual_channel1);
+	else if (link_config.vc_ch == 2)
+		ret = adv7482_write_registers(client,
+					adv7482_set_virtual_channel2);
+	else if (link_config.vc_ch == 3)
+		ret = adv7482_write_registers(client,
+					adv7482_set_virtual_channel3);
+	if (ret < 0)
+		goto err_unreg_subdev;
+
+	state->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.flags |= MEDIA_ENT_F_ATV_DECODER;
+	ret = media_entity_pads_init(&sd->entity, 1, &state->pad);
+	if (ret)
+		goto err_free_ctrl;
+
+	ret = v4l2_async_register_subdev(sd);
+	if (ret)
+		goto err_free_ctrl;
+
+	return 0;
+
+err_free_ctrl:
+	adv7482_exit_controls(state);
+
+err_unreg_subdev:
+	mutex_destroy(&state->mutex);
+	v4l2_device_unregister_subdev(sd);
+	kfree(state);
+err:
+	dev_err(&client->dev, ": Failed to probe: %d\n", ret);
+	return ret;
+}
+
+/*
+ * adv7482_remove - Remove ADV7482 device support
+ * @client: pointer to i2c_client structure
+ *
+ * Reset the ADV7482 device
+ */
+static int adv7482_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct adv7482_state *state = to_state(sd);
+
+	v4l2_async_unregister_subdev(sd);
+
+	media_entity_cleanup(&sd->entity);
+	adv7482_exit_controls(state);
+
+	mutex_destroy(&state->mutex);
+	kfree(to_state(sd));
+	return 0;
+}
+
+static const struct i2c_device_id adv7482_id[] = {
+	{DRIVER_NAME, 0},
+	{},
+};
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * adv7482_suspend - Suspend ADV7482 device
+ * @client: pointer to i2c_client structure
+ * @state: power management state
+ *
+ * Power down the ADV7482 device
+ */
+static int adv7482_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int ret;
+
+	ret = adv7482_write_register(client, ADV7482_I2C_IO,
+				ADV7482_IO_PWR_MAN_REG, ADV7482_IO_PWR_OFF);
+
+	return ret;
+}
+
+/*
+ * adv7482_resume - Resume ADV7482 device
+ * @client: pointer to i2c_client structure
+ *
+ * Power on and initialize the ADV7482 device
+ */
+static int adv7482_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adv7482_link_config link_config;
+	int ret;
+
+	ret = adv7482_write_register(client, ADV7482_I2C_IO,
+				ADV7482_IO_PWR_MAN_REG, ADV7482_IO_PWR_ON);
+	if (ret < 0)
+		return ret;
+
+	ret = adv7482_parse_dt(dev->of_node, &link_config);
+	if (ret)
+		return ret;
+
+	/* Initializes ADV7482 to its default values */
+	ret = adv7482_write_registers(client, link_config.regs);
+
+	/* Power down */
+	ret = adv7482_write_registers(client, link_config.power_down);
+
+	if (link_config.sdp_in && link_config.hdmi_in) {
+		/* Power up hdmi rx */
+		ret = adv7482_write_registers(client,
+						adv7482_power_up_hdmi_rx);
+		if (ret < 0)
+			return ret;
+
+		/* Enable csi4 and sci1 */
+		ret = adv7482_write_registers(client,
+						adv7482_enable_csi4_csi1);
+		if (ret < 0)
+			return ret;
+	}
+
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(adv7482_pm_ops, adv7482_suspend, adv7482_resume);
+#define ADV7482_PM_OPS (&adv7482_pm_ops)
+
+#else
+#define ADV7482_PM_OPS NULL
+#endif
+
+MODULE_DEVICE_TABLE(i2c, adv7482_id);
+
+static const struct of_device_id adv7482_of_ids[] = {
+	{ .compatible = "adi,adv7482", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adv7482_of_ids);
+
+static struct i2c_driver adv7482_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.pm = ADV7482_PM_OPS,
+		.of_match_table = adv7482_of_ids,
+	},
+	.probe		= adv7482_probe,
+	.remove		= adv7482_remove,
+	.id_table	= adv7482_id,
+};
+
+module_i2c_driver(adv7482_driver);
+
+MODULE_DESCRIPTION("HDMI Receiver ADV7482 video decoder driver");
+MODULE_ALIAS("platform:adv7482");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Koji Matsuoka <koji.matsuoka.xm@renesas.com>");
diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c
index c68239e..4d03ea7 100644
--- a/drivers/media/media-entity.c
+++ b/drivers/media/media-entity.c
@@ -242,6 +242,35 @@ EXPORT_SYMBOL_GPL(media_entity_pads_init);
  * Graph traversal
  */
 
+/**
+ * media_entity_has_route - Check if two entity pads are connected internally
+ * @entity: The entity
+ * @pad0: The first pad index
+ * @pad1: The second pad index
+ *
+ * This function can be used to check whether two pads of an entity are
+ * connected internally in the entity.
+ *
+ * The caller must hold entity->source->parent->mutex.
+ *
+ * Return: true if the pads are connected internally and false otherwise.
+ */
+bool media_entity_has_route(struct media_entity *entity, unsigned int pad0,
+			    unsigned int pad1)
+{
+	if (pad0 >= entity->num_pads || pad1 >= entity->num_pads)
+		return false;
+
+	if (pad0 == pad1)
+		return true;
+
+	if (!entity->ops || !entity->ops->has_route)
+		return true;
+
+	return entity->ops->has_route(entity, pad0, pad1);
+}
+EXPORT_SYMBOL_GPL(media_entity_has_route);
+
 static struct media_entity *
 media_entity_other(struct media_entity *entity, struct media_link *link)
 {
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index e9fc288..ba2b892 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -328,6 +328,27 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called vsp1.
 
+config VIDEO_RENESAS_DEBUG
+	bool "Renesas VSP1 underrun debug messages"
+	depends on VIDEO_RENESAS_VSP1
+	---help---
+	  Enable debug underrun messages on Renesas VSP1 Video Processing
+	  Engine driver.
+	  If you set to enable, When an underrun occurred, a debug underrun
+	  message is output.
+
+config VIDEO_RENESAS_VSP_ALPHA_BIT_ARGB1555
+	int "Renesas VSP alpha bit function of ARGB1555"
+	depends on VIDEO_RENESAS_VSP1
+	range 0 1
+	default 0
+	help
+	  Choose this option if you select A bit function of ARGB1555.
+	  If you set value to 0, pixel alpha blending is performed
+	  when the value of A is 0.
+	  If you set value to 1, pixel alpha blending is performed
+	  when the value of A is 1.
+
 config VIDEO_TI_VPE
 	tristate "TI VPE (Video Processing Engine) driver"
 	depends on VIDEO_DEV && VIDEO_V4L2
diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig
index 111d2a1..64a55e7 100644
--- a/drivers/media/platform/rcar-vin/Kconfig
+++ b/drivers/media/platform/rcar-vin/Kconfig
@@ -5,7 +5,27 @@
 	select VIDEOBUF2_DMA_CONTIG
 	---help---
 	  Support for Renesas R-Car Video Input (VIN) driver.
-	  Supports R-Car Gen2 SoCs.
+	  Supports R-Car Gen2 and Gen3 SoCs.
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called rcar-vin.
+
+config VIDEO_RCAR_VIN_DEBUG
+	bool "Renesas VIN overflow debug messages"
+	depends on VIDEO_RCAR_VIN
+	---help---
+	  Enable debug overflow messages on R-Car Video
+	  Input driver.
+	  If you set to enable, When an overflow occurred,
+	  a debug overflow message is output.
+
+config VIDEO_RCAR_CSI2
+	tristate "R-Car MIPI CSI-2 Interface driver"
+	depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF
+	depends on ARCH_RENESAS || COMPILE_TEST
+	---help---
+	  Support for Renesas R-Car MIPI CSI-2 interface driver.
+	  Supports R-Car Gen3 SoCs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rcar-csi2.
diff --git a/drivers/media/platform/rcar-vin/Makefile b/drivers/media/platform/rcar-vin/Makefile
index 48c5632..d159605 100644
--- a/drivers/media/platform/rcar-vin/Makefile
+++ b/drivers/media/platform/rcar-vin/Makefile
@@ -1,3 +1,5 @@
-rcar-vin-objs = rcar-core.o rcar-dma.o rcar-v4l2.o
+rcar-vin-objs = rcar-core.o rcar-dma.o rcar-v4l2.o rcar-group.o
 
 obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin.o
+
+obj-$(CONFIG_VIDEO_RCAR_CSI2) += rcar-csi2.o
diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c
index 098a0b1..0fa2ed4 100644
--- a/drivers/media/platform/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/rcar-vin/rcar-core.c
@@ -26,36 +26,132 @@
 #include "rcar-vin.h"
 
 /* -----------------------------------------------------------------------------
- * Async notifier
+ * Subdevice group helpers
+ */
+
+#define rvin_group_call_func(v, f, args...)				\
+	(v->slave.v4l2_dev ? vin_to_group(v)->f(&v->slave, ##args) : -ENODEV)
+
+int rvin_subdev_get(struct rvin_dev *vin)
+{
+	int i, num = 0;
+
+	for (i = 0; i < RVIN_INPUT_MAX; i++) {
+		vin->inputs[i].type = RVIN_INPUT_NONE;
+		vin->inputs[i].hint = false;
+	}
+
+	/* Get inputs from CSI2 group */
+	if (vin->slave.v4l2_dev)
+		num = rvin_group_call_func(vin, get, vin->inputs);
+
+	/* Add local digital input */
+	if (num < RVIN_INPUT_MAX && vin->digital.subdev) {
+		vin->inputs[num].type = RVIN_INPUT_DIGITAL;
+		strncpy(vin->inputs[num].name, "Digital", RVIN_INPUT_NAME_SIZE);
+		vin->inputs[num].sink_idx =
+			sd_to_pad_idx(vin->digital.subdev, MEDIA_PAD_FL_SINK);
+		vin->inputs[num].source_idx =
+			sd_to_pad_idx(vin->digital.subdev, MEDIA_PAD_FL_SOURCE);
+		/* If last input was digital we want it again */
+		if (vin->current_input == RVIN_INPUT_DIGITAL)
+			vin->inputs[num].hint = true;
+	}
+
+	/* Make sure we have at least one input */
+	if (vin->inputs[0].type == RVIN_INPUT_NONE) {
+		vin_err(vin, "No inputs for channel with current selection\n");
+		return -EBUSY;
+	}
+	vin->current_input = 0;
+
+	/* Search for hint and prefer digital over CSI2 run over all elements */
+	for (i = 0; i < RVIN_INPUT_MAX; i++)
+		if (vin->inputs[i].hint)
+			vin->current_input = i;
+
+	return 0;
+}
+
+int rvin_subdev_put(struct rvin_dev *vin)
+{
+	/* Store what type of input we used */
+	vin->current_input = vin->inputs[vin->current_input].type;
+
+	if (vin->slave.v4l2_dev)
+		rvin_group_call_func(vin, put);
+
+	return 0;
+}
+
+int rvin_subdev_set_input(struct rvin_dev *vin, struct rvin_input_item *item)
+{
+	if (rvin_input_is_csi(vin))
+		return rvin_group_call_func(vin, set_input, item);
+
+	if (vin->digital.subdev)
+		return 0;
+
+	return -EBUSY;
+}
+
+int rvin_subdev_get_code(struct rvin_dev *vin, u32 *code)
+{
+	if (rvin_input_is_csi(vin))
+		return rvin_group_call_func(vin, get_code, code);
+
+	*code = vin->digital.code;
+	return 0;
+}
+
+int rvin_subdev_get_mbus_cfg(struct rvin_dev *vin,
+			     struct v4l2_mbus_config *mbus_cfg)
+{
+	if (rvin_input_is_csi(vin))
+		return rvin_group_call_func(vin, get_mbus_cfg, mbus_cfg);
+
+	*mbus_cfg = vin->digital.mbus_cfg;
+	return 0;
+}
+
+struct v4l2_subdev_pad_config*
+rvin_subdev_alloc_pad_config(struct rvin_dev *vin)
+{
+	struct v4l2_subdev_pad_config *cfg;
+
+	if (rvin_input_is_csi(vin)) {
+		if (rvin_group_call_func(vin, alloc_pad_config, &cfg))
+			return NULL;
+		return cfg;
+	}
+
+	return v4l2_subdev_alloc_pad_config(vin->digital.subdev);
+}
+
+int rvin_subdev_ctrl_add_handler(struct rvin_dev *vin)
+{
+	int ret;
+
+	v4l2_ctrl_handler_free(&vin->ctrl_handler);
+
+	ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16);
+	if (ret < 0)
+		return ret;
+
+	if (rvin_input_is_csi(vin))
+		return rvin_group_call_func(vin, ctrl_add_handler,
+					    &vin->ctrl_handler);
+
+	return v4l2_ctrl_add_handler(&vin->ctrl_handler,
+				     vin->digital.subdev->ctrl_handler, NULL);
+}
+
+/* -----------------------------------------------------------------------------
+ * Async notifier for local Digital
  */
 
 #define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier)
 
-static bool rvin_mbus_supported(struct rvin_graph_entity *entity)
-{
-	struct v4l2_subdev *sd = entity->subdev;
-	struct v4l2_subdev_mbus_code_enum code = {
-		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
-	};
-
-	code.index = 0;
-	while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) {
-		code.index++;
-		switch (code.code) {
-		case MEDIA_BUS_FMT_YUYV8_1X16:
-		case MEDIA_BUS_FMT_UYVY8_2X8:
-		case MEDIA_BUS_FMT_UYVY10_2X10:
-		case MEDIA_BUS_FMT_RGB888_1X24:
-			entity->code = code.code;
-			return true;
-		default:
-			break;
-		}
-	}
-
-	return false;
-}
-
 static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier)
 {
 	struct rvin_dev *vin = notifier_to_vin(notifier);
@@ -77,7 +173,7 @@ static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier)
 		return ret;
 	}
 
-	return rvin_v4l2_probe(vin);
+	return 0;
 }
 
 static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier,
@@ -88,7 +184,6 @@ static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier,
 
 	if (vin->digital.subdev == subdev) {
 		vin_dbg(vin, "unbind digital subdev %s\n", subdev->name);
-		rvin_v4l2_remove(vin);
 		vin->digital.subdev = NULL;
 		return;
 	}
@@ -146,7 +241,7 @@ static int rvin_digitial_parse_v4l2(struct rvin_dev *vin,
 	return 0;
 }
 
-static int rvin_digital_graph_parse(struct rvin_dev *vin)
+static int rvin_digital_get(struct rvin_dev *vin)
 {
 	struct device_node *ep, *np;
 	int ret;
@@ -158,7 +253,8 @@ static int rvin_digital_graph_parse(struct rvin_dev *vin)
 	 * Port 0 id 0 is local digital input, try to get it.
 	 * Not all instances can or will have this, that is OK
 	 */
-	ep = of_graph_get_endpoint_by_regs(vin->dev->of_node, 0, 0);
+	ep = of_graph_get_endpoint_by_regs(vin->dev->of_node, RVIN_PORT_LOCAL,
+					   0);
 	if (!ep)
 		return 0;
 
@@ -186,13 +282,18 @@ static int rvin_digital_graph_init(struct rvin_dev *vin)
 	struct v4l2_async_subdev **subdevs = NULL;
 	int ret;
 
-	ret = rvin_digital_graph_parse(vin);
+	ret = rvin_digital_get(vin);
 	if (ret)
 		return ret;
 
 	if (!vin->digital.asd.match.of.node) {
 		vin_dbg(vin, "No digital subdevice found\n");
-		return -ENODEV;
+
+		/* OK for Gen3 where we can be part of a subdevice group */
+		if (vin->chip == RCAR_H3 || vin->chip == RCAR_M3)
+			return 0;
+
+		return -EINVAL;
 	}
 
 	/* Register the subdevices notifier. */
@@ -200,7 +301,7 @@ static int rvin_digital_graph_init(struct rvin_dev *vin)
 	if (subdevs == NULL)
 		return -ENOMEM;
 
-	subdevs[0] = &vin->digital.asd;
+	subdevs[0] =  &vin->digital.asd;
 
 	vin_dbg(vin, "Found digital subdevice %s\n",
 		of_node_full_name(subdevs[0]->match.of.node));
@@ -225,23 +326,50 @@ static int rvin_digital_graph_init(struct rvin_dev *vin)
  */
 
 static const struct of_device_id rvin_of_id_table[] = {
+	{ .compatible = "renesas,vin-r8a7796", .data = (void *)RCAR_M3 },
+	{ .compatible = "renesas,vin-r8a7795", .data = (void *)RCAR_H3 },
 	{ .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 },
 	{ .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 },
 	{ .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 },
 	{ .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 },
 	{ .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 },
 	{ .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 },
+	{ .compatible = "renesas,rcar-gen3-vin", .data = (void *)RCAR_GEN3 },
 	{ .compatible = "renesas,rcar-gen2-vin", .data = (void *)RCAR_GEN2 },
 	{ },
 };
 MODULE_DEVICE_TABLE(of, rvin_of_id_table);
 
+static int rvin_probe_channel(struct platform_device *pdev,
+			      struct rvin_dev *vin)
+{
+	struct resource *mem;
+	int irq, ret;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (mem == NULL)
+		return -EINVAL;
+
+	vin->base = devm_ioremap_resource(vin->dev, mem);
+	if (IS_ERR(vin->base))
+		return PTR_ERR(vin->base);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq <= 0)
+		return irq;
+
+	ret = rvin_dma_probe(vin, irq);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static int rcar_vin_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *match;
 	struct rvin_dev *vin;
-	struct resource *mem;
-	int irq, ret;
+	int ret;
 
 	vin = devm_kzalloc(&pdev->dev, sizeof(*vin), GFP_KERNEL);
 	if (!vin)
@@ -254,34 +382,76 @@ static int rcar_vin_probe(struct platform_device *pdev)
 	vin->dev = &pdev->dev;
 	vin->chip = (enum chip_id)match->data;
 
-	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (mem == NULL)
-		return -EINVAL;
+	/* Prefer digital input */
+	vin->current_input = RVIN_INPUT_DIGITAL;
 
-	vin->base = devm_ioremap_resource(vin->dev, mem);
-	if (IS_ERR(vin->base))
-		return PTR_ERR(vin->base);
-
-	irq = platform_get_irq(pdev, 0);
-	if (irq < 0)
-		return irq;
-
-	ret = rvin_dma_probe(vin, irq);
+	/* Initialize the top-level structure */
+	ret = v4l2_device_register(vin->dev, &vin->v4l2_dev);
 	if (ret)
 		return ret;
 
-	ret = rvin_digital_graph_init(vin);
-	if (ret < 0)
-		goto error;
+	ret = rvin_probe_channel(pdev, vin);
+	if (ret)
+		goto err_register;
 
 	pm_suspend_ignore_children(&pdev->dev, true);
 	pm_runtime_enable(&pdev->dev);
 
+	if (vin->chip == RCAR_H3 || vin->chip == RCAR_M3) {
+		vin->api = rvin_group_probe(&pdev->dev, &vin->v4l2_dev);
+
+		if (strcmp(dev_name(vin->v4l2_dev.dev),
+						"e6ef0000.video") == 0)
+			vin->index = RCAR_VIDEO_0;
+		else if (strcmp(dev_name(vin->v4l2_dev.dev),
+						"e6ef1000.video") == 0)
+			vin->index = RCAR_VIDEO_1;
+		else if (strcmp(dev_name(vin->v4l2_dev.dev),
+						"e6ef2000.video") == 0)
+			vin->index = RCAR_VIDEO_2;
+		else if (strcmp(dev_name(vin->v4l2_dev.dev),
+						"e6ef3000.video") == 0)
+			vin->index = RCAR_VIDEO_3;
+		else if (strcmp(dev_name(vin->v4l2_dev.dev),
+						"e6ef4000.video") == 0)
+			vin->index = RCAR_VIDEO_4;
+		else if (strcmp(dev_name(vin->v4l2_dev.dev),
+						"e6ef5000.video") == 0)
+			vin->index = RCAR_VIDEO_5;
+		else if (strcmp(dev_name(vin->v4l2_dev.dev),
+						"e6ef6000.video") == 0)
+			vin->index = RCAR_VIDEO_6;
+		else if (strcmp(dev_name(vin->v4l2_dev.dev),
+						"e6ef7000.video") == 0)
+			vin->index = RCAR_VIDEO_7;
+		else
+			vin->index = RCAR_VIN_CH_NONE;
+
+	} else
+		vin->api = NULL;
+
+	ret = rvin_digital_graph_init(vin);
+	if (ret < 0)
+		goto err_dma;
+
+	ret = rvin_v4l2_probe(vin);
+	if (ret)
+		goto err_dma;
+
 	platform_set_drvdata(pdev, vin);
 
+	ret = rvin_subdev_probe(vin);
+	if (ret)
+		goto err_subdev;
+
 	return 0;
-error:
+
+err_subdev:
+	rvin_v4l2_remove(vin);
+err_dma:
 	rvin_dma_remove(vin);
+err_register:
+	v4l2_device_unregister(&vin->v4l2_dev);
 
 	return ret;
 }
@@ -292,16 +462,57 @@ static int rcar_vin_remove(struct platform_device *pdev)
 
 	pm_runtime_disable(&pdev->dev);
 
+	rvin_subdev_remove(vin);
+
+	rvin_v4l2_remove(vin);
+
 	v4l2_async_notifier_unregister(&vin->notifier);
 
+	if (vin->api)
+		rvin_group_remove(vin->api);
+
 	rvin_dma_remove(vin);
 
+	v4l2_device_unregister(&vin->v4l2_dev);
+
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int rcar_vin_suspend(struct device *dev)
+{
+	/* Empty function for now */
+	return 0;
+}
+
+static int rcar_vin_resume(struct device *dev)
+{
+	int ifmd_val = -1;
+	struct rvin_dev *vin = dev_get_drvdata(dev);
+
+	if (vin->index == 0)
+		ifmd_val = 0;
+
+	if (vin->index == 4)
+		ifmd_val = 1;
+
+	if (ifmd_val >= 0)
+		rvin_set_ifmd(vin, ifmd_val);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rcar_vin_pm_ops,
+			rcar_vin_suspend, rcar_vin_resume);
+#define DEV_PM_OPS (&rcar_vin_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
 static struct platform_driver rcar_vin_driver = {
 	.driver = {
 		.name = "rcar-vin",
+		.pm		= DEV_PM_OPS,
 		.of_match_table = rvin_of_id_table,
 	},
 	.probe = rcar_vin_probe,
diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c
new file mode 100644
index 0000000..2d4018e
--- /dev/null
+++ b/drivers/media/platform/rcar-vin/rcar-csi2.c
@@ -0,0 +1,608 @@
+/*
+ * Driver for Renesas R-Car MIPI CSI-2
+ *
+ * Copyright (C) 2016 Renesas Electronics Corp.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/sys_soc.h>
+
+#include <media/v4l2-of.h>
+#include <media/v4l2-subdev.h>
+
+enum chip_id {
+	RCAR_H3_WS20,
+	RCAR_GEN3,
+};
+
+/* Register offsets */
+#define TREF_REG		0x00 /* Control Timing Select */
+#define SRST_REG		0x04 /* Software Reset */
+#define PHYCNT_REG		0x08 /* PHY Operation Control */
+#define CHKSUM_REG		0x0C /* Checksum Control */
+#define VCDT_REG		0x10 /* Channel Data Type Select */
+#define VCDT2_REG		0x14 /* Channel Data Type Select 2 */
+#define FRDT_REG		0x18 /* Frame Data Type Select */
+#define FLD_REG			0x1C /* Field Detection Control */
+#define ASTBY_REG		0x20 /* Automatic Standby Control */
+#define LNGDT0_REG		0x28 /* Long Data Type Setting 0 */
+#define LNGDT1_REG		0x2C /* Long Data Type Setting 1 */
+#define INTEN_REG		0x30 /* Interrupt Enable */
+#define INTCLOSE_REG		0x34 /* Interrupt Source Mask */
+#define INTSTATE_REG		0x38 /* Interrupt Status Monitor */
+#define INTERRSTATE_REG		0x3C /* Interrupt Error Status Monitor */
+#define SHPDAT_REG		0x40 /* Short Packet Data */
+#define SHPCNT_REG		0x44 /* Short Packet Count */
+#define LINKCNT_REG		0x48 /* LINK Operation Control */
+#define LSWAP_REG		0x4C /* Lane Swap */
+#define PHTW_REG		0x50 /* PHY Test Interface Write */
+#define PHTC_REG		0x58 /* PHY Test Interface Clear */
+#define PHYPLL_REG		0x68 /* PHY Frequency Control */
+#define PHEERM_REG		0x74 /* PHY ESC Error Monitor */
+#define PHCLM_REG		0x78 /* PHY Clock Lane Monitor */
+#define PHDLM_REG		0x7C /* PHY Data Lane Monitor */
+#define CSI0CLKFCPR_REG		0x254/* CSI0CLK Frequency Configuration Preset */
+
+/* Control Timing Select bits */
+#define TREF_TREF			(1 << 0)
+
+/* Software Reset bits */
+#define SRST_SRST			(1 << 0)
+
+/* PHY Operation Control bits */
+#define PHYCNT_SHUTDOWNZ		(1 << 17)
+#define PHYCNT_RSTZ			(1 << 16)
+#define PHYCNT_ENABLECLK		(1 << 4)
+#define PHYCNT_ENABLE_3			(1 << 3)
+#define PHYCNT_ENABLE_2			(1 << 2)
+#define PHYCNT_ENABLE_1			(1 << 1)
+#define PHYCNT_ENABLE_0			(1 << 0)
+
+/* Checksum Control bits */
+#define CHKSUM_ECC_EN			(1 << 1)
+#define CHKSUM_CRC_EN			(1 << 0)
+
+/*
+ * Channel Data Type Select bits
+ * VCDT[0-15]:  Channel 1 VCDT[16-31]:  Channel 2
+ * VCDT2[0-15]: Channel 3 VCDT2[16-31]: Channel 4
+ */
+#define VCDT_VCDTN_EN			(1 << 15)
+#define VCDT_SEL_VC(n)			((n & 0x3) << 8)
+#define VCDT_SEL_DTN_ON			(1 << 6)
+#define VCDT_SEL_DT(n)			((n & 0x1f) << 0)
+
+/* Field Detection Control bits */
+#define FLD_FLD_NUM(n)			((n & 0xff) << 16)
+#define FLD_FLD_EN4			(1 << 3)
+#define FLD_FLD_EN3			(1 << 2)
+#define FLD_FLD_EN2			(1 << 1)
+#define FLD_FLD_EN			(1 << 0)
+
+/* LINK Operation Control bits */
+#define LINKCNT_MONITOR_EN		(1 << 31)
+#define LINKCNT_REG_MONI_PACT_EN	(1 << 25)
+#define LINKCNT_ICLK_NONSTOP		(1 << 24)
+
+/* Lane Swap bits */
+#define LSWAP_L3SEL(n)			((n & 0x3) << 6)
+#define LSWAP_L2SEL(n)			((n & 0x3) << 4)
+#define LSWAP_L1SEL(n)			((n & 0x3) << 2)
+#define LSWAP_L0SEL(n)			((n & 0x3) << 0)
+
+/* PHY Test Interface Clear bits */
+#define PHTC_TESTCLR			(1 << 0)
+
+/* PHY Frequency Control bits */
+#define PHYPLL_HSFREQRANGE_80MBPS	(0x00 << 16)
+#define PHYPLL_HSFREQRANGE_90MBPS	(0x10 << 16)
+#define PHYPLL_HSFREQRANGE_100MBPS	(0x20 << 16)
+#define PHYPLL_HSFREQRANGE_110MBPS	(0x30 << 16)
+#define PHYPLL_HSFREQRANGE_120MBPS	(0x01 << 16)
+#define PHYPLL_HSFREQRANGE_130MBPS	(0x11 << 16)
+#define PHYPLL_HSFREQRANGE_140MBPS	(0x21 << 16)
+#define PHYPLL_HSFREQRANGE_150MBPS	(0x31 << 16)
+#define PHYPLL_HSFREQRANGE_160MBPS	(0x02 << 16)
+#define PHYPLL_HSFREQRANGE_170MBPS	(0x12 << 16)
+#define PHYPLL_HSFREQRANGE_180MBPS	(0x22 << 16)
+#define PHYPLL_HSFREQRANGE_190MBPS	(0x32 << 16)
+#define PHYPLL_HSFREQRANGE_205MBPS	(0x03 << 16)
+#define PHYPLL_HSFREQRANGE_220MBPS	(0x13 << 16)
+#define PHYPLL_HSFREQRANGE_235MBPS	(0x23 << 16)
+#define PHYPLL_HSFREQRANGE_250MBPS	(0x33 << 16)
+#define PHYPLL_HSFREQRANGE_275MBPS	(0x04 << 16)
+#define PHYPLL_HSFREQRANGE_300MBPS	(0x14 << 16)
+#define PHYPLL_HSFREQRANGE_325MBPS	(0x05 << 16)
+#define PHYPLL_HSFREQRANGE_350MBPS	(0x15 << 16)
+#define PHYPLL_HSFREQRANGE_400MBPS	(0x25 << 16)
+#define PHYPLL_HSFREQRANGE_450MBPS	(0x06 << 16)
+#define PHYPLL_HSFREQRANGE_500MBPS	(0x16 << 16)
+#define PHYPLL_HSFREQRANGE_550MBPS	(0x07 << 16)
+#define PHYPLL_HSFREQRANGE_600MBPS	(0x17 << 16)
+#define PHYPLL_HSFREQRANGE_650MBPS	(0x08 << 16)
+#define PHYPLL_HSFREQRANGE_700MBPS	(0x18 << 16)
+#define PHYPLL_HSFREQRANGE_750MBPS	(0x09 << 16)
+#define PHYPLL_HSFREQRANGE_800MBPS	(0x19 << 16)
+#define PHYPLL_HSFREQRANGE_850MBPS	(0x29 << 16)
+#define PHYPLL_HSFREQRANGE_900MBPS	(0x39 << 16)
+#define PHYPLL_HSFREQRANGE_950MBPS	(0x0A << 16)
+#define PHYPLL_HSFREQRANGE_1000MBPS	(0x1A << 16)
+#define PHYPLL_HSFREQRANGE_1050MBPS	(0x2A << 16)
+#define PHYPLL_HSFREQRANGE_1100MBPS	(0x3A << 16)
+#define PHYPLL_HSFREQRANGE_1150MBPS	(0x0B << 16)
+#define PHYPLL_HSFREQRANGE_1200MBPS	(0x1B << 16)
+#define PHYPLL_HSFREQRANGE_1250MBPS	(0x2B << 16)
+#define PHYPLL_HSFREQRANGE_1300MBPS	(0x3B << 16)
+#define PHYPLL_HSFREQRANGE_1350MBPS	(0x0C << 16)
+#define PHYPLL_HSFREQRANGE_1400MBPS	(0x1C << 16)
+#define PHYPLL_HSFREQRANGE_1450MBPS	(0x2C << 16)
+#define PHYPLL_HSFREQRANGE_1500MBPS	(0x3C << 16)
+
+/* CSI0CLK frequency configuration bit */
+#define CSI0CLKFREQRANGE(n)		((n & 0x3f) << 16)
+
+struct rcar_csi2 {
+	struct device *dev;
+	void __iomem *base;
+	spinlock_t lock;
+
+	unsigned short lanes;
+	unsigned char swap[4];
+
+	struct v4l2_subdev subdev;
+	struct v4l2_mbus_framefmt mf;
+
+	u32 vc_num;
+	enum chip_id chip;
+};
+
+#define csi_dbg(p, fmt, arg...)		dev_dbg(p->dev, fmt, ##arg)
+#define csi_info(p, fmt, arg...)	dev_info(p->dev, fmt, ##arg)
+#define csi_warn(p, fmt, arg...)	dev_warn(p->dev, fmt, ##arg)
+#define csi_err(p, fmt, arg...)		dev_err(p->dev, fmt, ##arg)
+
+static irqreturn_t rcar_csi2_irq(int irq, void *data)
+{
+	struct rcar_csi2 *priv = data;
+	u32 int_status;
+	unsigned int handled = 0;
+
+	spin_lock(&priv->lock);
+
+	int_status = ioread32(priv->base + INTSTATE_REG);
+	if (!int_status)
+		goto done;
+
+	/* ack interrupts */
+	iowrite32(int_status, priv->base + INTSTATE_REG);
+	handled = 1;
+
+done:
+	spin_unlock(&priv->lock);
+
+	return IRQ_RETVAL(handled);
+
+}
+
+static void rcar_csi2_reset(struct rcar_csi2 *priv)
+{
+	iowrite32(SRST_SRST, priv->base + SRST_REG);
+	udelay(5);
+	iowrite32(0, priv->base + SRST_REG);
+}
+
+static void rcar_csi2_wait_phy_start(struct rcar_csi2 *priv)
+{
+	int timeout;
+
+	/* Read the PHY clock lane monitor register (PHCLM). */
+	for (timeout = 100; timeout > 0; timeout--) {
+		if (ioread32(priv->base + PHCLM_REG) & 0x01) {
+			csi_dbg(priv, "Detected the PHY clock lane\n");
+			break;
+		}
+		msleep(20);
+	}
+	if (!timeout)
+		csi_err(priv, "Timeout of reading the PHY clock lane\n");
+
+
+	/* Read the PHY data lane monitor register (PHDLM). */
+	for (timeout = 100; timeout > 0; timeout--) {
+		if (ioread32(priv->base + PHDLM_REG) & 0x01) {
+			csi_dbg(priv, "Detected the PHY data lane\n");
+			break;
+		}
+		msleep(20);
+	}
+	if (!timeout)
+		csi_err(priv, "Timeout of reading the PHY data lane\n");
+
+}
+
+static int rcar_csi2_start(struct rcar_csi2 *priv)
+{
+	u32 fld, phycnt, phypll, vcdt, vcdt2, tmp, pixels;
+	int i;
+
+	csi_dbg(priv, "Input size (%dx%d%c)\n", priv->mf.width, priv->mf.height,
+		priv->mf.field == V4L2_FIELD_NONE ? 'p' : 'i');
+
+	vcdt = vcdt2 = 0;
+	for (i = 0; i < priv->vc_num; i++) {
+		tmp = VCDT_SEL_VC(i) | VCDT_VCDTN_EN | VCDT_SEL_DTN_ON;
+
+		switch (priv->mf.code) {
+		case MEDIA_BUS_FMT_RGB888_1X24:
+			/* 24 == RGB888 */
+			tmp |= 0x24;
+			break;
+		case MEDIA_BUS_FMT_YUYV8_1X16:
+		case MEDIA_BUS_FMT_YUYV8_2X8:
+		case MEDIA_BUS_FMT_YUYV10_2X10:
+			/* 1E == YUV422 8-bit */
+			tmp |= 0x1e;
+			break;
+		default:
+			csi_warn(priv,
+				 "Unknown media bus format, try it anyway\n");
+			break;
+		}
+
+		/* Store in correct reg and offset */
+		if (i < 2)
+			vcdt |= tmp << ((i % 2) * 16);
+		else
+			vcdt2 |= tmp << ((i % 2) * 16);
+	}
+
+	switch (priv->lanes) {
+	case 1:
+		fld = FLD_FLD_NUM(1) | FLD_FLD_EN4 | FLD_FLD_EN3 |
+			FLD_FLD_EN2 |  FLD_FLD_EN;
+		phycnt = PHYCNT_ENABLECLK | PHYCNT_ENABLE_0;
+		phypll = PHYPLL_HSFREQRANGE_400MBPS;
+		break;
+	case 4:
+		fld = FLD_FLD_NUM(2) | FLD_FLD_EN4 | FLD_FLD_EN3 |
+			FLD_FLD_EN2 | FLD_FLD_EN;
+		phycnt = PHYCNT_ENABLECLK | PHYCNT_ENABLE_3 |
+			PHYCNT_ENABLE_2 | PHYCNT_ENABLE_1 | PHYCNT_ENABLE_0;
+
+		/* Calculate MBPS per lane, assume 32 bits per pixel at 60Hz */
+		pixels = (priv->mf.width * priv->mf.height);
+		if (pixels <= 640 * 480)
+			phypll = PHYPLL_HSFREQRANGE_100MBPS;
+		else if (pixels <= 720 * 576)
+			phypll = PHYPLL_HSFREQRANGE_190MBPS;
+		else if (pixels <= 1280 * 720)
+			phypll = PHYPLL_HSFREQRANGE_450MBPS;
+		else if (pixels <= 1920 * 1080) {
+			if (priv->mf.field == V4L2_FIELD_NONE)
+				phypll = PHYPLL_HSFREQRANGE_900MBPS;
+			else
+				phypll = PHYPLL_HSFREQRANGE_450MBPS;
+			}
+		else
+			goto error;
+
+		break;
+	default:
+		goto error;
+	}
+
+	/* Init */
+	iowrite32(TREF_TREF, priv->base + TREF_REG);
+	rcar_csi2_reset(priv);
+	iowrite32(0, priv->base + PHTC_REG);
+
+	/* Configure */
+	iowrite32(fld, priv->base + FLD_REG);
+	iowrite32(vcdt, priv->base + VCDT_REG);
+	iowrite32(vcdt2, priv->base + VCDT2_REG);
+	iowrite32(LSWAP_L0SEL(priv->swap[0]) | LSWAP_L1SEL(priv->swap[1]) |
+		  LSWAP_L2SEL(priv->swap[2]) | LSWAP_L3SEL(priv->swap[3]),
+		  priv->base + LSWAP_REG);
+
+	if (priv->chip == RCAR_H3_WS20) {
+		/* Set PHY Test Interface Write Register for external
+		 * reference resistor is unnecessary in R-Car H3(WS2.0)
+		 */
+		iowrite32(0x012701e2, priv->base + PHTW_REG);
+		iowrite32(0x010101e3, priv->base + PHTW_REG);
+		iowrite32(0x010101e4, priv->base + PHTW_REG);
+		iowrite32(0x01100104, priv->base + PHTW_REG);
+	}
+
+	/* Start */
+	iowrite32(phypll, priv->base + PHYPLL_REG);
+
+	/* Set CSI0CLK Frequency Configuration Preset Register for external
+	 * reference resistor is unnecessary in R-Car H3(WS2.0)
+	 */
+	if (priv->chip == RCAR_H3_WS20)
+		iowrite32(CSI0CLKFREQRANGE(32), priv->base + CSI0CLKFCPR_REG);
+
+	iowrite32(phycnt, priv->base + PHYCNT_REG);
+	iowrite32(LINKCNT_MONITOR_EN | LINKCNT_REG_MONI_PACT_EN |
+		  LINKCNT_ICLK_NONSTOP, priv->base + LINKCNT_REG);
+	iowrite32(phycnt | PHYCNT_SHUTDOWNZ, priv->base + PHYCNT_REG);
+	iowrite32(phycnt | PHYCNT_SHUTDOWNZ | PHYCNT_RSTZ,
+		  priv->base + PHYCNT_REG);
+
+	rcar_csi2_wait_phy_start(priv);
+
+	return 0;
+error:
+	csi_err(priv, "Unsupported resolution (%dx%d%c)\n",
+		priv->mf.width, priv->mf.height,
+		priv->mf.field == V4L2_FIELD_NONE ? 'p' : 'i');
+
+	return -EINVAL;
+}
+
+static void rcar_csi2_stop(struct rcar_csi2 *priv)
+{
+	iowrite32(0, priv->base + PHYCNT_REG);
+
+	rcar_csi2_reset(priv);
+}
+
+static int rcar_csi2_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct rcar_csi2 *priv = container_of(sd, struct rcar_csi2, subdev);
+
+	if (enable)
+		return rcar_csi2_start(priv);
+
+	rcar_csi2_stop(priv);
+
+	return 0;
+}
+
+static int rcar_csi2_set_pad_format(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_pad_config *cfg,
+				    struct v4l2_subdev_format *format)
+{
+	struct rcar_csi2 *priv = container_of(sd, struct rcar_csi2, subdev);
+
+	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+		priv->mf = format->format;
+
+	return 0;
+}
+
+static int rcar_csi2_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct rcar_csi2 *priv = container_of(sd, struct rcar_csi2, subdev);
+
+	if (on)
+		pm_runtime_get_sync(priv->dev);
+	else
+		pm_runtime_put_sync(priv->dev);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops rcar_csi2_video_ops = {
+	.s_stream = rcar_csi2_s_stream,
+};
+
+static struct v4l2_subdev_core_ops rcar_csi2_subdev_core_ops = {
+	.s_power = rcar_csi2_s_power,
+};
+
+static const struct v4l2_subdev_pad_ops rcar_csi2_pad_ops = {
+	.set_fmt = rcar_csi2_set_pad_format,
+};
+
+static struct v4l2_subdev_ops rcar_csi2_subdev_ops = {
+	.video	= &rcar_csi2_video_ops,
+	.core	= &rcar_csi2_subdev_core_ops,
+	.pad	= &rcar_csi2_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Platform Device Driver
+ */
+/* H3 WS2.0  */
+static const struct soc_device_attribute r8a7795es20[] = {
+    { .soc_id = "r8a7795", .revision = "ES2.0" },
+    { },
+};
+
+static const struct of_device_id rcar_csi2_of_table[] = {
+	{ .compatible = "renesas,csi2-r8a7796", .data = (void *)RCAR_GEN3 },
+	{ .compatible = "renesas,csi2-r8a7795", .data = (void *)RCAR_GEN3 },
+	{ .compatible = "renesas,rcar-gen3-csi2", .data = (void *)RCAR_GEN3 },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, rcar_csi2_of_table);
+
+static int rcar_csi2_parse_dt(struct rcar_csi2 *priv)
+{
+	struct v4l2_of_endpoint v4l2_ep;
+	struct device_node *ep;
+	int i, n, ret;
+	u32 vc_num;
+
+	ep = of_graph_get_endpoint_by_regs(priv->dev->of_node, 0, 0);
+	if (!ep)
+		return -EINVAL;
+
+	ret = v4l2_of_parse_endpoint(ep, &v4l2_ep);
+	of_node_put(ep);
+	if (ret) {
+		csi_err(priv, "Could not parse v4l2 endpoint\n");
+		return -EINVAL;
+	}
+
+	if (v4l2_ep.bus_type != V4L2_MBUS_CSI2) {
+		csi_err(priv, "Unsupported media bus type for %s\n",
+			of_node_full_name(ep));
+		return -EINVAL;
+	}
+
+	switch (v4l2_ep.bus.mipi_csi2.num_data_lanes) {
+	case 1:
+	case 4:
+		priv->lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
+		break;
+	default:
+		csi_err(priv, "Unsupported number of lanes\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < 4; i++)
+		priv->swap[i] = i;
+
+	for (i = 0; i < priv->lanes; i++) {
+		/* Check for valid lane number */
+		if (v4l2_ep.bus.mipi_csi2.data_lanes[i] < 1 ||
+		    v4l2_ep.bus.mipi_csi2.data_lanes[i] > 4) {
+			csi_err(priv, "data lanes must be in 1-4 range\n");
+			return -EINVAL;
+		}
+
+		/* Use lane numbers 0-3 internally */
+		priv->swap[i] = v4l2_ep.bus.mipi_csi2.data_lanes[i] - 1;
+
+
+	}
+
+	/* Make sure there are no duplicates */
+	for (i = 0; i < priv->lanes; i++) {
+		for (n = i + 1; n < priv->lanes; n++) {
+			if (priv->swap[i] == priv->swap[n]) {
+				csi_err(priv,
+					"Requested swapping not possible\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	if (!of_property_read_u32(ep, "virtual-channel-number", &vc_num))
+		priv->vc_num = vc_num;
+
+	return 0;
+}
+
+static int rcar_csi2_probe_resources(struct rcar_csi2 *priv,
+				     struct platform_device *pdev)
+{
+	struct resource *mem;
+	int irq;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem)
+		return -ENODEV;
+
+	priv->base = devm_ioremap_resource(&pdev->dev, mem);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	irq = platform_get_irq(pdev, 0);
+	if (!irq)
+		return -ENODEV;
+
+	return devm_request_irq(&pdev->dev, irq, rcar_csi2_irq, IRQF_SHARED,
+				dev_name(&pdev->dev), priv);
+}
+
+static int rcar_csi2_probe(struct platform_device *pdev)
+{
+	struct rcar_csi2 *priv;
+	const struct of_device_id *match;
+	int ret;
+	u32 vc_num;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(struct rcar_csi2), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	match = of_match_device(of_match_ptr(rcar_csi2_of_table), &pdev->dev);
+	if (!match)
+		return -ENODEV;
+
+	priv->dev = &pdev->dev;
+	spin_lock_init(&priv->lock);
+
+	priv->vc_num = 0;
+	priv->chip = (enum chip_id)match->data;
+
+	if (soc_device_match(r8a7795es20))
+		priv->chip = RCAR_H3_WS20;
+
+	ret = rcar_csi2_parse_dt(priv);
+	if (ret)
+		return ret;
+
+	ret = rcar_csi2_probe_resources(priv, pdev);
+	if (ret) {
+		csi_err(priv, "Failed to get resources\n");
+		return ret;
+	}
+
+	vc_num = priv->vc_num;
+	platform_set_drvdata(pdev, priv);
+
+retry:
+	priv->subdev.owner = THIS_MODULE;
+	priv->subdev.dev = &pdev->dev;
+	v4l2_subdev_init(&priv->subdev, &rcar_csi2_subdev_ops);
+	v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
+	snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "rcar_csi2.%s",
+		 dev_name(&pdev->dev));
+
+	ret = v4l2_async_register_subdev(&priv->subdev);
+	if (ret < 0)
+		return ret;
+
+	vc_num--;
+	if (vc_num > 0)
+		goto retry;
+
+	pm_runtime_enable(&pdev->dev);
+
+	csi_info(priv, "%d lanes found. virtual channel number %d use\n",
+		 priv->lanes, priv->vc_num);
+
+	return 0;
+}
+
+static int rcar_csi2_remove(struct platform_device *pdev)
+{
+	struct rcar_csi2 *priv = platform_get_drvdata(pdev);
+
+	v4l2_async_unregister_subdev(&priv->subdev);
+
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static struct platform_driver __refdata rcar_csi2_pdrv = {
+	.remove	= rcar_csi2_remove,
+	.probe	= rcar_csi2_probe,
+	.driver	= {
+		.name	= "rcar-csi2",
+		.of_match_table	= of_match_ptr(rcar_csi2_of_table),
+	},
+};
+
+module_platform_driver(rcar_csi2_pdrv);
+
+MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");
+MODULE_DESCRIPTION("Renesas R-Car MIPI CSI-2 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
index 9ccd5ff..10978d1 100644
--- a/drivers/media/platform/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/rcar-vin/rcar-dma.c
@@ -14,8 +14,14 @@
  * option) any later version.
  */
 
+#ifdef CONFIG_VIDEO_RCAR_VIN_DEBUG
+#define DEBUG
+#endif
+
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
 
 #include <media/videobuf2-dma-contig.h>
 
@@ -33,21 +39,28 @@
 #define VNELPRC_REG	0x10	/* Video n End Line Pre-Clip Register */
 #define VNSPPRC_REG	0x14	/* Video n Start Pixel Pre-Clip Register */
 #define VNEPPRC_REG	0x18	/* Video n End Pixel Pre-Clip Register */
-#define VNSLPOC_REG	0x1C	/* Video n Start Line Post-Clip Register */
-#define VNELPOC_REG	0x20	/* Video n End Line Post-Clip Register */
-#define VNSPPOC_REG	0x24	/* Video n Start Pixel Post-Clip Register */
-#define VNEPPOC_REG	0x28	/* Video n End Pixel Post-Clip Register */
 #define VNIS_REG	0x2C	/* Video n Image Stride Register */
 #define VNMB_REG(m)	(0x30 + ((m) << 2)) /* Video n Memory Base m Register */
 #define VNIE_REG	0x40	/* Video n Interrupt Enable Register */
 #define VNINTS_REG	0x44	/* Video n Interrupt Status Register */
 #define VNSI_REG	0x48	/* Video n Scanline Interrupt Register */
 #define VNMTC_REG	0x4C	/* Video n Memory Transfer Control Register */
-#define VNYS_REG	0x50	/* Video n Y Scale Register */
-#define VNXS_REG	0x54	/* Video n X Scale Register */
 #define VNDMR_REG	0x58	/* Video n Data Mode Register */
 #define VNDMR2_REG	0x5C	/* Video n Data Mode Register 2 */
 #define VNUVAOF_REG	0x60	/* Video n UV Address Offset Register */
+#define VNUDS_CTRL_REG		0x80	/* Scaling Control Registers */
+#define VNUDS_SCALE_REG		0x84	/* Scaling Factor Register */
+#define VNUDS_PASS_BWIDTH_REG	0x90	/* Passband Registers */
+#define VNUDS_IPC_REG		0x98	/* 2D IPC Setting Register */
+#define VNUDS_CLIP_SIZE_REG	0xA4	/* UDS Output Size Clipping Register */
+
+/* Register offsets specific for Gen2 */
+#define VNSLPOC_REG	0x1C	/* Video n Start Line Post-Clip Register */
+#define VNELPOC_REG	0x20	/* Video n End Line Post-Clip Register */
+#define VNSPPOC_REG	0x24	/* Video n Start Pixel Post-Clip Register */
+#define VNEPPOC_REG	0x28	/* Video n End Pixel Post-Clip Register */
+#define VNYS_REG	0x50	/* Video n Y Scale Register */
+#define VNXS_REG	0x54	/* Video n X Scale Register */
 #define VNC1A_REG	0x80	/* Video n Coefficient Set C1A Register */
 #define VNC1B_REG	0x84	/* Video n Coefficient Set C1B Register */
 #define VNC1C_REG	0x88	/* Video n Coefficient Set C1C Register */
@@ -73,9 +86,13 @@
 #define VNC8B_REG	0xF4	/* Video n Coefficient Set C8B Register */
 #define VNC8C_REG	0xF8	/* Video n Coefficient Set C8C Register */
 
+/* Register offsets specific for Gen3 */
+#define VNCSI_IFMD_REG		0x20 /* Video n CSI2 Interface Mode Register */
 
 /* Register bit fields for R-Car VIN */
 /* Video n Main Control Register bits */
+#define VNMC_DPINE		(1 << 27) /* Gen3 specific */
+#define VNMC_SCLE		(1 << 26) /* Gen3 specific */
 #define VNMC_FOC		(1 << 21)
 #define VNMC_YCAL		(1 << 19)
 #define VNMC_INF_YUV8_BT656	(0 << 16)
@@ -106,12 +123,19 @@
 /* Video n Interrupt Enable Register bits */
 #define VNIE_FIE		(1 << 4)
 #define VNIE_EFE		(1 << 1)
+#define VNIE_FOE		(1 << 0)
+
+/* Video n Interrupt Status Register bits */
+#define VNINTS_FIS		(1 << 4)
+#define VNINTS_EFS		(1 << 1)
+#define VNINTS_FOS		(1 << 0)
 
 /* Video n Data Mode Register bits */
 #define VNDMR_EXRGB		(1 << 8)
 #define VNDMR_BPSM		(1 << 4)
 #define VNDMR_DTMD_YCSEP	(1 << 1)
-#define VNDMR_DTMD_ARGB1555	(1 << 0)
+#define VNDMR_DTMD_ARGB		(1 << 0)
+#define VNDMR_DTMD_YCSEP_YCBCR420	(3 << 0)
 
 /* Video n Data Mode Register 2 bits */
 #define VNDMR2_VPS		(1 << 30)
@@ -119,6 +143,47 @@
 #define VNDMR2_FTEV		(1 << 17)
 #define VNDMR2_VLV(n)		((n & 0xf) << 12)
 
+/* Video n CSI2 Interface Mode Register (Gen3) */
+#define VNCSI_IFMD_DES1		(1 << 26)
+#define VNCSI_IFMD_DES0		(1 << 25)
+#define VNCSI_IFMD_CSI_CHSEL(n) ((n & 0xf) << 0)
+
+/* Video n UDS Control Register bits */
+#define VNUDS_CTRL_AMD		(1 << 30)
+#define VNUDS_CTRL_BC		(1 << 20)
+#define VNUDS_CTRL_TDIPC	(1 << 1)
+
+#define VIN_UT_IRQ	0x01
+
+static int vin_debug;
+module_param_named(debug, vin_debug, int, 0600);
+static int overflow_video[RCAR_VIDEO_MAX];
+module_param_array(overflow_video, int, NULL, 0600);
+
+#ifdef CONFIG_VIDEO_RCAR_VIN_DEBUG
+#define VIN_IRQ_DEBUG(fmt, args...)					\
+	do {								\
+		if (unlikely(vin_debug & VIN_UT_IRQ))			\
+			vin_ut_debug_printk(__func__, fmt, ##args);	\
+	} while (0)
+#else
+#define VIN_IRQ_DEBUG(fmt, args...)
+#endif
+
+void vin_ut_debug_printk(const char *function_name, const char *format, ...)
+{
+	struct va_format vaf;
+	va_list args;
+
+	va_start(args, format);
+	vaf.fmt = format;
+	vaf.va = &args;
+
+	pr_debug("[" DRV_NAME ":%s] %pV", function_name, &vaf);
+
+	va_end(args);
+}
+
 static void rvin_write(struct rvin_dev *vin, u32 value, u32 offset)
 {
 	iowrite32(value, vin->base + offset);
@@ -129,12 +194,54 @@ static u32 rvin_read(struct rvin_dev *vin, u32 offset)
 	return ioread32(vin->base + offset);
 }
 
+int rvin_is_scaling(struct rvin_dev *vin)
+{
+	if (vin->chip == RCAR_H3 || vin->chip == RCAR_M3) {
+		if ((vin->crop.width != vin->format.width) ||
+			(vin->crop.height != vin->format.height))
+			return 1;
+	}
+
+	return 0;
+}
+
+int rvin_set_ifmd(struct rvin_dev *vin, int val)
+{
+	u32 ifmd;
+
+	if (vin->chip != RCAR_H3 && vin->chip != RCAR_M3)
+		return 0;
+
+	if ((vin->index != 0) && (vin->index != 4))
+		return 0;
+
+	pm_runtime_get_sync(vin->v4l2_dev.dev);
+
+	ifmd = VNCSI_IFMD_DES1 | VNCSI_IFMD_DES0 |
+		VNCSI_IFMD_CSI_CHSEL(val);
+
+	rvin_write(vin, ifmd, VNCSI_IFMD_REG);
+
+	vin_dbg(vin, "Set IFMD 0x%x\n", ifmd);
+
+	pm_runtime_put(vin->v4l2_dev.dev);
+
+	return 0;
+}
+
 static int rvin_setup(struct rvin_dev *vin)
 {
-	u32 vnmc, dmr, dmr2, interrupts;
 	v4l2_std_id std;
+	u32 code, vnmc, dmr, dmr2, interrupts;
+	struct v4l2_mbus_config mbus_cfg;
 	bool progressive = false, output_is_yuv = false, input_is_yuv = false;
 
+	if (rvin_subdev_get_mbus_cfg(vin, &mbus_cfg))
+		return -EINVAL;
+
+	if (rvin_subdev_get_code(vin, &code))
+		return -EINVAL;
+
 	switch (vin->format.field) {
 	case V4L2_FIELD_TOP:
 		vnmc = VNMC_IM_ODD;
@@ -146,7 +253,7 @@ static int rvin_setup(struct rvin_dev *vin)
 		/* Default to TB */
 		vnmc = VNMC_IM_FULL;
 		/* Use BT if video standard can be read and is 60 Hz format */
-		if (!v4l2_subdev_call(vin_to_source(vin), video, g_std, &std)) {
+		if (!rvin_subdev_call_local(vin, video, g_std, &std)) {
 			if (std & V4L2_STD_525_60)
 				vnmc = VNMC_IM_FULL | VNMC_FOC;
 		}
@@ -174,7 +281,7 @@ static int rvin_setup(struct rvin_dev *vin)
 	/*
 	 * Input interface
 	 */
-	switch (vin->digital.code) {
+	switch (code) {
 	case MEDIA_BUS_FMT_YUYV8_1X16:
 		/* BT.601/BT.1358 16bit YCbCr422 */
 		vnmc |= VNMC_INF_YUV16;
@@ -182,7 +289,7 @@ static int rvin_setup(struct rvin_dev *vin)
 		break;
 	case MEDIA_BUS_FMT_UYVY8_2X8:
 		/* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */
-		vnmc |= vin->digital.mbus_cfg.type == V4L2_MBUS_BT656 ?
+		vnmc |= mbus_cfg.type == V4L2_MBUS_BT656 ?
 			VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601;
 		input_is_yuv = true;
 		break;
@@ -191,7 +298,7 @@ static int rvin_setup(struct rvin_dev *vin)
 		break;
 	case MEDIA_BUS_FMT_UYVY10_2X10:
 		/* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */
-		vnmc |= vin->digital.mbus_cfg.type == V4L2_MBUS_BT656 ?
+		vnmc |= mbus_cfg.type == V4L2_MBUS_BT656 ?
 			VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601;
 		input_is_yuv = true;
 		break;
@@ -200,20 +307,30 @@ static int rvin_setup(struct rvin_dev *vin)
 	}
 
 	/* Enable VSYNC Field Toogle mode after one VSYNC input */
-	dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1);
+	if (vin->chip == RCAR_H3 || vin->chip == RCAR_M3)
+		dmr2 = VNDMR2_FTEV;
+	else
+		dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1);
 
 	/* Hsync Signal Polarity Select */
-	if (!(vin->digital.mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
+	if (!(mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
 		dmr2 |= VNDMR2_HPS;
 
 	/* Vsync Signal Polarity Select */
-	if (!(vin->digital.mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
+	if (!(mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
 		dmr2 |= VNDMR2_VPS;
 
 	/*
 	 * Output format
 	 */
 	switch (vin->format.pixelformat) {
+	case V4L2_PIX_FMT_NV12:
+		rvin_write(vin,
+			   ALIGN(vin->format.width * vin->format.height, 0x80),
+			   VNUVAOF_REG);
+		dmr = VNDMR_DTMD_YCSEP_YCBCR420;
+		output_is_yuv = true;
+		break;
 	case V4L2_PIX_FMT_NV16:
 		rvin_write(vin,
 			   ALIGN(vin->format.width * vin->format.height, 0x80),
@@ -229,12 +346,15 @@ static int rvin_setup(struct rvin_dev *vin)
 		dmr = 0;
 		output_is_yuv = true;
 		break;
-	case V4L2_PIX_FMT_XRGB555:
-		dmr = VNDMR_DTMD_ARGB1555;
+	case V4L2_PIX_FMT_ARGB555:
+		dmr = VNDMR_DTMD_ARGB;
 		break;
 	case V4L2_PIX_FMT_RGB565:
 		dmr = 0;
 		break;
+	case V4L2_PIX_FMT_ABGR32:
+		dmr = VNDMR_EXRGB | VNDMR_DTMD_ARGB;
+		break;
 	case V4L2_PIX_FMT_XBGR32:
 		/* Note: not supported on M1 */
 		dmr = VNDMR_EXRGB;
@@ -252,9 +372,27 @@ static int rvin_setup(struct rvin_dev *vin)
 	if (input_is_yuv == output_is_yuv)
 		vnmc |= VNMC_BPS;
 
+	if (vin->chip == RCAR_H3 || vin->chip == RCAR_M3) {
+		/* Select between CSI-2 and Digital input */
+		if (mbus_cfg.type == V4L2_MBUS_CSI2)
+			vnmc &= ~VNMC_DPINE;
+		else
+			vnmc |= VNMC_DPINE;
+	}
+
+	if ((vin->format.pixelformat != V4L2_PIX_FMT_NV12) &&
+		(rvin_is_scaling(vin)))
+		vnmc |= VNMC_SCLE;
+
 	/* Progressive or interlaced mode */
 	interrupts = progressive ? VNIE_FIE : VNIE_EFE;
 
+	/* Enable Overflow */
+	if (vin_debug) {
+		vin_dbg(vin, "Enable Overflow\n");
+		interrupts |= VNIE_FOE;
+	}
+
 	/* Ack interrupts */
 	rvin_write(vin, interrupts, VNINTS_REG);
 	/* Enable interrupts */
@@ -307,6 +445,13 @@ static void rvin_capture_stop(struct rvin_dev *vin)
 {
 	rvin_capture_off(vin);
 
+	if (vin->chip == RCAR_H3 || vin->chip == RCAR_M3) {
+		u32 vnmc;
+
+		vnmc = rvin_read(vin, VNMC_REG);
+		rvin_write(vin, vnmc & ~VNMC_SCLE, VNMC_REG);
+	}
+
 	/* Disable module */
 	rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG);
 }
@@ -753,28 +898,10 @@ static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs)
 	rvin_write(vin, p_set->coeff_set[23], VNC8C_REG);
 }
 
-void rvin_crop_scale_comp(struct rvin_dev *vin)
+static void rvin_crop_scale_comp_gen2(struct rvin_dev *vin)
 {
 	u32 xs, ys;
 
-	/* Set Start/End Pixel/Line Pre-Clip */
-	rvin_write(vin, vin->crop.left, VNSPPRC_REG);
-	rvin_write(vin, vin->crop.left + vin->crop.width - 1, VNEPPRC_REG);
-	switch (vin->format.field) {
-	case V4L2_FIELD_INTERLACED:
-	case V4L2_FIELD_INTERLACED_TB:
-	case V4L2_FIELD_INTERLACED_BT:
-		rvin_write(vin, vin->crop.top / 2, VNSLPRC_REG);
-		rvin_write(vin, (vin->crop.top + vin->crop.height) / 2 - 1,
-			   VNELPRC_REG);
-		break;
-	default:
-		rvin_write(vin, vin->crop.top, VNSLPRC_REG);
-		rvin_write(vin, vin->crop.top + vin->crop.height - 1,
-			   VNELPRC_REG);
-		break;
-	}
-
 	/* Set scaling coefficient */
 	ys = 0;
 	if (vin->crop.height != vin->compose.height)
@@ -812,11 +939,6 @@ void rvin_crop_scale_comp(struct rvin_dev *vin)
 		break;
 	}
 
-	if (vin->format.pixelformat == V4L2_PIX_FMT_NV16)
-		rvin_write(vin, ALIGN(vin->format.width, 0x20), VNIS_REG);
-	else
-		rvin_write(vin, ALIGN(vin->format.width, 0x10), VNIS_REG);
-
 	vin_dbg(vin,
 		"Pre-Clip: %ux%u@%u:%u YS: %d XS: %d Post-Clip: %ux%u@%u:%u\n",
 		vin->crop.width, vin->crop.height, vin->crop.left,
@@ -824,6 +946,110 @@ void rvin_crop_scale_comp(struct rvin_dev *vin)
 		0, 0);
 }
 
+struct rcar_vin_uds_regs {
+	unsigned long ctrl;
+	unsigned long scale;
+	unsigned long pass_bwidth;
+	unsigned long clip_size;
+};
+
+static unsigned long rvin_get_bwidth(unsigned long ratio)
+{
+	unsigned long bwidth;
+	unsigned long mant, frac;
+
+	mant = (ratio & 0xF000) >> 12;
+	frac = ratio & 0x0FFF;
+	if (mant)
+		bwidth = 64 * 4096 * mant / (4096 * mant + frac);
+	else
+		bwidth = 64;
+
+	return bwidth;
+}
+
+static unsigned long rvin_compute_ratio(unsigned int input,
+		unsigned int output)
+{
+	return ((input * 4096 / output) == 0x10000) ?
+		 0xFFFF : (input * 4096 / output);
+}
+
+int rvin_crop_scale_comp_gen3(struct rvin_dev *vin)
+{
+	struct rcar_vin_uds_regs regs;
+	unsigned long ratio_h, ratio_v;
+	unsigned long bwidth_h, bwidth_v;
+	unsigned long ctrl;
+	unsigned long clip_size;
+	u32 vnmc;
+
+	ratio_h = rvin_compute_ratio(vin->crop.width, vin->format.width);
+	ratio_v = rvin_compute_ratio(vin->crop.height, vin->format.height);
+
+	if ((ratio_h > 0x10000) || (ratio_v > 0x10000))
+		dev_warn(vin->dev, "Scaling rate parameter error\n");
+
+	bwidth_h = rvin_get_bwidth(ratio_h);
+	bwidth_v = rvin_get_bwidth(ratio_v);
+
+	ctrl = VNUDS_CTRL_AMD;
+
+	if (vin->format.field == V4L2_FIELD_NONE)
+		clip_size = (vin->format.width << 16) |
+			    (vin->format.height);
+	else
+		clip_size = (vin->format.width << 16) |
+			    (vin->format.height / 2);
+
+	regs.ctrl = ctrl;
+	regs.scale = (ratio_h << 16) | ratio_v;
+	regs.pass_bwidth = (bwidth_h << 16) | bwidth_v;
+	regs.clip_size = clip_size;
+
+	vnmc = rvin_read(vin, VNMC_REG);
+	rvin_write(vin, vnmc | VNMC_SCLE, VNMC_REG);
+	rvin_write(vin, regs.ctrl, VNUDS_CTRL_REG);
+	rvin_write(vin, regs.scale, VNUDS_SCALE_REG);
+	rvin_write(vin, regs.pass_bwidth, VNUDS_PASS_BWIDTH_REG);
+	rvin_write(vin, regs.clip_size, VNUDS_CLIP_SIZE_REG);
+	rvin_write(vin, vnmc, VNMC_REG);
+
+	return 0;
+}
+
+void rvin_crop_scale_comp(struct rvin_dev *vin)
+{
+	/* Set Start/End Pixel/Line Pre-Clip */
+	rvin_write(vin, vin->crop.left, VNSPPRC_REG);
+	rvin_write(vin, vin->crop.left + vin->crop.width - 1, VNEPPRC_REG);
+	switch (vin->format.field) {
+	case V4L2_FIELD_INTERLACED:
+	case V4L2_FIELD_INTERLACED_TB:
+	case V4L2_FIELD_INTERLACED_BT:
+		rvin_write(vin, vin->crop.top / 2, VNSLPRC_REG);
+		rvin_write(vin, (vin->crop.top + vin->crop.height) / 2 - 1,
+			   VNELPRC_REG);
+		break;
+	default:
+		rvin_write(vin, vin->crop.top, VNSLPRC_REG);
+		rvin_write(vin, vin->crop.top + vin->crop.height - 1,
+			   VNELPRC_REG);
+		break;
+	}
+
+	if (vin->chip != RCAR_H3 && vin->chip != RCAR_M3)
+		rvin_crop_scale_comp_gen2(vin);
+	else
+		rvin_crop_scale_comp_gen3(vin);
+
+	if ((vin->format.pixelformat == V4L2_PIX_FMT_NV16) ||
+		(vin->format.pixelformat == V4L2_PIX_FMT_NV12))
+		rvin_write(vin, ALIGN(vin->format.width, 0x20), VNIS_REG);
+	else
+		rvin_write(vin, ALIGN(vin->format.width, 0x10), VNIS_REG);
+}
+
 void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix,
 		    u32 width, u32 height)
 {
@@ -895,6 +1121,7 @@ static irqreturn_t rvin_irq(int irq, void *data)
 	int slot;
 	unsigned int sequence, handled = 0;
 	unsigned long flags;
+	int vin_ovr_cnt = 0;
 
 	spin_lock_irqsave(&vin->qlock, flags);
 
@@ -905,6 +1132,13 @@ static irqreturn_t rvin_irq(int irq, void *data)
 	rvin_ack_interrupt(vin);
 	handled = 1;
 
+	/* overflow occurs */
+	if (vin_debug && (int_status & VNINTS_FOS)) {
+		vin_ovr_cnt = ++overflow_video[vin->index];
+		VIN_IRQ_DEBUG("overflow occurrs num[%d] at VIN (%s)\n",
+				vin_ovr_cnt, dev_name(vin->v4l2_dev.dev));
+	}
+
 	/* Nothing to do if capture status is 'STOPPED' */
 	if (vin->state == STOPPED) {
 		vin_dbg(vin, "IRQ while state stopped\n");
@@ -1000,6 +1234,16 @@ static int rvin_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
 {
 	struct rvin_dev *vin = vb2_get_drv_priv(vq);
 
+	if (rvin_is_scaling(vin) && (vin->format.width % 32)) {
+		vin_err(vin, "Scaling parameter error\n");
+		return -EINVAL;
+	}
+
+	if (!rvin_is_scaling(vin) && (vin->format.width % 16)) {
+		vin_err(vin, "Image stride parameter error\n");
+		return -EINVAL;
+	}
+
 	/* Make sure the image size is large enough. */
 	if (*nplanes)
 		return sizes[0] < vin->format.sizeimage ? -EINVAL : 0;
@@ -1050,12 +1294,10 @@ static void rvin_buffer_queue(struct vb2_buffer *vb)
 static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count)
 {
 	struct rvin_dev *vin = vb2_get_drv_priv(vq);
-	struct v4l2_subdev *sd;
 	unsigned long flags;
 	int ret;
 
-	sd = vin_to_source(vin);
-	v4l2_subdev_call(sd, video, s_stream, 1);
+	rvin_subdev_call(vin, video, s_stream, 1);
 
 	spin_lock_irqsave(&vin->qlock, flags);
 
@@ -1080,7 +1322,7 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count)
 	/* Return all buffers if something went wrong */
 	if (ret) {
 		return_all_buffers(vin, VB2_BUF_STATE_QUEUED);
-		v4l2_subdev_call(sd, video, s_stream, 0);
+		rvin_subdev_call(vin, video, s_stream, 0);
 	}
 
 	spin_unlock_irqrestore(&vin->qlock, flags);
@@ -1091,7 +1333,6 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count)
 static void rvin_stop_streaming(struct vb2_queue *vq)
 {
 	struct rvin_dev *vin = vb2_get_drv_priv(vq);
-	struct v4l2_subdev *sd;
 	unsigned long flags;
 	int retries = 0;
 
@@ -1130,8 +1371,7 @@ static void rvin_stop_streaming(struct vb2_queue *vq)
 
 	spin_unlock_irqrestore(&vin->qlock, flags);
 
-	sd = vin_to_source(vin);
-	v4l2_subdev_call(sd, video, s_stream, 0);
+	rvin_subdev_call(vin, video, s_stream, 0);
 
 	/* disable interrupts */
 	rvin_disable_interrupts(vin);
@@ -1150,8 +1390,6 @@ static const struct vb2_ops rvin_qops = {
 void rvin_dma_remove(struct rvin_dev *vin)
 {
 	mutex_destroy(&vin->lock);
-
-	v4l2_device_unregister(&vin->v4l2_dev);
 }
 
 int rvin_dma_probe(struct rvin_dev *vin, int irq)
@@ -1159,11 +1397,6 @@ int rvin_dma_probe(struct rvin_dev *vin, int irq)
 	struct vb2_queue *q = &vin->queue;
 	int i, ret;
 
-	/* Initialize the top-level structure */
-	ret = v4l2_device_register(vin->dev, &vin->v4l2_dev);
-	if (ret)
-		return ret;
-
 	mutex_init(&vin->lock);
 	INIT_LIST_HEAD(&vin->buf_list);
 
@@ -1176,7 +1409,7 @@ int rvin_dma_probe(struct rvin_dev *vin, int irq)
 
 	/* buffer queue */
 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF;
+	q->io_modes = VB2_MMAP | VB2_READ | VB2_USERPTR | VB2_DMABUF;
 	q->lock = &vin->lock;
 	q->drv_priv = vin;
 	q->buf_struct_size = sizeof(struct rvin_buffer);
@@ -1200,9 +1433,77 @@ int rvin_dma_probe(struct rvin_dev *vin, int irq)
 		goto error;
 	}
 
+	vin_debug = 0;
+
 	return 0;
 error:
 	rvin_dma_remove(vin);
 
 	return ret;
 }
+
+/* -----------------------------------------------------------------------------
+ * Salve Subdevice
+ */
+
+static int rvin_subdev_s_gpio(struct v4l2_subdev *sd, u32 val)
+{
+	struct rvin_dev *vin = container_of(sd, struct rvin_dev, slave);
+	u32 ifmd;
+
+	if (vin->chip != RCAR_H3 && vin->chip != RCAR_M3)
+		return 0;
+
+	pm_runtime_get_sync(vin->v4l2_dev.dev);
+
+	/*
+	 * Undocumented feature: Writing to VNCSI_IFMD_REG will go
+	 * through and on read back look correct but won't have
+	 * any effect if VNMC_REG is not first set to 0.
+	 */
+	rvin_write(vin, 0, VNMC_REG);
+
+	ifmd = VNCSI_IFMD_DES1 | VNCSI_IFMD_DES0 |
+		VNCSI_IFMD_CSI_CHSEL(val);
+
+	rvin_write(vin, ifmd, VNCSI_IFMD_REG);
+
+	vin_dbg(vin, "Set IFMD 0x%x\n", ifmd);
+
+	pm_runtime_put(vin->v4l2_dev.dev);
+
+	return 0;
+}
+
+static struct v4l2_subdev_core_ops rvin_subdev_core_ops = {
+	.s_gpio		= rvin_subdev_s_gpio,
+};
+
+static struct v4l2_subdev_ops rvin_subdev_ops = {
+	.core	= &rvin_subdev_core_ops,
+};
+
+int rvin_subdev_probe(struct rvin_dev *vin)
+{
+	vin->slave.v4l2_dev = NULL;
+
+	if (vin->chip != RCAR_H3 && vin->chip != RCAR_M3)
+		return 0;
+
+	vin->slave.owner = THIS_MODULE;
+	vin->slave.dev = vin->dev;
+	v4l2_subdev_init(&vin->slave, &rvin_subdev_ops);
+	v4l2_set_subdevdata(&vin->slave, vin->dev);
+	snprintf(vin->slave.name, V4L2_SUBDEV_NAME_SIZE, "rcar-vin-slave.%s",
+		 dev_name(vin->dev));
+
+	return v4l2_async_register_subdev(&vin->slave);
+}
+
+void rvin_subdev_remove(struct rvin_dev *vin)
+{
+	if (vin->chip != RCAR_H3 && vin->chip != RCAR_M3)
+		return;
+
+	v4l2_async_unregister_subdev(&vin->slave);
+}
diff --git a/drivers/media/platform/rcar-vin/rcar-group.c b/drivers/media/platform/rcar-vin/rcar-group.c
new file mode 100644
index 0000000..2b98e4c
--- /dev/null
+++ b/drivers/media/platform/rcar-vin/rcar-group.c
@@ -0,0 +1,1359 @@
+/*
+ * Driver for Renesas R-Car VIN
+ *
+ * Copyright (C) 2016 Renesas Electronics Corp.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/soc/renesas/rcar_prr.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
+
+#include "rcar-group.h"
+#include "rcar-vin.h"
+
+/* Max chsel supported by HW */
+#define RVIN_CHSEL_MAX 5
+#define RVIN_H3_WS11_CHSEL_MAX 6
+#define RVIN_VCSEL_MAX 4
+
+enum rvin_csi_id {
+	RVIN_CSI20_VC0,
+	RVIN_CSI20_VC1,
+	RVIN_CSI20_VC2,
+	RVIN_CSI20_VC3,
+	RVIN_CSI21_VC0,
+	RVIN_CSI21_VC1,
+	RVIN_CSI21_VC2,
+	RVIN_CSI21_VC3,
+	RVIN_CSI40_VC0,
+	RVIN_CSI40_VC1,
+	RVIN_CSI40_VC2,
+	RVIN_CSI40_VC3,
+	RVIN_CSI41_VC0,
+	RVIN_CSI41_VC1,
+	RVIN_CSI41_VC2,
+	RVIN_CSI41_VC3,
+	RVIN_CSI_MAX,
+	RVIN_CSI_ERROR,
+};
+
+enum rvin_chan_id {
+	RVIN_CHAN0,
+	RVIN_CHAN1,
+	RVIN_CHAN2,
+	RVIN_CHAN3,
+	RVIN_CHAN4,
+	RVIN_CHAN5,
+	RVIN_CHAN6,
+	RVIN_CHAN7,
+	RVIN_CHAN_MAX,
+	RVIN_CHAN_ERROR,
+};
+
+struct rvin_group_map_item {
+	enum rvin_csi_id csi;
+	const char *name;
+};
+
+static const struct rvin_group_map_item
+rvin_group_h3_ws11_map[RVIN_CHAN_MAX][RVIN_H3_WS11_CHSEL_MAX] = {
+	{
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel1: 0" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel1: 1" },
+		{ .csi = RVIN_CSI21_VC0, .name = "CSI21/VC0 chsel1: 2" },
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel1: 3" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel1: 4" },
+		{ .csi = RVIN_CSI21_VC0, .name = "CSI21/VC0 chsel1: 5" },
+	}, {
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel1: 0" },
+		{ .csi = RVIN_CSI21_VC0, .name = "CSI21/VC0 chsel1: 1" },
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel1: 2" },
+		{ .csi = RVIN_CSI40_VC1, .name = "CSI40/VC1 chsel1: 3" },
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel1: 4" },
+		{ .csi = RVIN_CSI21_VC1, .name = "CSI21/VC1 chsel1: 5" },
+	}, {
+		{ .csi = RVIN_CSI21_VC0, .name = "CSI21/VC0 chsel1: 0" },
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel1: 1" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel1: 2" },
+		{ .csi = RVIN_CSI40_VC2, .name = "CSI40/VC2 chsel1: 3" },
+		{ .csi = RVIN_CSI20_VC2, .name = "CSI20/VC2 chsel1: 4" },
+		{ .csi = RVIN_CSI21_VC2, .name = "CSI21/VC2 chsel1: 5" },
+	}, {
+		{ .csi = RVIN_CSI40_VC1, .name = "CSI40/VC1 chsel1: 0" },
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel1: 1" },
+		{ .csi = RVIN_CSI21_VC1, .name = "CSI21/VC1 chsel1: 2" },
+		{ .csi = RVIN_CSI40_VC3, .name = "CSI40/VC3 chsel1: 3" },
+		{ .csi = RVIN_CSI20_VC3, .name = "CSI20/VC3 chsel1: 4" },
+		{ .csi = RVIN_CSI21_VC3, .name = "CSI21/VC3 chsel1: 5" },
+	}, {
+		{ .csi = RVIN_CSI41_VC0, .name = "CSI41/VC0 chsel2: 0" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel2: 1" },
+		{ .csi = RVIN_CSI21_VC0, .name = "CSI21/VC0 chsel2: 2" },
+		{ .csi = RVIN_CSI41_VC0, .name = "CSI41/VC0 chsel2: 3" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel2: 4" },
+		{ .csi = RVIN_CSI21_VC0, .name = "CSI21/VC0 chsel2: 5" },
+	}, {
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel2: 0" },
+		{ .csi = RVIN_CSI21_VC0, .name = "CSI21/VC0 chsel2: 1" },
+		{ .csi = RVIN_CSI41_VC0, .name = "CSI41/VC0 chsel2: 2" },
+		{ .csi = RVIN_CSI41_VC1, .name = "CSI41/VC1 chsel2: 3" },
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel2: 4" },
+		{ .csi = RVIN_CSI21_VC1, .name = "CSI21/VC1 chsel2: 5" },
+	}, {
+		{ .csi = RVIN_CSI21_VC0, .name = "CSI21/VC0 chsel2: 0" },
+		{ .csi = RVIN_CSI41_VC0, .name = "CSI41/VC0 chsel2: 1" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel2: 2" },
+		{ .csi = RVIN_CSI41_VC2, .name = "CSI41/VC2 chsel2: 3" },
+		{ .csi = RVIN_CSI20_VC2, .name = "CSI20/VC2 chsel2: 4" },
+		{ .csi = RVIN_CSI21_VC2, .name = "CSI21/VC2 chsel2: 5" },
+	}, {
+		{ .csi = RVIN_CSI41_VC1, .name = "CSI41/VC1 chsel2: 0" },
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel2: 1" },
+		{ .csi = RVIN_CSI21_VC1, .name = "CSI21/VC1 chsel2: 2" },
+		{ .csi = RVIN_CSI41_VC3, .name = "CSI41/VC3 chsel2: 3" },
+		{ .csi = RVIN_CSI20_VC3, .name = "CSI20/VC3 chsel2: 4" },
+		{ .csi = RVIN_CSI21_VC3, .name = "CSI21/VC3 chsel2: 5" },
+	},
+};
+
+static const struct rvin_group_map_item
+rvin_group_h3_map[RVIN_CHAN_MAX][RVIN_CHSEL_MAX] = {
+	{
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel1: 0" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel1: 1" },
+		{ .csi = RVIN_CSI40_VC1, .name = "CSI40/VC1 chsel1: 2" },
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel1: 3" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel1: 4" },
+	}, {
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel1: 0" },
+		{ .csi = RVIN_CSI40_VC1, .name = "CSI40/VC1 chsel1: 1" },
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel1: 2" },
+		{ .csi = RVIN_CSI40_VC1, .name = "CSI40/VC1 chsel1: 3" },
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel1: 4" },
+	}, {
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel1: 0" },
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel1: 1" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel1: 2" },
+		{ .csi = RVIN_CSI40_VC2, .name = "CSI40/VC2 chsel1: 3" },
+		{ .csi = RVIN_CSI20_VC2, .name = "CSI20/VC2 chsel1: 4" },
+	}, {
+		{ .csi = RVIN_CSI40_VC1, .name = "CSI40/VC1 chsel1: 0" },
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel1: 1" },
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel1: 2" },
+		{ .csi = RVIN_CSI40_VC3, .name = "CSI40/VC3 chsel1: 3" },
+		{ .csi = RVIN_CSI20_VC3, .name = "CSI20/VC3 chsel1: 4" },
+	}, {
+		{ .csi = RVIN_CSI41_VC0, .name = "CSI41/VC0 chsel2: 0" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel2: 1" },
+		{ .csi = RVIN_CSI41_VC1, .name = "CSI41/VC1 chsel2: 2" },
+		{ .csi = RVIN_CSI41_VC0, .name = "CSI41/VC0 chsel2: 3" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel2: 4" },
+	}, {
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel2: 0" },
+		{ .csi = RVIN_CSI41_VC1, .name = "CSI41/VC1 chsel2: 1" },
+		{ .csi = RVIN_CSI41_VC0, .name = "CSI41/VC0 chsel2: 2" },
+		{ .csi = RVIN_CSI41_VC1, .name = "CSI41/VC1 chsel2: 3" },
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel2: 4" },
+	}, {
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel2: 0" },
+		{ .csi = RVIN_CSI41_VC0, .name = "CSI41/VC0 chsel2: 1" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel2: 2" },
+		{ .csi = RVIN_CSI41_VC2, .name = "CSI41/VC2 chsel2: 3" },
+		{ .csi = RVIN_CSI20_VC2, .name = "CSI20/VC2 chsel2: 4" },
+	}, {
+		{ .csi = RVIN_CSI41_VC1, .name = "CSI41/VC1 chsel2: 0" },
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel2: 1" },
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel2: 2" },
+		{ .csi = RVIN_CSI41_VC3, .name = "CSI41/VC3 chsel2: 3" },
+		{ .csi = RVIN_CSI20_VC3, .name = "CSI20/VC3 chsel2: 4" },
+	},
+};
+
+static const struct rvin_group_map_item
+rvin_group_m3_map[RVIN_CHAN_MAX][RVIN_CHSEL_MAX] = {
+	{
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel1: 0" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel1: 1" },
+		{ .csi = RVIN_CSI_ERROR, .name = "No operate" },
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel1: 3" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel1: 4" },
+	}, {
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel1: 0" },
+		{ .csi = RVIN_CSI_ERROR, .name = "No operate" },
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel1: 2" },
+		{ .csi = RVIN_CSI40_VC1, .name = "CSI40/VC1 chsel1: 3" },
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel1: 4" },
+	}, {
+		{ .csi = RVIN_CSI_ERROR, .name = "No operate" },
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel1: 1" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel1: 2" },
+		{ .csi = RVIN_CSI40_VC2, .name = "CSI40/VC2 chsel1: 3" },
+		{ .csi = RVIN_CSI20_VC2, .name = "CSI20/VC2 chsel1: 4" },
+	}, {
+		{ .csi = RVIN_CSI40_VC1, .name = "CSI40/VC1 chsel1: 0" },
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel1: 1" },
+		{ .csi = RVIN_CSI_ERROR, .name = "No operate" },
+		{ .csi = RVIN_CSI40_VC3, .name = "CSI40/VC3 chsel1: 3" },
+		{ .csi = RVIN_CSI20_VC3, .name = "CSI20/VC3 chsel1: 4" },
+	}, {
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel2: 0" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel2: 1" },
+		{ .csi = RVIN_CSI_ERROR, .name = "No operate" },
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel2: 3" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel2: 4" },
+	}, {
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel2: 0" },
+		{ .csi = RVIN_CSI_ERROR, .name = "No operate" },
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel2: 2" },
+		{ .csi = RVIN_CSI40_VC1, .name = "CSI40/VC1 chsel2: 3" },
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel2: 4" },
+	}, {
+		{ .csi = RVIN_CSI_ERROR, .name = "No operate" },
+		{ .csi = RVIN_CSI40_VC0, .name = "CSI40/VC0 chsel2: 1" },
+		{ .csi = RVIN_CSI20_VC0, .name = "CSI20/VC0 chsel2: 2" },
+		{ .csi = RVIN_CSI40_VC2, .name = "CSI40/VC2 chsel2: 3" },
+		{ .csi = RVIN_CSI20_VC2, .name = "CSI20/VC2 chsel2: 4" },
+	}, {
+		{ .csi = RVIN_CSI40_VC1, .name = "CSI40/VC1 chsel2: 0" },
+		{ .csi = RVIN_CSI20_VC1, .name = "CSI20/VC1 chsel2: 1" },
+		{ .csi = RVIN_CSI_ERROR, .name = "No operate" },
+		{ .csi = RVIN_CSI40_VC3, .name = "CSI40/VC3 chsel2: 3" },
+		{ .csi = RVIN_CSI20_VC3, .name = "CSI20/VC3 chsel2: 4" },
+	},
+};
+
+struct rvin_group {
+	struct device *dev;
+	struct v4l2_device *v4l2_dev;
+	struct mutex lock;
+
+	struct rvin_group_api api;
+
+	struct v4l2_async_notifier notifier;
+
+	struct rvin_graph_entity bridge[RVIN_CSI_MAX];
+	struct rvin_graph_entity source[RVIN_CSI_MAX];
+	int stream[RVIN_CSI_MAX];
+	int power[RVIN_CSI_MAX];
+
+	struct rvin_graph_entity chan[RVIN_CHAN_MAX];
+	int users[RVIN_CHAN_MAX];
+
+	int chsel1;
+	int chsel2;
+};
+
+#define grp_dbg(d, fmt, arg...)		dev_dbg(d->dev, fmt, ##arg)
+#define grp_info(d, fmt, arg...)	dev_info(d->dev, fmt, ##arg)
+#define grp_err(d, fmt, arg...)		dev_err(d->dev, fmt, ##arg)
+
+/* -----------------------------------------------------------------------------
+ * Group API - Helpers
+ */
+
+static enum rvin_chan_id sd_to_chan(struct rvin_group *grp,
+				    struct v4l2_subdev *sd)
+{
+	enum rvin_chan_id chan = RVIN_CHAN_ERROR;
+	int i;
+
+	for (i = 0; i < RVIN_CHAN_MAX; i++) {
+		if (grp->chan[i].subdev == sd) {
+			chan =  i;
+			break;
+		}
+	}
+
+	/* Something is wrong, subdevice can't be resolved to channel id */
+	BUG_ON(chan == RVIN_CHAN_ERROR);
+
+	return chan;
+}
+
+static enum rvin_chan_id chan_to_master(struct rvin_group *grp,
+					enum rvin_chan_id chan)
+{
+	enum rvin_chan_id master;
+
+	switch (chan) {
+	case RVIN_CHAN0:
+	case RVIN_CHAN1:
+	case RVIN_CHAN2:
+	case RVIN_CHAN3:
+		master = RVIN_CHAN0;
+		break;
+	case RVIN_CHAN4:
+	case RVIN_CHAN5:
+	case RVIN_CHAN6:
+	case RVIN_CHAN7:
+		master = RVIN_CHAN4;
+		break;
+	default:
+		master = RVIN_CHAN_ERROR;
+		break;
+	}
+
+	/* Something is wrong, subdevice can't be resolved to channel id */
+	BUG_ON(master == RVIN_CHAN_ERROR);
+
+	return master;
+}
+
+static enum rvin_csi_id rvin_group_get_csi(struct rvin_group *grp,
+					   struct v4l2_subdev *sd, int chsel)
+{
+	struct rvin_dev *vin =
+		container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev);
+	enum rvin_chan_id chan;
+	enum rvin_csi_id csi = RVIN_CSI_ERROR;
+	int chsel_max;
+
+	if ((vin->chip == RCAR_H3) && (RCAR_PRR_IS_PRODUCT(H3)
+		&& (RCAR_PRR_CHK_CUT(H3, WS11) <= 0)))
+		chsel_max = RVIN_H3_WS11_CHSEL_MAX;
+	else
+		chsel_max = RVIN_CHSEL_MAX;
+
+	if (chsel < 0 || chsel > chsel_max)
+		return RVIN_CSI_ERROR;
+
+	chan = sd_to_chan(grp, sd);
+
+	if ((vin->chip == RCAR_H3) && (RCAR_PRR_IS_PRODUCT(H3)
+		&& (RCAR_PRR_CHK_CUT(H3, WS11) <= 0)))
+		csi = rvin_group_h3_ws11_map[chan][chsel].csi;
+	else if (vin->chip == RCAR_H3)
+		csi = rvin_group_h3_map[chan][chsel].csi;
+	else if (vin->chip == RCAR_M3)
+		csi = rvin_group_m3_map[chan][chsel].csi;
+
+	/* Not all CSI source might be available */
+	if (!grp->bridge[csi].subdev || !grp->source[csi].subdev)
+		return RVIN_CSI_ERROR;
+
+	return csi;
+}
+
+static int rvin_group_chsel_get(struct rvin_group *grp, struct v4l2_subdev *sd)
+{
+	enum rvin_chan_id master;
+
+	master = chan_to_master(grp, sd_to_chan(grp, sd));
+
+	if (master == RVIN_CHAN0)
+		return grp->chsel1;
+
+	return grp->chsel2;
+}
+
+static void rvin_group_chsel_set(struct rvin_group *grp, struct v4l2_subdev *sd,
+				 int chsel)
+{
+	enum rvin_chan_id master;
+
+	master = chan_to_master(grp, sd_to_chan(grp, sd));
+
+	if (master == RVIN_CHAN0)
+		grp->chsel1 = chsel;
+	else
+		grp->chsel2 = chsel;
+}
+
+static enum rvin_csi_id sd_to_csi(struct rvin_group *grp,
+				  struct v4l2_subdev *sd)
+{
+	return rvin_group_get_csi(grp, sd, rvin_group_chsel_get(grp, sd));
+}
+
+/* -----------------------------------------------------------------------------
+ * Group API - logic
+ */
+
+static int rvin_group_get_sink_idx(struct media_entity *entity, int source_idx)
+{
+	unsigned int i;
+
+	/* Iterates through the pads to find a connected sink pad. */
+	for (i = 0; i < entity->num_pads; ++i) {
+		struct media_pad *sink = &entity->pads[i];
+
+		if (!(sink->flags & MEDIA_PAD_FL_SINK))
+			continue;
+
+		if (sink->index == source_idx)
+			continue;
+
+		if (media_entity_has_route(entity, sink->index, source_idx))
+			return sink->index;
+	}
+
+	/* If not found return the first pad, guaranteed to be a sink pad. */
+	return 0;
+}
+
+static int rvin_group_get(struct v4l2_subdev *sd,
+			  struct rvin_input_item *inputs)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	struct rvin_dev *vin =
+		container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev);
+	enum rvin_chan_id chan, master;
+	enum rvin_csi_id csi;
+	int i, num = 0;
+	int chsel_max;
+
+	mutex_lock(&grp->lock);
+
+	chan = sd_to_chan(grp, sd);
+
+	/* If subgroup master is not present channel is useless */
+	master = chan_to_master(grp, chan);
+	if (!grp->chan[master].subdev) {
+		grp_err(grp, "chan%d: No group master found\n", chan);
+		goto out;
+	}
+
+	/* Make sure channel is usable with current chsel */
+	if (sd_to_csi(grp, sd) == RVIN_CSI_ERROR) {
+		grp_info(grp, "chan%d: Unavailable with current chsel\n", chan);
+		goto out;
+	}
+
+	if ((vin->chip == RCAR_H3) && (RCAR_PRR_IS_PRODUCT(H3)
+		&& (RCAR_PRR_CHK_CUT(H3, WS11) <= 0)))
+		chsel_max = RVIN_H3_WS11_CHSEL_MAX;
+	else
+		chsel_max = RVIN_CHSEL_MAX;
+
+	/* Create list of valid inputs */
+	for (i = 0; i < chsel_max; i++) {
+		csi = rvin_group_get_csi(grp, sd, i);
+		if (rvin_group_get_csi(grp, sd, i) != RVIN_CSI_ERROR) {
+			inputs[num].type = RVIN_INPUT_CSI2;
+			inputs[num].chsel = i;
+			inputs[num].hint = rvin_group_chsel_get(grp, sd) == i;
+			inputs[num].source_idx = grp->source[csi].source_idx;
+			inputs[num].sink_idx =
+				rvin_group_get_sink_idx(
+					&grp->source[csi].subdev->entity,
+					inputs[num].source_idx);
+			if ((vin->chip == RCAR_H3) && (RCAR_PRR_IS_PRODUCT(H3)
+				&& (RCAR_PRR_CHK_CUT(H3, WS11) <= 0)))
+				strlcpy(inputs[num].name,
+					rvin_group_h3_ws11_map[chan][i].name,
+					RVIN_INPUT_NAME_SIZE);
+			else if (vin->chip == RCAR_H3)
+				strlcpy(inputs[num].name,
+					rvin_group_h3_map[chan][i].name,
+					RVIN_INPUT_NAME_SIZE);
+			else if (vin->chip == RCAR_M3)
+				strlcpy(inputs[num].name,
+					rvin_group_m3_map[chan][i].name,
+					RVIN_INPUT_NAME_SIZE);
+			grp_dbg(grp, "chan%d: %s source pad: %d sink pad: %d\n",
+				chan, inputs[num].name, inputs[num].source_idx,
+				inputs[num].sink_idx);
+			num++;
+		}
+	}
+
+	grp->users[chan]++;
+out:
+	mutex_unlock(&grp->lock);
+
+	return num;
+}
+
+static int rvin_group_put(struct v4l2_subdev *sd)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+
+	mutex_lock(&grp->lock);
+	grp->users[sd_to_chan(grp, sd)]--;
+	mutex_unlock(&grp->lock);
+
+	return 0;
+}
+
+static int rvin_group_set_input(struct v4l2_subdev *sd,
+				struct rvin_input_item *item)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_chan_id chan, master;
+	int i, chsel, ret = 0;
+
+	chan = sd_to_chan(grp, sd);
+	chsel = item->chsel;
+
+	mutex_lock(&grp->lock);
+
+	/* No need to set chsel if it's already set */
+	if (chsel == rvin_group_chsel_get(grp, sd))
+		goto out;
+
+	/* Do not allow a chsel that is not usable for channel */
+	if (rvin_group_get_csi(grp, sd, chsel) == RVIN_CSI_ERROR) {
+		grp_err(grp, "chan%d: Invalid chsel\n", chan);
+		goto out;
+	}
+
+	/* If subgroup master is not present we can't write the chsel */
+	master = chan_to_master(grp, chan);
+	if (!grp->chan[master].subdev) {
+		grp_err(grp, "chan%d: No group master found\n", chan);
+		goto out;
+	}
+
+	/*
+	 * Check that all needed resurces are free. We don't want to
+	 * change input selection if somone else uses our subgroup or
+	 * if there are another user of our channel.
+	 */
+	for (i = 0; i < RVIN_CHAN_MAX; i++) {
+
+		/* Only look in our sub group */
+		if (master != chan_to_master(grp, i))
+			continue;
+
+		/* Need to be only user of channel and subgroup to set hsel */
+		if ((i == chan && grp->users[i] != 1) ||
+		    (i != chan && grp->users[i] != 0)) {
+			grp_info(grp, "chan%d: %s in use, can't set chsel\n",
+				 chan, i == chan ? "Channel" : "Group");
+			ret = -EBUSY;
+			goto out;
+		}
+	}
+
+	ret = v4l2_subdev_call(grp->chan[master].subdev, core, s_gpio, chsel);
+	rvin_group_chsel_set(grp, sd, chsel);
+out:
+	mutex_unlock(&grp->lock);
+	return ret;
+}
+
+static int rvin_group_get_code(struct v4l2_subdev *sd, u32 *code)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR)
+		return -ENODEV;
+
+	*code = grp->source[csi].code;
+
+	return 0;
+}
+
+static int rvin_group_get_mbus_cfg(struct v4l2_subdev *sd,
+				   struct v4l2_mbus_config *mbus_cfg)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR)
+		return -ENODEV;
+
+	*mbus_cfg = grp->source[csi].mbus_cfg;
+
+	return 0;
+}
+
+static int rvin_group_ctrl_add_handler(struct v4l2_subdev *sd,
+				       struct v4l2_ctrl_handler *hdl)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR)
+		return -ENODEV;
+
+	return v4l2_ctrl_add_handler(hdl, grp->source[csi].subdev->ctrl_handler,
+				     NULL);
+}
+
+static int rvin_group_alloc_pad_config(struct v4l2_subdev *sd,
+				       struct v4l2_subdev_pad_config **cfg)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR)
+		return -ENODEV;
+
+	*cfg =  v4l2_subdev_alloc_pad_config(grp->source[csi].subdev);
+
+	return 0;
+}
+
+static int rvin_group_g_tvnorms_input(struct v4l2_subdev *sd,
+				      struct rvin_input_item *item,
+				      v4l2_std_id *std)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = rvin_group_get_csi(grp, sd, item->chsel);
+
+	if (csi == RVIN_CSI_ERROR)
+		return -EINVAL;
+
+	return v4l2_subdev_call(grp->source[csi].subdev, video,
+				g_tvnorms, std);
+}
+
+static int rvin_group_g_input_status(struct v4l2_subdev *sd,
+				     struct rvin_input_item *item, u32 *status)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = rvin_group_get_csi(grp, sd, item->chsel);
+
+	if (csi == RVIN_CSI_ERROR)
+		return -EINVAL;
+
+	return v4l2_subdev_call(grp->source[csi].subdev, video,
+				g_input_status, status);
+}
+
+static int rvin_group_dv_timings_cap(struct v4l2_subdev *sd,
+				     struct rvin_input_item *item,
+				     struct v4l2_dv_timings_cap *cap)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = rvin_group_get_csi(grp, sd, item->chsel);
+
+	if (csi == RVIN_CSI_ERROR)
+		return -EINVAL;
+
+	return v4l2_subdev_call(grp->source[csi].subdev, pad,
+				dv_timings_cap, cap);
+}
+
+static int rvin_group_enum_dv_timings(struct v4l2_subdev *sd,
+				      struct rvin_input_item *item,
+				      struct v4l2_enum_dv_timings *timings)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = rvin_group_get_csi(grp, sd, item->chsel);
+
+	if (csi == RVIN_CSI_ERROR)
+		return -EINVAL;
+
+	return v4l2_subdev_call(grp->source[csi].subdev, pad,
+				enum_dv_timings, timings);
+}
+
+static const struct rvin_group_input_ops rvin_input_ops = {
+	.g_tvnorms = &rvin_group_g_tvnorms_input,
+	.g_input_status = &rvin_group_g_input_status,
+	.dv_timings_cap = &rvin_group_dv_timings_cap,
+	.enum_dv_timings = &rvin_group_enum_dv_timings,
+};
+
+/* -----------------------------------------------------------------------------
+ * Basic group subdev operations
+ */
+
+static int rvin_group_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+	int ret = 0;
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR)
+		return -ENODEV;
+
+	mutex_lock(&grp->lock);
+	/* If we already are powerd just increment the usage */
+	if ((on && grp->power[csi] != 0) || (!on && grp->power[csi] != 1))
+		goto unlock;
+
+	/* Important to start bridge fist, it needs a quiet bus to start */
+	ret = v4l2_subdev_call(grp->bridge[csi].subdev, core, s_power, on);
+	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		goto unlock_err;
+	ret = v4l2_subdev_call(grp->source[csi].subdev, core, s_power, on);
+	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		goto unlock_err;
+
+	grp_dbg(grp, "csi%d: power: %d bridge: %s source: %s\n", csi,
+		on, grp->bridge[csi].subdev->name,
+		grp->source[csi].subdev->name);
+unlock:
+	grp->power[csi] += on ? 1 : -1;
+unlock_err:
+	mutex_unlock(&grp->lock);
+	return ret;
+}
+
+static const struct v4l2_subdev_core_ops rvin_group_core_ops = {
+	.s_power	= &rvin_group_s_power,
+};
+
+static int rvin_group_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR)
+		return -ENODEV;
+
+	return v4l2_subdev_call(grp->source[csi].subdev, video, g_std, std);
+}
+
+static int rvin_group_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR)
+		return -ENODEV;
+
+	return v4l2_subdev_call(grp->source[csi].subdev, video, s_std, std);
+}
+
+static int rvin_group_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR)
+		return -ENODEV;
+
+	return v4l2_subdev_call(grp->source[csi].subdev, video, querystd, std);
+}
+
+static int rvin_group_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *tvnorms)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR)
+		return -ENODEV;
+
+	return v4l2_subdev_call(grp->source[csi].subdev, video, g_tvnorms,
+				tvnorms);
+}
+
+static int rvin_group_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+	int ret = 0;
+
+	mutex_lock(&grp->lock);
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR) {
+		ret = -ENODEV;
+		goto unlock_err;
+	}
+
+	/* If we already are streaming just increment the usage */
+	if ((enable && grp->stream[csi] != 0) ||
+	    (!enable && grp->stream[csi] != 1))
+		goto unlock;
+
+	/* Important to start bridge fist, it needs a quiet bus to start */
+	ret = v4l2_subdev_call(grp->bridge[csi].subdev, video, s_stream,
+			       enable);
+	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		goto unlock_err;
+	ret = v4l2_subdev_call(grp->source[csi].subdev, video, s_stream,
+			       enable);
+	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		goto unlock_err;
+
+	grp_dbg(grp, "csi%d: stream: %d bridge: %s source %s\n", csi,
+		enable, grp->bridge[csi].subdev->name,
+		grp->source[csi].subdev->name);
+unlock:
+	grp->stream[csi] += enable ? 1 : -1;
+unlock_err:
+	mutex_unlock(&grp->lock);
+	return ret;
+}
+
+static int rvin_group_g_dv_timings(struct v4l2_subdev *sd,
+				   struct v4l2_dv_timings *timings)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR)
+		return -ENODEV;
+
+	return v4l2_subdev_call(grp->source[csi].subdev, video,
+				g_dv_timings, timings);
+}
+
+static int rvin_group_s_dv_timings(struct v4l2_subdev *sd,
+				   struct v4l2_dv_timings *timings)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR)
+		return -ENODEV;
+
+	return v4l2_subdev_call(grp->source[csi].subdev, video,
+				s_dv_timings, timings);
+}
+
+static int rvin_group_query_dv_timings(struct v4l2_subdev *sd,
+				       struct v4l2_dv_timings *timings)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR)
+		return -ENODEV;
+
+	return v4l2_subdev_call(grp->source[csi].subdev, video,
+				query_dv_timings, timings);
+}
+
+static const struct v4l2_subdev_video_ops rvin_group_video_ops = {
+	.g_std			= rvin_group_g_std,
+	.s_std			= rvin_group_s_std,
+	.querystd		= rvin_group_querystd,
+	.g_tvnorms		= rvin_group_g_tvnorms,
+	.s_stream		= rvin_group_s_stream,
+	.g_dv_timings		= rvin_group_g_dv_timings,
+	.s_dv_timings		= rvin_group_s_dv_timings,
+	.query_dv_timings	= rvin_group_query_dv_timings,
+};
+
+static int rvin_group_get_fmt(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_pad_config *pad_cfg,
+			      struct v4l2_subdev_format *fmt)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR)
+		return -ENODEV;
+
+	return v4l2_subdev_call(grp->source[csi].subdev, pad, get_fmt,
+				pad_cfg, fmt);
+}
+
+static int rvin_group_set_fmt(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_pad_config *pad_cfg,
+			      struct v4l2_subdev_format *fmt)
+{
+	struct rvin_group *grp = v4l2_get_subdev_hostdata(sd);
+	enum rvin_csi_id csi;
+	int ret;
+
+	csi = sd_to_csi(grp, sd);
+	if (csi == RVIN_CSI_ERROR)
+		return -ENODEV;
+
+	/* First the source and then inform the bridge about the format. */
+	ret = v4l2_subdev_call(grp->source[csi].subdev, pad, set_fmt,
+			       pad_cfg, fmt);
+	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		return ret;
+	return v4l2_subdev_call(grp->bridge[csi].subdev, pad, set_fmt,
+				NULL, fmt);
+}
+
+static const struct v4l2_subdev_pad_ops rvin_group_pad_ops = {
+	.get_fmt	= rvin_group_get_fmt,
+	.set_fmt	= rvin_group_set_fmt,
+};
+
+static const struct v4l2_subdev_ops rvin_group_ops = {
+	.core		= &rvin_group_core_ops,
+	.video		= &rvin_group_video_ops,
+	.pad		= &rvin_group_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Async notifier
+ */
+
+#define notifier_to_grp(n) container_of(n, struct rvin_group, notifier)
+
+static void __verify_source_pad(struct rvin_graph_entity *entity)
+{
+	if (entity->source_idx >= entity->subdev->entity.num_pads)
+		goto use_default;
+	if (entity->subdev->entity.pads[entity->source_idx].flags !=
+	    MEDIA_PAD_FL_SOURCE)
+		goto use_default;
+	return;
+use_default:
+	entity->source_idx = sd_to_pad_idx(entity->subdev, MEDIA_PAD_FL_SOURCE);
+}
+
+
+static int rvin_graph_notify_complete(struct v4l2_async_notifier *notifier)
+{
+	struct rvin_group *grp = notifier_to_grp(notifier);
+	int i;
+
+	for (i = 0; i < RVIN_CSI_MAX; i++) {
+		if (!grp->source[i].subdev)
+			continue;
+
+		__verify_source_pad(&grp->source[i]);
+
+		if (!rvin_mbus_supported(&grp->source[i])) {
+			grp_err(grp,
+				"Unsupported media bus format for %s pad %d\n",
+				grp->source[i].subdev->name,
+				grp->source[i].source_idx);
+			return -EINVAL;
+		}
+
+		grp_dbg(grp, "Found media bus format for %s pad %d: %d\n",
+			grp->source[i].subdev->name, grp->source[i].source_idx,
+			grp->source[i].code);
+	}
+	return 0;
+}
+
+static void rvin_graph_notify_unbind(struct v4l2_async_notifier *notifier,
+				     struct v4l2_subdev *subdev,
+				     struct v4l2_async_subdev *asd)
+{
+	struct rvin_group *grp = notifier_to_grp(notifier);
+	bool matched = false;
+	int i;
+
+	for (i = 0; i < RVIN_CSI_MAX; i++) {
+		if (grp->bridge[i].subdev == subdev) {
+			grp_dbg(grp, "unbind bridge subdev %s\n", subdev->name);
+			grp->bridge[i].subdev = NULL;
+			matched = true;
+		}
+		if (grp->source[i].subdev == subdev) {
+			grp_dbg(grp, "unbind source subdev %s\n", subdev->name);
+			grp->source[i].subdev = NULL;
+			matched = true;
+		}
+	}
+
+	for (i = 0; i < RVIN_CHAN_MAX; i++) {
+		if (grp->chan[i].subdev == subdev) {
+			grp_dbg(grp, "unbind chan subdev %s\n", subdev->name);
+			grp->chan[i].subdev = NULL;
+			matched = true;
+		}
+	}
+
+	if (!matched)
+		grp_err(grp, "no entity for subdev %s to unbind\n", subdev->name);
+}
+
+static int rvin_graph_notify_bound(struct v4l2_async_notifier *notifier,
+				   struct v4l2_subdev *subdev,
+				   struct v4l2_async_subdev *asd)
+{
+	struct rvin_group *grp = notifier_to_grp(notifier);
+	bool matched = false;
+	int i, j;
+
+	v4l2_set_subdev_hostdata(subdev, grp);
+
+	for (i = 0; i < RVIN_CSI_MAX; i++) {
+		if (grp->bridge[i].asd.match.of.node == subdev->dev->of_node) {
+			grp_dbg(grp, "bound bridge subdev %s\n", subdev->name);
+			grp->bridge[i].subdev = subdev;
+
+			for (j = i; j < (i + RVIN_VCSEL_MAX); j++)
+				grp->bridge[j].subdev = subdev;
+
+			matched = true;
+		}
+		if (grp->source[i].asd.match.of.node == subdev->dev->of_node) {
+			grp_dbg(grp, "bound source subdev %s\n", subdev->name);
+			grp->source[i].subdev = subdev;
+			matched = true;
+		}
+	}
+
+	for (i = 0; i < RVIN_CHAN_MAX; i++) {
+		if (grp->chan[i].asd.match.of.node == subdev->dev->of_node) {
+			grp_dbg(grp, "bound chan subdev %s\n", subdev->name);
+			grp->chan[i].subdev = subdev;
+
+			/* Write initial chsel if binding subgroup master */
+			if (i == RVIN_CHAN0)
+				v4l2_subdev_call(subdev, core, s_gpio,
+						 grp->chsel1);
+			if (i == RVIN_CHAN4)
+				v4l2_subdev_call(subdev, core, s_gpio,
+						 grp->chsel2);
+
+			matched = true;
+		}
+	}
+
+	if (matched)
+		return 0;
+
+	grp_err(grp, "no entity for subdev %s to bind\n", subdev->name);
+	return -EINVAL;
+}
+
+static int rvin_parse_v4l2_endpoint(struct rvin_group *grp,
+				    struct device_node *ep,
+				    struct v4l2_mbus_config *mbus_cfg)
+{
+	struct v4l2_of_endpoint v4l2_ep;
+	int ret;
+
+	ret = v4l2_of_parse_endpoint(ep, &v4l2_ep);
+	if (ret) {
+		grp_err(grp, "Could not parse v4l2 endpoint\n");
+		return -EINVAL;
+	}
+
+	if (v4l2_ep.bus_type != V4L2_MBUS_CSI2) {
+		grp_err(grp, "Unsupported media bus type for %s\n",
+			of_node_full_name(ep));
+		return -EINVAL;
+	}
+
+	mbus_cfg->type = v4l2_ep.bus_type;
+	mbus_cfg->flags = v4l2_ep.bus.mipi_csi2.flags;
+
+	return 0;
+}
+
+static int rvin_get_csi_source(struct rvin_group *grp, int id)
+{
+	struct device_node *ep, *np, *rp, *bridge = NULL, *source = NULL;
+	struct v4l2_mbus_config mbus_cfg;
+	struct of_endpoint endpoint;
+	int ret;
+	u32 souce_cnt = 0, loop_cnt = 0;
+
+	grp->bridge[id].asd.match.of.node = NULL;
+	grp->bridge[id].subdev = NULL;
+	grp->source[id].asd.match.of.node = NULL;
+	grp->source[id].subdev = NULL;
+
+	/* Not all indexes will be defined, this is OK */
+	ep = of_graph_get_endpoint_by_regs(grp->dev->of_node, RVIN_PORT_CSI,
+					   id);
+	if (!ep)
+		return 0;
+
+	/* Get bridge */
+	bridge = of_graph_get_remote_port_parent(ep);
+	of_node_put(ep);
+	if (!bridge) {
+		grp_err(grp, "No bridge found for endpoint '%s'\n",
+			of_node_full_name(ep));
+		return -EINVAL;
+	}
+
+	/* Not all bridges are available, this is OK */
+	if (!of_device_is_available(bridge)) {
+		of_node_put(bridge);
+		return 0;
+	}
+
+	/* Get source */
+	for_each_endpoint_of_node(bridge, ep) {
+		np = of_graph_get_remote_port_parent(ep);
+		if (!np) {
+			grp_err(grp, "No remote found for endpoint '%s'\n",
+				of_node_full_name(ep));
+			of_node_put(bridge);
+			of_node_put(ep);
+			return -EINVAL;
+		}
+
+		if (grp->dev->of_node == np) {
+			/* Ignore loop-back */
+		} else if (!of_device_is_available(np)) {
+			/* Not all sources are available, this is OK */
+		} else if (source) {
+			souce_cnt++;
+
+			if (souce_cnt == loop_cnt) {
+				grp_dbg(grp, "Multiple source for %s\n",
+					of_node_full_name(source));
+
+				ret = rvin_parse_v4l2_endpoint(grp, ep,
+								 &mbus_cfg);
+				if (ret) {
+					of_node_put(bridge);
+					of_node_put(ep);
+					of_node_put(np);
+					return ret;
+				}
+				source = np;
+				grp->source[id].mbus_cfg = mbus_cfg;
+				grp->source[id].asd.match.of.node = source;
+				grp->source[id].asd.match_type =
+							 V4L2_ASYNC_MATCH_OF;
+			}
+		} else {
+			/* Get endpoint information */
+			rp = of_graph_get_remote_port(ep);
+			of_graph_parse_endpoint(rp, &endpoint);
+			of_node_put(rp);
+
+			ret = rvin_parse_v4l2_endpoint(grp, ep, &mbus_cfg);
+			if (ret) {
+				of_node_put(bridge);
+				of_node_put(ep);
+				of_node_put(np);
+				return ret;
+			}
+			source = np;
+			grp->source[id].mbus_cfg = mbus_cfg;
+			grp->source[id].source_idx = endpoint.id;
+			grp->source[id].asd.match.of.node = source;
+			grp->source[id].asd.match_type = V4L2_ASYNC_MATCH_OF;
+
+			/* Multiple source registration */
+			if ((id % 4) == 1)
+				loop_cnt = 1;
+			else if ((id % 4) == 2)
+				loop_cnt = 2;
+			else if ((id % 4) == 3)
+				loop_cnt = 3;
+			else
+				loop_cnt = 0;
+		}
+
+		of_node_put(np);
+	}
+	of_node_put(bridge);
+
+	grp->bridge[id].asd.match.of.node = bridge;
+	grp->bridge[id].asd.match_type = V4L2_ASYNC_MATCH_OF;
+
+	grp_dbg(grp, "csi%d: bridge: %s source: %s pad: %d", id,
+		of_node_full_name(grp->bridge[id].asd.match.of.node),
+		of_node_full_name(grp->source[id].asd.match.of.node),
+		grp->source[id].source_idx);
+
+	return ret;
+}
+
+static int rvin_get_remote_channels(struct rvin_group *grp, int id)
+{
+	struct device_node *ep, *remote;
+	int ret = 0;
+
+	grp->chan[id].asd.match.of.node = NULL;
+	grp->chan[id].subdev = NULL;
+
+	/* Not all indexes will be defined, this is OK */
+	ep = of_graph_get_endpoint_by_regs(grp->dev->of_node, RVIN_PORT_REMOTE,
+					   id);
+	if (!ep)
+		return 0;
+
+	/* Find remote subdevice */
+	remote = of_graph_get_remote_port_parent(ep);
+	if (!remote) {
+		grp_err(grp, "No remote parent for endpoint '%s'\n",
+			of_node_full_name(ep));
+		ret = -EINVAL;
+		goto out_ep;
+	}
+
+	/* Not all remotes will be available, this is OK */
+	if (!of_device_is_available(remote)) {
+		ret = 0;
+		goto out_remote;
+	}
+
+	grp->chan[id].asd.match.of.node = remote;
+	grp->chan[id].asd.match_type = V4L2_ASYNC_MATCH_OF;
+
+	grp_dbg(grp, "chan%d: node: '%s'\n", id,
+		of_node_full_name(grp->chan[id].asd.match.of.node));
+
+out_remote:
+	of_node_put(remote);
+out_ep:
+	of_node_put(ep);
+
+	return ret;
+}
+
+static int __node_add(struct v4l2_async_subdev **subdev, int num,
+		       struct rvin_graph_entity *entity)
+{
+	int i;
+
+	if (!entity->asd.match.of.node)
+		return 0;
+
+	for (i = 0; i < num; i++) {
+
+		if (!subdev[i]) {
+			subdev[i] = &entity->asd;
+			return 1;
+		}
+
+		if (subdev[i]->match.of.node == entity->asd.match.of.node)
+			break;
+	}
+
+	return 0;
+}
+
+static int rvin_graph_init(struct rvin_group *grp)
+{
+	struct v4l2_async_subdev **subdevs = NULL;
+	int i, ret, found = 0, matched = 0;
+
+	/* Try to get CSI2 sources */
+	for (i = 0; i < RVIN_CSI_MAX; i++) {
+		ret = rvin_get_csi_source(grp, i);
+		if (ret)
+			return ret;
+		if (grp->bridge[i].asd.match.of.node &&
+		    grp->source[i].asd.match.of.node)
+			found += 2;
+	}
+
+	/* Try to get slave channels */
+	for (i = 0; i < RVIN_CHAN_MAX; i++) {
+		ret = rvin_get_remote_channels(grp, i);
+		if (ret)
+			return ret;
+		if (grp->chan[i].asd.match.of.node)
+			found++;
+	}
+
+	if (!found)
+		return -ENODEV;
+
+	/* Register the subdevices notifier. */
+	subdevs = devm_kzalloc(grp->dev, sizeof(*subdevs) * found, GFP_KERNEL);
+	if (subdevs == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < RVIN_CSI_MAX; i++) {
+		matched += __node_add(subdevs, found, &grp->bridge[i]);
+		matched += __node_add(subdevs, found, &grp->source[i]);
+	}
+	for (i = 0; i < RVIN_CHAN_MAX; i++)
+		matched += __node_add(subdevs, found, &grp->chan[i]);
+
+	grp_dbg(grp, "found %d group subdevice(s) %d are unique\n", found, matched);
+
+	grp->notifier.num_subdevs = matched;
+	grp->notifier.subdevs = subdevs;
+	grp->notifier.bound = rvin_graph_notify_bound;
+	grp->notifier.unbind = rvin_graph_notify_unbind;
+	grp->notifier.complete = rvin_graph_notify_complete;
+
+	ret = v4l2_async_notifier_register(grp->v4l2_dev, &grp->notifier);
+	if (ret < 0) {
+		grp_err(grp, "Notifier registration failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Base
+ */
+
+struct rvin_group_api *rvin_group_probe(struct device *dev,
+					struct v4l2_device *v4l2_dev)
+{
+	struct rvin_group *grp;
+	int i, ret;
+
+	grp = devm_kzalloc(dev, sizeof(*grp), GFP_KERNEL);
+	if (!grp)
+		return NULL;
+
+	grp->dev = dev;
+	grp->v4l2_dev = v4l2_dev;
+	grp->chsel1 = 0;
+	grp->chsel2 = 1;
+
+	ret = RCAR_PRR_INIT();
+	if (ret) {
+		devm_kfree(dev, grp);
+		grp_dbg(grp, "product register init fail.\n");
+		return NULL;
+	}
+
+	for (i = 0; i < RVIN_CSI_MAX; i++) {
+		grp->power[i] = 0;
+		grp->stream[i] = 0;
+	}
+
+	for (i = 0; i < RVIN_CHAN_MAX; i++)
+		grp->users[i] = 0;
+
+	ret = rvin_graph_init(grp);
+	if (ret) {
+		devm_kfree(dev, grp);
+		return NULL;
+	}
+
+	mutex_init(&grp->lock);
+
+	grp->api.ops = &rvin_group_ops;
+	grp->api.input_ops = &rvin_input_ops;
+
+	grp->api.get = rvin_group_get;
+	grp->api.put = rvin_group_put;
+	grp->api.set_input = rvin_group_set_input;
+	grp->api.get_code = rvin_group_get_code;
+	grp->api.get_mbus_cfg = rvin_group_get_mbus_cfg;
+	grp->api.ctrl_add_handler = rvin_group_ctrl_add_handler;
+	grp->api.alloc_pad_config = rvin_group_alloc_pad_config;
+
+	return &grp->api;
+}
+
+int rvin_group_remove(struct rvin_group_api *api)
+{
+	struct rvin_group *grp = container_of(api, struct rvin_group, api);
+
+	v4l2_async_notifier_unregister(&grp->notifier);
+
+	mutex_destroy(&grp->lock);
+
+	return 0;
+}
diff --git a/drivers/media/platform/rcar-vin/rcar-group.h b/drivers/media/platform/rcar-vin/rcar-group.h
new file mode 100644
index 0000000..9171aeb
--- /dev/null
+++ b/drivers/media/platform/rcar-vin/rcar-group.h
@@ -0,0 +1,168 @@
+/*
+ * Driver for Renesas R-Car VIN
+ *
+ * Copyright (C) 2016 Renesas Electronics Corp.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __RCAR_GROUP__
+#define __RCAR_GROUP__
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+
+#define RVIN_PORT_LOCAL 0
+#define RVIN_PORT_CSI 1
+#define RVIN_PORT_REMOTE 2
+
+enum rvin_input_type {
+	RVIN_INPUT_NONE,
+	RVIN_INPUT_DIGITAL,
+	RVIN_INPUT_CSI2,
+};
+
+/* Max number of inputs supported */
+#define RVIN_INPUT_MAX 7
+#define RVIN_INPUT_NAME_SIZE 32
+
+/**
+ * struct rvin_input_item - One possible input for the channel
+ * @name:	User-friendly name of the input
+ * @type:	Type of the input or RVIN_INPUT_NONE if not available
+ * @chsel:	The chsel value needed to select this input
+ * @sink_idx:	Sink pad number from the subdevice associated with the input
+ * @source_idx:	Source pad number from the subdevice associated with the input
+ */
+struct rvin_input_item {
+	char name[RVIN_INPUT_NAME_SIZE];
+	enum rvin_input_type type;
+	int chsel;
+	bool hint;
+	int sink_idx;
+	int source_idx;
+};
+
+/**
+ * struct rvin_graph_entity - Video endpoint from async framework
+ * @asd:	sub-device descriptor for async framework
+ * @subdev:	subdevice matched using async framework
+ * @code:	Media bus format from source
+ * @mbus_cfg:	Media bus format from DT
+ * @source_idx:	Source pad on remote device
+ */
+struct rvin_graph_entity {
+	struct v4l2_async_subdev asd;
+	struct v4l2_subdev *subdev;
+
+	u32 code;
+	struct v4l2_mbus_config mbus_cfg;
+
+	unsigned int source_idx;
+};
+
+static inline int rvin_mbus_supported(struct rvin_graph_entity *entity)
+{
+	struct v4l2_subdev *sd = entity->subdev;
+	struct v4l2_subdev_mbus_code_enum code = {
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+	};
+
+	code.index = 0;
+	code.pad = entity->source_idx;
+
+	while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) {
+		code.index++;
+		switch (code.code) {
+		case MEDIA_BUS_FMT_YUYV8_1X16:
+		case MEDIA_BUS_FMT_UYVY8_2X8:
+		case MEDIA_BUS_FMT_UYVY10_2X10:
+		case MEDIA_BUS_FMT_RGB888_1X24:
+			entity->code = code.code;
+			return true;
+		default:
+			break;
+		}
+	}
+
+	/*
+	 * Older versions where looking for the wrong media bus format.
+	 * It where looking for a YUVY format but then treated it as a
+	 * UYVY format. This was not noticed since atlest one subdevice
+	 * used for testing (adv7180) reported a YUVY media bus format
+	 * but provided UYVY data. There might be other unknown subdevices
+	 * which also do this, to not break compatibility try to use them
+	 * in legacy mode.
+	 */
+	code.index = 0;
+	while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) {
+		code.index++;
+		switch (code.code) {
+		case MEDIA_BUS_FMT_YUYV8_2X8:
+			entity->code = MEDIA_BUS_FMT_UYVY8_2X8;
+			return true;
+		case MEDIA_BUS_FMT_YUYV10_2X10:
+			entity->code = MEDIA_BUS_FMT_UYVY10_2X10;
+			return true;
+		default:
+			break;
+		}
+	}
+
+	return false;
+}
+
+static inline int sd_to_pad_idx(struct v4l2_subdev *sd, int flag)
+{
+	int pad_idx;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++)
+		if (sd->entity.pads[pad_idx].flags == flag)
+			break;
+
+	if (pad_idx >= sd->entity.num_pads)
+		pad_idx = 0;
+#else
+	pad_idx = 0;
+#endif
+	return pad_idx;
+}
+
+struct rvin_group_input_ops {
+	int (*g_input_status)(struct v4l2_subdev *sd,
+			      struct rvin_input_item *item, u32 *status);
+	int (*g_tvnorms)(struct v4l2_subdev *sd,
+			 struct rvin_input_item *item, v4l2_std_id *std);
+	int (*dv_timings_cap)(struct v4l2_subdev *sd,
+			      struct rvin_input_item *item,
+			      struct v4l2_dv_timings_cap *cap);
+	int (*enum_dv_timings)(struct v4l2_subdev *sd,
+			       struct rvin_input_item *item,
+			       struct v4l2_enum_dv_timings *timings);
+};
+
+struct rvin_group_api {
+	int (*get)(struct v4l2_subdev *sd, struct rvin_input_item *inputs);
+	int (*put)(struct v4l2_subdev *sd);
+	int (*set_input)(struct v4l2_subdev *sd, struct rvin_input_item *item);
+	int (*get_code)(struct v4l2_subdev *sd, u32 *code);
+	int (*get_mbus_cfg)(struct v4l2_subdev *sd,
+			    struct v4l2_mbus_config *mbus_cfg);
+
+	int (*ctrl_add_handler)(struct v4l2_subdev *sd,
+				struct v4l2_ctrl_handler *hdl);
+	int (*alloc_pad_config)(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config **cfg);
+
+	const struct v4l2_subdev_ops *ops;
+	const struct rvin_group_input_ops *input_ops;
+};
+
+struct rvin_group_api *rvin_group_probe(struct device *dev,
+					struct v4l2_device *v4l2_dev);
+int rvin_group_remove(struct rvin_group_api *grp);
+
+#endif
diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c
index 2bbe6d4..928ed14 100644
--- a/drivers/media/platform/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c
@@ -25,6 +25,8 @@
 #define RVIN_DEFAULT_FORMAT	V4L2_PIX_FMT_YUYV
 #define RVIN_MAX_WIDTH		2048
 #define RVIN_MAX_HEIGHT		2048
+#define RVIN_MAX_WIDTH_GEN3	4096
+#define RVIN_MAX_HEIGHT_GEN3	4096
 
 /* -----------------------------------------------------------------------------
  * Format Conversions
@@ -32,6 +34,10 @@
 
 static const struct rvin_video_format rvin_formats[] = {
 	{
+		.fourcc			= V4L2_PIX_FMT_NV12,
+		.bpp			= 1,
+	},
+	{
 		.fourcc			= V4L2_PIX_FMT_NV16,
 		.bpp			= 1,
 	},
@@ -48,10 +54,14 @@ static const struct rvin_video_format rvin_formats[] = {
 		.bpp			= 2,
 	},
 	{
-		.fourcc			= V4L2_PIX_FMT_XRGB555,
+		.fourcc			= V4L2_PIX_FMT_ARGB555,
 		.bpp			= 2,
 	},
 	{
+		.fourcc			= V4L2_PIX_FMT_ABGR32,
+		.bpp			= 4,
+	},
+	{
 		.fourcc			= V4L2_PIX_FMT_XBGR32,
 		.bpp			= 4,
 	},
@@ -85,6 +95,9 @@ static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix)
 	if (pix->pixelformat == V4L2_PIX_FMT_NV16)
 		return pix->bytesperline * pix->height * 2;
 
+	if (pix->pixelformat == V4L2_PIX_FMT_NV12)
+		return pix->bytesperline * pix->height * 3 / 2;
+
 	return pix->bytesperline * pix->height;
 }
 
@@ -92,99 +105,37 @@ static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix)
  * V4L2
  */
 
-static void rvin_reset_crop_compose(struct rvin_dev *vin)
-{
-	vin->crop.top = vin->crop.left = 0;
-	vin->crop.width = vin->source.width;
-	vin->crop.height = vin->source.height;
-
-	vin->compose.top = vin->compose.left = 0;
-	vin->compose.width = vin->format.width;
-	vin->compose.height = vin->format.height;
-}
-
-static int rvin_reset_format(struct rvin_dev *vin)
-{
-	struct v4l2_subdev_format fmt = {
-		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
-	};
-	struct v4l2_mbus_framefmt *mf = &fmt.format;
-	int ret;
-
-	fmt.pad = vin->src_pad_idx;
-
-	ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt);
-	if (ret)
-		return ret;
-
-	vin->format.width	= mf->width;
-	vin->format.height	= mf->height;
-	vin->format.colorspace	= mf->colorspace;
-	vin->format.field	= mf->field;
-
-	/*
-	 * If the subdevice uses ALTERNATE field mode and G_STD is
-	 * implemented use the VIN HW to combine the two fields to
-	 * one INTERLACED frame. The ALTERNATE field mode can still
-	 * be requested in S_FMT and be respected, this is just the
-	 * default which is applied at probing or when S_STD is called.
-	 */
-	if (vin->format.field == V4L2_FIELD_ALTERNATE &&
-	    v4l2_subdev_has_op(vin_to_source(vin), video, g_std))
-		vin->format.field = V4L2_FIELD_INTERLACED;
-
-	switch (vin->format.field) {
-	case V4L2_FIELD_TOP:
-	case V4L2_FIELD_BOTTOM:
-	case V4L2_FIELD_ALTERNATE:
-		vin->format.height /= 2;
-		break;
-	case V4L2_FIELD_NONE:
-	case V4L2_FIELD_INTERLACED_TB:
-	case V4L2_FIELD_INTERLACED_BT:
-	case V4L2_FIELD_INTERLACED:
-		break;
-	default:
-		vin->format.field = V4L2_FIELD_NONE;
-		break;
-	}
-
-	rvin_reset_crop_compose(vin);
-
-	return 0;
-}
-
 static int __rvin_try_format_source(struct rvin_dev *vin,
 				    u32 which,
 				    struct v4l2_pix_format *pix,
 				    struct rvin_source_fmt *source)
 {
-	struct v4l2_subdev *sd;
 	struct v4l2_subdev_pad_config *pad_cfg;
 	struct v4l2_subdev_format format = {
 		.which = which,
 	};
+	u32 code;
 	enum v4l2_field field;
 	int ret;
 
-	sd = vin_to_source(vin);
+	ret = rvin_subdev_get_code(vin, &code);
+	if (ret)
+		return -EINVAL;
 
-	v4l2_fill_mbus_format(&format.format, pix, vin->digital.code);
+	v4l2_fill_mbus_format(&format.format, pix, code);
 
-	pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+	pad_cfg = rvin_subdev_alloc_pad_config(vin);
 	if (pad_cfg == NULL)
 		return -ENOMEM;
 
-	format.pad = vin->src_pad_idx;
+	format.pad = vin->inputs[vin->current_input].source_idx;
 
 	field = pix->field;
 
-	ret = v4l2_subdev_call(sd, pad, set_fmt, pad_cfg, &format);
+	ret = rvin_subdev_call(vin, pad, set_fmt, pad_cfg, &format);
 	if (ret < 0 && ret != -ENOIOCTLCMD)
 		goto done;
 
-	v4l2_fill_pix_format(pix, &format.format);
-
 	pix->field = field;
 
 	source->width = pix->width;
@@ -204,7 +155,9 @@ static int __rvin_try_format(struct rvin_dev *vin,
 			     struct rvin_source_fmt *source)
 {
 	const struct rvin_video_format *info;
-	u32 rwidth, rheight, walign;
+	u32 rwidth, rheight, walign, max_width, max_height;
+	int ret;
+	v4l2_std_id std;
 
 	/* Requested */
 	rwidth = pix->width;
@@ -244,7 +197,17 @@ static int __rvin_try_format(struct rvin_dev *vin,
 	case V4L2_FIELD_NONE:
 	case V4L2_FIELD_INTERLACED_TB:
 	case V4L2_FIELD_INTERLACED_BT:
+		break;
 	case V4L2_FIELD_INTERLACED:
+		ret = rvin_subdev_call(vin, video, querystd, &std);
+		if (ret == -ENOIOCTLCMD)
+			pix->field = V4L2_FIELD_NONE;
+		else if (ret < 0)
+			goto out;
+		else
+			pix->field = std & V4L2_STD_625_50 ?
+				V4L2_FIELD_INTERLACED_TB :
+				V4L2_FIELD_INTERLACED_BT;
 		break;
 	default:
 		pix->field = V4L2_FIELD_NONE;
@@ -255,12 +218,39 @@ static int __rvin_try_format(struct rvin_dev *vin,
 	if (source->width != rwidth || source->height != rheight)
 		rvin_scale_try(vin, pix, rwidth, rheight);
 
-	/* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */
-	walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1;
+	/* At the time of capturing NV16/NV12 format, user has to specify
+	 * the width of the multiple of 32 for H/W specification.
+	 */
+	if (((pix->pixelformat == V4L2_PIX_FMT_NV16) ||
+		(pix->pixelformat == V4L2_PIX_FMT_NV12)) &&
+		(pix->width % 32)) {
+		vin_err(vin,
+			"specify width of 32 multiple in separate format.\n");
+		return -EINVAL;
+	}
+
+	/* HW limit width to a multiple of 32 (2^5) for NV16/12 else 2 (2^1) */
+	if ((pix->pixelformat == V4L2_PIX_FMT_NV12) ||
+		(pix->pixelformat == V4L2_PIX_FMT_NV16))
+		walign = 5;
+	else if ((pix->pixelformat == V4L2_PIX_FMT_YUYV) ||
+		(pix->pixelformat == V4L2_PIX_FMT_UYVY))
+		walign = 1;
+	else
+		walign = 0;
 
 	/* Limit to VIN capabilities */
-	v4l_bound_align_image(&pix->width, 2, RVIN_MAX_WIDTH, walign,
-			      &pix->height, 4, RVIN_MAX_HEIGHT, 2, 0);
+	if (vin->chip == RCAR_H3 || vin->chip == RCAR_M3) {
+		max_width = RVIN_MAX_WIDTH_GEN3;
+		max_height = RVIN_MAX_HEIGHT_GEN3;
+	} else {
+		max_width = RVIN_MAX_WIDTH;
+		max_height = RVIN_MAX_HEIGHT;
+	}
+
+	/* Limit to VIN capabilities */
+	v4l_bound_align_image(&pix->width, 5, max_width, walign,
+			      &pix->height, 2, max_height, 0, 0);
 
 	pix->bytesperline = max_t(u32, pix->bytesperline,
 				  rvin_format_bytesperline(pix));
@@ -272,11 +262,26 @@ static int __rvin_try_format(struct rvin_dev *vin,
 		return -EINVAL;
 	}
 
+	if ((vin->chip != RCAR_H3 && vin->chip != RCAR_M3) &&
+		(pix->pixelformat == V4L2_PIX_FMT_NV12)) {
+		vin_err(vin, "pixel format NV12 is supported from GEN3\n");
+		return -EINVAL;
+	}
+
+	if ((vin->chip != RCAR_H3 && vin->chip != RCAR_M3 &&
+		vin->chip != RCAR_GEN2) &&
+		(pix->pixelformat == V4L2_PIX_FMT_NV12)) {
+		vin_err(vin, "pixel format ARGB8888 is supported from GEN2\n");
+		return -EINVAL;
+	}
+
 	vin_dbg(vin, "Requested %ux%u Got %ux%u bpl: %d size: %d\n",
 		rwidth, rheight, pix->width, pix->height,
 		pix->bytesperline, pix->sizeimage);
 
 	return 0;
+out:
+	return ret;
 }
 
 static int rvin_querycap(struct file *file, void *priv,
@@ -301,10 +306,8 @@ static int rvin_try_fmt_vid_cap(struct file *file, void *priv,
 				 &source);
 }
 
-static int rvin_s_fmt_vid_cap(struct file *file, void *priv,
-			      struct v4l2_format *f)
+static int __rvin_s_fmt_vid_cap(struct rvin_dev *vin, struct v4l2_format *f)
 {
-	struct rvin_dev *vin = video_drvdata(file);
 	struct rvin_source_fmt source;
 	int ret;
 
@@ -321,11 +324,17 @@ static int rvin_s_fmt_vid_cap(struct file *file, void *priv,
 
 	vin->format = f->fmt.pix;
 
-	rvin_reset_crop_compose(vin);
-
 	return 0;
 }
 
+static int rvin_s_fmt_vid_cap(struct file *file, void *priv,
+			      struct v4l2_format *f)
+{
+	struct rvin_dev *vin = video_drvdata(file);
+
+	return __rvin_s_fmt_vid_cap(vin, f);
+}
+
 static int rvin_g_fmt_vid_cap(struct file *file, void *priv,
 			      struct v4l2_format *f)
 {
@@ -457,79 +466,217 @@ static int rvin_cropcap(struct file *file, void *priv,
 			struct v4l2_cropcap *crop)
 {
 	struct rvin_dev *vin = video_drvdata(file);
-	struct v4l2_subdev *sd = vin_to_source(vin);
+	struct v4l2_subdev_format fmt = {
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+	};
+	struct v4l2_mbus_framefmt *mf = &fmt.format;
+	int ret;
 
 	if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		return -EINVAL;
 
-	return v4l2_subdev_call(sd, video, g_pixelaspect, &crop->pixelaspect);
+	/* Changing the standard will change the width/height */
+	ret = rvin_subdev_call(vin, pad, get_fmt, NULL, &fmt);
+	if (ret) {
+		vin_err(vin, "Failed to get initial format\n");
+		return ret;
+	}
+
+	crop->bounds.left = 0;
+	crop->bounds.top  = 0;
+	crop->bounds.width = mf->width;
+	crop->bounds.height = mf->height;
+
+	/* default cropping rectangle */
+	crop->defrect = crop->bounds;
+	crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	return 0;
+}
+
+static int rvin_attach_subdevices(struct rvin_dev *vin)
+{
+	struct v4l2_subdev_format fmt = {
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+	};
+	struct v4l2_mbus_framefmt *mf = &fmt.format;
+	struct v4l2_format f;
+	int ret;
+
+	ret = rvin_subdev_set_input(vin, &vin->inputs[vin->current_input]);
+	if (ret)
+		return ret;
+
+	ret = rvin_subdev_call(vin, core, s_power, 1);
+	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		return ret;
+
+	vin->vdev.tvnorms = 0;
+	ret = rvin_subdev_call(vin, video, g_tvnorms, &vin->vdev.tvnorms);
+	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		goto error;
+
+	/* Add controlls */
+	ret = rvin_subdev_ctrl_add_handler(vin);
+	if (ret < 0)
+		goto error;
+
+	v4l2_ctrl_handler_setup(&vin->ctrl_handler);
+
+	fmt.pad = vin->inputs[vin->current_input].source_idx;
+
+	/* Try to improve our guess of a reasonable window format */
+	ret = rvin_subdev_call(vin, pad, get_fmt, NULL, &fmt);
+	if (ret)
+		goto error;
+
+	/* Set default format */
+	vin->format.width	= mf->width;
+	vin->format.height	= mf->height;
+	vin->format.colorspace	= mf->colorspace;
+	vin->format.field	= mf->field;
+	vin->format.pixelformat	= RVIN_DEFAULT_FORMAT;
+
+	/* Set initial crop and compose */
+	vin->crop.top = vin->crop.left = 0;
+	vin->crop.width = mf->width;
+	vin->crop.height = mf->height;
+
+	vin->compose.top = vin->compose.left = 0;
+	vin->compose.width = mf->width;
+	vin->compose.height = mf->height;
+
+	f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	f.fmt.pix = vin->format;
+	ret = __rvin_s_fmt_vid_cap(vin, &f);
+	if (ret)
+		goto error;
+
+	return 0;
+error:
+	rvin_subdev_call(vin, core, s_power, 0);
+	return ret;
+}
+
+static void rvin_detach_subdevices(struct rvin_dev *vin)
+{
+	rvin_subdev_call(vin, core, s_power, 0);
 }
 
 static int rvin_enum_input(struct file *file, void *priv,
 			   struct v4l2_input *i)
 {
 	struct rvin_dev *vin = video_drvdata(file);
-	struct v4l2_subdev *sd = vin_to_source(vin);
+	struct v4l2_dv_timings_cap cap;
+	struct rvin_input_item *item;
 	int ret;
 
-	if (i->index != 0)
+	if (i->index > RVIN_INPUT_MAX ||
+	    vin->inputs[i->index].type == RVIN_INPUT_NONE)
 		return -EINVAL;
 
-	ret = v4l2_subdev_call(sd, video, g_input_status, &i->status);
+	item = &vin->inputs[i->index];
+
+	ret = rvin_subdev_call_input(vin, i->index, video,
+				     g_input_status, &i->status);
+
 	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
 		return ret;
 
 	i->type = V4L2_INPUT_TYPE_CAMERA;
-	i->std = vin->vdev.tvnorms;
+	strlcpy(i->name, item->name, sizeof(i->name));
 
-	if (v4l2_subdev_has_op(sd, pad, dv_timings_cap))
+	/* Test if pad supports dv_timings_cap */
+	cap.pad = vin->inputs[i->index].sink_idx;
+	ret = rvin_subdev_call_input(vin, i->index, pad, dv_timings_cap, &cap);
+	if (ret) {
+		i->capabilities = V4L2_IN_CAP_STD;
+		ret = rvin_subdev_call_input(vin,
+					     vin->current_input,
+					     video, g_tvnorms, &i->std);
+		if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+			return ret;
+
+	} else {
 		i->capabilities = V4L2_IN_CAP_DV_TIMINGS;
-
-	strlcpy(i->name, "Camera", sizeof(i->name));
+		i->std = 0;
+	}
 
 	return 0;
 }
 
 static int rvin_g_input(struct file *file, void *priv, unsigned int *i)
 {
-	*i = 0;
+	struct rvin_dev *vin = video_drvdata(file);
+
+	*i = vin->current_input;
 	return 0;
 }
 
 static int rvin_s_input(struct file *file, void *priv, unsigned int i)
 {
-	if (i > 0)
+	struct rvin_dev *vin = video_drvdata(file);
+	int ret;
+
+	if (i > RVIN_INPUT_MAX || vin->inputs[i].type == RVIN_INPUT_NONE)
 		return -EINVAL;
-	return 0;
+
+	rvin_detach_subdevices(vin);
+
+	ret = rvin_subdev_set_input(vin, &vin->inputs[i]);
+	if (!ret)
+		vin->current_input = i;
+
+	/* Power on new subdevice */
+	return rvin_attach_subdevices(vin);
 }
 
 static int rvin_querystd(struct file *file, void *priv, v4l2_std_id *a)
 {
 	struct rvin_dev *vin = video_drvdata(file);
-	struct v4l2_subdev *sd = vin_to_source(vin);
 
-	return v4l2_subdev_call(sd, video, querystd, a);
+	return rvin_subdev_call(vin, video, querystd, a);
 }
 
 static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a)
 {
 	struct rvin_dev *vin = video_drvdata(file);
+	struct v4l2_subdev_format fmt = {
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+	};
+	struct v4l2_mbus_framefmt *mf = &fmt.format;
 	int ret;
 
-	ret = v4l2_subdev_call(vin_to_source(vin), video, s_std, a);
+	ret = rvin_subdev_call(vin, video, s_std, a);
 	if (ret < 0)
 		return ret;
 
 	/* Changing the standard will change the width/height */
-	return rvin_reset_format(vin);
+	ret = rvin_subdev_call(vin, pad, get_fmt, NULL, &fmt);
+	if (ret) {
+		vin_err(vin, "Failed to get initial format\n");
+		return ret;
+	}
+
+	vin->format.width = mf->width;
+	vin->format.height = mf->height;
+
+	vin->crop.top = vin->crop.left = 0;
+	vin->crop.width = mf->width;
+	vin->crop.height = mf->height;
+
+	vin->compose.top = vin->compose.left = 0;
+	vin->compose.width = mf->width;
+	vin->compose.height = mf->height;
+
+	return 0;
 }
 
 static int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a)
 {
 	struct rvin_dev *vin = video_drvdata(file);
-	struct v4l2_subdev *sd = vin_to_source(vin);
 
-	return v4l2_subdev_call(sd, video, g_std, a);
+	return rvin_subdev_call(vin, video, g_std, a);
 }
 
 static int rvin_subscribe_event(struct v4l2_fh *fh,
@@ -546,15 +693,15 @@ static int rvin_enum_dv_timings(struct file *file, void *priv_fh,
 				struct v4l2_enum_dv_timings *timings)
 {
 	struct rvin_dev *vin = video_drvdata(file);
-	struct v4l2_subdev *sd = vin_to_source(vin);
-	int pad, ret;
+	int input, ret;
 
-	pad = timings->pad;
-	timings->pad = vin->sink_pad_idx;
+	input = timings->pad;
 
-	ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings);
+	timings->pad = vin->inputs[input].sink_idx;
 
-	timings->pad = pad;
+	ret = rvin_subdev_call_input(vin, input, pad, enum_dv_timings, timings);
+
+	timings->pad = input;
 
 	return ret;
 }
@@ -563,52 +710,48 @@ static int rvin_s_dv_timings(struct file *file, void *priv_fh,
 			     struct v4l2_dv_timings *timings)
 {
 	struct rvin_dev *vin = video_drvdata(file);
-	struct v4l2_subdev *sd = vin_to_source(vin);
-	int ret;
+	int err;
 
-	ret = v4l2_subdev_call(sd, video, s_dv_timings, timings);
-	if (ret)
-		return ret;
-
-	vin->source.width = timings->bt.width;
-	vin->source.height = timings->bt.height;
-	vin->format.width = timings->bt.width;
-	vin->format.height = timings->bt.height;
-
-	return 0;
+	err = rvin_subdev_call(vin, video, s_dv_timings, timings);
+	if (!err) {
+		vin->source.width = timings->bt.width;
+		vin->source.height = timings->bt.height;
+		vin->format.width = timings->bt.width;
+		vin->format.height = timings->bt.height;
+	}
+	return err;
 }
 
 static int rvin_g_dv_timings(struct file *file, void *priv_fh,
 			     struct v4l2_dv_timings *timings)
 {
 	struct rvin_dev *vin = video_drvdata(file);
-	struct v4l2_subdev *sd = vin_to_source(vin);
 
-	return v4l2_subdev_call(sd, video, g_dv_timings, timings);
+	return rvin_subdev_call(vin, video, g_dv_timings, timings);
 }
 
 static int rvin_query_dv_timings(struct file *file, void *priv_fh,
 				 struct v4l2_dv_timings *timings)
 {
 	struct rvin_dev *vin = video_drvdata(file);
-	struct v4l2_subdev *sd = vin_to_source(vin);
 
-	return v4l2_subdev_call(sd, video, query_dv_timings, timings);
+	return rvin_subdev_call(vin, video, query_dv_timings, timings);
 }
 
 static int rvin_dv_timings_cap(struct file *file, void *priv_fh,
 			       struct v4l2_dv_timings_cap *cap)
 {
 	struct rvin_dev *vin = video_drvdata(file);
-	struct v4l2_subdev *sd = vin_to_source(vin);
-	int pad, ret;
+	int input, ret;
 
-	pad = cap->pad;
-	cap->pad = vin->sink_pad_idx;
+	input = cap->pad;
 
-	ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap);
+	cap->pad = vin->inputs[input].sink_idx;
 
-	cap->pad = pad;
+	ret = rvin_subdev_call_input(vin, input, pad,
+				     dv_timings_cap, cap);
+
+	cap->pad = input;
 
 	return ret;
 }
@@ -616,7 +759,6 @@ static int rvin_dv_timings_cap(struct file *file, void *priv_fh,
 static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)
 {
 	struct rvin_dev *vin = video_drvdata(file);
-	struct v4l2_subdev *sd = vin_to_source(vin);
 	int input, ret;
 
 	if (edid->pad)
@@ -625,7 +767,7 @@ static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)
 	input = edid->pad;
 	edid->pad = vin->sink_pad_idx;
 
-	ret = v4l2_subdev_call(sd, pad, get_edid, edid);
+	ret = rvin_subdev_call_local(vin, pad, get_edid, edid);
 
 	edid->pad = input;
 
@@ -635,7 +777,6 @@ static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)
 static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)
 {
 	struct rvin_dev *vin = video_drvdata(file);
-	struct v4l2_subdev *sd = vin_to_source(vin);
 	int input, ret;
 
 	if (edid->pad)
@@ -644,7 +785,7 @@ static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)
 	input = edid->pad;
 	edid->pad = vin->sink_pad_idx;
 
-	ret = v4l2_subdev_call(sd, pad, set_edid, edid);
+	ret = rvin_subdev_call_local(vin, pad, set_edid, edid);
 
 	edid->pad = input;
 
@@ -699,80 +840,6 @@ static const struct v4l2_ioctl_ops rvin_ioctl_ops = {
  * File Operations
  */
 
-static int rvin_power_on(struct rvin_dev *vin)
-{
-	int ret;
-	struct v4l2_subdev *sd = vin_to_source(vin);
-
-	pm_runtime_get_sync(vin->v4l2_dev.dev);
-
-	ret = v4l2_subdev_call(sd, core, s_power, 1);
-	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
-		return ret;
-	return 0;
-}
-
-static int rvin_power_off(struct rvin_dev *vin)
-{
-	int ret;
-	struct v4l2_subdev *sd = vin_to_source(vin);
-
-	ret = v4l2_subdev_call(sd, core, s_power, 0);
-
-	pm_runtime_put(vin->v4l2_dev.dev);
-
-	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
-		return ret;
-
-	return 0;
-}
-
-static int rvin_initialize_device(struct file *file)
-{
-	struct rvin_dev *vin = video_drvdata(file);
-	int ret;
-
-	struct v4l2_format f = {
-		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
-		.fmt.pix = {
-			.width		= vin->format.width,
-			.height		= vin->format.height,
-			.field		= vin->format.field,
-			.colorspace	= vin->format.colorspace,
-			.pixelformat	= vin->format.pixelformat,
-		},
-	};
-
-	ret = rvin_power_on(vin);
-	if (ret < 0)
-		return ret;
-
-	pm_runtime_enable(&vin->vdev.dev);
-	ret = pm_runtime_resume(&vin->vdev.dev);
-	if (ret < 0 && ret != -ENOSYS)
-		goto eresume;
-
-	/*
-	 * Try to configure with default parameters. Notice: this is the
-	 * very first open, so, we cannot race against other calls,
-	 * apart from someone else calling open() simultaneously, but
-	 * .host_lock is protecting us against it.
-	 */
-	ret = rvin_s_fmt_vid_cap(file, NULL, &f);
-	if (ret < 0)
-		goto esfmt;
-
-	v4l2_ctrl_handler_setup(&vin->ctrl_handler);
-
-	return 0;
-esfmt:
-	pm_runtime_disable(&vin->vdev.dev);
-eresume:
-	rvin_power_off(vin);
-
-	return ret;
-}
-
 static int rvin_open(struct file *file)
 {
 	struct rvin_dev *vin = video_drvdata(file);
@@ -784,17 +851,30 @@ static int rvin_open(struct file *file)
 
 	ret = v4l2_fh_open(file);
 	if (ret)
-		goto unlock;
+		goto err_out;
 
-	if (!v4l2_fh_is_singular_file(file))
-		goto unlock;
+	ret = rvin_subdev_get(vin);
+	if (ret)
+		goto err_open;
 
-	if (rvin_initialize_device(file)) {
-		v4l2_fh_release(file);
-		ret = -ENODEV;
+	if (v4l2_fh_is_singular_file(file)) {
+		pm_runtime_get_sync(vin->dev);
+		ret = rvin_attach_subdevices(vin);
+		if (ret) {
+			vin_err(vin, "Error attaching subdevices\n");
+			goto err_get;
+		}
 	}
 
-unlock:
+	mutex_unlock(&vin->lock);
+
+	return 0;
+err_get:
+	pm_runtime_put(vin->dev);
+	rvin_subdev_put(vin);
+err_open:
+	v4l2_fh_release(file);
+err_out:
 	mutex_unlock(&vin->lock);
 	return ret;
 }
@@ -818,11 +898,12 @@ static int rvin_release(struct file *file)
 	 * Then de-initialize hw module.
 	 */
 	if (fh_singular) {
-		pm_runtime_suspend(&vin->vdev.dev);
-		pm_runtime_disable(&vin->vdev.dev);
-		rvin_power_off(vin);
+		rvin_detach_subdevices(vin);
+		pm_runtime_put(vin->dev);
 	}
 
+	rvin_subdev_put(vin);
+
 	mutex_unlock(&vin->lock);
 
 	return ret;
@@ -868,41 +949,10 @@ static void rvin_notify(struct v4l2_subdev *sd,
 int rvin_v4l2_probe(struct rvin_dev *vin)
 {
 	struct video_device *vdev = &vin->vdev;
-	struct v4l2_subdev *sd = vin_to_source(vin);
-	int pad_idx, ret;
-
-	v4l2_set_subdev_hostdata(sd, vin);
+	int ret;
 
 	vin->v4l2_dev.notify = rvin_notify;
 
-	ret = v4l2_subdev_call(sd, video, g_tvnorms, &vin->vdev.tvnorms);
-	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
-		return ret;
-
-	if (vin->vdev.tvnorms == 0) {
-		/* Disable the STD API if there are no tvnorms defined */
-		v4l2_disable_ioctl(&vin->vdev, VIDIOC_G_STD);
-		v4l2_disable_ioctl(&vin->vdev, VIDIOC_S_STD);
-		v4l2_disable_ioctl(&vin->vdev, VIDIOC_QUERYSTD);
-		v4l2_disable_ioctl(&vin->vdev, VIDIOC_ENUMSTD);
-	}
-
-	/* Add the controls */
-	/*
-	 * Currently the subdev with the largest number of controls (13) is
-	 * ov6550. So let's pick 16 as a hint for the control handler. Note
-	 * that this is a hint only: too large and you waste some memory, too
-	 * small and there is a (very) small performance hit when looking up
-	 * controls in the internal hash.
-	 */
-	ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16);
-	if (ret < 0)
-		return ret;
-
-	ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, sd->ctrl_handler, NULL);
-	if (ret < 0)
-		return ret;
-
 	/* video node */
 	vdev->fops = &rvin_fops;
 	vdev->v4l2_dev = &vin->v4l2_dev;
@@ -915,25 +965,6 @@ int rvin_v4l2_probe(struct rvin_dev *vin)
 	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
 		V4L2_CAP_READWRITE;
 
-	vin->src_pad_idx = 0;
-	for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++)
-		if (sd->entity.pads[pad_idx].flags == MEDIA_PAD_FL_SOURCE)
-			break;
-	if (pad_idx >= sd->entity.num_pads)
-		return -EINVAL;
-
-	vin->src_pad_idx = pad_idx;
-
-	vin->sink_pad_idx = 0;
-	for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++)
-		if (sd->entity.pads[pad_idx].flags == MEDIA_PAD_FL_SINK) {
-			vin->sink_pad_idx = pad_idx;
-			break;
-		}
-
-	vin->format.pixelformat	= RVIN_DEFAULT_FORMAT;
-	rvin_reset_format(vin);
-
 	ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1);
 	if (ret) {
 		vin_err(vin, "Failed to register video device\n");
diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h
index 727e215..e129054 100644
--- a/drivers/media/platform/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/rcar-vin/rcar-vin.h
@@ -23,6 +23,10 @@
 #include <media/v4l2-device.h>
 #include <media/videobuf2-v4l2.h>
 
+#include "rcar-group.h"
+
+#define DRV_NAME "rcar-vin"
+
 /* Number of HW buffers */
 #define HW_BUFFER_NUM 3
 
@@ -30,9 +34,12 @@
 #define HW_BUFFER_MASK 0x7f
 
 enum chip_id {
+	RCAR_M3,
+	RCAR_H3,
+	RCAR_GEN3,
+	RCAR_GEN2,
 	RCAR_H1,
 	RCAR_M1,
-	RCAR_GEN2,
 };
 
 /**
@@ -68,19 +75,17 @@ struct rvin_video_format {
 	u8 bpp;
 };
 
-/**
- * struct rvin_graph_entity - Video endpoint from async framework
- * @asd:	sub-device descriptor for async framework
- * @subdev:	subdevice matched using async framework
- * @code:	Media bus format from source
- * @mbus_cfg:	Media bus format from DT
- */
-struct rvin_graph_entity {
-	struct v4l2_async_subdev asd;
-	struct v4l2_subdev *subdev;
-
-	u32 code;
-	struct v4l2_mbus_config mbus_cfg;
+enum gen3_vin_ch {
+	RCAR_VIN_CH_NONE = -1,
+	RCAR_VIDEO_0,
+	RCAR_VIDEO_1,
+	RCAR_VIDEO_2,
+	RCAR_VIDEO_3,
+	RCAR_VIDEO_4,
+	RCAR_VIDEO_5,
+	RCAR_VIDEO_6,
+	RCAR_VIDEO_7,
+	RCAR_VIDEO_MAX,
 };
 
 /**
@@ -113,6 +118,12 @@ struct rvin_graph_entity {
  *
  * @crop:		active cropping
  * @compose:		active composing
+ *
+ * @slave:		subdevice used to register with the group master
+ * @api:		group api controller (only used on master channel)
+ *
+ * @current_input:	currently used input in @inputs
+ * @inputs:		list of valid inputs sources
  */
 struct rvin_dev {
 	struct device *dev;
@@ -142,9 +153,15 @@ struct rvin_dev {
 
 	struct v4l2_rect crop;
 	struct v4l2_rect compose;
-};
 
-#define vin_to_source(vin)		vin->digital.subdev
+	struct v4l2_subdev slave;
+	struct rvin_group_api *api;
+
+	int current_input;
+	struct rvin_input_item inputs[RVIN_INPUT_MAX];
+
+	unsigned int index;
+};
 
 /* Debug */
 #define vin_dbg(d, fmt, arg...)		dev_dbg(d->dev, fmt, ##arg)
@@ -152,9 +169,13 @@ struct rvin_dev {
 #define vin_warn(d, fmt, arg...)	dev_warn(d->dev, fmt, ##arg)
 #define vin_err(d, fmt, arg...)		dev_err(d->dev, fmt, ##arg)
 
+int rvin_set_ifmd(struct rvin_dev *vin, int val);
 int rvin_dma_probe(struct rvin_dev *vin, int irq);
 void rvin_dma_remove(struct rvin_dev *vin);
 
+int rvin_subdev_probe(struct rvin_dev *vin);
+void rvin_subdev_remove(struct rvin_dev *vin);
+
 int rvin_v4l2_probe(struct rvin_dev *vin);
 void rvin_v4l2_remove(struct rvin_dev *vin);
 
@@ -165,4 +186,39 @@ void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix,
 		    u32 width, u32 height);
 void rvin_crop_scale_comp(struct rvin_dev *vin);
 
+/* Subdevice group helpers */
+#define rvin_input_is_csi(v) (v->inputs[v->current_input].type == \
+			      RVIN_INPUT_CSI2)
+#define vin_to_group(v) container_of(v->slave.v4l2_dev, struct rvin_dev, \
+				     v4l2_dev)->api
+#define rvin_subdev_call_local(v, o, f, args...)			\
+	(v->digital.subdev ?						\
+	 v4l2_subdev_call(v->digital.subdev, o, f, ##args) : -ENODEV)
+#define rvin_subdev_call_group(v, o, f, args...)			\
+	(!(v)->slave.v4l2_dev ? -ENODEV :				\
+	 (vin_to_group(v)->ops->o && vin_to_group(v)->ops->o->f) ?	\
+	 vin_to_group(v)->ops->o->f(&v->slave, ##args) : -ENOIOCTLCMD)
+#define rvin_subdev_call_group_input(v, i, f, args...)			\
+	(!(v)->slave.v4l2_dev ? -ENODEV :				\
+	 (vin_to_group(v)->input_ops->f ?				\
+	  vin_to_group(v)->input_ops->f(&v->slave, i, ##args) : -ENOIOCTLCMD))
+#define rvin_subdev_call(v, o, f, args...)				\
+	(rvin_input_is_csi(v) ? rvin_subdev_call_group(v, o, f, ##args) :\
+	 rvin_subdev_call_local(v, o, f, ##args))
+#define rvin_subdev_call_input(v, i, o, f, args...)			\
+	(v->inputs[i].type == RVIN_INPUT_CSI2 ?				\
+	 rvin_subdev_call_group_input(v, &v->inputs[i], f, ##args) :	\
+	 rvin_subdev_call_local(v, o, f, ##args))
+
+int rvin_subdev_get(struct rvin_dev *vin);
+int rvin_subdev_put(struct rvin_dev *vin);
+int rvin_subdev_set_input(struct rvin_dev *vin, struct rvin_input_item *item);
+
+int rvin_subdev_get_code(struct rvin_dev *vin, u32 *code);
+int rvin_subdev_get_mbus_cfg(struct rvin_dev *vin,
+			     struct v4l2_mbus_config *mbus_cfg);
+
+int rvin_subdev_ctrl_add_handler(struct rvin_dev *vin);
+struct v4l2_subdev_pad_config *rvin_subdev_alloc_pad_config(struct rvin_dev
+							    *vin);
 #endif
diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile
index a33afc3..7e2258c 100644
--- a/drivers/media/platform/vsp1/Makefile
+++ b/drivers/media/platform/vsp1/Makefile
@@ -4,6 +4,6 @@
 vsp1-y					+= vsp1_clu.o vsp1_hsit.o vsp1_lut.o
 vsp1-y					+= vsp1_bru.o vsp1_sru.o vsp1_uds.o
 vsp1-y					+= vsp1_hgo.o vsp1_hgt.o vsp1_histo.o
-vsp1-y					+= vsp1_lif.o
+vsp1-y					+= vsp1_lif.o vsp1_brs.o
 
 obj-$(CONFIG_VIDEO_RENESAS_VSP1)	+= vsp1.o
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
index 85387a6..d8a32a3 100644
--- a/drivers/media/platform/vsp1/vsp1.h
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -1,7 +1,7 @@
 /*
  * vsp1.h  --  R-Car VSP1 Driver
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -31,6 +31,7 @@ struct vsp1_drm;
 struct vsp1_entity;
 struct vsp1_platform_data;
 struct vsp1_bru;
+struct vsp1_brs;
 struct vsp1_clu;
 struct vsp1_hgo;
 struct vsp1_hgt;
@@ -44,6 +45,7 @@ struct vsp1_uds;
 #define VSP1_MAX_RPF		5
 #define VSP1_MAX_UDS		3
 #define VSP1_MAX_WPF		4
+#define VSP1_MAX_LIF		2
 
 #define VSP1_HAS_LIF		(1 << 0)
 #define VSP1_HAS_LUT		(1 << 1)
@@ -54,6 +56,7 @@ struct vsp1_uds;
 #define VSP1_HAS_WPF_HFLIP	(1 << 6)
 #define VSP1_HAS_HGO		(1 << 7)
 #define VSP1_HAS_HGT		(1 << 8)
+#define VSP1_HAS_BRS		(1 << 9)
 
 struct vsp1_device_info {
 	u32 version;
@@ -65,6 +68,7 @@ struct vsp1_device_info {
 	unsigned int wpf_count;
 	unsigned int num_bru_inputs;
 	bool uapi;
+	bool header_mode;
 };
 
 struct vsp1_device {
@@ -76,12 +80,13 @@ struct vsp1_device {
 	struct rcar_fcp_device *fcp;
 
 	struct vsp1_bru *bru;
+	struct vsp1_brs *brs;
 	struct vsp1_clu *clu;
 	struct vsp1_hgo *hgo;
 	struct vsp1_hgt *hgt;
 	struct vsp1_hsit *hsi;
 	struct vsp1_hsit *hst;
-	struct vsp1_lif *lif;
+	struct vsp1_lif *lif[VSP1_MAX_LIF];
 	struct vsp1_lut *lut;
 	struct vsp1_rwpf *rpf[VSP1_MAX_RPF];
 	struct vsp1_sru *sru;
@@ -95,11 +100,21 @@ struct vsp1_device {
 	struct media_device media_dev;
 	struct media_entity_operations media_ops;
 
+	unsigned int num_brs_inputs;
+
+	bool auto_fld_mode;
+
 	struct vsp1_drm *drm;
+	int index;
+
+	dma_addr_t dl_addr;
+	unsigned int dl_body;
 };
 
+int vsp1_gen3_vspdl_check(struct vsp1_device *vsp1);
 int vsp1_device_get(struct vsp1_device *vsp1);
 void vsp1_device_put(struct vsp1_device *vsp1);
+void vsp1_underrun_workaround(struct vsp1_device *vsp1, bool reset);
 
 int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index);
 
diff --git a/drivers/media/platform/vsp1/vsp1_brs.c b/drivers/media/platform/vsp1/vsp1_brs.c
new file mode 100644
index 0000000..e9811a7
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_brs.c
@@ -0,0 +1,448 @@
+/*
+ * vsp1_brs.c  --  R-Car VSP1 Blend ROP Sub Unit
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This file is based on the drivers/media/platform/vsp1/vsp1_bru.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_brs.h"
+#include "vsp1_dl.h"
+#include "vsp1_pipe.h"
+#include "vsp1_rwpf.h"
+#include "vsp1_video.h"
+
+#define BRS_MIN_SIZE				1U
+#define BRS_MAX_SIZE				8190U
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline void vsp1_brs_write(struct vsp1_brs *brs, struct vsp1_dl_list *dl,
+				  u32 reg, u32 data)
+{
+	vsp1_dl_list_write(dl, reg, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * Controls
+ */
+
+static int brs_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct vsp1_brs *brs =
+		container_of(ctrl->handler, struct vsp1_brs, ctrls);
+
+	switch (ctrl->id) {
+	case V4L2_CID_BG_COLOR:
+		brs->bgcolor = ctrl->val;
+		break;
+	}
+
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops brs_ctrl_ops = {
+	.s_ctrl = brs_s_ctrl,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+/*
+ * The BRS can't perform format conversion, all sink and source formats must be
+ * identical. We pick the format on the first sink pad (pad 0) and propagate it
+ * to all other pads.
+ */
+
+static int brs_enum_mbus_code(struct v4l2_subdev *subdev,
+			      struct v4l2_subdev_pad_config *cfg,
+			      struct v4l2_subdev_mbus_code_enum *code)
+{
+	static const unsigned int codes[] = {
+		MEDIA_BUS_FMT_ARGB8888_1X32,
+		MEDIA_BUS_FMT_AYUV8_1X32,
+	};
+
+	return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
+					  ARRAY_SIZE(codes));
+}
+
+static int brs_enum_frame_size(struct v4l2_subdev *subdev,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->index)
+		return -EINVAL;
+
+	if (fse->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
+	    fse->code != MEDIA_BUS_FMT_AYUV8_1X32)
+		return -EINVAL;
+
+	fse->min_width = BRS_MIN_SIZE;
+	fse->max_width = BRS_MAX_SIZE;
+	fse->min_height = BRS_MIN_SIZE;
+	fse->max_height = BRS_MAX_SIZE;
+
+	return 0;
+}
+
+static struct v4l2_rect *brs_get_compose(struct vsp1_brs *brs,
+					 struct v4l2_subdev_pad_config *cfg,
+					 unsigned int pad)
+{
+	return v4l2_subdev_get_try_compose(&brs->entity.subdev, cfg, pad);
+}
+
+static void brs_try_format(struct vsp1_brs *brs,
+			   struct v4l2_subdev_pad_config *config,
+			   unsigned int pad, struct v4l2_mbus_framefmt *fmt)
+{
+	struct v4l2_mbus_framefmt *format;
+
+	switch (pad) {
+	case BRS_PAD_SINK(0):
+		/* Default to YUV if the requested format is not supported. */
+		if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
+		    fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
+			fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
+		break;
+
+	default:
+		/* The BRS can't perform format conversion. */
+		format = vsp1_entity_get_pad_format(&brs->entity, config,
+						    BRS_PAD_SINK(0));
+		fmt->code = format->code;
+		break;
+	}
+
+	fmt->width = clamp(fmt->width, BRS_MIN_SIZE, BRS_MAX_SIZE);
+	fmt->height = clamp(fmt->height, BRS_MIN_SIZE, BRS_MAX_SIZE);
+	fmt->field = V4L2_FIELD_NONE;
+	fmt->colorspace = V4L2_COLORSPACE_SRGB;
+}
+
+static int brs_set_format(struct v4l2_subdev *subdev,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct vsp1_brs *brs = to_brs(subdev);
+	struct v4l2_subdev_pad_config *config;
+	struct v4l2_mbus_framefmt *format;
+	int ret = 0;
+
+	mutex_lock(&brs->entity.lock);
+
+	config = vsp1_entity_get_pad_config(&brs->entity, cfg, fmt->which);
+	if (!config) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	brs_try_format(brs, config, fmt->pad, &fmt->format);
+
+	format = vsp1_entity_get_pad_format(&brs->entity, config, fmt->pad);
+	*format = fmt->format;
+
+	/* Reset the compose rectangle */
+	if (fmt->pad != brs->entity.source_pad) {
+		struct v4l2_rect *compose;
+
+		compose = brs_get_compose(brs, config, fmt->pad);
+		compose->left = 0;
+		compose->top = 0;
+		compose->width = format->width;
+		compose->height = format->height;
+	}
+
+	/* Propagate the format code to all pads */
+	if (fmt->pad == BRS_PAD_SINK(0)) {
+		unsigned int i;
+
+		for (i = 0; i <= brs->entity.source_pad; ++i) {
+			format = vsp1_entity_get_pad_format(&brs->entity,
+							    config, i);
+			format->code = fmt->format.code;
+		}
+	}
+
+done:
+	mutex_unlock(&brs->entity.lock);
+	return ret;
+}
+
+static int brs_get_selection(struct v4l2_subdev *subdev,
+			     struct v4l2_subdev_pad_config *cfg,
+			     struct v4l2_subdev_selection *sel)
+{
+	struct vsp1_brs *brs = to_brs(subdev);
+	struct v4l2_subdev_pad_config *config;
+
+	if (sel->pad == brs->entity.source_pad)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = BRS_MAX_SIZE;
+		sel->r.height = BRS_MAX_SIZE;
+		return 0;
+
+	case V4L2_SEL_TGT_COMPOSE:
+		config = vsp1_entity_get_pad_config(&brs->entity, cfg,
+						    sel->which);
+		if (!config)
+			return -EINVAL;
+
+		mutex_lock(&brs->entity.lock);
+		sel->r = *brs_get_compose(brs, config, sel->pad);
+		mutex_unlock(&brs->entity.lock);
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int brs_set_selection(struct v4l2_subdev *subdev,
+			     struct v4l2_subdev_pad_config *cfg,
+			     struct v4l2_subdev_selection *sel)
+{
+	struct vsp1_brs *brs = to_brs(subdev);
+	struct v4l2_subdev_pad_config *config;
+	struct v4l2_mbus_framefmt *format;
+	struct v4l2_rect *compose;
+	int ret = 0;
+
+	if (sel->pad == brs->entity.source_pad)
+		return -EINVAL;
+
+	if (sel->target != V4L2_SEL_TGT_COMPOSE)
+		return -EINVAL;
+
+	mutex_lock(&brs->entity.lock);
+
+	config = vsp1_entity_get_pad_config(&brs->entity, cfg, sel->which);
+	if (!config) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	/*
+	 * The compose rectangle top left corner must be inside the output
+	 * frame.
+	 */
+	format = vsp1_entity_get_pad_format(&brs->entity, config,
+					    brs->entity.source_pad);
+	sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
+	sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
+
+	/* Scaling isn't supported, the compose rectangle size must be identical
+	 * to the sink format size.
+	 */
+	format = vsp1_entity_get_pad_format(&brs->entity, config, sel->pad);
+	sel->r.width = format->width;
+	sel->r.height = format->height;
+
+	compose = brs_get_compose(brs, config, sel->pad);
+	*compose = sel->r;
+
+done:
+	mutex_unlock(&brs->entity.lock);
+	return ret;
+}
+
+static const struct v4l2_subdev_pad_ops brs_pad_ops = {
+	.init_cfg = vsp1_entity_init_cfg,
+	.enum_mbus_code = brs_enum_mbus_code,
+	.enum_frame_size = brs_enum_frame_size,
+	.get_fmt = vsp1_subdev_get_pad_format,
+	.set_fmt = brs_set_format,
+	.get_selection = brs_get_selection,
+	.set_selection = brs_set_selection,
+};
+
+static const struct v4l2_subdev_ops brs_ops = {
+	.pad    = &brs_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * VSP1 Entity Operations
+ */
+
+static void brs_configure(struct vsp1_entity *entity,
+			  struct vsp1_pipeline *pipe,
+			  struct vsp1_dl_list *dl,
+			  enum vsp1_entity_params params)
+{
+	struct vsp1_brs *brs = to_brs(&entity->subdev);
+	struct v4l2_mbus_framefmt *format;
+	unsigned int flags;
+	unsigned int i;
+
+	if (params != VSP1_ENTITY_PARAMS_INIT)
+		return;
+
+	format = vsp1_entity_get_pad_format(&brs->entity, brs->entity.config,
+					    brs->entity.source_pad);
+
+	if (pipe->vmute_flag) {
+		vsp1_brs_write(brs, dl, VI6_BRS_INCTRL, 0);
+		vsp1_brs_write(brs, dl, VI6_BRS_VIRRPF_SIZE,
+		  (format->width << VI6_BRS_VIRRPF_SIZE_HSIZE_SHIFT) |
+		  (format->height << VI6_BRS_VIRRPF_SIZE_VSIZE_SHIFT));
+		vsp1_brs_write(brs, dl, VI6_BRS_VIRRPF_LOC, 0);
+		vsp1_brs_write(brs, dl, VI6_BRS_VIRRPF_COL, (0xFF << 24));
+
+		for (i = 0; i < brs->entity.source_pad; ++i) {
+			vsp1_brs_write(brs, dl, VI6_BRS_BLD(i),
+			VI6_BRS_BLD_CCMDX_255_SRC_A |
+			VI6_BRS_BLD_CCMDY_SRC_A |
+			VI6_BRS_BLD_ACMDX_255_SRC_A |
+			VI6_BRS_BLD_ACMDY_COEFY |
+			VI6_BRS_BLD_COEFY_MASK);
+		}
+
+		return;
+	}
+
+	/* The hardware is extremely flexible but we have no userspace API to
+	 * expose all the parameters, nor is it clear whether we would have use
+	 * cases for all the supported modes. Let's just harcode the parameters
+	 * to sane default values for now.
+	 */
+
+	/* Disable dithering and enable color data normalization unless the
+	 * format at the pipeline output is premultiplied.
+	 */
+	flags = pipe->output ? pipe->output->format.flags : 0;
+	vsp1_brs_write(brs, dl, VI6_BRS_INCTRL,
+		       flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ?
+		       0 : VI6_BRS_INCTRL_NRM);
+
+	/* Set the background position to cover the whole output image and
+	 * configure its color.
+	 */
+	vsp1_brs_write(brs, dl, VI6_BRS_VIRRPF_SIZE,
+		       (format->width << VI6_BRS_VIRRPF_SIZE_HSIZE_SHIFT) |
+		       (format->height << VI6_BRS_VIRRPF_SIZE_VSIZE_SHIFT));
+	vsp1_brs_write(brs, dl, VI6_BRS_VIRRPF_LOC, 0);
+
+	vsp1_brs_write(brs, dl, VI6_BRS_VIRRPF_COL, brs->bgcolor |
+		       (0xff << VI6_BRS_VIRRPF_COL_A_SHIFT));
+
+	for (i = 0; i < brs->entity.source_pad; ++i) {
+		bool premultiplied = false;
+		u32 ctrl = 0;
+
+		/* Configure all Blend/ROP units corresponding to an enabled BRS
+		 * input for alpha blending. Blend/ROP units corresponding to
+		 * disabled BRS inputs are used in ROP NOP mode to ignore the
+		 * SRC input.
+		 */
+		if (brs->inputs[i].rpf) {
+			ctrl |= VI6_BRS_CTRL_RBC;
+
+			premultiplied = brs->inputs[i].rpf->format.flags
+				      & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
+		} else {
+			ctrl |= VI6_BRS_CTRL_CROP(VI6_ROP_NOP)
+			     |  VI6_BRS_CTRL_AROP(VI6_ROP_NOP);
+		}
+
+		/* Select the virtual RPF as the Blend/ROP unit A DST input to
+		 * serve as a background color.
+		 */
+		if (i == 0)
+			ctrl |= VI6_BRS_CTRL_DSTSEL_VRPF;
+
+		/* Route BRS inputs 0 to 3 as SRC inputs to Blend/ROP units A to
+		 * D in that order. The Blend/ROP unit B SRC is hardwired to the
+		 * ROP unit output, the corresponding register bits must be set
+		 * to 0.
+		 */
+		if (i != 1)
+			ctrl |= VI6_BRS_CTRL_SRCSEL_BRSIN(i);
+
+		vsp1_brs_write(brs, dl, VI6_BRS_CTRL(i), ctrl);
+
+		/* Harcode the blending formula to
+		 *
+		 *	DSTc = DSTc * (1 - SRCa) + SRCc * SRCa
+		 *	DSTa = DSTa * (1 - SRCa) + SRCa
+		 *
+		 * when the SRC input isn't premultiplied, and to
+		 *
+		 *	DSTc = DSTc * (1 - SRCa) + SRCc
+		 *	DSTa = DSTa * (1 - SRCa) + SRCa
+		 *
+		 * otherwise.
+		 */
+		vsp1_brs_write(brs, dl, VI6_BRS_BLD(i),
+			       VI6_BRS_BLD_CCMDX_255_SRC_A |
+			       (premultiplied ? VI6_BRS_BLD_CCMDY_COEFY :
+						VI6_BRS_BLD_CCMDY_SRC_A) |
+			       VI6_BRS_BLD_ACMDX_255_SRC_A |
+			       VI6_BRS_BLD_ACMDY_COEFY |
+			       (0xff << VI6_BRS_BLD_COEFY_SHIFT));
+	}
+}
+
+static const struct vsp1_entity_operations brs_entity_ops = {
+	.configure = brs_configure,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_brs *vsp1_brs_create(struct vsp1_device *vsp1)
+{
+	struct vsp1_brs *brs;
+	int ret;
+
+	brs = devm_kzalloc(vsp1->dev, sizeof(*brs), GFP_KERNEL);
+	if (brs == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	brs->entity.ops = &brs_entity_ops;
+	brs->entity.type = VSP1_ENTITY_BRS;
+
+	ret = vsp1_entity_init(vsp1, &brs->entity, "brs",
+			       vsp1->info->rpf_count + 1, &brs_ops,
+			       MEDIA_ENT_F_PROC_VIDEO_COMPOSER);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	/* Initialize the control handler. */
+	v4l2_ctrl_handler_init(&brs->ctrls, 1);
+	v4l2_ctrl_new_std(&brs->ctrls, &brs_ctrl_ops, V4L2_CID_BG_COLOR,
+			  0, 0xffffff, 1, 0);
+
+	brs->bgcolor = 0;
+
+	brs->entity.subdev.ctrl_handler = &brs->ctrls;
+
+	if (brs->ctrls.error) {
+		dev_err(vsp1->dev, "brs: failed to initialize controls\n");
+		ret = brs->ctrls.error;
+		vsp1_entity_destroy(&brs->entity);
+		return ERR_PTR(ret);
+	}
+
+	return brs;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_brs.h b/drivers/media/platform/vsp1/vsp1_brs.h
new file mode 100644
index 0000000..b29bf55
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_brs.h
@@ -0,0 +1,46 @@
+/*
+ * vsp1_brs.h  --  R-Car VSP1 Blend ROP Sub Unit
+ *
+ * Copyright (C) 2016 Renesas Corporation
+ *
+ * This file is based on the drivers/media/platform/vsp1/vsp1_bru.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_BRS_H__
+#define __VSP1_BRS_H__
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+struct vsp1_rwpf;
+
+#define BRS_PAD_SINK(n)				(n)
+
+struct vsp1_brs {
+	struct vsp1_entity entity;
+
+	struct v4l2_ctrl_handler ctrls;
+
+	struct {
+		struct vsp1_rwpf *rpf;
+	} inputs[VSP1_MAX_RPF];
+
+	u32 bgcolor;
+};
+
+static inline struct vsp1_brs *to_brs(struct v4l2_subdev *subdev)
+{
+	return container_of(subdev, struct vsp1_brs, entity.subdev);
+}
+
+struct vsp1_brs *vsp1_brs_create(struct vsp1_device *vsp1);
+
+#endif /* __VSP1_BRS_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c
index ee8355c..8b3164a 100644
--- a/drivers/media/platform/vsp1/vsp1_bru.c
+++ b/drivers/media/platform/vsp1/vsp1_bru.c
@@ -1,7 +1,7 @@
 /*
  * vsp1_bru.c  --  R-Car VSP1 Blend ROP Unit
  *
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -300,6 +300,26 @@ static void bru_configure(struct vsp1_entity *entity,
 	format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config,
 					    bru->entity.source_pad);
 
+	if (pipe->vmute_flag) {
+		vsp1_bru_write(bru, dl, VI6_BRU_INCTRL, 0);
+		vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_SIZE,
+		  (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
+		  (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
+		vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_LOC, 0);
+		vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_COL, (0xFF << 24));
+
+		for (i = 0; i < bru->entity.source_pad; ++i) {
+			vsp1_bru_write(bru, dl, VI6_BRU_BLD(i),
+			VI6_BRU_BLD_CCMDX_255_SRC_A |
+			VI6_BRU_BLD_CCMDY_SRC_A |
+			VI6_BRU_BLD_ACMDX_255_SRC_A |
+			VI6_BRU_BLD_ACMDY_COEFY |
+			VI6_BRU_BLD_COEFY_MASK);
+		}
+
+		return;
+	}
+
 	/* The hardware is extremely flexible but we have no userspace API to
 	 * expose all the parameters, nor is it clear whether we would have use
 	 * cases for all the supported modes. Let's just harcode the parameters
diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
index ad545af..77ae01c 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/vsp1/vsp1_dl.c
@@ -1,7 +1,7 @@
 /*
  * vsp1_dl.h  --  R-Car VSP1 Display List
  *
- * Copyright (C) 2015 Renesas Corporation
+ * Copyright (C) 2015-2016 Renesas Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -15,12 +15,18 @@
 #include <linux/dma-mapping.h>
 #include <linux/gfp.h>
 #include <linux/slab.h>
+#include <linux/soc/renesas/rcar_prr.h>
 #include <linux/workqueue.h>
 
+#include <media/rcar-fcp.h>
+
 #include "vsp1.h"
 #include "vsp1_dl.h"
+#include "vsp1_pipe.h"
+#include "vsp1_rwpf.h"
 
 #define VSP1_DL_NUM_ENTRIES		256
+#define VSP1_DL_EXT_OFFSET		0x1000
 
 #define VSP1_DLH_INT_ENABLE		(1 << 1)
 #define VSP1_DLH_AUTO_START		(1 << 0)
@@ -35,6 +41,24 @@ struct vsp1_dl_header {
 	struct vsp1_dl_header_list lists[8];
 	u32 next_header;
 	u32 flags;
+	/* if (VI6_DL_EXT_CTRL.EXT) */
+	u32 zero_bits;
+	/* zero_bits:6 + pre_ext_dl_exec:1 + */
+	/* post_ext_dl_exec:1 + zero_bits:8 + pre_ext_dl_num_cmd:16 */
+	u32 pre_post_num;
+	u32 pre_ext_dl_plist;
+	/* zero_bits:16 + post_ext_dl_num_cmd:16 */
+	u32 post_ext_dl_num_cmd;
+	u32 post_ext_dl_p_list;
+} __attribute__((__packed__));
+
+struct vsp1_ext_dl_body {
+	u32 ext_dl_cmd[2];
+	u32 ext_dl_data[2];
+} __attribute__((__packed__));
+
+struct vsp1_ext_addr {
+	u32 addr;
 } __attribute__((__packed__));
 
 struct vsp1_dl_entry {
@@ -68,6 +92,10 @@ struct vsp1_dl_body {
  * @dlm: the display list manager
  * @header: display list header, NULL for headerless lists
  * @dma: DMA address for the header
+ * @ext_body: display list extended body
+ * @ext_dma: DMA address for extended body
+ * @src_dst_addr: display list (Auto-FLD) source/destination address
+ * @ext_addr_dma: DMA address for display list (Auto-FLD)
  * @body0: first display list body
  * @fragments: list of extra display list bodies
  * @chain: entry in the display list partition chain
@@ -79,6 +107,12 @@ struct vsp1_dl_list {
 	struct vsp1_dl_header *header;
 	dma_addr_t dma;
 
+	struct vsp1_ext_dl_body *ext_body;
+	dma_addr_t ext_dma;
+
+	struct vsp1_ext_addr *src_dst_addr;
+	dma_addr_t ext_addr_dma;
+
 	struct vsp1_dl_body body0;
 	struct list_head fragments;
 
@@ -133,12 +167,13 @@ static int vsp1_dl_body_init(struct vsp1_device *vsp1,
 			     size_t extra_size)
 {
 	size_t size = num_entries * sizeof(*dlb->entries) + extra_size;
+	struct device *fcp = rcar_fcp_get_device(vsp1->fcp);
 
 	dlb->vsp1 = vsp1;
 	dlb->size = size;
 
-	dlb->entries = dma_alloc_wc(vsp1->dev, dlb->size, &dlb->dma,
-				    GFP_KERNEL);
+	dlb->entries = dma_alloc_wc(fcp ? fcp : vsp1->dev, dlb->size +
+			 (VSP1_DL_EXT_OFFSET * 2), &dlb->dma, GFP_KERNEL);
 	if (!dlb->entries)
 		return -ENOMEM;
 
@@ -150,7 +185,10 @@ static int vsp1_dl_body_init(struct vsp1_device *vsp1,
  */
 static void vsp1_dl_body_cleanup(struct vsp1_dl_body *dlb)
 {
-	dma_free_wc(dlb->vsp1->dev, dlb->size, dlb->entries, dlb->dma);
+	struct device *fcp = rcar_fcp_get_device(dlb->vsp1->fcp);
+
+	dma_free_wc(fcp ? fcp : dlb->vsp1->dev, dlb->size +
+			 (VSP1_DL_EXT_OFFSET * 2), dlb->entries, dlb->dma);
 }
 
 /**
@@ -227,6 +265,102 @@ void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
  * Display List Transaction Management
  */
 
+void vsp1_dl_set_addr_auto_fld(struct vsp1_dl_list *dl, struct vsp1_rwpf *rpf)
+{
+	const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
+	const struct v4l2_rect *crop;
+	u32 y_top_index, y_bot_index;
+	u32 u_top_index, u_bot_index;
+	u32 v_top_index, v_bot_index;
+	dma_addr_t y_top_addr, y_bot_addr;
+	dma_addr_t u_top_addr, u_bot_addr;
+	dma_addr_t v_top_addr, v_bot_addr;
+	u32 width, stride;
+
+	crop = vsp1_rwpf_get_crop(rpf, rpf->entity.config);
+	width = ALIGN(crop->width, 16);
+	stride = width * fmtinfo->bpp[0] / 8;
+
+	y_top_index = rpf->entity.index * 8;
+	y_bot_index = rpf->entity.index * 8 + 1;
+	u_top_index = rpf->entity.index * 8 + 2;
+	u_bot_index = rpf->entity.index * 8 + 3;
+	v_top_index = rpf->entity.index * 8 + 4;
+	v_bot_index = rpf->entity.index * 8 + 5;
+
+	switch (rpf->fmtinfo->fourcc) {
+	case V4L2_PIX_FMT_YUV420M:
+		y_top_addr = rpf->mem.addr[0] + rpf->offsets[0];
+		y_bot_addr = rpf->mem.addr[0] + rpf->offsets[0] + stride;
+		u_top_addr = rpf->mem.addr[1] + rpf->offsets[1];
+		u_bot_addr = rpf->mem.addr[1] + rpf->offsets[1] + stride / 2;
+		v_top_addr = rpf->mem.addr[2] + rpf->offsets[1];
+		v_bot_addr = rpf->mem.addr[2] + rpf->offsets[1] + stride / 2;
+		break;
+
+	case V4L2_PIX_FMT_YUV422M:
+		y_top_addr = rpf->mem.addr[0] + rpf->offsets[0];
+		y_bot_addr = rpf->mem.addr[0] + rpf->offsets[0] + stride * 2;
+		u_top_addr = rpf->mem.addr[1] + rpf->offsets[1];
+		u_bot_addr = rpf->mem.addr[1] + rpf->offsets[1] + stride;
+		v_top_addr = rpf->mem.addr[2] + rpf->offsets[1];
+		v_bot_addr = rpf->mem.addr[2] + rpf->offsets[1] + stride;
+		break;
+
+	case V4L2_PIX_FMT_YUV444M:
+		y_top_addr = rpf->mem.addr[0] + rpf->offsets[0];
+		y_bot_addr = rpf->mem.addr[0] + rpf->offsets[0] + stride * 3;
+		u_top_addr = rpf->mem.addr[1] + rpf->offsets[1];
+		u_bot_addr = rpf->mem.addr[1] + rpf->offsets[1] + stride * 3;
+		v_top_addr = rpf->mem.addr[2] + rpf->offsets[1];
+		v_bot_addr = rpf->mem.addr[2] + rpf->offsets[1] + stride * 3;
+		break;
+
+	case V4L2_PIX_FMT_YVU420M:
+		y_top_addr = rpf->mem.addr[0] + rpf->offsets[0];
+		y_bot_addr = rpf->mem.addr[0] + rpf->offsets[0] + stride;
+		u_top_addr = rpf->mem.addr[2] + rpf->offsets[1];
+		u_bot_addr = rpf->mem.addr[2] + rpf->offsets[1] + stride / 2;
+		v_top_addr = rpf->mem.addr[1] + rpf->offsets[1];
+		v_bot_addr = rpf->mem.addr[1] + rpf->offsets[1] + stride / 2;
+		break;
+
+	case V4L2_PIX_FMT_YVU422M:
+		y_top_addr = rpf->mem.addr[0] + rpf->offsets[0];
+		y_bot_addr = rpf->mem.addr[0] + rpf->offsets[0] + stride * 2;
+		u_top_addr = rpf->mem.addr[2] + rpf->offsets[1];
+		u_bot_addr = rpf->mem.addr[2] + rpf->offsets[1] + stride;
+		v_top_addr = rpf->mem.addr[1] + rpf->offsets[1];
+		v_bot_addr = rpf->mem.addr[1] + rpf->offsets[1] + stride;
+		break;
+
+	case V4L2_PIX_FMT_YVU444M:
+		y_top_addr = rpf->mem.addr[0] + rpf->offsets[0];
+		y_bot_addr = rpf->mem.addr[0] + rpf->offsets[0] + stride * 3;
+		u_top_addr = rpf->mem.addr[2] + rpf->offsets[1];
+		u_bot_addr = rpf->mem.addr[2] + rpf->offsets[1] + stride * 3;
+		v_top_addr = rpf->mem.addr[1] + rpf->offsets[1];
+		v_bot_addr = rpf->mem.addr[1] + rpf->offsets[1] + stride * 3;
+		break;
+
+	default:
+		y_top_addr = rpf->mem.addr[0] + rpf->offsets[0];
+		y_bot_addr = rpf->mem.addr[0] + rpf->offsets[0] + stride;
+		u_top_addr = rpf->mem.addr[1] + rpf->offsets[1];
+		u_bot_addr = rpf->mem.addr[1] + rpf->offsets[1] + stride;
+		v_top_addr = rpf->mem.addr[2] + rpf->offsets[1];
+		v_bot_addr = rpf->mem.addr[2] + rpf->offsets[1] + stride;
+		break;
+	}
+
+	dl->src_dst_addr[y_top_index].addr = y_top_addr;
+	dl->src_dst_addr[y_bot_index].addr = y_bot_addr;
+	dl->src_dst_addr[u_top_index].addr = u_top_addr;
+	dl->src_dst_addr[u_bot_index].addr = u_bot_addr;
+	dl->src_dst_addr[v_top_index].addr = v_top_addr;
+	dl->src_dst_addr[v_bot_index].addr = v_bot_addr;
+}
+
 static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm)
 {
 	struct vsp1_dl_list *dl;
@@ -263,6 +397,16 @@ static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm)
 		dl->header = ((void *)dl->body0.entries) + header_offset;
 		dl->dma = dl->body0.dma + header_offset;
 
+		dl->ext_body = ((void *)dl->body0.entries) +
+				 header_offset + VSP1_DL_EXT_OFFSET;
+		dl->ext_dma = dl->body0.dma + header_offset +
+				 VSP1_DL_EXT_OFFSET;
+
+		dl->src_dst_addr = ((void *)dl->body0.entries) +
+				 header_offset + (VSP1_DL_EXT_OFFSET * 2);
+		dl->ext_addr_dma = dl->body0.dma + header_offset +
+				 (VSP1_DL_EXT_OFFSET * 2);
+
 		memset(dl->header, 0, sizeof(*dl->header));
 		dl->header->lists[0].addr = dl->body0.dma;
 	}
@@ -472,6 +616,30 @@ static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last)
 		dl->header->flags = VSP1_DLH_AUTO_START;
 	} else {
 		dl->header->flags = VSP1_DLH_INT_ENABLE;
+		if (dl->dlm->vsp1->info->header_mode) {
+			dl->header->next_header = dl->dma;
+			dl->header->flags |= VSP1_DLH_AUTO_START;
+		}
+
+		if (dl->dlm->vsp1->auto_fld_mode) {
+			/* Set extended display list header */
+			/* pre_ext_dl_exec = 1, pre_ext_dl_num_cmd = 1 */
+			dl->header->pre_post_num = (1 << 25) | (0x01);
+			dl->header->pre_ext_dl_plist = dl->ext_dma;
+			dl->header->post_ext_dl_num_cmd = 0;
+			dl->header->post_ext_dl_p_list = 0;
+
+			/* Set extended display list (Auto-FLD) */
+			/* Set opecode */
+			dl->ext_body->ext_dl_cmd[0] = 0x00000003;
+			/* RPF[0]-[4] address is updated */
+			dl->ext_body->ext_dl_cmd[1] = 0x001f0001;
+
+			/* Set pointer of source/destination address */
+			dl->ext_body->ext_dl_data[0] = dl->ext_addr_dma;
+			/* Should be set to 0 */
+			dl->ext_body->ext_dl_data[1] = 0;
+		}
 	}
 }
 
@@ -507,7 +675,13 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl)
 		 */
 		vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma);
 
+		if (RCAR_PRR_IS_PRODUCT(H3) &&
+			(RCAR_PRR_CHK_CUT(H3, WS11) <= 0))
+			vsp1->dl_addr = dl->dma;
+
 		dlm->active = dl;
+		__vsp1_dl_list_put(dl);
+
 		goto done;
 	}
 
@@ -531,6 +705,13 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl)
 	vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD |
 		   (dl->body0.num_entries * sizeof(*dl->header->lists)));
 
+	if (RCAR_PRR_IS_PRODUCT(H3) &&
+		(RCAR_PRR_CHK_CUT(H3, WS11) <= 0)) {
+		vsp1->dl_addr = dl->body0.dma;
+		vsp1->dl_body = VI6_DL_BODY_SIZE_UPD |
+			(dl->body0.num_entries * sizeof(*dl->header->lists));
+	}
+
 	__vsp1_dl_list_put(dlm->queued);
 	dlm->queued = dl;
 
@@ -600,6 +781,13 @@ void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
 			   (dl->body0.num_entries *
 			    sizeof(*dl->header->lists)));
 
+		if (RCAR_PRR_IS_PRODUCT(H3) &&
+			(RCAR_PRR_CHK_CUT(H3, WS11) <= 0)) {
+			vsp1->dl_addr = dl->body0.dma;
+			vsp1->dl_body = VI6_DL_BODY_SIZE_UPD |
+				(dl->body0.num_entries *
+				 sizeof(*dl->header->lists));
+		}
 		dlm->queued = dl;
 		dlm->pending = NULL;
 	}
@@ -609,20 +797,26 @@ void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
 }
 
 /* Hardware Setup */
-void vsp1_dlm_setup(struct vsp1_device *vsp1)
+void vsp1_dlm_setup(struct vsp1_device *vsp1, unsigned int lif_index)
 {
 	u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT)
 		 | VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0
 		 | VI6_DL_CTRL_DLE;
 
+	if ((vsp1->info->header_mode) && (vsp1->auto_fld_mode)) {
+		vsp1_write(vsp1, VI6_DL_EXT_CTRL(lif_index),
+			(0x02 << VI6_DL_EXT_CTRL_POLINT_SHIFT)
+			| VI6_DL_EXT_CTRL_DLPRI | VI6_DL_EXT_CTRL_EXT);
+	}
+
 	/* The DRM pipeline operates with display lists in Continuous Frame
 	 * Mode, all other pipelines use manual start.
 	 */
-	if (vsp1->drm)
+	if ((vsp1->drm) && (!vsp1->info->header_mode))
 		ctrl |= VI6_DL_CTRL_CFM0 | VI6_DL_CTRL_NH0;
 
 	vsp1_write(vsp1, VI6_DL_CTRL, ctrl);
-	vsp1_write(vsp1, VI6_DL_SWAP, VI6_DL_SWAP_LWS);
+	vsp1_write(vsp1, VI6_DL_SWAP(lif_index), VI6_DL_SWAP_LWS);
 }
 
 void vsp1_dlm_reset(struct vsp1_dl_manager *dlm)
@@ -682,13 +876,14 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
 {
 	struct vsp1_dl_manager *dlm;
 	unsigned int i;
+	int ret;
 
 	dlm = devm_kzalloc(vsp1->dev, sizeof(*dlm), GFP_KERNEL);
 	if (!dlm)
 		return NULL;
 
 	dlm->index = index;
-	dlm->mode = index == 0 && !vsp1->info->uapi
+	dlm->mode = index == 0 && !vsp1->info->uapi && !vsp1->info->header_mode
 		  ? VSP1_DL_MODE_HEADERLESS : VSP1_DL_MODE_HEADER;
 	dlm->vsp1 = vsp1;
 
@@ -707,18 +902,28 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
 		list_add_tail(&dl->list, &dlm->free);
 	}
 
+	ret = RCAR_PRR_INIT();
+	if (ret) {
+		dev_dbg(dlm->vsp1->dev, "product register init fail.\n");
+		return NULL;
+	}
+
 	return dlm;
 }
 
 void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm)
 {
 	struct vsp1_dl_list *dl, *next;
+	struct vsp1_device *vsp1 = dlm->vsp1;
 
 	if (!dlm)
 		return;
 
 	cancel_work_sync(&dlm->gc_work);
 
+	if (vsp1->info->header_mode)
+		return;
+
 	list_for_each_entry_safe(dl, next, &dlm->free, list) {
 		list_del(&dl->list);
 		vsp1_dl_list_free(dl);
diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
index 7131aa3..39c084a 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.h
+++ b/drivers/media/platform/vsp1/vsp1_dl.h
@@ -1,7 +1,7 @@
 /*
  * vsp1_dl.h  --  R-Car VSP1 Display List
  *
- * Copyright (C) 2015 Renesas Corporation
+ * Copyright (C) 2015-2016 Renesas Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -20,7 +20,7 @@ struct vsp1_dl_fragment;
 struct vsp1_dl_list;
 struct vsp1_dl_manager;
 
-void vsp1_dlm_setup(struct vsp1_device *vsp1);
+void vsp1_dlm_setup(struct vsp1_device *vsp1, unsigned int lif_index);
 
 struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
 					unsigned int index,
@@ -31,6 +31,7 @@ void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
 void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
 
 struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm);
+void vsp1_dl_set_addr_auto_fld(struct vsp1_dl_list *dl, struct vsp1_rwpf *rpf);
 void vsp1_dl_list_put(struct vsp1_dl_list *dl);
 void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data);
 void vsp1_dl_list_commit(struct vsp1_dl_list *dl);
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index 0daf5f2..1b3e949 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -1,7 +1,7 @@
 /*
  * vsp1_drm.c  --  R-Car VSP1 DRM API
  *
- * Copyright (C) 2015 Renesas Electronics Corporation
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -14,6 +14,7 @@
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
 #include <linux/slab.h>
+#include <linux/soc/renesas/rcar_prr.h>
 
 #include <media/media-entity.h>
 #include <media/rcar-fcp.h>
@@ -22,6 +23,7 @@
 
 #include "vsp1.h"
 #include "vsp1_bru.h"
+#include "vsp1_brs.h"
 #include "vsp1_dl.h"
 #include "vsp1_drm.h"
 #include "vsp1_lif.h"
@@ -33,9 +35,9 @@
  * Interrupt Handling
  */
 
-void vsp1_drm_display_start(struct vsp1_device *vsp1)
+void vsp1_drm_display_start(struct vsp1_device *vsp1, unsigned int lif_index)
 {
-	vsp1_dlm_irq_display_start(vsp1->drm->pipe.output->dlm);
+	vsp1_dlm_irq_display_start(vsp1->drm->pipe[lif_index].output->dlm);
 }
 
 /* -----------------------------------------------------------------------------
@@ -53,6 +55,20 @@ int vsp1_du_init(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(vsp1_du_init);
 
+int vsp1_du_if_set_mute(struct device *dev, bool on, unsigned int lif_index)
+{
+	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+	struct vsp1_pipeline *pipe = &vsp1->drm->pipe[lif_index];
+
+	if (on)
+		pipe->vmute_flag = true;
+	else
+		pipe->vmute_flag = false;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(vsp1_du_if_set_mute);
+
 /**
  * vsp1_du_setup_lif - Setup the output part of the VSP pipeline
  * @dev: the VSP device
@@ -72,17 +88,38 @@ EXPORT_SYMBOL_GPL(vsp1_du_init);
  * Return 0 on success or a negative error code on failure.
  */
 int vsp1_du_setup_lif(struct device *dev, unsigned int width,
-		      unsigned int height)
+		      unsigned int height, unsigned int lif_index,
+		      bool suspend)
 {
 	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
-	struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
+	struct vsp1_pipeline *pipe = &vsp1->drm->pipe[lif_index];
 	struct vsp1_bru *bru = vsp1->bru;
+	struct vsp1_brs *brs = vsp1->brs;
+	unsigned int init_bru_num, end_bru_num;
+	unsigned int init_brs_num, end_brs_num;
 	struct v4l2_subdev_format format;
 	unsigned int i;
 	int ret;
 
-	dev_dbg(vsp1->dev, "%s: configuring LIF with format %ux%u\n",
-		__func__, width, height);
+	dev_dbg(vsp1->dev, "%s: configuring LIF%d with format %ux%u\n",
+		__func__, lif_index, width, height);
+
+	if (vsp1_gen3_vspdl_check(vsp1)) {
+		if (!vsp1->brs || !vsp1->lif[1])
+			return -ENXIO;
+
+		init_bru_num = 0;
+		init_brs_num = vsp1->info->rpf_count -
+			       vsp1->num_brs_inputs;
+		end_bru_num = vsp1->info->rpf_count -
+			      vsp1->num_brs_inputs;
+		end_brs_num = vsp1->info->rpf_count;
+	} else {
+		init_bru_num = 0;
+		init_brs_num = 0;
+		end_bru_num = bru->entity.source_pad;
+		end_brs_num = 0;
+	}
 
 	if (width == 0 || height == 0) {
 		/* Zero width or height means the CRTC is being disabled, stop
@@ -94,10 +131,20 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int width,
 
 		media_entity_pipeline_stop(&pipe->output->entity.subdev.entity);
 
-		for (i = 0; i < bru->entity.source_pad; ++i) {
-			vsp1->drm->inputs[i].enabled = false;
-			bru->inputs[i].rpf = NULL;
-			pipe->inputs[i] = NULL;
+		if (!suspend) {
+			if (lif_index == 1) {
+				for (i = init_brs_num; i < end_brs_num; ++i) {
+					vsp1->drm->inputs[i].enabled = false;
+					brs->inputs[i].rpf = NULL;
+					pipe->inputs[i] = NULL;
+				}
+			} else {
+				for (i = init_bru_num; i < end_bru_num; ++i) {
+					vsp1->drm->inputs[i].enabled = false;
+					bru->inputs[i].rpf = NULL;
+					pipe->inputs[i] = NULL;
+				}
+			}
 		}
 
 		pipe->num_inputs = 0;
@@ -116,16 +163,67 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int width,
 	memset(&format, 0, sizeof(format));
 	format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
 
-	for (i = 0; i < bru->entity.source_pad; ++i) {
-		format.pad = i;
+	if (lif_index == 1) {
+		for (i = init_brs_num; i < end_brs_num; ++i) {
+			format.pad = i;
 
-		format.format.width = width;
-		format.format.height = height;
-		format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
-		format.format.field = V4L2_FIELD_NONE;
+			format.format.width = width;
+			format.format.height = height;
+			format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
+			format.format.field = V4L2_FIELD_NONE;
 
-		ret = v4l2_subdev_call(&bru->entity.subdev, pad,
-				       set_fmt, NULL, &format);
+			ret = v4l2_subdev_call(&brs->entity.subdev, pad,
+					       set_fmt, NULL, &format);
+			if (ret < 0)
+				return ret;
+
+			dev_dbg(vsp1->dev,
+				"%s: set format %ux%u (%x) on BRS pad %u\n",
+				__func__, format.format.width,
+				format.format.height,
+				format.format.code, i);
+		}
+		format.pad = brs->entity.source_pad;
+	} else {
+		for (i = init_bru_num; i < end_bru_num; ++i) {
+			format.pad = i;
+
+			format.format.width = width;
+			format.format.height = height;
+			format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
+			format.format.field = V4L2_FIELD_NONE;
+
+			ret = v4l2_subdev_call(&bru->entity.subdev, pad,
+					       set_fmt, NULL, &format);
+			if (ret < 0)
+				return ret;
+
+			dev_dbg(vsp1->dev,
+				"%s: set format %ux%u (%x) on BRU pad %u\n",
+				__func__, format.format.width,
+				format.format.height,
+				format.format.code, i);
+		}
+		format.pad = bru->entity.source_pad;
+	}
+
+	format.format.width = width;
+	format.format.height = height;
+	format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
+	format.format.field = V4L2_FIELD_NONE;
+
+	if (lif_index == 1) {
+		ret = v4l2_subdev_call(&brs->entity.subdev, pad, set_fmt, NULL,
+				       &format);
+		if (ret < 0)
+			return ret;
+
+		dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on BRS pad %u\n",
+			__func__, format.format.width, format.format.height,
+			format.format.code, i);
+	} else {
+		ret = v4l2_subdev_call(&bru->entity.subdev, pad, set_fmt, NULL,
+				       &format);
 		if (ret < 0)
 			return ret;
 
@@ -134,50 +232,35 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int width,
 			format.format.code, i);
 	}
 
-	format.pad = bru->entity.source_pad;
-	format.format.width = width;
-	format.format.height = height;
-	format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
-	format.format.field = V4L2_FIELD_NONE;
-
-	ret = v4l2_subdev_call(&bru->entity.subdev, pad, set_fmt, NULL,
-			       &format);
-	if (ret < 0)
-		return ret;
-
-	dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on BRU pad %u\n",
-		__func__, format.format.width, format.format.height,
-		format.format.code, i);
-
 	format.pad = RWPF_PAD_SINK;
-	ret = v4l2_subdev_call(&vsp1->wpf[0]->entity.subdev, pad, set_fmt, NULL,
-			       &format);
+	ret = v4l2_subdev_call(&vsp1->wpf[lif_index]->entity.subdev,
+			       pad, set_fmt, NULL, &format);
 	if (ret < 0)
 		return ret;
 
-	dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on WPF0 sink\n",
+	dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on WPF%d sink\n",
 		__func__, format.format.width, format.format.height,
-		format.format.code);
+		format.format.code, lif_index);
 
 	format.pad = RWPF_PAD_SOURCE;
-	ret = v4l2_subdev_call(&vsp1->wpf[0]->entity.subdev, pad, get_fmt, NULL,
-			       &format);
+	ret = v4l2_subdev_call(&vsp1->wpf[lif_index]->entity.subdev,
+			       pad, get_fmt, NULL, &format);
 	if (ret < 0)
 		return ret;
 
-	dev_dbg(vsp1->dev, "%s: got format %ux%u (%x) on WPF0 source\n",
+	dev_dbg(vsp1->dev, "%s: got format %ux%u (%x) on WPF%d source\n",
 		__func__, format.format.width, format.format.height,
-		format.format.code);
+		format.format.code, lif_index);
 
 	format.pad = LIF_PAD_SINK;
-	ret = v4l2_subdev_call(&vsp1->lif->entity.subdev, pad, set_fmt, NULL,
-			       &format);
+	ret = v4l2_subdev_call(&vsp1->lif[lif_index]->entity.subdev,
+			       pad, set_fmt, NULL, &format);
 	if (ret < 0)
 		return ret;
 
-	dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on LIF sink\n",
+	dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on LIF%d sink\n",
 		__func__, format.format.width, format.format.height,
-		format.format.code);
+		format.format.code, lif_index);
 
 	/* Verify that the format at the output of the pipeline matches the
 	 * requested frame size and media bus code.
@@ -216,10 +299,10 @@ EXPORT_SYMBOL_GPL(vsp1_du_setup_lif);
  * vsp1_du_atomic_begin - Prepare for an atomic update
  * @dev: the VSP device
  */
-void vsp1_du_atomic_begin(struct device *dev)
+void vsp1_du_atomic_begin(struct device *dev, unsigned int lif_index)
 {
 	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
-	struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
+	struct vsp1_pipeline *pipe = &vsp1->drm->pipe[lif_index];
 
 	vsp1->drm->num_inputs = pipe->num_inputs;
 
@@ -299,8 +382,22 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
 	rpf->fmtinfo = fmtinfo;
 	rpf->format.num_planes = fmtinfo->planes;
 	rpf->format.plane_fmt[0].bytesperline = cfg->pitch;
-	rpf->format.plane_fmt[1].bytesperline = cfg->pitch;
+	if ((rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YUV420M) ||
+		(rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YVU420M) ||
+		(rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YUV422M) ||
+		(rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YVU422M))
+		rpf->format.plane_fmt[1].bytesperline = cfg->pitch / 2;
+	else
+		rpf->format.plane_fmt[1].bytesperline = cfg->pitch;
 	rpf->alpha = cfg->alpha;
+	rpf->interlaced = cfg->interlaced;
+
+	if (RCAR_PRR_IS_PRODUCT(H3) &&
+		(RCAR_PRR_CHK_CUT(H3, WS11) <= 0) && rpf->interlaced) {
+		dev_err(vsp1->dev,
+			"Interlaced mode is not supported.\n");
+		return -EINVAL;
+	}
 
 	rpf->mem.addr[0] = cfg->mem[0];
 	rpf->mem.addr[1] = cfg->mem[1];
@@ -321,7 +418,9 @@ static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1,
 	struct v4l2_subdev_selection sel;
 	struct v4l2_subdev_format format;
 	const struct v4l2_rect *crop;
+	struct v4l2_subdev *subdev;
 	int ret;
+	bool brs_use = false;
 
 	/* Configure the format on the RPF sink pad and propagate it up to the
 	 * BRU sink pad.
@@ -387,28 +486,40 @@ static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1,
 	/* BRU sink, propagate the format from the RPF source. */
 	format.pad = bru_input;
 
-	ret = v4l2_subdev_call(&vsp1->bru->entity.subdev, pad, set_fmt, NULL,
+	if (bru_input >=
+		(vsp1->info->rpf_count - vsp1->num_brs_inputs))
+		brs_use = true;
+
+	if (vsp1_gen3_vspdl_check(vsp1)) {
+		if (brs_use)
+			subdev = &vsp1->brs->entity.subdev;
+		else
+			subdev = &vsp1->bru->entity.subdev;
+	} else
+		subdev = &vsp1->bru->entity.subdev;
+
+	ret = v4l2_subdev_call(subdev, pad, set_fmt, NULL,
 			       &format);
 	if (ret < 0)
 		return ret;
 
-	dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on BRU pad %u\n",
+	dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
 		__func__, format.format.width, format.format.height,
-		format.format.code, format.pad);
+		format.format.code, (brs_use ? "BRS" : "BRU"), format.pad);
 
 	sel.pad = bru_input;
 	sel.target = V4L2_SEL_TGT_COMPOSE;
 	sel.r = vsp1->drm->inputs[rpf->entity.index].compose;
 
-	ret = v4l2_subdev_call(&vsp1->bru->entity.subdev, pad, set_selection,
+	ret = v4l2_subdev_call(subdev, pad, set_selection,
 			       NULL, &sel);
 	if (ret < 0)
 		return ret;
 
 	dev_dbg(vsp1->dev,
-		"%s: set selection (%u,%u)/%ux%u on BRU pad %u\n",
+		"%s: set selection (%u,%u)/%ux%u on %s pad %u\n",
 		__func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
-		sel.pad);
+		(brs_use ? "BRS" : "BRU"), sel.pad);
 
 	return 0;
 }
@@ -422,20 +533,36 @@ static unsigned int rpf_zpos(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf)
  * vsp1_du_atomic_flush - Commit an atomic update
  * @dev: the VSP device
  */
-void vsp1_du_atomic_flush(struct device *dev)
+void vsp1_du_atomic_flush(struct device *dev, unsigned int lif_index)
 {
 	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
-	struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
+	struct vsp1_pipeline *pipe = &vsp1->drm->pipe[lif_index];
 	struct vsp1_rwpf *inputs[VSP1_MAX_RPF] = { NULL, };
 	struct vsp1_entity *entity;
 	unsigned long flags;
 	unsigned int i;
+	unsigned int init_rpf, end_rpf;
 	int ret;
 
 	/* Count the number of enabled inputs and sort them by Z-order. */
 	pipe->num_inputs = 0;
 
-	for (i = 0; i < vsp1->info->rpf_count; ++i) {
+	if (vsp1_gen3_vspdl_check(vsp1)) {
+		if (lif_index == 1) {
+			init_rpf = vsp1->info->rpf_count -
+				       vsp1->num_brs_inputs;
+			end_rpf = vsp1->info->rpf_count;
+		} else {
+			init_rpf = 0;
+			end_rpf = vsp1->info->rpf_count -
+				      vsp1->num_brs_inputs;
+		}
+	} else {
+		init_rpf = 0;
+		end_rpf = vsp1->info->rpf_count;
+	}
+
+	for (i = init_rpf; i < end_rpf; ++i) {
 		struct vsp1_rwpf *rpf = vsp1->rpf[i];
 		unsigned int j;
 
@@ -448,29 +575,42 @@ void vsp1_du_atomic_flush(struct device *dev)
 
 		/* Insert the RPF in the sorted RPFs array. */
 		for (j = pipe->num_inputs++; j > 0; --j) {
-			if (rpf_zpos(vsp1, inputs[j-1]) <= rpf_zpos(vsp1, rpf))
+			if (rpf_zpos(vsp1, inputs[j-1+init_rpf]) <=
+					rpf_zpos(vsp1, rpf))
 				break;
-			inputs[j] = inputs[j-1];
+			inputs[j+init_rpf] = inputs[j-1+init_rpf];
 		}
 
-		inputs[j] = rpf;
+		inputs[j+init_rpf] = rpf;
 	}
 
+	if (!vsp1_gen3_vspdl_check(vsp1))
+		end_rpf = vsp1->info->num_bru_inputs;
+
 	/* Setup the RPF input pipeline for every enabled input. */
-	for (i = 0; i < vsp1->info->num_bru_inputs; ++i) {
+	for (i = init_rpf; i < end_rpf; ++i) {
 		struct vsp1_rwpf *rpf = inputs[i];
+		bool brs_use = false;
 
 		if (!rpf) {
 			vsp1->bru->inputs[i].rpf = NULL;
+			if ((lif_index == 1) && (vsp1->brs))
+				vsp1->brs->inputs[i].rpf = NULL;
 			continue;
 		}
 
 		vsp1->bru->inputs[i].rpf = rpf;
+		if ((lif_index == 1) && (vsp1->brs)) {
+			vsp1->brs->inputs[i].rpf = rpf;
+			rpf->brs_input = 2;
+			brs_use = true;
+		}
 		rpf->bru_input = i;
 		rpf->entity.sink_pad = i;
 
-		dev_dbg(vsp1->dev, "%s: connecting RPF.%u to BRU:%u\n",
-			__func__, rpf->entity.index, i);
+		dev_dbg(vsp1->dev, "%s: connecting RPF.%u to %s:%u\n",
+			__func__, rpf->entity.index,
+			(brs_use ? "BRS" : "BRU"), i);
 
 		ret = vsp1_du_setup_rpf_pipe(vsp1, rpf, i);
 		if (ret < 0)
@@ -509,13 +649,14 @@ void vsp1_du_atomic_flush(struct device *dev)
 
 	/* Start or stop the pipeline if needed. */
 	if (!vsp1->drm->num_inputs && pipe->num_inputs) {
-		vsp1_write(vsp1, VI6_DISP_IRQ_STA, 0);
-		vsp1_write(vsp1, VI6_DISP_IRQ_ENB, VI6_DISP_IRQ_ENB_DSTE);
+		vsp1_write(vsp1, VI6_DISP_IRQ_STA(lif_index), 0);
+		vsp1_write(vsp1, VI6_DISP_IRQ_ENB(lif_index),
+					 VI6_DISP_IRQ_ENB_DSTE);
 		spin_lock_irqsave(&pipe->irqlock, flags);
 		vsp1_pipeline_run(pipe);
 		spin_unlock_irqrestore(&pipe->irqlock, flags);
 	} else if (vsp1->drm->num_inputs && !pipe->num_inputs) {
-		vsp1_write(vsp1, VI6_DISP_IRQ_ENB, 0);
+		vsp1_write(vsp1, VI6_DISP_IRQ_ENB(lif_index), 0);
 		vsp1_pipeline_stop(pipe);
 	}
 }
@@ -543,6 +684,58 @@ void vsp1_du_unmap_sg(struct device *dev, struct sg_table *sgt)
 }
 EXPORT_SYMBOL_GPL(vsp1_du_unmap_sg);
 
+int vsp1_du_setup_wb(struct device *dev, u32 pixelformat, unsigned int pitch,
+		      dma_addr_t mem[2], unsigned int lif_index)
+{
+	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+	struct vsp1_pipeline *pipe = &vsp1->drm->pipe[lif_index];
+	struct vsp1_rwpf *wpf = pipe->output;
+	const struct vsp1_format_info *fmtinfo;
+	struct vsp1_rwpf *rpf = pipe->inputs[0];
+	unsigned long flags;
+	int i;
+
+	fmtinfo = vsp1_get_format_info(vsp1, pixelformat);
+	if (!fmtinfo) {
+		dev_err(vsp1->dev, "Unsupport pixel format %08x for RPF\n",
+			pixelformat);
+		return -EINVAL;
+	}
+
+	if (rpf->interlaced) {
+		dev_err(vsp1->dev, "Prohibited in interlaced mode\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&pipe->irqlock, flags);
+
+	wpf->fmtinfo = fmtinfo;
+	wpf->format.num_planes = fmtinfo->planes;
+	wpf->format.plane_fmt[0].bytesperline = pitch;
+	wpf->format.plane_fmt[1].bytesperline = pitch;
+
+	for (i = 0; i < wpf->format.num_planes; ++i)
+		wpf->buf_addr[i] = mem[i];
+
+	pipe->output->write_back = 3;
+
+	spin_unlock_irqrestore(&pipe->irqlock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(vsp1_du_setup_wb);
+
+void vsp1_du_wait_wb(struct device *dev, u32 count, unsigned int lif_index)
+{
+	int ret;
+	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+	struct vsp1_pipeline *pipe = &vsp1->drm->pipe[lif_index];
+
+	ret = wait_event_interruptible(pipe->event_wait,
+				       (pipe->output->write_back == count));
+}
+EXPORT_SYMBOL_GPL(vsp1_du_wait_wb);
+
 /* -----------------------------------------------------------------------------
  * Initialization
  */
@@ -551,15 +744,34 @@ int vsp1_drm_create_links(struct vsp1_device *vsp1)
 {
 	const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE;
 	unsigned int i;
+	unsigned int init_bru_num, end_bru_num;
+	unsigned int init_brs_num, end_brs_num;
 	int ret;
 
 	/* VSPD instances require a BRU to perform composition and a LIF to
 	 * output to the DU.
 	 */
-	if (!vsp1->bru || !vsp1->lif)
+	if (!vsp1->bru || !vsp1->lif[0])
 		return -ENXIO;
 
-	for (i = 0; i < vsp1->info->rpf_count; ++i) {
+	if (vsp1_gen3_vspdl_check(vsp1)) {
+		if (!vsp1->brs || !vsp1->lif[1])
+			return -ENXIO;
+
+		init_bru_num = 0;
+		init_brs_num = vsp1->info->rpf_count -
+			       vsp1->num_brs_inputs;
+		end_bru_num = vsp1->info->rpf_count -
+			      vsp1->num_brs_inputs;
+		end_brs_num = vsp1->info->rpf_count;
+	} else {
+		init_bru_num = 0;
+		init_brs_num = 0;
+		end_bru_num = vsp1->info->rpf_count;
+		end_brs_num = 0;
+	}
+
+	for (i = init_bru_num; i < end_bru_num; ++i) {
 		struct vsp1_rwpf *rpf = vsp1->rpf[i];
 
 		ret = media_create_pad_link(&rpf->entity.subdev.entity,
@@ -568,11 +780,23 @@ int vsp1_drm_create_links(struct vsp1_device *vsp1)
 					    i, flags);
 		if (ret < 0)
 			return ret;
-
 		rpf->entity.sink = &vsp1->bru->entity.subdev.entity;
 		rpf->entity.sink_pad = i;
 	}
 
+	for (i = init_brs_num; i < end_brs_num; ++i) {
+		struct vsp1_rwpf *rpf = vsp1->rpf[i];
+
+		ret = media_create_pad_link(&rpf->entity.subdev.entity,
+					    RWPF_PAD_SOURCE,
+					    &vsp1->brs->entity.subdev.entity,
+					    i, flags);
+		if (ret < 0)
+			return ret;
+		rpf->entity.sink = &vsp1->brs->entity.subdev.entity;
+		rpf->entity.sink_pad = i;
+	}
+
 	ret = media_create_pad_link(&vsp1->bru->entity.subdev.entity,
 				    vsp1->bru->entity.source_pad,
 				    &vsp1->wpf[0]->entity.subdev.entity,
@@ -585,41 +809,99 @@ int vsp1_drm_create_links(struct vsp1_device *vsp1)
 
 	ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity,
 				    RWPF_PAD_SOURCE,
-				    &vsp1->lif->entity.subdev.entity,
+				    &vsp1->lif[0]->entity.subdev.entity,
 				    LIF_PAD_SINK, flags);
 	if (ret < 0)
 		return ret;
 
+	if (vsp1_gen3_vspdl_check(vsp1)) {
+		ret = media_create_pad_link(
+					&vsp1->brs->entity.subdev.entity,
+					vsp1->brs->entity.source_pad,
+					&vsp1->wpf[1]->entity.subdev.entity,
+					RWPF_PAD_SINK, flags);
+		if (ret < 0)
+			return ret;
+
+		vsp1->brs->entity.sink = &vsp1->wpf[1]->entity.subdev.entity;
+		vsp1->brs->entity.sink_pad = RWPF_PAD_SINK;
+
+		ret = media_create_pad_link(
+					&vsp1->wpf[1]->entity.subdev.entity,
+					RWPF_PAD_SOURCE,
+					&vsp1->lif[1]->entity.subdev.entity,
+					LIF_PAD_SINK, flags);
+		if (ret < 0)
+			return ret;
+	}
+
 	return 0;
 }
 
 int vsp1_drm_init(struct vsp1_device *vsp1)
 {
 	struct vsp1_pipeline *pipe;
-	unsigned int i;
+	unsigned int i, j, lif_cnt = 1;
+	int ret, rpf_share = -1;
 
 	vsp1->drm = devm_kzalloc(vsp1->dev, sizeof(*vsp1->drm), GFP_KERNEL);
 	if (!vsp1->drm)
 		return -ENOMEM;
 
-	pipe = &vsp1->drm->pipe;
-
-	vsp1_pipeline_init(pipe);
-
-	/* The DRM pipeline is static, add entities manually. */
-	for (i = 0; i < vsp1->info->rpf_count; ++i) {
-		struct vsp1_rwpf *input = vsp1->rpf[i];
-
-		list_add_tail(&input->entity.list_pipe, &pipe->entities);
+	if (vsp1_gen3_vspdl_check(vsp1)) {
+		lif_cnt = 2;
+		rpf_share = vsp1->info->rpf_count - vsp1->num_brs_inputs;
 	}
 
-	list_add_tail(&vsp1->bru->entity.list_pipe, &pipe->entities);
-	list_add_tail(&vsp1->wpf[0]->entity.list_pipe, &pipe->entities);
-	list_add_tail(&vsp1->lif->entity.list_pipe, &pipe->entities);
+	for (i = 0; i < lif_cnt; i++) {
 
-	pipe->bru = &vsp1->bru->entity;
-	pipe->lif = &vsp1->lif->entity;
-	pipe->output = vsp1->wpf[0];
+		pipe = &vsp1->drm->pipe[i];
+
+		vsp1_pipeline_init(pipe);
+
+		if (i == 1) {
+			list_add_tail(&vsp1->brs->entity.list_pipe,
+				      &pipe->entities);
+			pipe->brs = &vsp1->brs->entity;
+		} else {
+			list_add_tail(&vsp1->bru->entity.list_pipe,
+				      &pipe->entities);
+			pipe->bru = &vsp1->bru->entity;
+		}
+
+		/* The DRM pipeline is static, add entities manually. */
+		for (j = 0; j < vsp1->info->rpf_count; ++j) {
+			struct vsp1_rwpf *input = vsp1->rpf[j];
+
+			if (rpf_share < 0)
+				list_add_tail(&input->entity.list_pipe,
+						 &pipe->entities);
+			else {
+				if ((i == 0) && (j < rpf_share))
+					list_add_tail(&input->entity.list_pipe,
+						      &pipe->entities);
+				if ((i == 1) && (j >= rpf_share))
+					list_add_tail(&input->entity.list_pipe,
+						      &pipe->entities);
+			}
+		}
+
+		list_add_tail(&vsp1->wpf[i]->entity.list_pipe,
+			      &pipe->entities);
+		list_add_tail(&vsp1->lif[i]->entity.list_pipe,
+			      &pipe->entities);
+
+		pipe->lif = &vsp1->lif[i]->entity;
+		pipe->output = vsp1->wpf[i];
+		pipe->output->write_back = 0;
+		init_waitqueue_head(&pipe->event_wait);
+	}
+
+	ret = RCAR_PRR_INIT();
+	if (ret) {
+		dev_dbg(vsp1->dev, "product register init fail.\n");
+		return ret;
+	}
 
 	return 0;
 }
diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h
index 9e28ab9..1bcee33 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.h
+++ b/drivers/media/platform/vsp1/vsp1_drm.h
@@ -1,7 +1,7 @@
 /*
  * vsp1_drm.h  --  R-Car VSP1 DRM/KMS Interface
  *
- * Copyright (C) 2015 Renesas Electronics Corporation
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -25,7 +25,7 @@
  *	position for every input
  */
 struct vsp1_drm {
-	struct vsp1_pipeline pipe;
+	struct vsp1_pipeline pipe[VSP1_MAX_LIF];
 	unsigned int num_inputs;
 	struct {
 		bool enabled;
@@ -39,6 +39,6 @@ int vsp1_drm_init(struct vsp1_device *vsp1);
 void vsp1_drm_cleanup(struct vsp1_device *vsp1);
 int vsp1_drm_create_links(struct vsp1_device *vsp1);
 
-void vsp1_drm_display_start(struct vsp1_device *vsp1);
+void vsp1_drm_display_start(struct vsp1_device *vsp1, unsigned int lif_index);
 
 #endif /* __VSP1_DRM_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index 3b08497..8bec172 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -1,7 +1,7 @@
 /*
  * vsp1_drv.c  --  R-Car VSP1 Driver
  *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -11,6 +11,10 @@
  * (at your option) any later version.
  */
 
+#ifdef CONFIG_VIDEO_RENESAS_DEBUG
+#define DEBUG
+#endif
+
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/device.h>
@@ -21,12 +25,14 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/videodev2.h>
+#include <linux/soc/renesas/rcar_prr.h>
 
 #include <media/rcar-fcp.h>
 #include <media/v4l2-subdev.h>
 
 #include "vsp1.h"
 #include "vsp1_bru.h"
+#include "vsp1_brs.h"
 #include "vsp1_clu.h"
 #include "vsp1_dl.h"
 #include "vsp1_drm.h"
@@ -41,40 +47,190 @@
 #include "vsp1_uds.h"
 #include "vsp1_video.h"
 
+#define VSP1_UT_IRQ	0x01
+
+static unsigned int vsp1_debug;	/* 1 to enable debug output */
+module_param_named(debug, vsp1_debug, int, 0600);
+static int underrun_vspd[4];
+module_param_array(underrun_vspd, int, NULL, 0600);
+
+#ifdef CONFIG_VIDEO_RENESAS_DEBUG
+#define VSP1_IRQ_DEBUG(fmt, args...)					\
+	do {								\
+		if (unlikely(vsp1_debug & VSP1_UT_IRQ))			\
+			vsp1_ut_debug_printk(__func__, fmt, ##args);	\
+	} while (0)
+#else
+#define VSP1_IRQ_DEBUG(fmt, args...)
+#endif
+
+void vsp1_ut_debug_printk(const char *function_name, const char *format, ...)
+{
+	struct va_format vaf;
+	va_list args;
+
+	va_start(args, format);
+	vaf.fmt = format;
+	vaf.va = &args;
+
+	pr_debug("[vsp1 :%s] %pV", function_name, &vaf);
+
+	va_end(args);
+}
+
+#define SRCR7_REG		0xe61501cc
+#define	FCPVD0_REG		0xfea27000
+#define	FCPVD1_REG		0xfea2f000
+#define	FCPVD2_REG		0xfea37000
+#define	FCPVD3_REG		0xfea3f000
+
+#define FCP_RST_REG		0x0010
+#define FCP_RST_SOFTRST		0x00000001
+#define FCP_RST_WORKAROUND	0x00000010
+
+#define FCP_STA_REG		0x0018
+#define FCP_STA_ACT		0x00000001
+
+static void __iomem *fcpv_reg[4];
+static const unsigned int fcpvd_offset[] = {
+	FCPVD0_REG, FCPVD1_REG, FCPVD2_REG, FCPVD3_REG
+};
+
+int vsp1_gen3_vspdl_check(struct vsp1_device *vsp1)
+{
+	if (((vsp1->version & VI6_IP_VERSION_MODEL_MASK) ==
+		VI6_IP_VERSION_MODEL_VSPDL_H3) && (vsp1->index == 0))
+		return true;
+
+	return false;
+}
+
+static int vsp1_gen3_vspd_check(struct vsp1_device *vsp1)
+{
+	if ((vsp1->version & VI6_IP_VERSION_MODEL_MASK) ==
+		VI6_IP_VERSION_MODEL_VSPD_GEN3)
+		return true;
+
+	if ((vsp1->version & VI6_IP_VERSION_MODEL_MASK) ==
+		VI6_IP_VERSION_MODEL_VSPDL_H3)
+		return true;
+
+	return false;
+}
+
+void vsp1_underrun_workaround(struct vsp1_device *vsp1, bool reset)
+{
+	unsigned int timeout = 0;
+
+	/* 1. Disable clock stop of VSP */
+	vsp1_write(vsp1, VI6_CLK_CTRL0, VI6_CLK_CTRL0_WORKAROUND);
+	vsp1_write(vsp1, VI6_CLK_CTRL1, VI6_CLK_CTRL1_WORKAROUND);
+	vsp1_write(vsp1, VI6_CLK_DCSWT, VI6_CLK_DCSWT_WORKAROUND1);
+	vsp1_write(vsp1, VI6_CLK_DCSM0, VI6_CLK_DCSM0_WORKAROUND);
+	vsp1_write(vsp1, VI6_CLK_DCSM1, VI6_CLK_DCSM1_WORKAROUND);
+
+	/* 2. Stop operation of VSP except bus access with module reset */
+	vsp1_write(vsp1, VI6_MRESET_ENB0, VI6_MRESET_ENB0_WORKAROUND1);
+	vsp1_write(vsp1, VI6_MRESET_ENB1, VI6_MRESET_ENB1_WORKAROUND);
+	vsp1_write(vsp1, VI6_MRESET, VI6_MRESET_WORKAROUND);
+
+	/* 3. Stop operation of FCPV with software reset */
+	iowrite32(FCP_RST_SOFTRST, fcpv_reg[vsp1->index] + FCP_RST_REG);
+
+	/* 4. Wait until FCP_STA.ACT become 0. */
+	while (1) {
+		if ((ioread32(fcpv_reg[vsp1->index] + FCP_STA_REG) &
+			FCP_STA_ACT) != FCP_STA_ACT)
+			break;
+
+		if (timeout == 100)
+			break;
+
+		timeout++;
+		udelay(1);
+	}
+
+	/* 5. Initialize the whole FCPV with module reset */
+	iowrite32(FCP_RST_WORKAROUND, fcpv_reg[vsp1->index] + FCP_RST_REG);
+
+	/* 6. Stop the whole operation of VSP with module reset */
+	/*    (note that register setting is not cleared) */
+	vsp1_write(vsp1, VI6_MRESET_ENB0, VI6_MRESET_ENB0_WORKAROUND2);
+	vsp1_write(vsp1, VI6_MRESET_ENB1, VI6_MRESET_ENB1_WORKAROUND);
+	vsp1_write(vsp1, VI6_MRESET, VI6_MRESET_WORKAROUND);
+
+	/* 7. Enable clock stop of VSP */
+	vsp1_write(vsp1, VI6_CLK_CTRL0, 0);
+	vsp1_write(vsp1, VI6_CLK_CTRL1, 0);
+	vsp1_write(vsp1, VI6_CLK_DCSWT, VI6_CLK_DCSWT_WORKAROUND2);
+	vsp1_write(vsp1, VI6_CLK_DCSM0, 0);
+	vsp1_write(vsp1, VI6_CLK_DCSM1, 0);
+
+	/* 8. Restart VSPD */
+	if (!reset) {
+		vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), vsp1->dl_addr);
+		/* Necessary when headerless display list */
+		if (!vsp1->info->header_mode)
+			vsp1_write(vsp1, VI6_DL_BODY_SIZE, vsp1->dl_body);
+		vsp1_write(vsp1, VI6_CMD(0), VI6_CMD_STRCMD);
+	}
+}
+
 /* -----------------------------------------------------------------------------
  * Interrupt Handling
  */
-
 static irqreturn_t vsp1_irq_handler(int irq, void *data)
 {
-	u32 mask = VI6_WFP_IRQ_STA_DFE | VI6_WFP_IRQ_STA_FRE;
+	u32 mask = VI6_WFP_IRQ_STA_DFE | VI6_WFP_IRQ_STA_FRE
+				       | VI6_WFP_IRQ_STA_UND;
 	struct vsp1_device *vsp1 = data;
 	irqreturn_t ret = IRQ_NONE;
 	unsigned int i;
 	u32 status;
+	unsigned int vsp_und_cnt = 0;
+	bool underrun = false;
 
 	for (i = 0; i < vsp1->info->wpf_count; ++i) {
 		struct vsp1_rwpf *wpf = vsp1->wpf[i];
+		struct vsp1_pipeline *pipe;
 
 		if (wpf == NULL)
 			continue;
 
+		pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
 		status = vsp1_read(vsp1, VI6_WPF_IRQ_STA(i));
 		vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask);
 
+		if (vsp1_debug && (status & VI6_WFP_IRQ_STA_UND) &&
+			vsp1_gen3_vspd_check(vsp1)) {
+			vsp_und_cnt = ++underrun_vspd[vsp1->index];
+			VSP1_IRQ_DEBUG(
+				"Underrun occurred num[%d] at VSPD (%s)\n",
+				vsp_und_cnt, dev_name(vsp1->dev));
+		}
+
 		if (status & VI6_WFP_IRQ_STA_DFE) {
-			vsp1_pipeline_frame_end(wpf->pipe);
+			vsp1_pipeline_frame_end(pipe);
+			ret = IRQ_HANDLED;
+		}
+		if (status & VI6_WFP_IRQ_STA_UND)
+			underrun = true;
+	}
+
+	for (i = 0; i < vsp1->info->wpf_count; ++i) {
+		status = vsp1_read(vsp1, VI6_DISP_IRQ_STA(i));
+		vsp1_write(vsp1, VI6_DISP_IRQ_STA(i),
+				 ~status & VI6_DISP_IRQ_STA_DST);
+
+		if (status & VI6_DISP_IRQ_STA_DST) {
+			vsp1_drm_display_start(vsp1, i);
 			ret = IRQ_HANDLED;
 		}
 	}
 
-	status = vsp1_read(vsp1, VI6_DISP_IRQ_STA);
-	vsp1_write(vsp1, VI6_DISP_IRQ_STA, ~status & VI6_DISP_IRQ_STA_DST);
-
-	if (status & VI6_DISP_IRQ_STA_DST) {
-		vsp1_drm_display_start(vsp1);
-		ret = IRQ_HANDLED;
-	}
+	if ((RCAR_PRR_IS_PRODUCT(H3) && (RCAR_PRR_CHK_CUT(H3, WS11) <= 0)) &&
+		underrun && vsp1_gen3_vspd_check(vsp1))
+		vsp1_underrun_workaround(vsp1, false);
 
 	return ret;
 }
@@ -172,10 +328,19 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1)
 			return ret;
 	}
 
-	if (vsp1->lif) {
+	if (vsp1->lif[0]) {
 		ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity,
 					    RWPF_PAD_SOURCE,
-					    &vsp1->lif->entity.subdev.entity,
+					    &vsp1->lif[0]->entity.subdev.entity,
+					    LIF_PAD_SINK, 0);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (vsp1->lif[1]) {
+		ret = media_create_pad_link(&vsp1->wpf[1]->entity.subdev.entity,
+					    RWPF_PAD_SOURCE,
+					    &vsp1->lif[1]->entity.subdev.entity,
 					    LIF_PAD_SINK, 0);
 		if (ret < 0)
 			return ret;
@@ -277,6 +442,16 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
 		list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities);
 	}
 
+	if (vsp1->info->features & VSP1_HAS_BRS) {
+		vsp1->brs = vsp1_brs_create(vsp1);
+		if (IS_ERR(vsp1->brs)) {
+			ret = PTR_ERR(vsp1->brs);
+			goto done;
+		}
+
+		list_add_tail(&vsp1->brs->entity.list_dev, &vsp1->entities);
+	}
+
 	if (vsp1->info->features & VSP1_HAS_CLU) {
 		vsp1->clu = vsp1_clu_create(vsp1);
 		if (IS_ERR(vsp1->clu)) {
@@ -330,13 +505,27 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
 	 * enabled skip the LIF, even when present.
 	 */
 	if (vsp1->info->features & VSP1_HAS_LIF && !vsp1->info->uapi) {
-		vsp1->lif = vsp1_lif_create(vsp1);
-		if (IS_ERR(vsp1->lif)) {
-			ret = PTR_ERR(vsp1->lif);
-			goto done;
-		}
+		if (vsp1_gen3_vspdl_check(vsp1)) {
+			for (i = 0; i < 2; i++) {
+				vsp1->lif[i] = vsp1_lif_create(vsp1, i);
+				if (IS_ERR(vsp1->lif[i])) {
+					ret = PTR_ERR(vsp1->lif[i]);
+					goto done;
+				}
 
-		list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities);
+				list_add_tail(&vsp1->lif[i]->entity.list_dev,
+					      &vsp1->entities);
+			}
+		} else {
+			vsp1->lif[0] = vsp1_lif_create(vsp1, i);
+			if (IS_ERR(vsp1->lif[0])) {
+				ret = PTR_ERR(vsp1->lif[0]);
+				goto done;
+			}
+
+			list_add_tail(&vsp1->lif[0]->entity.list_dev,
+				      &vsp1->entities);
+		}
 	}
 
 	if (vsp1->info->features & VSP1_HAS_LUT) {
@@ -466,7 +655,12 @@ int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index)
 	if (!(status & VI6_STATUS_SYS_ACT(index)))
 		return 0;
 
-	vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(index));
+	if ((RCAR_PRR_IS_PRODUCT(H3) && (RCAR_PRR_CHK_CUT(H3, WS11) <= 0)) &&
+		vsp1_gen3_vspd_check(vsp1))
+		vsp1_underrun_workaround(vsp1, true);
+	else
+		vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(index));
+
 	for (timeout = 10; timeout > 0; --timeout) {
 		status = vsp1_read(vsp1, VI6_STATUS);
 		if (!(status & VI6_STATUS_SYS_ACT(index)))
@@ -510,13 +704,19 @@ static int vsp1_device_init(struct vsp1_device *vsp1)
 	vsp1_write(vsp1, VI6_DPR_HST_ROUTE, VI6_DPR_NODE_UNUSED);
 	vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED);
 	vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED);
+	vsp1_write(vsp1, VI6_DPR_BRS_ROUTE, VI6_DPR_NODE_UNUSED);
 
 	vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
 		   (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
 	vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
 		   (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
 
-	vsp1_dlm_setup(vsp1);
+	for (i = 0; i < vsp1->info->wpf_count; ++i) {
+		if ((i == 1) && (!vsp1_gen3_vspdl_check(vsp1)))
+			break;
+
+		vsp1_dlm_setup(vsp1, i);
+	}
 
 	return 0;
 }
@@ -530,10 +730,23 @@ static int vsp1_device_init(struct vsp1_device *vsp1)
  */
 int vsp1_device_get(struct vsp1_device *vsp1)
 {
-	int ret;
+	int ret = 0;
+
+	ret = rcar_fcp_enable(vsp1->fcp);
+	if (ret < 0)
+		return ret;
 
 	ret = pm_runtime_get_sync(vsp1->dev);
-	return ret < 0 ? ret : 0;
+	if (ret < 0)
+		return ret;
+
+	if (vsp1->info) {
+		ret = vsp1_device_init(vsp1);
+		if (ret < 0)
+			return ret;
+	}
+
+	return ret;
 }
 
 /*
@@ -545,6 +758,7 @@ int vsp1_device_get(struct vsp1_device *vsp1)
 void vsp1_device_put(struct vsp1_device *vsp1)
 {
 	pm_runtime_put_sync(vsp1->dev);
+	rcar_fcp_disable(vsp1->fcp);
 }
 
 /* -----------------------------------------------------------------------------
@@ -556,7 +770,7 @@ static int __maybe_unused vsp1_pm_suspend(struct device *dev)
 	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 
 	vsp1_pipelines_suspend(vsp1);
-	pm_runtime_force_suspend(vsp1->dev);
+	vsp1_device_put(vsp1);
 
 	return 0;
 }
@@ -564,39 +778,23 @@ static int __maybe_unused vsp1_pm_suspend(struct device *dev)
 static int __maybe_unused vsp1_pm_resume(struct device *dev)
 {
 	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+	int ret = 0;
 
-	pm_runtime_force_resume(vsp1->dev);
+	ret = rcar_fcp_enable(vsp1->fcp);
+	if (ret < 0)
+		return ret;
+
+	ret = pm_runtime_get_sync(vsp1->dev);
+	if (ret < 0)
+		return ret;
+
 	vsp1_pipelines_resume(vsp1);
 
 	return 0;
 }
 
-static int __maybe_unused vsp1_pm_runtime_suspend(struct device *dev)
-{
-	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
-
-	rcar_fcp_disable(vsp1->fcp);
-
-	return 0;
-}
-
-static int __maybe_unused vsp1_pm_runtime_resume(struct device *dev)
-{
-	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
-	int ret;
-
-	if (vsp1->info) {
-		ret = vsp1_device_init(vsp1);
-		if (ret < 0)
-			return ret;
-	}
-
-	return rcar_fcp_enable(vsp1->fcp);
-}
-
 static const struct dev_pm_ops vsp1_pm_ops = {
 	SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume)
-	SET_RUNTIME_PM_OPS(vsp1_pm_runtime_suspend, vsp1_pm_runtime_resume, NULL)
 };
 
 /* -----------------------------------------------------------------------------
@@ -709,6 +907,17 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
 		.rpf_count = 5,
 		.wpf_count = 2,
 		.num_bru_inputs = 5,
+		.header_mode = true,
+	}, {
+		.version = VI6_IP_VERSION_MODEL_VSPDL_H3,
+		.model = "VSP2-D",
+		.gen = 3,
+		.features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP
+			  | VSP1_HAS_BRS,
+		.rpf_count = 5,
+		.wpf_count = 2,
+		.num_bru_inputs = 5,
+		.header_mode = true,
 	},
 };
 
@@ -762,6 +971,10 @@ static int vsp1_probe(struct platform_device *pdev)
 		}
 	}
 
+	/* Get using number of BRS module */
+	of_property_read_u32(pdev->dev.of_node, "renesas,#brs",
+					&vsp1->num_brs_inputs);
+
 	/* Configure device parameters based on the version register. */
 	pm_runtime_enable(&pdev->dev);
 
@@ -789,6 +1002,27 @@ static int vsp1_probe(struct platform_device *pdev)
 
 	dev_dbg(&pdev->dev, "IP version 0x%08x\n", vsp1->version);
 
+	ret = RCAR_PRR_INIT();
+	if (ret) {
+		dev_dbg(vsp1->dev, "product register init fail.\n");
+		return ret;
+	}
+
+	if (vsp1->info->header_mode && !(RCAR_PRR_IS_PRODUCT(H3) &&
+		(RCAR_PRR_CHK_CUT(H3, WS11) <= 0)))
+		vsp1->auto_fld_mode = true;
+	else
+		vsp1->auto_fld_mode = false;
+
+	if (strcmp(dev_name(vsp1->dev), "fea20000.vsp") == 0)
+		vsp1->index = 0;
+	else if (strcmp(dev_name(vsp1->dev), "fea28000.vsp") == 0)
+		vsp1->index = 1;
+	else if (strcmp(dev_name(vsp1->dev), "fea30000.vsp") == 0)
+		vsp1->index = 2;
+	else if (strcmp(dev_name(vsp1->dev), "fea38000.vsp") == 0)
+		vsp1->index = 3;
+
 	/* Instanciate entities */
 	ret = vsp1_create_entities(vsp1);
 	if (ret < 0) {
@@ -796,6 +1030,10 @@ static int vsp1_probe(struct platform_device *pdev)
 		goto done;
 	}
 
+	if ((RCAR_PRR_IS_PRODUCT(H3) && (RCAR_PRR_CHK_CUT(H3, WS11) <= 0)) &&
+		vsp1_gen3_vspd_check(vsp1))
+		fcpv_reg[vsp1->index] =
+			ioremap(fcpvd_offset[vsp1->index], 0x20);
 done:
 	if (ret)
 		pm_runtime_disable(&pdev->dev);
@@ -807,11 +1045,16 @@ static int vsp1_remove(struct platform_device *pdev)
 {
 	struct vsp1_device *vsp1 = platform_get_drvdata(pdev);
 
+	vsp1_device_put(vsp1);
 	vsp1_destroy_entities(vsp1);
 	rcar_fcp_put(vsp1->fcp);
 
 	pm_runtime_disable(&pdev->dev);
 
+	if ((RCAR_PRR_IS_PRODUCT(H3) && (RCAR_PRR_CHK_CUT(H3, WS11) <= 0)) &&
+		vsp1_gen3_vspd_check(vsp1))
+		iounmap(fcpv_reg[vsp1->index]);
+
 	return 0;
 }
 
diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c
index 5263387..a64da29 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/vsp1/vsp1_entity.c
@@ -1,7 +1,7 @@
 /*
  * vsp1_entity.c  --  R-Car VSP1 Base Entity
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -36,6 +36,8 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity,
 {
 	struct vsp1_entity *source;
 	struct vsp1_entity *sink;
+	struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
+	u32 route_data;
 
 	if (entity->type == VSP1_ENTITY_HGO) {
 		u32 smppt;
@@ -68,8 +70,15 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity,
 		return;
 
 	sink = media_entity_to_vsp1_entity(source->sink);
-	vsp1_dl_list_write(dl, source->route->reg,
-			   sink->route->inputs[source->sink_pad]);
+
+	route_data = sink->route->inputs[source->sink_pad];
+	if (entity->type == VSP1_ENTITY_BRS)
+		route_data = sink->route->inputs[source->sink_pad] |
+			     VI6_DPR_ROUTE_BRSSEL_BRS;
+
+	dev_dbg(vsp1->dev, "type:%d, reg:0x%x, route_data:0x%x\n",
+		entity->type, source->route->reg, route_data);
+	vsp1_dl_list_write(dl, source->route->reg, route_data);
 }
 
 /* -----------------------------------------------------------------------------
@@ -449,12 +458,18 @@ static const struct vsp1_route vsp1_routes[] = {
 	  { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1),
 	    VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3),
 	    VI6_DPR_NODE_BRU_IN(4) }, VI6_DPR_NODE_BRU_OUT },
+	{ VSP1_ENTITY_BRS, 0, VI6_DPR_BRS_ROUTE,
+	  { 0, 0, 0, VI6_DPR_NODE_BRS_IN(0), VI6_DPR_NODE_BRS_IN(1), },
+	    VI6_DPR_NODE_BRS_OUT },
 	VSP1_ENTITY_ROUTE(CLU),
 	{ VSP1_ENTITY_HGO, 0, 0, { 0, }, 0 },
 	{ VSP1_ENTITY_HGT, 0, 0, { 0, }, 0 },
 	VSP1_ENTITY_ROUTE(HSI),
 	VSP1_ENTITY_ROUTE(HST),
-	{ VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, }, VI6_DPR_NODE_LIF },
+	{ VSP1_ENTITY_LIF, 0, 0,
+	  { VI6_DPR_NODE_LIF(0), }, VI6_DPR_NODE_LIF(0) },
+	{ VSP1_ENTITY_LIF, 1, 0,
+	  { VI6_DPR_NODE_LIF(1), }, VI6_DPR_NODE_LIF(1) },
 	VSP1_ENTITY_ROUTE(LUT),
 	VSP1_ENTITY_ROUTE_RPF(0),
 	VSP1_ENTITY_ROUTE_RPF(1),
diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h
index c169a06..d76cf16 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.h
+++ b/drivers/media/platform/vsp1/vsp1_entity.h
@@ -35,6 +35,7 @@ enum vsp1_entity_type {
 	VSP1_ENTITY_SRU,
 	VSP1_ENTITY_UDS,
 	VSP1_ENTITY_WPF,
+	VSP1_ENTITY_BRS,
 };
 
 /**
diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c
index e32acae..69bcf7e 100644
--- a/drivers/media/platform/vsp1/vsp1_lif.c
+++ b/drivers/media/platform/vsp1/vsp1_lif.c
@@ -1,7 +1,7 @@
 /*
  * vsp1_lif.c  --  R-Car VSP1 LCD Controller Interface
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -134,9 +134,9 @@ static void lif_configure(struct vsp1_entity *entity,
 {
 	const struct v4l2_mbus_framefmt *format;
 	struct vsp1_lif *lif = to_lif(&entity->subdev);
-	unsigned int hbth = 1300;
-	unsigned int obth = 400;
-	unsigned int lbth = 200;
+	unsigned int hbth = 0;
+	unsigned int obth = 3000;
+	unsigned int lbth = 0;
 
 	if (params != VSP1_ENTITY_PARAMS_INIT)
 		return;
@@ -146,11 +146,11 @@ static void lif_configure(struct vsp1_entity *entity,
 
 	obth = min(obth, (format->width + 1) / 2 * format->height - 4);
 
-	vsp1_lif_write(lif, dl, VI6_LIF_CSBTH,
+	vsp1_lif_write(lif, dl, VI6_LIF_CSBTH(lif->entity.index),
 			(hbth << VI6_LIF_CSBTH_HBTH_SHIFT) |
 			(lbth << VI6_LIF_CSBTH_LBTH_SHIFT));
 
-	vsp1_lif_write(lif, dl, VI6_LIF_CTRL,
+	vsp1_lif_write(lif, dl, VI6_LIF_CTRL(lif->entity.index),
 			(obth << VI6_LIF_CTRL_OBTH_SHIFT) |
 			(format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) |
 			VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN);
@@ -164,7 +164,8 @@ static const struct vsp1_entity_operations lif_entity_ops = {
  * Initialization and Cleanup
  */
 
-struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1)
+struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1,
+				 unsigned int lif_index)
 {
 	struct vsp1_lif *lif;
 	int ret;
@@ -175,6 +176,7 @@ struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1)
 
 	lif->entity.ops = &lif_entity_ops;
 	lif->entity.type = VSP1_ENTITY_LIF;
+	lif->entity.index = lif_index;
 
 	/* The LIF is never exposed to userspace, but media entity registration
 	 * requires a function to be set. Use PROC_VIDEO_PIXEL_FORMATTER just to
diff --git a/drivers/media/platform/vsp1/vsp1_lif.h b/drivers/media/platform/vsp1/vsp1_lif.h
index 7b35879..c2d11f2 100644
--- a/drivers/media/platform/vsp1/vsp1_lif.h
+++ b/drivers/media/platform/vsp1/vsp1_lif.h
@@ -1,7 +1,7 @@
 /*
  * vsp1_lif.h  --  R-Car VSP1 LCD Controller Interface
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -32,6 +32,7 @@ static inline struct vsp1_lif *to_lif(struct v4l2_subdev *subdev)
 	return container_of(subdev, struct vsp1_lif, entity.subdev);
 }
 
-struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1);
+struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1,
+				 unsigned int lif_index);
 
 #endif /* __VSP1_LIF_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c
index 7327192..20af576 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/vsp1/vsp1_pipe.c
@@ -1,7 +1,7 @@
 /*
  * vsp1_pipe.c  --  R-Car VSP1 Pipeline
  *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -21,6 +21,7 @@
 
 #include "vsp1.h"
 #include "vsp1_bru.h"
+#include "vsp1_brs.h"
 #include "vsp1_dl.h"
 #include "vsp1_entity.h"
 #include "vsp1_hgo.h"
@@ -119,7 +120,7 @@ static const struct vsp1_format_info vsp1_video_formats[] = {
 	{ V4L2_PIX_FMT_YVU420M, MEDIA_BUS_FMT_AYUV8_1X32,
 	  VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
 	  VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-	  3, { 8, 8, 8 }, false, true, 2, 2, false },
+	  3, { 8, 8, 8 }, false, false, 2, 2, false },
 	{ V4L2_PIX_FMT_YUV422M, MEDIA_BUS_FMT_AYUV8_1X32,
 	  VI6_FMT_Y_U_V_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
 	  VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
@@ -127,7 +128,7 @@ static const struct vsp1_format_info vsp1_video_formats[] = {
 	{ V4L2_PIX_FMT_YVU422M, MEDIA_BUS_FMT_AYUV8_1X32,
 	  VI6_FMT_Y_U_V_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
 	  VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-	  3, { 8, 8, 8 }, false, true, 2, 1, false },
+	  3, { 8, 8, 8 }, false, false, 2, 1, false },
 	{ V4L2_PIX_FMT_YUV444M, MEDIA_BUS_FMT_AYUV8_1X32,
 	  VI6_FMT_Y_U_V_444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
 	  VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
@@ -135,7 +136,7 @@ static const struct vsp1_format_info vsp1_video_formats[] = {
 	{ V4L2_PIX_FMT_YVU444M, MEDIA_BUS_FMT_AYUV8_1X32,
 	  VI6_FMT_Y_U_V_444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
 	  VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
-	  3, { 8, 8, 8 }, false, true, 1, 1, false },
+	  3, { 8, 8, 8 }, false, false, 1, 1, false },
 };
 
 /**
@@ -180,6 +181,13 @@ void vsp1_pipeline_reset(struct vsp1_pipeline *pipe)
 			bru->inputs[i].rpf = NULL;
 	}
 
+	if (pipe->brs) {
+		struct vsp1_brs *brs = to_brs(&pipe->brs->subdev);
+
+		for (i = 0; i < ARRAY_SIZE(brs->inputs); ++i)
+			brs->inputs[i].rpf = NULL;
+	}
+
 	for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) {
 		if (pipe->inputs[i]) {
 			pipe->inputs[i]->pipe = NULL;
@@ -209,6 +217,7 @@ void vsp1_pipeline_reset(struct vsp1_pipeline *pipe)
 	pipe->buffers_ready = 0;
 	pipe->num_inputs = 0;
 	pipe->bru = NULL;
+	pipe->brs = NULL;
 	pipe->hgo = NULL;
 	pipe->hgt = NULL;
 	pipe->lif = NULL;
@@ -224,6 +233,7 @@ void vsp1_pipeline_init(struct vsp1_pipeline *pipe)
 
 	INIT_LIST_HEAD(&pipe->entities);
 	pipe->state = VSP1_PIPELINE_STOPPED;
+	pipe->vmute_flag = false;
 }
 
 /* Must be called with the pipe irqlock held. */
@@ -315,9 +325,18 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe)
 
 void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
 {
+	unsigned long flags;
+
 	if (pipe == NULL)
 		return;
 
+	spin_lock_irqsave(&pipe->irqlock, flags);
+	if (pipe->output->write_back != 0) {
+		pipe->output->write_back--;
+		wake_up_interruptible(&pipe->event_wait);
+	}
+	spin_unlock_irqrestore(&pipe->irqlock, flags);
+
 	vsp1_dlm_irq_frame_end(pipe->output->dlm);
 
 	if (pipe->hgo)
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h
index f181949..305e2ae 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.h
+++ b/drivers/media/platform/vsp1/vsp1_pipe.h
@@ -1,7 +1,7 @@
 /*
  * vsp1_pipe.h  --  R-Car VSP1 Pipeline
  *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -102,6 +102,7 @@ struct vsp1_pipeline {
 	struct vsp1_rwpf *inputs[VSP1_MAX_RPF];
 	struct vsp1_rwpf *output;
 	struct vsp1_entity *bru;
+	struct vsp1_entity *brs;
 	struct vsp1_entity *hgo;
 	struct vsp1_entity *hgt;
 	struct vsp1_entity *lif;
@@ -109,6 +110,7 @@ struct vsp1_pipeline {
 	struct vsp1_entity *uds_input;
 
 	struct list_head entities;
+	wait_queue_head_t event_wait;
 
 	struct vsp1_dl_list *dl;
 
@@ -116,8 +118,18 @@ struct vsp1_pipeline {
 	unsigned int partitions;
 	struct v4l2_rect partition;
 	unsigned int current_partition;
+
+	bool vmute_flag;
 };
 
+static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e)
+{
+	if (likely(e->pipe))
+		return container_of(e->pipe, struct vsp1_pipeline, pipe);
+	else
+		return NULL;
+}
+
 void vsp1_pipeline_reset(struct vsp1_pipeline *pipe);
 void vsp1_pipeline_init(struct vsp1_pipeline *pipe);
 
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
index cd3e32a..7dcbd63 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -1,7 +1,7 @@
 /*
  * vsp1_regs.h  --  R-Car VSP1 Registers Definitions
  *
- * Copyright (C) 2013 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -20,32 +20,58 @@
 #define VI6_CMD(n)			(0x0000 + (n) * 4)
 #define VI6_CMD_STRCMD			(1 << 0)
 
+#define VI6_CLK_CTRL0			0x0010
+#define VI6_CLK_CTRL0_WORKAROUND	0x10010F1F
+
+#define VI6_CLK_CTRL1			0x0014
+#define VI6_CLK_CTRL1_WORKAROUND	0xFF00FFFF
+
 #define VI6_CLK_DCSWT			0x0018
 #define VI6_CLK_DCSWT_CSTPW_MASK	(0xff << 8)
 #define VI6_CLK_DCSWT_CSTPW_SHIFT	8
 #define VI6_CLK_DCSWT_CSTRW_MASK	(0xff << 0)
 #define VI6_CLK_DCSWT_CSTRW_SHIFT	0
+#define VI6_CLK_DCSWT_WORKAROUND1	0x00130808
+#define VI6_CLK_DCSWT_WORKAROUND2	0x00000808
+
+#define VI6_CLK_DCSM0			0x001C
+#define VI6_CLK_DCSM0_WORKAROUND	0x1FFF0F1F
+
+#define VI6_CLK_DCSM1			0x0020
+#define VI6_CLK_DCSM1_WORKAROUND	0xFF00FFFF
 
 #define VI6_SRESET			0x0028
 #define VI6_SRESET_SRTS(n)		(1 << (n))
 
+#define VI6_MRESET_ENB0			0x002C
+#define VI6_MRESET_ENB0_WORKAROUND1	0x0000001F
+#define VI6_MRESET_ENB0_WORKAROUND2	0x30000F1F
+
+#define VI6_MRESET_ENB1			0x0030
+#define VI6_MRESET_ENB1_WORKAROUND	0xFF00FFFF
+
+#define VI6_MRESET			0x0034
+#define VI6_MRESET_WORKAROUND		0x00000001
+
 #define VI6_STATUS			0x0038
 #define VI6_STATUS_SYS_ACT(n)		(1 << ((n) + 8))
 
 #define VI6_WPF_IRQ_ENB(n)		(0x0048 + (n) * 12)
+#define VI6_WFP_IRQ_ENB_UNDE		(1 << 16)
 #define VI6_WFP_IRQ_ENB_DFEE		(1 << 1)
 #define VI6_WFP_IRQ_ENB_FREE		(1 << 0)
 
 #define VI6_WPF_IRQ_STA(n)		(0x004c + (n) * 12)
+#define VI6_WFP_IRQ_STA_UND		(1 << 16)
 #define VI6_WFP_IRQ_STA_DFE		(1 << 1)
 #define VI6_WFP_IRQ_STA_FRE		(1 << 0)
 
-#define VI6_DISP_IRQ_ENB		0x0078
+#define VI6_DISP_IRQ_ENB(n)		(0x0078 + (n) * 60)
 #define VI6_DISP_IRQ_ENB_DSTE		(1 << 8)
 #define VI6_DISP_IRQ_ENB_MAEE		(1 << 5)
 #define VI6_DISP_IRQ_ENB_LNEE(n)	(1 << (n))
 
-#define VI6_DISP_IRQ_STA		0x007c
+#define VI6_DISP_IRQ_STA(n)		(0x007c + (n) * 60)
 #define VI6_DISP_IRQ_STA_DST		(1 << 8)
 #define VI6_DISP_IRQ_STA_MAE		(1 << 5)
 #define VI6_DISP_IRQ_STA_LNE(n)		(1 << (n))
@@ -69,12 +95,12 @@
 
 #define VI6_DL_HDR_ADDR(n)		(0x0104 + (n) * 4)
 
-#define VI6_DL_SWAP			0x0114
+#define VI6_DL_SWAP(n)			(0x0114 + (n) * 56)
 #define VI6_DL_SWAP_LWS			(1 << 2)
 #define VI6_DL_SWAP_WDS			(1 << 1)
 #define VI6_DL_SWAP_BTS			(1 << 0)
 
-#define VI6_DL_EXT_CTRL			0x011c
+#define VI6_DL_EXT_CTRL(n)		(0x011c + (n) * 36)
 #define VI6_DL_EXT_CTRL_NWE		(1 << 16)
 #define VI6_DL_EXT_CTRL_POLINT_MASK	(0x3f << 8)
 #define VI6_DL_EXT_CTRL_POLINT_SHIFT	8
@@ -302,7 +328,7 @@
 #define VI6_WPF_DSTM_ADDR_C0		0x1028
 #define VI6_WPF_DSTM_ADDR_C1		0x102c
 
-#define VI6_WPF_WRBCK_CTRL		0x1034
+#define VI6_WPF_WRBCK_CTRL(n)		(0x1034 + ((n) * 0x100))
 #define VI6_WPF_WRBCK_CTRL_WBMD		(1 << 0)
 
 /* -----------------------------------------------------------------------------
@@ -321,6 +347,10 @@
 #define VI6_DPR_HST_ROUTE		0x2044
 #define VI6_DPR_HSI_ROUTE		0x2048
 #define VI6_DPR_BRU_ROUTE		0x204c
+#define VI6_DPR_BRS_ROUTE		0x2050
+#define VI6_DPR_ROUTE_BRSSEL_MASK	(0x01 << 28)
+#define VI6_DPR_ROUTE_BRSSEL_ILV	(0x00 << 28)
+#define VI6_DPR_ROUTE_BRSSEL_BRS	(0x01 << 28)
 #define VI6_DPR_ROUTE_FXA_MASK		(0xff << 16)
 #define VI6_DPR_ROUTE_FXA_SHIFT		16
 #define VI6_DPR_ROUTE_FP_MASK		(0x3f << 8)
@@ -340,11 +370,13 @@
 #define VI6_DPR_NODE_UDS(n)		(17 + (n))
 #define VI6_DPR_NODE_LUT		22
 #define VI6_DPR_NODE_BRU_IN(n)		(((n) <= 3) ? 23 + (n) : 49)
+#define VI6_DPR_NODE_BRS_IN(n)		(38 + (n))
 #define VI6_DPR_NODE_BRU_OUT		27
 #define VI6_DPR_NODE_CLU		29
 #define VI6_DPR_NODE_HST		30
 #define VI6_DPR_NODE_HSI		31
-#define VI6_DPR_NODE_LIF		55
+#define VI6_DPR_NODE_BRS_OUT		40
+#define VI6_DPR_NODE_LIF(n)		(54 + (n))
 #define VI6_DPR_NODE_WPF(n)		(56 + (n))
 #define VI6_DPR_NODE_UNUSED		63
 
@@ -650,17 +682,101 @@
 #define VI6_HGT_REGRST_RCLEA		(1 << 0)
 
 /* -----------------------------------------------------------------------------
+ * BRS Control Registers
+ */
+
+#define VI6_BRS_INCTRL			0x3900
+#define VI6_BRS_INCTRL_NRM		(1 << 28)
+#define VI6_BRS_INCTRL_DnON		(1 << (16 + (n)))
+#define VI6_BRS_INCTRL_DITHn_OFF	(0 << ((n) * 4))
+#define VI6_BRS_INCTRL_DITHn_18BPP	(1 << ((n) * 4))
+#define VI6_BRS_INCTRL_DITHn_16BPP	(2 << ((n) * 4))
+#define VI6_BRS_INCTRL_DITHn_15BPP	(3 << ((n) * 4))
+#define VI6_BRS_INCTRL_DITHn_12BPP	(4 << ((n) * 4))
+#define VI6_BRS_INCTRL_DITHn_8BPP	(5 << ((n) * 4))
+#define VI6_BRS_INCTRL_DITHn_MASK	(7 << ((n) * 4))
+#define VI6_BRS_INCTRL_DITHn_SHIFT	((n) * 4)
+
+#define VI6_BRS_VIRRPF_SIZE		0x3904
+#define VI6_BRS_VIRRPF_SIZE_HSIZE_MASK	(0x1fff << 16)
+#define VI6_BRS_VIRRPF_SIZE_HSIZE_SHIFT	16
+#define VI6_BRS_VIRRPF_SIZE_VSIZE_MASK	(0x1fff << 0)
+#define VI6_BRS_VIRRPF_SIZE_VSIZE_SHIFT	0
+
+#define VI6_BRS_VIRRPF_LOC		0x3908
+#define VI6_BRS_VIRRPF_LOC_HCOORD_MASK	(0x1fff << 16)
+#define VI6_BRS_VIRRPF_LOC_HCOORD_SHIFT	16
+#define VI6_BRS_VIRRPF_LOC_VCOORD_MASK	(0x1fff << 0)
+#define VI6_BRS_VIRRPF_LOC_VCOORD_SHIFT	0
+
+#define VI6_BRS_VIRRPF_COL		0x390c
+#define VI6_BRS_VIRRPF_COL_A_MASK	(0xff << 24)
+#define VI6_BRS_VIRRPF_COL_A_SHIFT	24
+#define VI6_BRS_VIRRPF_COL_RCR_MASK	(0xff << 16)
+#define VI6_BRS_VIRRPF_COL_RCR_SHIFT	16
+#define VI6_BRS_VIRRPF_COL_GY_MASK	(0xff << 8)
+#define VI6_BRS_VIRRPF_COL_GY_SHIFT	8
+#define VI6_BRS_VIRRPF_COL_BCB_MASK	(0xff << 0)
+#define VI6_BRS_VIRRPF_COL_BCB_SHIFT	0
+
+#define VI6_BRS_CTRL(n)			(0x3910 + (n) * 8)
+#define VI6_BRS_CTRL_RBC		(1 << 31)
+#define VI6_BRS_CTRL_DSTSEL_BRSIN(n)	((n) << 20)
+#define VI6_BRS_CTRL_DSTSEL_VRPF	(4 << 20)
+#define VI6_BRS_CTRL_DSTSEL_MASK	(7 << 20)
+#define VI6_BRS_CTRL_SRCSEL_BRSIN(n)	((n) << 16)
+#define VI6_BRS_CTRL_SRCSEL_VRPF	(4 << 16)
+#define VI6_BRS_CTRL_SRCSEL_MASK	(7 << 16)
+#define VI6_BRS_CTRL_CROP(rop)		((rop) << 4)
+#define VI6_BRS_CTRL_CROP_MASK		(0xf << 4)
+#define VI6_BRS_CTRL_AROP(rop)		((rop) << 0)
+#define VI6_BRS_CTRL_AROP_MASK		(0xf << 0)
+
+#define VI6_BRS_BLD(n)			(0x3914 + (n) * 8)
+#define VI6_BRS_BLD_CBES		(1 << 31)
+#define VI6_BRS_BLD_CCMDX_DST_A		(0 << 28)
+#define VI6_BRS_BLD_CCMDX_255_DST_A	(1 << 28)
+#define VI6_BRS_BLD_CCMDX_SRC_A		(2 << 28)
+#define VI6_BRS_BLD_CCMDX_255_SRC_A	(3 << 28)
+#define VI6_BRS_BLD_CCMDX_COEFX		(4 << 28)
+#define VI6_BRS_BLD_CCMDX_MASK		(7 << 28)
+#define VI6_BRS_BLD_CCMDY_DST_A		(0 << 24)
+#define VI6_BRS_BLD_CCMDY_255_DST_A	(1 << 24)
+#define VI6_BRS_BLD_CCMDY_SRC_A		(2 << 24)
+#define VI6_BRS_BLD_CCMDY_255_SRC_A	(3 << 24)
+#define VI6_BRS_BLD_CCMDY_COEFY		(4 << 24)
+#define VI6_BRS_BLD_CCMDY_MASK		(7 << 24)
+#define VI6_BRS_BLD_CCMDY_SHIFT		24
+#define VI6_BRS_BLD_ABES		(1 << 23)
+#define VI6_BRS_BLD_ACMDX_DST_A		(0 << 20)
+#define VI6_BRS_BLD_ACMDX_255_DST_A	(1 << 20)
+#define VI6_BRS_BLD_ACMDX_SRC_A		(2 << 20)
+#define VI6_BRS_BLD_ACMDX_255_SRC_A	(3 << 20)
+#define VI6_BRS_BLD_ACMDX_COEFX		(4 << 20)
+#define VI6_BRS_BLD_ACMDX_MASK		(7 << 20)
+#define VI6_BRS_BLD_ACMDY_DST_A		(0 << 16)
+#define VI6_BRS_BLD_ACMDY_255_DST_A	(1 << 16)
+#define VI6_BRS_BLD_ACMDY_SRC_A		(2 << 16)
+#define VI6_BRS_BLD_ACMDY_255_SRC_A	(3 << 16)
+#define VI6_BRS_BLD_ACMDY_COEFY		(4 << 16)
+#define VI6_BRS_BLD_ACMDY_MASK		(7 << 16)
+#define VI6_BRS_BLD_COEFX_MASK		(0xff << 8)
+#define VI6_BRS_BLD_COEFX_SHIFT		8
+#define VI6_BRS_BLD_COEFY_MASK		(0xff << 0)
+#define VI6_BRS_BLD_COEFY_SHIFT		0
+
+/* -----------------------------------------------------------------------------
  * LIF Control Registers
  */
 
-#define VI6_LIF_CTRL			0x3b00
+#define VI6_LIF_CTRL(n)			(0x3b00 - ((n) * 0x100))
 #define VI6_LIF_CTRL_OBTH_MASK		(0x7ff << 16)
 #define VI6_LIF_CTRL_OBTH_SHIFT		16
 #define VI6_LIF_CTRL_CFMT		(1 << 4)
 #define VI6_LIF_CTRL_REQSEL		(1 << 1)
 #define VI6_LIF_CTRL_LIF_EN		(1 << 0)
 
-#define VI6_LIF_CSBTH			0x3b04
+#define VI6_LIF_CSBTH(n)		(0x3b04 - ((n) * 0x100))
 #define VI6_LIF_CSBTH_HBTH_MASK		(0x7ff << 16)
 #define VI6_LIF_CSBTH_HBTH_SHIFT	16
 #define VI6_LIF_CSBTH_LBTH_MASK		(0x7ff << 0)
@@ -689,6 +805,7 @@
 #define VI6_IP_VERSION_MODEL_VSPBD_GEN3	(0x15 << 8)
 #define VI6_IP_VERSION_MODEL_VSPBC_GEN3	(0x16 << 8)
 #define VI6_IP_VERSION_MODEL_VSPD_GEN3	(0x17 << 8)
+#define VI6_IP_VERSION_MODEL_VSPDL_H3	(0x19 << 8)
 #define VI6_IP_VERSION_SOC_MASK		(0xff << 0)
 #define VI6_IP_VERSION_SOC_H		(0x01 << 0)
 #define VI6_IP_VERSION_SOC_M		(0x02 << 0)
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c
index 03d7160..f70757f 100644
--- a/drivers/media/platform/vsp1/vsp1_rpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rpf.c
@@ -1,7 +1,7 @@
 /*
  * vsp1_rpf.c  --  R-Car VSP1 Read Pixel Formatter
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -51,6 +51,7 @@ static void rpf_configure(struct vsp1_entity *entity,
 			  struct vsp1_dl_list *dl,
 			  enum vsp1_entity_params params)
 {
+	struct vsp1_device *vsp1 = entity->vsp1;
 	struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
 	const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
 	const struct v4l2_pix_format_mplane *format = &rpf->format;
@@ -60,6 +61,16 @@ static void rpf_configure(struct vsp1_entity *entity,
 	unsigned int top = 0;
 	u32 pstride;
 	u32 infmt;
+	u32 alph_sel = 0;
+	u32 i;
+	u32 crop_width, crop_height, crop_x, crop_y;
+
+	if (pipe->vmute_flag) {
+		for (i = 0; i < vsp1->info->rpf_count; ++i)
+			vsp1_rpf_write(rpf, dl, VI6_DPR_RPF_ROUTE(i),
+						VI6_DPR_NODE_UNUSED);
+		return;
+	}
 
 	if (params == VSP1_ENTITY_PARAMS_RUNTIME) {
 		vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET,
@@ -72,8 +83,8 @@ static void rpf_configure(struct vsp1_entity *entity,
 	}
 
 	if (params == VSP1_ENTITY_PARAMS_PARTITION) {
-		unsigned int offsets[2];
 		struct v4l2_rect crop;
+		u32 fourcc;
 
 		/*
 		 * Source size and crop offsets.
@@ -113,29 +124,85 @@ static void rpf_configure(struct vsp1_entity *entity,
 				   / output->width;
 		}
 
-		vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE,
-			       (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
-			       (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
-		vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE,
-			       (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
-			       (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
+		crop_width = crop.width;
+		crop_x = crop.left;
+		fourcc = rpf->fmtinfo->fourcc;
 
-		offsets[0] = crop.top * format->plane_fmt[0].bytesperline
-			   + crop.left * fmtinfo->bpp[0] / 8;
+		if (rpf->interlaced) {
+			crop_height = crop.height / 2;
+			crop_y = crop.top / 2;
+
+			if ((fourcc == V4L2_PIX_FMT_UYVY) ||
+				(fourcc == V4L2_PIX_FMT_VYUY) ||
+				(fourcc == V4L2_PIX_FMT_YUYV) ||
+				(fourcc == V4L2_PIX_FMT_YVYU)) {
+				crop_width = round_down(crop_width, 2);
+				crop_x = round_down(crop_x, 2);
+			} else if ((fourcc == V4L2_PIX_FMT_NV12M) ||
+				(fourcc == V4L2_PIX_FMT_NV21M)) {
+				crop_width = round_down(crop_width, 2);
+				crop_height = round_down(crop_height, 2);
+				crop_x = round_down(crop_x, 2);
+				crop_y = round_down(crop_y, 2);
+			} else if ((fourcc == V4L2_PIX_FMT_NV16M) ||
+				(fourcc == V4L2_PIX_FMT_NV61M)) {
+				crop_width = round_down(crop_width, 2);
+				crop_x = round_down(crop_x, 2);
+			} else if ((fourcc == V4L2_PIX_FMT_YUV420M) ||
+				(fourcc == V4L2_PIX_FMT_YUV444M) ||
+				(fourcc == V4L2_PIX_FMT_YVU420M) ||
+				(fourcc == V4L2_PIX_FMT_YVU444M)) {
+				crop_width = round_down(crop_width, 2);
+				crop_height = round_down(crop_height, 2);
+			} else if ((fourcc == V4L2_PIX_FMT_YUV422M) ||
+				(fourcc == V4L2_PIX_FMT_YVU422M)) {
+				crop_width = round_down(crop_width, 2);
+				crop_height = round_down(crop_height, 2);
+				crop_x = round_down(crop_x, 2);
+				crop_y = round_down(crop_y, 2);
+			}
+		} else {
+			crop_height = crop.height;
+			crop_y = crop.top;
+		}
+
+		vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE,
+			      (crop_width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
+			      (crop_height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
+		vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE,
+			      (crop_width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
+			      (crop_height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
+
+		rpf->offsets[0] = crop_y * format->plane_fmt[0].bytesperline
+			   + crop_x * fmtinfo->bpp[0] / 8;
 
 		if (format->num_planes > 1)
-			offsets[1] = crop.top * format->plane_fmt[1].bytesperline
-				   + crop.left / fmtinfo->hsub
+			rpf->offsets[1] = crop_y
+				   * format->plane_fmt[1].bytesperline
+				   + crop_x / fmtinfo->hsub
 				   * fmtinfo->bpp[1] / 8;
 		else
-			offsets[1] = 0;
+			rpf->offsets[1] = 0;
 
-		vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y,
-			       rpf->mem.addr[0] + offsets[0]);
-		vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0,
-			       rpf->mem.addr[1] + offsets[1]);
-		vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1,
-			       rpf->mem.addr[2] + offsets[1]);
+		if (vsp1->auto_fld_mode)
+			vsp1_dl_set_addr_auto_fld(dl, rpf);
+		else {
+			vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y,
+					rpf->mem.addr[0] + rpf->offsets[0]);
+			if ((fourcc == V4L2_PIX_FMT_YVU420M) ||
+				(fourcc == V4L2_PIX_FMT_YVU422M) ||
+				(fourcc == V4L2_PIX_FMT_YVU444M)) {
+				vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0,
+					rpf->mem.addr[2] + rpf->offsets[1]);
+				vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1,
+					rpf->mem.addr[1] + rpf->offsets[1]);
+			} else {
+				vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0,
+					rpf->mem.addr[1] + rpf->offsets[1]);
+				vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1,
+					rpf->mem.addr[2] + rpf->offsets[1]);
+			}
+		}
 		return;
 	}
 
@@ -146,7 +213,10 @@ static void rpf_configure(struct vsp1_entity *entity,
 		pstride |= format->plane_fmt[1].bytesperline
 			<< VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
 
-	vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride);
+	if (rpf->interlaced)
+		vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride * 2);
+	else
+		vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride);
 
 	/* Format */
 	sink_format = vsp1_entity_get_pad_format(&rpf->entity,
@@ -182,7 +252,23 @@ static void rpf_configure(struct vsp1_entity *entity,
 		top = compose->top;
 	}
 
-	vsp1_rpf_write(rpf, dl, VI6_RPF_LOC,
+	if (pipe->brs) {
+		const struct v4l2_rect *compose;
+
+		compose = vsp1_entity_get_pad_selection(pipe->brs,
+							pipe->brs->config,
+							rpf->brs_input,
+							V4L2_SEL_TGT_COMPOSE);
+		left = compose->left;
+		top = compose->top;
+	}
+
+	if (rpf->interlaced)
+		vsp1_rpf_write(rpf, dl, VI6_RPF_LOC,
+		       (left << VI6_RPF_LOC_HCOORD_SHIFT) |
+		       ((top / 2) << VI6_RPF_LOC_VCOORD_SHIFT));
+	else
+		vsp1_rpf_write(rpf, dl, VI6_RPF_LOC,
 		       (left << VI6_RPF_LOC_HCOORD_SHIFT) |
 		       (top << VI6_RPF_LOC_VCOORD_SHIFT));
 
@@ -208,20 +294,46 @@ static void rpf_configure(struct vsp1_entity *entity,
 	 *
 	 * In all cases, disable color keying.
 	 */
-	vsp1_rpf_write(rpf, dl, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |
-		       (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
-				       : VI6_RPF_ALPH_SEL_ASEL_FIXED));
+	switch (fmtinfo->fourcc) {
+	case V4L2_PIX_FMT_ARGB555:
+		if (CONFIG_VIDEO_RENESAS_VSP_ALPHA_BIT_ARGB1555 == 1)
+			alph_sel = VI6_RPF_ALPH_SEL_ASEL_SELECT |
+				   VI6_RPF_ALPH_SEL_AEXT_EXT |
+				   VI6_RPF_ALPH_SEL_ALPHA1_MASK |
+				   (rpf->alpha &
+				   VI6_RPF_ALPH_SEL_ALPHA0_MASK);
+		else
+			alph_sel = VI6_RPF_ALPH_SEL_ASEL_SELECT |
+				   VI6_RPF_ALPH_SEL_AEXT_EXT |
+				   ((rpf->alpha & 0xff) << 8) |
+				   VI6_RPF_ALPH_SEL_ALPHA0_MASK;
+		break;
+	case V4L2_PIX_FMT_ARGB444:
+		alph_sel = VI6_RPF_ALPH_SEL_AEXT_ONE;
+		break;
+	case V4L2_PIX_FMT_ABGR32:
+	case V4L2_PIX_FMT_ARGB32:
+		break;
+	default:
+		alph_sel = VI6_RPF_ALPH_SEL_AEXT_EXT |
+			   (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
+			   : VI6_RPF_ALPH_SEL_ASEL_FIXED);
+		break;
+	}
+
+	vsp1_rpf_write(rpf, dl, VI6_RPF_ALPH_SEL, alph_sel);
 
 	if (entity->vsp1->info->gen == 3) {
 		u32 mult;
 
-		if (fmtinfo->alpha) {
+		if ((fmtinfo->alpha) &&
+			(fmtinfo->fourcc != V4L2_PIX_FMT_ARGB555)) {
 			/* When the input contains an alpha channel enable the
 			 * alpha multiplier. If the input is premultiplied we
 			 * need to multiply both the alpha channel and the pixel
 			 * components by the global alpha value to keep them
 			 * premultiplied. Otherwise multiply the alpha channel
-			 * only.
+			 * only. The alpha multiplier is disabled in ARGB1555.
 			 */
 			bool premultiplied = format->flags
 					   & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
index b4ffc38..26aef3e 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
@@ -1,7 +1,7 @@
 /*
  * vsp1_rwpf.h  --  R-Car VSP1 Read and Write Pixel Formatters
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -48,11 +48,13 @@ struct vsp1_rwpf {
 	struct v4l2_pix_format_mplane format;
 	const struct vsp1_format_info *fmtinfo;
 	unsigned int bru_input;
+	unsigned int brs_input;
 
 	unsigned int alpha;
 
 	u32 mult_alpha;
 	u32 outfmt;
+	unsigned int offsets[2];
 
 	struct {
 		spinlock_t lock;
@@ -65,6 +67,11 @@ struct vsp1_rwpf {
 	struct vsp1_rwpf_memory mem;
 
 	struct vsp1_dl_manager *dlm;
+
+	bool interlaced;
+
+	int write_back;
+	dma_addr_t buf_addr[3];
 };
 
 static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev)
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index 94b4285..cc4f3e3 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -1,7 +1,7 @@
 /*
  * vsp1_video.c  --  R-Car VSP1 Video Node
  *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -27,6 +27,8 @@
 #include <media/videobuf2-v4l2.h>
 #include <media/videobuf2-dma-contig.h>
 
+#include <media/rcar-fcp.h>
+
 #include "vsp1.h"
 #include "vsp1_bru.h"
 #include "vsp1_dl.h"
@@ -1109,6 +1111,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
 {
 	struct vsp1_video *video;
 	const char *direction;
+	struct device *fcp;
 	int ret;
 
 	video = devm_kzalloc(vsp1->dev, sizeof(*video), GFP_KERNEL);
@@ -1166,7 +1169,8 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
 	video->queue.ops = &vsp1_video_queue_qops;
 	video->queue.mem_ops = &vb2_dma_contig_memops;
 	video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
-	video->queue.dev = video->vsp1->dev;
+	fcp = rcar_fcp_get_device(vsp1->fcp);
+	video->queue.dev = fcp ? fcp : video->vsp1->dev;
 	ret = vb2_queue_init(&video->queue);
 	if (ret < 0) {
 		dev_err(video->vsp1->dev, "failed to initialize vb2 queue\n");
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index 958e7f2..6f26b62 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -1,7 +1,7 @@
 /*
  * vsp1_wpf.c  --  R-Car VSP1 Write Pixel Formatter
  *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -248,6 +248,20 @@ static void wpf_configure(struct vsp1_entity *entity,
 	u32 outfmt = 0;
 	u32 srcrpf = 0;
 
+	if (pipe->vmute_flag) {
+		vsp1_wpf_write(wpf, dl, VI6_WPF_SRCRPF +
+			(0x100 * wpf->entity.index),
+			 VI6_WPF_SRCRPF_VIRACT_MST);
+		vsp1_wpf_write(wpf, dl, VI6_WPF_HSZCLIP +
+			(0x100 * wpf->entity.index), 0);
+		vsp1_wpf_write(wpf, dl, VI6_WPF_VSZCLIP +
+			(0x100 * wpf->entity.index), 0);
+		vsp1_wpf_write(wpf, dl, VI6_DPR_WPF_FPORCH(wpf->entity.index),
+			VI6_DPR_WPF_FPORCH_FP_WPFN);
+
+		return;
+	}
+
 	if (params == VSP1_ENTITY_PARAMS_RUNTIME) {
 		const unsigned int mask = BIT(WPF_CTRL_VFLIP)
 					| BIT(WPF_CTRL_HFLIP);
@@ -384,10 +398,21 @@ static void wpf_configure(struct vsp1_entity *entity,
 	}
 
 	/* Format */
-	if (!pipe->lif) {
+	if (!pipe->lif || (wpf->write_back == 2)) {
 		const struct v4l2_pix_format_mplane *format = &wpf->format;
 		const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
 
+		if (pipe->lif) {
+			vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y,
+						wpf->buf_addr[0]);
+			if (format->num_planes > 1)
+				vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0,
+						wpf->buf_addr[1]);
+			if (format->num_planes > 2)
+				vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1,
+						wpf->buf_addr[2]);
+		}
+
 		outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT;
 
 		if (wpf->flip.rotate)
@@ -424,7 +449,12 @@ static void wpf_configure(struct vsp1_entity *entity,
 	vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index),
 			   VI6_DPR_WPF_FPORCH_FP_WPFN);
 
-	vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, 0);
+	if (pipe->lif && (pipe->output->write_back == 2))
+		vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL(wpf->entity.index),
+					VI6_WPF_WRBCK_CTRL_WBMD);
+	else
+		vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL(wpf->entity.index),
+				   0);
 
 	/* Sources. If the pipeline has a single input and BRU is not used,
 	 * configure it as the master layer. Otherwise configure all
@@ -450,7 +480,7 @@ static void wpf_configure(struct vsp1_entity *entity,
 	/* Enable interrupts */
 	vsp1_dl_list_write(dl, VI6_WPF_IRQ_STA(wpf->entity.index), 0);
 	vsp1_dl_list_write(dl, VI6_WPF_IRQ_ENB(wpf->entity.index),
-			   VI6_WFP_IRQ_ENB_DFEE);
+			   VI6_WFP_IRQ_ENB_FREE | VI6_WFP_IRQ_ENB_UNDE);
 }
 
 static unsigned int wpf_max_width(struct vsp1_entity *entity,
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 5274f50..7ec33c5 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -558,6 +558,16 @@
 	  This provides support for the SDHI SD/SDIO controller found in
 	  SuperH and ARM SH-Mobile SoCs
 
+config MMC_SDHI_PIO
+	bool "SH-Mobile SDHI PIO transfer mode setting"
+	depends on MMC_SDHI
+	help
+	  This setting switches the transfer mode of the PIO and DMA.
+
+	  Select Yes or No according to the following.
+	  When switching the transfer mode from DMA to PIO, say Y here.
+	  When switching the transfer mode from PIO to DMA, say N here.
+
 config MMC_CB710
 	tristate "ENE CB710 MMC/SD Interface support"
 	depends on PCI
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e2bdaaf..1ac115f 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -37,7 +37,13 @@
 obj-$(CONFIG_MMC_TMIO)		+= tmio_mmc.o
 obj-$(CONFIG_MMC_TMIO_CORE)	+= tmio_mmc_core.o
 tmio_mmc_core-y			:= tmio_mmc_pio.o
+ifeq ($(CONFIG_ARM64),y)
+# if CONFIG_ARM64, we assume it is gen3
+tmio_mmc_core-$(subst m,y,$(CONFIG_MMC_SDHI))	+= tmio_mmc_dma_gen3.o
+else
+# if no, We assume it is gen2 or older
 tmio_mmc_core-$(subst m,y,$(CONFIG_MMC_SDHI))	+= tmio_mmc_dma.o
+endif
 obj-$(CONFIG_MMC_SDHI)		+= sh_mobile_sdhi.o
 obj-$(CONFIG_MMC_CB710)		+= cb710-mmc.o
 obj-$(CONFIG_MMC_VIA_SDMMC)	+= via-sdmmc.o
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index 665ea53..a91e598 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -1,8 +1,8 @@
 /*
  * SuperH Mobile SDHI
  *
+ * Copyright (C) 2016 Renesas Electronics Corporation
  * Copyright (C) 2016 Sang Engineering, Wolfram Sang
- * Copyright (C) 2015-16 Renesas Electronics Corporation
  * Copyright (C) 2009 Magnus Damm
  *
  * This program is free software; you can redistribute it and/or modify
@@ -37,7 +37,7 @@
 
 #include "tmio_mmc.h"
 
-#define EXT_ACC           0xe4
+#define HOST_MODE           0xe4
 
 #define SDHI_VER_GEN2_SDR50	0x490c
 /* very old datasheets said 0x490c for SDR104, too. They are wrong! */
@@ -59,6 +59,9 @@ struct sh_mobile_sdhi_of_data {
 	enum dma_slave_buswidth dma_buswidth;
 	dma_addr_t dma_rx_offset;
 	unsigned bus_shift;
+	unsigned int max_blk_count;
+	unsigned short max_segs;
+	bool sdbuf_64bit;
 	int scc_offset;
 	struct sh_mobile_sdhi_scc *taps;
 	int taps_num;
@@ -107,9 +110,15 @@ static struct sh_mobile_sdhi_scc rcar_gen3_scc_taps[] = {
 
 static const struct sh_mobile_sdhi_of_data of_rcar_gen3_compatible = {
 	.tmio_flags	= TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
-			  TMIO_MMC_CLK_ACTUAL | TMIO_MMC_MIN_RCAR2,
-	.capabilities	= MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
+			  TMIO_MMC_CLK_ACTUAL | TMIO_MMC_MIN_RCAR2 |
+			  TMIO_MMC_CLK_NO_SLEEP,
+	.capabilities	= MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
+			  MMC_CAP_CMD23,
 	.bus_shift	= 2,
+	/* Gen3 SDHI DMAC can handle 0xffffffff blk count, but seg = 1 */
+	.max_blk_count	= 0xffffffff,
+	.max_segs = 1,
+	.sdbuf_64bit = true,
 	.scc_offset	= 0x1000,
 	.taps		= rcar_gen3_scc_taps,
 	.taps_num	= ARRAY_SIZE(rcar_gen3_scc_taps),
@@ -171,7 +180,7 @@ static void sh_mobile_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
 		return;
 	}
 
-	sd_ctrl_write16(host, EXT_ACC, val);
+	sd_ctrl_write16(host, HOST_MODE, val);
 }
 
 static int sh_mobile_sdhi_clk_enable(struct tmio_mmc_host *host)
@@ -297,6 +306,7 @@ static int sh_mobile_sdhi_start_signal_voltage_switch(struct mmc_host *mmc,
 #define SH_MOBILE_SDHI_SCC_CKSEL	0x006
 #define SH_MOBILE_SDHI_SCC_RVSCNTL	0x008
 #define SH_MOBILE_SDHI_SCC_RVSREQ	0x00A
+#define SH_MOBILE_SDHI_SCC_TMPPORT2	0x00E
 
 /* Definitions for values the SH_MOBILE_SDHI_SCC_DTCNTL register */
 #define SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN		BIT(0)
@@ -309,6 +319,9 @@ static int sh_mobile_sdhi_start_signal_voltage_switch(struct mmc_host *mmc,
 #define SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN	BIT(1)
 /* Definitions for values the SH_MOBILE_SDHI_SCC_RVSREQ register */
 #define SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR	BIT(2)
+/* Definitions for values the SH_MOBILE_SDHI_SCC_TMPPORT2 register */
+#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN	BIT(31)
+#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL	BIT(4)
 
 static inline u32 sd_scc_read32(struct tmio_mmc_host *host,
 				struct sh_mobile_sdhi *priv, int addr)
@@ -327,7 +340,10 @@ static unsigned int sh_mobile_sdhi_init_tuning(struct tmio_mmc_host *host)
 {
 	struct sh_mobile_sdhi *priv;
 
-	if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
+	if (!(host->mmc->caps & MMC_CAP_UHS_SDR104) &&
+	    !(host->mmc->caps2 & MMC_CAP2_HS200_1_8V_SDR) &&
+	    !(host->mmc->caps2 & (MMC_CAP2_HS400_1_8V |
+					MMC_CAP2_HS200_1_8V_SDR)))
 		return 0;
 
 	priv = host_to_priv(host);
@@ -374,6 +390,27 @@ static void sh_mobile_sdhi_prepare_tuning(struct tmio_mmc_host *host,
 	sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, tap);
 }
 
+static void sh_mobile_sdhi_prepare_hs400_tuning(struct mmc_host *mmc,
+					struct mmc_ios *ios)
+{
+	struct tmio_mmc_host *host = mmc_priv(mmc);
+	struct sh_mobile_sdhi *priv = host_to_priv(host);
+
+	sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
+		sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+
+	/* Set HS400 mode */
+	sd_ctrl_write16(host, CTL_SDIF_MODE, 0x0001 |
+		sd_ctrl_read16(host, CTL_SDIF_MODE));
+	sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2,
+		(SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN |
+		SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) |
+		sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2));
+
+	sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
+		sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+}
+
 #define SH_MOBILE_SDHI_MAX_TAP 3
 
 static int sh_mobile_sdhi_select_tuning(struct tmio_mmc_host *host)
@@ -389,6 +426,15 @@ static int sh_mobile_sdhi_select_tuning(struct tmio_mmc_host *host)
 	/* Clear SCC_RVSREQ */
 	sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
 
+	/* Merge the results */
+	for (i = 0; i < host->tap_num * 2; i++) {
+		if (!test_bit(i, host->taps)) {
+			clear_bit(i % host->tap_num, host->taps);
+			clear_bit((i % host->tap_num) + host->tap_num,
+					host->taps);
+		}
+	}
+
 	/*
 	 * Find the longest consecutive run of successful probes.  If that
 	 * is more than SH_MOBILE_SDHI_MAX_TAP probes long then use the
@@ -438,7 +484,10 @@ static bool sh_mobile_sdhi_check_scc_error(struct tmio_mmc_host *host)
 {
 	struct sh_mobile_sdhi *priv;
 
-	if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
+	if (!(host->mmc->caps & MMC_CAP_UHS_SDR104) &&
+	    !(host->mmc->caps2 & MMC_CAP2_HS200_1_8V_SDR) &&
+	    !(host->mmc->caps2 & (MMC_CAP2_HS400_1_8V |
+					MMC_CAP2_HS200_1_8V_SDR)))
 		return 0;
 
 	priv = host_to_priv(host);
@@ -460,7 +509,10 @@ static void sh_mobile_sdhi_hw_reset(struct tmio_mmc_host *host)
 {
 	struct sh_mobile_sdhi *priv;
 
-	if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
+	if (!(host->mmc->caps & MMC_CAP_UHS_SDR104) &&
+	    !(host->mmc->caps2 & MMC_CAP2_HS200_1_8V_SDR) &&
+	    !(host->mmc->caps2 & (MMC_CAP2_HS400_1_8V |
+					MMC_CAP2_HS200_1_8V_SDR)))
 		return;
 
 	priv = host_to_priv(host);
@@ -473,6 +525,14 @@ static void sh_mobile_sdhi_hw_reset(struct tmio_mmc_host *host)
 		       ~SH_MOBILE_SDHI_SCC_CKSEL_DTSEL &
 		       sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL));
 
+	/* Reset HS400 mode */
+	sd_ctrl_write16(host, CTL_SDIF_MODE, ~0x0001 &
+			sd_ctrl_read16(host, CTL_SDIF_MODE));
+	sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2,
+			~(SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN |
+			SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) &
+			sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2));
+
 	sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
 			sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
 
@@ -513,7 +573,7 @@ static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
 	case CTL_SD_MEM_CARD_OPT:
 	case CTL_TRANSACTION_CTL:
 	case CTL_DMA_ENABLE:
-	case EXT_ACC:
+	case HOST_MODE:
 		return sh_mobile_sdhi_wait_idle(host);
 	}
 
@@ -540,10 +600,12 @@ static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card,
 
 static void sh_mobile_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable)
 {
+	int dma_width = host->dma->sdbuf_64bit ? 64 : 32;
+
 	sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0);
 
 	/* enable 32bit access if DMA mode if possibile */
-	sh_mobile_sdhi_sdbuf_width(host, enable ? 32 : 16);
+	sh_mobile_sdhi_sdbuf_width(host, enable ? dma_width : 16);
 }
 
 static int sh_mobile_sdhi_probe(struct platform_device *pdev)
@@ -597,6 +659,8 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
 		mmc_data->capabilities |= of_data->capabilities;
 		mmc_data->capabilities2 |= of_data->capabilities2;
 		mmc_data->dma_rx_offset = of_data->dma_rx_offset;
+		mmc_data->max_blk_count	= of_data->max_blk_count;
+		mmc_data->max_segs = of_data->max_segs;
 		dma_priv->dma_buswidth = of_data->dma_buswidth;
 		host->bus_shift = of_data->bus_shift;
 	}
@@ -619,10 +683,15 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
 		host->select_tuning	= sh_mobile_sdhi_select_tuning;
 		host->check_scc_error	= sh_mobile_sdhi_check_scc_error;
 		host->hw_reset		= sh_mobile_sdhi_hw_reset;
+		host->prepare_hs400_tuning =
+			sh_mobile_sdhi_prepare_hs400_tuning;
 	}
 
 	/* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */
-	if (!host->bus_shift && resource_size(res) > 0x100) /* old way to determine the shift */
+	if (resource_size(res) > 0x400)
+		host->bus_shift = 2;
+	else if (!host->bus_shift &&
+	    resource_size(res) > 0x100) /* old way to determine the shift */
 		host->bus_shift = 1;
 
 	if (mmd)
@@ -659,7 +728,10 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
 	if (ret < 0)
 		goto efree;
 
-	if (host->mmc->caps & MMC_CAP_UHS_SDR104) {
+	if ((host->mmc->caps & MMC_CAP_UHS_SDR104) ||
+	    (host->mmc->caps2 & MMC_CAP2_HS200_1_8V_SDR) ||
+	    (host->mmc->caps2 & (MMC_CAP2_HS400_1_8V |
+					MMC_CAP2_HS200_1_8V_SDR))) {
 		host->mmc->caps |= MMC_CAP_HW_RESET;
 
 		if (of_id && of_id->data) {
@@ -730,11 +802,13 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
 }
 
 static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
-	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
-			pm_runtime_force_resume)
 	SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
 			tmio_mmc_host_runtime_resume,
 			NULL)
+#ifdef CONFIG_PM_SLEEP
+	.suspend_late = tmio_mmc_host_suspend,
+	.resume_early = tmio_mmc_host_resume,
+#endif
 };
 
 static struct platform_driver sh_mobile_sdhi_driver = {
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index e897e7f..17ac517 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -30,7 +30,7 @@ static int tmio_mmc_suspend(struct device *dev)
 	const struct mfd_cell *cell = mfd_get_cell(pdev);
 	int ret;
 
-	ret = pm_runtime_force_suspend(dev);
+	ret = tmio_mmc_host_suspend(dev);
 
 	/* Tell MFD core it can disable us now.*/
 	if (!ret && cell->disable)
@@ -50,7 +50,7 @@ static int tmio_mmc_resume(struct device *dev)
 		ret = cell->resume(pdev);
 
 	if (!ret)
-		ret = pm_runtime_force_resume(dev);
+		ret = tmio_mmc_host_resume(dev);
 
 	return ret;
 }
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 637581f..e81dbe1 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -19,6 +19,7 @@
 #define TMIO_MMC_H
 
 #include <linux/dmaengine.h>
+#include <linux/completion.h>
 #include <linux/highmem.h>
 #include <linux/mutex.h>
 #include <linux/pagemap.h>
@@ -45,6 +46,7 @@
 #define CTL_DMA_ENABLE 0xd8
 #define CTL_RESET_SD 0xe0
 #define CTL_VERSION 0xe2
+#define CTL_SDIF_MODE 0xe6
 #define CTL_SDIO_REGS 0x100
 #define CTL_CLK_AND_WAIT_CTL 0x138
 #define CTL_RESET_SDIO 0x1e0
@@ -104,6 +106,7 @@ struct tmio_mmc_host;
 
 struct tmio_mmc_dma {
 	enum dma_slave_buswidth dma_buswidth;
+	bool sdbuf_64bit;
 	bool (*filter)(struct dma_chan *chan, void *arg);
 	void (*enable)(struct tmio_mmc_host *host, bool enable);
 };
@@ -154,6 +157,7 @@ struct tmio_mmc_host {
 	bool			native_hotplug;
 	bool			sdio_irq_enabled;
 	u32			scc_tappos;
+	struct completion	completion;
 
 	/* Mandatory callback */
 	int (*clk_enable)(struct tmio_mmc_host *host);
@@ -180,6 +184,7 @@ struct tmio_mmc_host {
 	/* Tuning values: 1 for success, 0 for failure */
 	DECLARE_BITMAP(taps, BITS_PER_BYTE * sizeof(long));
 	unsigned int tap_num;
+	void (*prepare_hs400_tuning)(struct mmc_host *mmc, struct mmc_ios *ios);
 };
 
 struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev);
@@ -244,6 +249,11 @@ int tmio_mmc_host_runtime_suspend(struct device *dev);
 int tmio_mmc_host_runtime_resume(struct device *dev);
 #endif
 
+#ifdef CONFIG_PM_SLEEP
+int tmio_mmc_host_suspend(struct device *dev);
+int tmio_mmc_host_resume(struct device *dev);
+#endif
+
 static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
 {
 	return readw(host->ctl + (addr << host->bus_shift));
diff --git a/drivers/mmc/host/tmio_mmc_dma_gen3.c b/drivers/mmc/host/tmio_mmc_dma_gen3.c
new file mode 100644
index 0000000..b289dfa
--- /dev/null
+++ b/drivers/mmc/host/tmio_mmc_dma_gen3.c
@@ -0,0 +1,196 @@
+/*
+ * linux/drivers/mmc/tmio_mmc_dma_gen3.c
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * R-Car Gen3 DMA function for TMIO MMC implementations
+ */
+
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/mfd/tmio.h>
+#include <linux/mmc/host.h>
+#include <linux/pagemap.h>
+#include <linux/scatterlist.h>
+
+#include "tmio_mmc.h"
+
+#define DM_CM_DTRAN_MODE	0x820
+#define DM_CM_DTRAN_CTRL	0x828
+#define DM_CM_RST		0x830
+#define DM_CM_INFO1		0x840
+#define DM_CM_INFO1_MASK	0x848
+#define DM_CM_INFO2		0x850
+#define DM_CM_INFO2_MASK	0x858
+#define DM_DTRAN_ADDR		0x880
+
+/* DM_CM_DTRAN_MODE */
+#define DTRAN_MODE_CH_NUM_CH0	0	/* "downstream" = for write commands */
+#define DTRAN_MODE_CH_NUM_CH1	BIT(16)	/* "uptream" = for read commands */
+#define DTRAN_MODE_BUS_WID_TH	(BIT(5) | BIT(4))
+#define DTRAN_MODE_ADDR_MODE	BIT(0)	/* 1 = Increment address */
+
+/* DM_CM_DTRAN_CTRL */
+#define DTRAN_CTRL_DM_START	BIT(0)
+
+/* DM_CM_RST */
+#define RST_DTRANRST1		BIT(9)
+#define RST_DTRANRST0		BIT(8)
+#define RST_RESERVED_BITS	GENMASK_ULL(32, 0)
+
+/* DM_CM_INFO1 and DM_CM_INFO1_MASK */
+#define INFO1_CLEAR		0
+#define INFO1_DTRANEND1		BIT(17)
+#define INFO1_DTRANEND0		BIT(16)
+
+/* DM_CM_INFO2 and DM_CM_INFO2_MASK */
+#define INFO2_DTRANERR1		BIT(17)
+#define INFO2_DTRANERR0		BIT(16)
+
+/*
+ * Specification of this driver:
+ * - host->chan_{rx,tx} will be used as a flag of enabling/disabling the dma
+ * - Since this SDHI DMAC register set has actual 32-bit and "bus_shift" is 2,
+ *   this driver cannot use original sd_ctrl_{write,read}32 functions.
+ */
+
+static void tmio_dm_write(struct tmio_mmc_host *host, int addr, u64 val)
+{
+	writeq(val, host->ctl + addr);
+}
+
+void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable)
+{
+	if (!host->chan_tx || !host->chan_rx)
+		return;
+
+	if (!enable)
+		tmio_dm_write(host, DM_CM_INFO1, INFO1_CLEAR);
+
+	if (host->dma->enable)
+		host->dma->enable(host, enable);
+}
+
+void tmio_mmc_abort_dma(struct tmio_mmc_host *host)
+{
+	u64 val = RST_DTRANRST1 | RST_DTRANRST0;
+
+	dev_dbg(&host->pdev->dev, "%s\n", __func__);
+
+	tmio_mmc_enable_dma(host, false);
+
+	tmio_dm_write(host, DM_CM_RST, RST_RESERVED_BITS & ~val);
+	tmio_dm_write(host, DM_CM_RST, RST_RESERVED_BITS | val);
+
+	tmio_mmc_enable_dma(host, true);
+}
+
+void tmio_mmc_start_dma(struct tmio_mmc_host *host, struct mmc_data *data)
+{
+	struct scatterlist *sg = host->sg_ptr;
+	u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE;
+	enum dma_data_direction dir;
+	int ret;
+	u32 irq_mask;
+
+	if (!host->chan_rx || !host->chan_tx)
+		return;
+
+	/* This DMAC cannot handle if sg_len is not 1 */
+	WARN_ON(host->sg_len > 1);
+
+	dev_dbg(&host->pdev->dev, "%s: %d, %x\n", __func__, host->sg_len,
+		data->flags);
+
+	/* This DMAC cannot handle if buffer is not 8-bytes alignment */
+	if (!IS_ALIGNED(sg->offset, 8)) {
+		host->force_pio = true;
+		tmio_mmc_enable_dma(host, false);
+		return;
+	}
+
+	if (data->flags & MMC_DATA_READ) {
+		dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
+		dir = DMA_FROM_DEVICE;
+		irq_mask = TMIO_STAT_RXRDY;
+	} else {
+		dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
+		dir = DMA_TO_DEVICE;
+		irq_mask = TMIO_STAT_TXRQ;
+	}
+
+	ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir);
+	if (ret < 0) {
+		dev_err(&host->pdev->dev, "%s: dma_map_sg failed\n", __func__);
+		return;
+	}
+
+	tmio_mmc_enable_dma(host, true);
+
+	/* disable PIO irqs to avoid "PIO IRQ in DMA mode!" */
+	tmio_mmc_disable_mmc_irqs(host, irq_mask);
+
+	/* set dma parameters */
+	tmio_dm_write(host, DM_CM_DTRAN_MODE, dtran_mode);
+	tmio_dm_write(host, DM_DTRAN_ADDR, sg->dma_address);
+}
+
+#ifndef CONFIG_MMC_SDHI_PIO
+static void tmio_mmc_issue_tasklet_fn(unsigned long arg)
+{
+	struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
+
+	dev_dbg(&host->pdev->dev, "%s\n", __func__);
+
+	tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND);
+
+	/* start the DMAC */
+	tmio_dm_write(host, DM_CM_DTRAN_CTRL, DTRAN_CTRL_DM_START);
+}
+
+static void tmio_mmc_complete_tasklet_fn(unsigned long arg)
+{
+	struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
+	enum dma_data_direction dir;
+
+	dev_dbg(&host->pdev->dev, "%s: %p\n", __func__, host->data);
+
+	if (!host->data)
+		return;
+
+	if (host->data->flags & MMC_DATA_READ)
+		dir = DMA_FROM_DEVICE;
+	else
+		dir = DMA_TO_DEVICE;
+
+	tmio_mmc_enable_dma(host, false);
+	dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->sg_len, dir);
+	tmio_mmc_do_data_irq(host);
+}
+#endif
+
+void tmio_mmc_request_dma(struct tmio_mmc_host *host,
+			  struct tmio_mmc_data *pdata)
+{
+#ifndef CONFIG_MMC_SDHI_PIO
+	/* Each value is set to non-zero to assume "enabling" each DMA */
+	host->chan_rx = host->chan_tx = (void *)0xdeadbeaf;
+
+	tasklet_init(&host->dma_complete, tmio_mmc_complete_tasklet_fn,
+		     (unsigned long)host);
+	tasklet_init(&host->dma_issue, tmio_mmc_issue_tasklet_fn,
+		     (unsigned long)host);
+#endif
+}
+
+void tmio_mmc_release_dma(struct tmio_mmc_host *host)
+{
+	/* Each value is set to zero to assume "disabling" each DMA */
+	host->chan_rx = host->chan_tx = NULL;
+}
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index a0f05eb..8b1f403 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -1,8 +1,8 @@
 /*
  * linux/drivers/mmc/host/tmio_mmc_pio.c
  *
+ * Copyright (C) 2016 Renesas Electronics Corporation
  * Copyright (C) 2016 Sang Engineering, Wolfram Sang
- * Copyright (C) 2015-16 Renesas Electronics Corporation
  * Copyright (C) 2011 Guennadi Liakhovetski
  * Copyright (C) 2007 Ian Molton
  * Copyright (C) 2004 Ian Molton
@@ -161,8 +161,9 @@ static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
 	msleep(host->pdata->flags & TMIO_MMC_MIN_RCAR2 ? 1 : 10);
 
 	if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
-		sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100);
-		msleep(10);
+		sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, CLK_CTL_SCLKEN);
+		if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP))
+			msleep(10);
 	}
 }
 
@@ -170,7 +171,8 @@ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
 {
 	if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
 		sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000);
-		msleep(10);
+		if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP))
+			msleep(10);
 	}
 
 	sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
@@ -197,8 +199,13 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host,
 		clock <<= 1;
 
 	/* 1/1 clock is option */
-	if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) && ((clk >> 22) & 0x1))
-		clk |= 0xff;
+	if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) &&
+	    ((clk >> 22) & 0x1)) {
+		if (!(host->mmc->ios.timing == MMC_TIMING_MMC_HS400))
+			clk |= 0xff;
+		else
+			clk &= ~0xff;
+	}
 
 	if (host->set_clk_div)
 		host->set_clk_div(host->pdev, (clk >> 22) & 1);
@@ -231,6 +238,7 @@ static void tmio_mmc_reset_work(struct work_struct *work)
 						  delayed_reset_work.work);
 	struct mmc_request *mrq;
 	unsigned long flags;
+	u16 clk;
 
 	spin_lock_irqsave(&host->lock, flags);
 	mrq = host->mrq;
@@ -264,7 +272,9 @@ static void tmio_mmc_reset_work(struct work_struct *work)
 
 	spin_unlock_irqrestore(&host->lock, flags);
 
+	clk = sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL) & 0x0ff;
 	tmio_mmc_reset(host);
+	sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk | 0x100);
 
 	/* Ready for new calls */
 	host->mrq = NULL;
@@ -278,6 +288,7 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
 {
 	struct mmc_request *mrq;
 	unsigned long flags;
+	struct mmc_command *cmd = host->cmd;
 
 	spin_lock_irqsave(&host->lock, flags);
 
@@ -302,6 +313,12 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
 	if (host->check_scc_error)
 		host->check_scc_error(host);
 
+	if (cmd == mrq->sbc) {
+		/* finish SET_BLOCK_COUNT request */
+		complete(&host->completion);
+		return;
+	}
+
 	mmc_request_done(host->mmc, mrq);
 }
 
@@ -333,9 +350,18 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command
 	u32 irq_mask = TMIO_MASK_CMD;
 
 	/* CMD12 is handled by hardware */
-	if (cmd->opcode == MMC_STOP_TRANSMISSION && !cmd->arg) {
-		sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x001);
-		return 0;
+	if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2)) {
+		if (cmd->opcode == MMC_STOP_TRANSMISSION && !cmd->arg) {
+			sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x001);
+			return 0;
+		}
+	} else if (cmd->opcode == MMC_STOP_TRANSMISSION && !cmd->arg) {
+		u32 status = sd_ctrl_read16_and_16_as_32(host, CTL_STATUS);
+
+		if (status & TMIO_STAT_CMD_BUSY) {
+			sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x001);
+			return 0;
+		}
 	}
 
 	switch (mmc_resp_type(cmd)) {
@@ -369,7 +395,8 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command
 			 * multiple block transfer
 			 */
 			if ((host->pdata->flags & TMIO_MMC_HAVE_CMD12_CTRL) &&
-			    (cmd->opcode == SD_IO_RW_EXTENDED))
+			    ((cmd->opcode == SD_IO_RW_EXTENDED) ||
+			     host->mrq->sbc))
 				c |= NO_CMD12_ISSUE;
 		}
 		if (data->flags & MMC_DATA_READ)
@@ -827,6 +854,7 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
 	struct tmio_mmc_host *host = mmc_priv(mmc);
 	unsigned long flags;
 	int ret;
+	u32 opcode;
 
 	spin_lock_irqsave(&host->lock, flags);
 
@@ -846,6 +874,36 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
 	spin_unlock_irqrestore(&host->lock, flags);
 
+	if (mrq->sbc) {
+		init_completion(&host->completion);
+		ret = tmio_mmc_start_command(host, mrq->sbc);
+		if (ret)
+			goto fail;
+		ret = wait_for_completion_timeout(&host->completion,
+					msecs_to_jiffies(CMDREQ_TIMEOUT));
+		if (ret < 0)
+			goto fail;
+		if (!ret) {
+			ret = -ETIMEDOUT;
+			goto fail;
+		}
+		host->last_req_ts = jiffies;
+		host->mrq = mrq;
+		if (((host->check_scc_error && host->check_scc_error(host)) ||
+			(mrq->cmd->error == -EILSEQ)) && host->mmc->card) {
+			if (mmc_card_mmc(host->mmc->card))
+				opcode = MMC_SEND_TUNING_BLOCK_HS200;
+			else
+				opcode = MMC_SEND_TUNING_BLOCK;
+			/* Start retuning */
+			ret = tmio_mmc_execute_tuning(mmc, opcode);
+			if (ret)
+				goto fail;
+			/* Restore request */
+			host->mrq = mrq;
+		}
+	}
+
 	if (mrq->data) {
 		ret = tmio_mmc_start_data(host, mrq->data);
 		if (ret)
@@ -949,6 +1007,11 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 	struct device *dev = &host->pdev->dev;
 	unsigned long flags;
 
+	/* HS400 Register setting */
+	if (ios->timing == MMC_TIMING_MMC_HS400)
+		if (host->prepare_hs400_tuning)
+			host->prepare_hs400_tuning(mmc, ios);
+
 	mutex_lock(&host->ios_lock);
 
 	spin_lock_irqsave(&host->lock, flags);
@@ -1142,10 +1205,10 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
 
 	mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities;
 	mmc->caps2 |= pdata->capabilities2;
-	mmc->max_segs = 32;
+	mmc->max_segs = pdata->max_segs ? pdata->max_segs : 32;
 	mmc->max_blk_size = 512;
-	mmc->max_blk_count = (PAGE_SIZE / mmc->max_blk_size) *
-		mmc->max_segs;
+	mmc->max_blk_count = pdata->max_blk_count ? :
+		(PAGE_SIZE / mmc->max_blk_size) * mmc->max_segs;
 	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
 	mmc->max_seg_size = mmc->max_req_size;
 
@@ -1311,4 +1374,23 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
 EXPORT_SYMBOL(tmio_mmc_host_runtime_resume);
 #endif
 
+#ifdef CONFIG_PM_SLEEP
+int tmio_mmc_host_suspend(struct device *dev)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+
+	tmio_mmc_hw_reset(mmc);
+
+	return 0;
+}
+EXPORT_SYMBOL(tmio_mmc_host_suspend);
+
+int tmio_mmc_host_resume(struct device *dev)
+{
+	/* Empty for now */
+	return 0;
+}
+EXPORT_SYMBOL(tmio_mmc_host_resume);
+#endif /* CONFIG_PM_SLEEP */
+
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h
index f110966..9a8dd874 100644
--- a/drivers/net/ethernet/renesas/ravb.h
+++ b/drivers/net/ethernet/renesas/ravb.h
@@ -76,6 +76,7 @@ enum ravb_reg {
 	CDAR20	= 0x0060,
 	CDAR21	= 0x0064,
 	ESR	= 0x0088,
+	APSR	= 0x008C,	/* R-Car Gen3 only */
 	RCR	= 0x0090,
 	RQC0	= 0x0094,
 	RQC1	= 0x0098,
@@ -83,6 +84,7 @@ enum ravb_reg {
 	RQC3	= 0x00A0,
 	RQC4	= 0x00A4,
 	RPC	= 0x00B0,
+	RTC	= 0x00B4,	/* R-Car Gen3 only */
 	UFCW	= 0x00BC,
 	UFCS	= 0x00C0,
 	UFCV0	= 0x00C4,
@@ -128,11 +130,52 @@ enum ravb_reg {
 	SFP29	= 0x0174,
 	SFP30	= 0x0178,
 	SFP31	= 0x017C,
+	SFV0	= 0x01B8,	/* R-Car Gen3 only */
+	SFV1	= 0x01BC,	/* R-Car Gen3 only */
 	SFM0	= 0x01C0,
 	SFM1	= 0x01C4,
+	SFL	= 0x01C8,	/* R-Car Gen3 only */
+	PCRC	= 0x01CC,	/* R-Car Gen3 only */
+	CIAR0	= 0x0200,	/* R-Car Gen3 only */
+	CIAR1	= 0x0204,	/* R-Car Gen3 only */
+	CIAR2	= 0x0208,	/* R-Car Gen3 only */
+	CIAR3	= 0x020C,	/* R-Car Gen3 only */
+	CIAR4	= 0x0210,	/* R-Car Gen3 only */
+	CIAR5	= 0x0214,	/* R-Car Gen3 only */
+	CIAR6	= 0x0218,	/* R-Car Gen3 only */
+	CIAR7	= 0x021C,	/* R-Car Gen3 only */
+	CIAR8	= 0x0220,	/* R-Car Gen3 only */
+	CIAR9	= 0x0224,	/* R-Car Gen3 only */
+	CIAR10	= 0x0228,	/* R-Car Gen3 only */
+	CIAR11	= 0x022C,	/* R-Car Gen3 only */
+	CIAR12	= 0x0230,	/* R-Car Gen3 only */
+	CIAR13	= 0x0234,	/* R-Car Gen3 only */
+	CIAR14	= 0x0238,	/* R-Car Gen3 only */
+	CIAR15	= 0x023C,	/* R-Car Gen3 only */
+	CIAR16	= 0x0240,	/* R-Car Gen3 only */
+	CIAR17	= 0x0244,	/* R-Car Gen3 only */
+	LIAR0	= 0x0280,	/* R-Car Gen3 only */
+	LIAR1	= 0x0284,	/* R-Car Gen3 only */
+	LIAR2	= 0x0288,	/* R-Car Gen3 only */
+	LIAR3	= 0x028C,	/* R-Car Gen3 only */
+	LIAR4	= 0x0290,	/* R-Car Gen3 only */
+	LIAR5	= 0x0294,	/* R-Car Gen3 only */
+	LIAR6	= 0x0298,	/* R-Car Gen3 only */
+	LIAR7	= 0x029C,	/* R-Car Gen3 only */
+	LIAR8	= 0x02A0,	/* R-Car Gen3 only */
+	LIAR9	= 0x02A4,	/* R-Car Gen3 only */
+	LIAR10	= 0x02A8,	/* R-Car Gen3 only */
+	LIAR11	= 0x02AC,	/* R-Car Gen3 only */
+	LIAR12	= 0x02B0,	/* R-Car Gen3 only */
+	LIAR13	= 0x02B4,	/* R-Car Gen3 only */
+	LIAR14	= 0x02B8,	/* R-Car Gen3 only */
+	LIAR15	= 0x02BC,	/* R-Car Gen3 only */
+	LIAR16	= 0x02C0,	/* R-Car Gen3 only */
+	LIAR17	= 0x02C4,	/* R-Car Gen3 only */
 	TGC	= 0x0300,
 	TCCR	= 0x0304,
 	TSR	= 0x0308,
+	MFA	= 0x030C,
 	TFA0	= 0x0310,
 	TFA1	= 0x0314,
 	TFA2	= 0x0318,
@@ -158,6 +201,8 @@ enum ravb_reg {
 	TIS	= 0x037C,
 	ISS	= 0x0380,
 	CIE	= 0x0384,	/* R-Car Gen3 only */
+	RIC3	= 0x0388,	/* R-Car Gen3 only */
+	RIS3	= 0x038C,	/* R-Car Gen3 only */
 	GCCR	= 0x0390,
 	GMTT	= 0x0394,
 	GPTC	= 0x0398,
@@ -171,15 +216,46 @@ enum ravb_reg {
 	GCT0	= 0x03B8,
 	GCT1	= 0x03BC,
 	GCT2	= 0x03C0,
+	GSR	= 0x03C4,	/* R-Car Gen3 only */
 	GIE	= 0x03CC,	/* R-Car Gen3 only */
 	GID	= 0x03D0,	/* R-Car Gen3 only */
+	GIL	= 0x03D4,	/* R-Car Gen3 only */
+	GACP	= 0x03DC,	/* R-Car Gen3 only */
+	GPTF0	= 0x03E0,	/* R-Car Gen3 only */
+	GPTF1	= 0x03E4,	/* R-Car Gen3 only */
+	GPTF2	= 0x03E8,	/* R-Car Gen3 only */
+	GPTF3	= 0x03EC,	/* R-Car Gen3 only */
+	GCAT0	= 0x0400,	/* R-Car Gen3 only */
+	GCAT1	= 0x0404,	/* R-Car Gen3 only */
+	GCAT2	= 0x0408,	/* R-Car Gen3 only */
+	GCAT3	= 0x040C,	/* R-Car Gen3 only */
+	GCAT4	= 0x0410,	/* R-Car Gen3 only */
+	GCAT5	= 0x0414,	/* R-Car Gen3 only */
+	GCAT6	= 0x0418,	/* R-Car Gen3 only */
+	GCAT7	= 0x041C,	/* R-Car Gen3 only */
+	GCAT8	= 0x0420,	/* R-Car Gen3 only */
+	GCAT9	= 0x0424,	/* R-Car Gen3 only */
+	GCAT10	= 0x0428,	/* R-Car Gen3 only */
+	GCAT11	= 0x042C,	/* R-Car Gen3 only */
+	GCAT12	= 0x0430,	/* R-Car Gen3 only */
+	GCAT13	= 0x0434,	/* R-Car Gen3 only */
+	GCAT14	= 0x0438,	/* R-Car Gen3 only */
+	GCAT15	= 0x043C,	/* R-Car Gen3 only */
 	DIL	= 0x0440,	/* R-Car Gen3 only */
+	EIL	= 0x0444,	/* R-Car Gen3 only */
+	TIL	= 0x0448,	/* R-Car Gen3 only */
+	DIE	= 0x0450,	/* R-Car Gen3 only */
+	DID	= 0x0454,	/* R-Car Gen3 only */
+	EIE	= 0x0458,	/* R-Car Gen3 only */
+	EID	= 0x045C,	/* R-Car Gen3 only */
 	RIE0	= 0x0460,	/* R-Car Gen3 only */
 	RID0	= 0x0464,	/* R-Car Gen3 only */
 	RIE2	= 0x0470,	/* R-Car Gen3 only */
 	RID2	= 0x0474,	/* R-Car Gen3 only */
 	TIE	= 0x0478,	/* R-Car Gen3 only */
 	TID	= 0x047c,	/* R-Car Gen3 only */
+	RIE3	= 0x0488,	/* R-Car Gen3 only */
+	RID3	= 0x048C,	/* R-Car Gen3 only */
 
 	/* E-MAC registers */
 	ECMR	= 0x0500,
@@ -189,9 +265,12 @@ enum ravb_reg {
 	PIR	= 0x0520,
 	PSR	= 0x0528,
 	PIPR	= 0x052c,
+	APR	= 0x0554,	/* R-Car Gen3 only */
 	MPR	= 0x0558,
 	PFTCR	= 0x055c,
 	PFRCR	= 0x0560,
+	PFTTLR	= 0x0564,	/* R-Car Gen3 only */
+	PFTTCR	= 0x0568,	/* R-Car Gen3 only */
 	GECMR	= 0x05b0,
 	MAHR	= 0x05c0,
 	MALR	= 0x05c8,
@@ -248,6 +327,15 @@ enum ESR_BIT {
 	ESR_EIL		= 0x00001000,
 };
 
+/* APSR */
+enum APSR_BIT {
+	APSR_MEMS		= 0x00000002,
+	APSR_CMSW		= 0x00000010,
+	APSR_DM			= 0x00006000,
+	APSR_DM_RDM		= 0x00002000,
+	APSR_DM_TDM		= 0x00004000,
+};
+
 /* RCR */
 enum RCR_BIT {
 	RCR_EFFS	= 0x00000001,
@@ -319,6 +407,14 @@ enum RTC_BIT {
 	RTC_MFL1	= 0x0FFF0000,
 };
 
+/* SFL */
+enum SFL_BIT {
+	SFL_LC		= 0x0000001F,
+	SFL_LC_SFM	= 0x0000001D,
+	SFL_LC_SFO	= 0x0000001E,
+	SFL_LC_LOADABLE	= 0x0000001F,
+};
+
 /* TGC */
 enum TGC_BIT {
 	TGC_TSM0	= 0x00000001,
@@ -947,7 +1043,6 @@ enum RAVB_QUEUE {
 #define RX_QUEUE_OFFSET	4
 #define NUM_RX_QUEUE	2
 #define NUM_TX_QUEUE	2
-#define NUM_TX_DESC	2	/* TX descriptors per packet */
 
 struct ravb_tstamp_skb {
 	struct list_head list;
@@ -1023,6 +1118,7 @@ struct ravb_private {
 
 	unsigned no_avb_link:1;
 	unsigned avb_link_active_low:1;
+	int num_tx_desc;	/* TX descriptors per packet */
 };
 
 static inline u32 ravb_read(struct net_device *ndev, enum ravb_reg reg)
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index 630536b..8ad1246 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -31,6 +31,9 @@
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/soc/renesas/rcar_prr.h>
 
 #include <asm/div64.h>
 
@@ -185,6 +188,10 @@ static void ravb_ring_free(struct net_device *ndev, int q)
 	struct ravb_private *priv = netdev_priv(ndev);
 	int ring_size;
 	int i;
+	int num_tx_desc = priv->num_tx_desc;
+	struct ravb_ex_rx_desc *rx_desc;
+	struct ravb_tx_desc *tx_desc;
+	u32 size;
 
 	/* Free RX skb ringbuffer */
 	if (priv->rx_skb[q]) {
@@ -207,6 +214,16 @@ static void ravb_ring_free(struct net_device *ndev, int q)
 	priv->tx_align[q] = NULL;
 
 	if (priv->rx_ring[q]) {
+		for (i = 0; i < priv->num_rx_ring[q]; i++) {
+			rx_desc = &priv->rx_ring[q][i];
+			if (rx_desc->dptr != 0) {
+				dma_unmap_single(ndev->dev.parent,
+						 le32_to_cpu(rx_desc->dptr),
+						 PKT_BUF_SZ,
+						 DMA_FROM_DEVICE);
+				rx_desc->dptr = 0;
+			}
+		}
 		ring_size = sizeof(struct ravb_ex_rx_desc) *
 			    (priv->num_rx_ring[q] + 1);
 		dma_free_coherent(ndev->dev.parent, ring_size, priv->rx_ring[q],
@@ -215,8 +232,18 @@ static void ravb_ring_free(struct net_device *ndev, int q)
 	}
 
 	if (priv->tx_ring[q]) {
+		for (i = 0; i < priv->num_tx_ring[q]; i++) {
+			tx_desc = &priv->tx_ring[q][i];
+			size = le16_to_cpu(tx_desc->ds_tagl) & TX_DS;
+			if (tx_desc->dptr != 0) {
+				dma_unmap_single(ndev->dev.parent,
+						 le32_to_cpu(tx_desc->dptr),
+						 size, DMA_TO_DEVICE);
+				tx_desc->dptr = 0;
+			}
+		}
 		ring_size = sizeof(struct ravb_tx_desc) *
-			    (priv->num_tx_ring[q] * NUM_TX_DESC + 1);
+			    (priv->num_tx_ring[q] * num_tx_desc + 1);
 		dma_free_coherent(ndev->dev.parent, ring_size, priv->tx_ring[q],
 				  priv->tx_desc_dma[q]);
 		priv->tx_ring[q] = NULL;
@@ -230,9 +257,10 @@ static void ravb_ring_format(struct net_device *ndev, int q)
 	struct ravb_ex_rx_desc *rx_desc;
 	struct ravb_tx_desc *tx_desc;
 	struct ravb_desc *desc;
+	int num_tx_desc = priv->num_tx_desc;
 	int rx_ring_size = sizeof(*rx_desc) * priv->num_rx_ring[q];
 	int tx_ring_size = sizeof(*tx_desc) * priv->num_tx_ring[q] *
-			   NUM_TX_DESC;
+			   num_tx_desc;
 	dma_addr_t dma_addr;
 	int i;
 
@@ -267,8 +295,10 @@ static void ravb_ring_format(struct net_device *ndev, int q)
 	for (i = 0, tx_desc = priv->tx_ring[q]; i < priv->num_tx_ring[q];
 	     i++, tx_desc++) {
 		tx_desc->die_dt = DT_EEMPTY;
-		tx_desc++;
-		tx_desc->die_dt = DT_EEMPTY;
+		if (num_tx_desc >= 2) {
+			tx_desc++;
+			tx_desc->die_dt = DT_EEMPTY;
+		}
 	}
 	tx_desc->dptr = cpu_to_le32((u32)priv->tx_desc_dma[q]);
 	tx_desc->die_dt = DT_LINKFIX; /* type */
@@ -291,6 +321,7 @@ static int ravb_ring_init(struct net_device *ndev, int q)
 	struct sk_buff *skb;
 	int ring_size;
 	int i;
+	int num_tx_desc = priv->num_tx_desc;
 
 	/* Allocate RX and TX skb rings */
 	priv->rx_skb[q] = kcalloc(priv->num_rx_ring[q],
@@ -326,7 +357,7 @@ static int ravb_ring_init(struct net_device *ndev, int q)
 
 	/* Allocate all TX descriptors. */
 	ring_size = sizeof(struct ravb_tx_desc) *
-		    (priv->num_tx_ring[q] * NUM_TX_DESC + 1);
+		    (priv->num_tx_ring[q] * num_tx_desc + 1);
 	priv->tx_ring[q] = dma_alloc_coherent(ndev->dev.parent, ring_size,
 					      &priv->tx_desc_dma[q],
 					      GFP_KERNEL);
@@ -440,10 +471,11 @@ static int ravb_tx_free(struct net_device *ndev, int q)
 	int free_num = 0;
 	int entry;
 	u32 size;
+	int num_tx_desc = priv->num_tx_desc;
 
 	for (; priv->cur_tx[q] - priv->dirty_tx[q] > 0; priv->dirty_tx[q]++) {
 		entry = priv->dirty_tx[q] % (priv->num_tx_ring[q] *
-					     NUM_TX_DESC);
+					     num_tx_desc);
 		desc = &priv->tx_ring[q][entry];
 		if (desc->die_dt != DT_FEMPTY)
 			break;
@@ -451,12 +483,12 @@ static int ravb_tx_free(struct net_device *ndev, int q)
 		dma_rmb();
 		size = le16_to_cpu(desc->ds_tagl) & TX_DS;
 		/* Free the original skb. */
-		if (priv->tx_skb[q][entry / NUM_TX_DESC]) {
+		if (priv->tx_skb[q][entry / num_tx_desc]) {
 			dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr),
 					 size, DMA_TO_DEVICE);
 			/* Last packet descriptor? */
-			if (entry % NUM_TX_DESC == NUM_TX_DESC - 1) {
-				entry /= NUM_TX_DESC;
+			if (entry % num_tx_desc == num_tx_desc - 1) {
+				entry /= num_tx_desc;
 				dev_kfree_skb_any(priv->tx_skb[q][entry]);
 				priv->tx_skb[q][entry] = NULL;
 				stats->tx_packets++;
@@ -928,11 +960,9 @@ static int ravb_poll(struct napi_struct *napi, int budget)
 	priv->rx_over_errors += priv->stats[RAVB_NC].rx_over_errors;
 	if (priv->rx_over_errors != ndev->stats.rx_over_errors) {
 		ndev->stats.rx_over_errors = priv->rx_over_errors;
-		netif_err(priv, rx_err, ndev, "Receive Descriptor Empty\n");
 	}
 	if (priv->rx_fifo_errors != ndev->stats.rx_fifo_errors) {
 		ndev->stats.rx_fifo_errors = priv->rx_fifo_errors;
-		netif_err(priv, rx_err, ndev, "Receive FIFO Overflow\n");
 	}
 out:
 	return budget - quota;
@@ -1015,16 +1045,40 @@ static int ravb_phy_init(struct net_device *ndev)
 	 * at this time.
 	 */
 	if (priv->chip_id == RCAR_GEN3) {
-		int err;
+		struct platform_device *pdev = priv->pdev;
+		struct device *dev = &pdev->dev;
+		enum of_gpio_flags flags;
+		int err, gpio;
 
-		err = phy_set_max_speed(phydev, SPEED_100);
-		if (err) {
-			netdev_err(ndev, "failed to limit PHY to 100Mbit/s\n");
-			phy_disconnect(phydev);
+		err = RCAR_PRR_INIT();
+		if (err)
 			return err;
+
+		if (RCAR_PRR_IS_PRODUCT(H3) &&
+		    (RCAR_PRR_CHK_CUT(H3, WS10) == 0)) {
+			err = phy_set_max_speed(phydev, SPEED_100);
+			if (err) {
+				netdev_err(ndev, "failed to limit PHY to 100Mbit/s\n");
+				phy_disconnect(phydev);
+				return err;
+			}
+			netdev_info(ndev, "limited PHY to 100Mbit/s\n");
 		}
 
-		netdev_info(ndev, "limited PHY to 100Mbit/s\n");
+		gpio = of_get_named_gpio_flags(np, "phy-int-gpio", 0, &flags);
+		if (gpio_is_valid(gpio)) {
+			err = devm_gpio_request_one(dev, gpio, GPIOF_DIR_IN,
+						    "phy-int-gpio");
+			if (err == 0 && (phydev->irq == gpio_to_irq(gpio))) {
+				if (flags & OF_GPIO_ACTIVE_LOW)
+					irq_set_irq_type(phydev->irq,
+							 IRQ_TYPE_LEVEL_LOW);
+				else
+					irq_set_irq_type(phydev->irq,
+							 IRQ_TYPE_LEVEL_HIGH);
+			}
+		}
+
 	}
 
 	/* 10BASE is not supported */
@@ -1483,41 +1537,56 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 	void *buffer;
 	u32 entry;
 	u32 len;
+	int num_tx_desc = priv->num_tx_desc;
 
 	spin_lock_irqsave(&priv->lock, flags);
 	if (priv->cur_tx[q] - priv->dirty_tx[q] > (priv->num_tx_ring[q] - 1) *
-	    NUM_TX_DESC) {
+	    num_tx_desc) {
 		netif_err(priv, tx_queued, ndev,
 			  "still transmitting with the full ring!\n");
 		netif_stop_subqueue(ndev, q);
 		spin_unlock_irqrestore(&priv->lock, flags);
 		return NETDEV_TX_BUSY;
 	}
-	entry = priv->cur_tx[q] % (priv->num_tx_ring[q] * NUM_TX_DESC);
-	priv->tx_skb[q][entry / NUM_TX_DESC] = skb;
+	entry = priv->cur_tx[q] % (priv->num_tx_ring[q] * num_tx_desc);
+	priv->tx_skb[q][entry / num_tx_desc] = skb;
 
 	if (skb_put_padto(skb, ETH_ZLEN))
 		goto drop;
 
-	buffer = PTR_ALIGN(priv->tx_align[q], DPTR_ALIGN) +
-		 entry / NUM_TX_DESC * DPTR_ALIGN;
-	len = PTR_ALIGN(skb->data, DPTR_ALIGN) - skb->data;
-	memcpy(buffer, skb->data, len);
-	dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE);
-	if (dma_mapping_error(ndev->dev.parent, dma_addr))
-		goto drop;
+	if (num_tx_desc >= 2) {
+		buffer = PTR_ALIGN(priv->tx_align[q], DPTR_ALIGN) +
+			 entry / num_tx_desc * DPTR_ALIGN;
+		len = PTR_ALIGN(skb->data, DPTR_ALIGN) - skb->data;
+		/* quick fix */
+		if (len == 0)
+			len = 4;
+		memcpy(buffer, skb->data, len);
+		dma_addr = dma_map_single(ndev->dev.parent, buffer, len,
+					  DMA_TO_DEVICE);
+		if (dma_mapping_error(ndev->dev.parent, dma_addr))
+			goto drop;
 
-	desc = &priv->tx_ring[q][entry];
-	desc->ds_tagl = cpu_to_le16(len);
-	desc->dptr = cpu_to_le32(dma_addr);
+		desc = &priv->tx_ring[q][entry];
+		desc->ds_tagl = cpu_to_le16(len);
+		desc->dptr = cpu_to_le32(dma_addr);
 
-	buffer = skb->data + len;
-	len = skb->len - len;
-	dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE);
-	if (dma_mapping_error(ndev->dev.parent, dma_addr))
-		goto unmap;
+		buffer = skb->data + len;
+		len = skb->len - len;
+		dma_addr = dma_map_single(ndev->dev.parent, buffer, len,
+					  DMA_TO_DEVICE);
+		if (dma_mapping_error(ndev->dev.parent, dma_addr))
+			goto unmap;
 
-	desc++;
+		desc++;
+	} else {
+		desc = &priv->tx_ring[q][entry];
+		len = skb->len;
+		dma_addr = dma_map_single(ndev->dev.parent, skb->data, skb->len,
+					  DMA_TO_DEVICE);
+		if (dma_mapping_error(ndev->dev.parent, dma_addr))
+			goto drop;
+	}
 	desc->ds_tagl = cpu_to_le16(len);
 	desc->dptr = cpu_to_le32(dma_addr);
 
@@ -1525,9 +1594,11 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 	if (q == RAVB_NC) {
 		ts_skb = kmalloc(sizeof(*ts_skb), GFP_ATOMIC);
 		if (!ts_skb) {
-			desc--;
-			dma_unmap_single(ndev->dev.parent, dma_addr, len,
-					 DMA_TO_DEVICE);
+			if (num_tx_desc >= 2) {
+				desc--;
+				dma_unmap_single(ndev->dev.parent, dma_addr,
+						 len, DMA_TO_DEVICE);
+			}
 			goto unmap;
 		}
 		ts_skb->skb = skb;
@@ -1544,15 +1615,19 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 	skb_tx_timestamp(skb);
 	/* Descriptor type must be set after all the above writes */
 	dma_wmb();
-	desc->die_dt = DT_FEND;
-	desc--;
-	desc->die_dt = DT_FSTART;
+	if (num_tx_desc >= 2) {
+		desc->die_dt = DT_FEND;
+		desc--;
+		desc->die_dt = DT_FSTART;
+	} else {
+		desc->die_dt = DT_FSINGLE;
+	}
 
 	ravb_modify(ndev, TCCR, TCCR_TSRQ0 << q, TCCR_TSRQ0 << q);
 
-	priv->cur_tx[q] += NUM_TX_DESC;
+	priv->cur_tx[q] += num_tx_desc;
 	if (priv->cur_tx[q] - priv->dirty_tx[q] >
-	    (priv->num_tx_ring[q] - 1) * NUM_TX_DESC && !ravb_tx_free(ndev, q))
+	    (priv->num_tx_ring[q] - 1) * num_tx_desc && !ravb_tx_free(ndev, q))
 		netif_stop_subqueue(ndev, q);
 
 exit:
@@ -1565,7 +1640,7 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 			 le16_to_cpu(desc->ds_tagl), DMA_TO_DEVICE);
 drop:
 	dev_kfree_skb_any(skb);
-	priv->tx_skb[q][entry / NUM_TX_DESC] = NULL;
+	priv->tx_skb[q][entry / num_tx_desc] = NULL;
 	goto exit;
 }
 
@@ -1833,6 +1908,7 @@ static const struct of_device_id ravb_match_table[] = {
 	{ .compatible = "renesas,etheravb-r8a7794", .data = (void *)RCAR_GEN2 },
 	{ .compatible = "renesas,etheravb-rcar-gen2", .data = (void *)RCAR_GEN2 },
 	{ .compatible = "renesas,etheravb-r8a7795", .data = (void *)RCAR_GEN3 },
+	{ .compatible = "renesas,etheravb-r8a7796", .data = (void *)RCAR_GEN3 },
 	{ .compatible = "renesas,etheravb-rcar-gen3", .data = (void *)RCAR_GEN3 },
 	{ }
 };
@@ -1887,6 +1963,29 @@ static void ravb_set_config_mode(struct net_device *ndev)
 	}
 }
 
+static void ravb_set_delay_mode(struct net_device *ndev)
+{
+	struct ravb_private *priv = netdev_priv(ndev);
+
+	if (priv->chip_id != RCAR_GEN2) {
+		switch (priv->phy_interface) {
+		case PHY_INTERFACE_MODE_RGMII_ID:
+			ravb_modify(ndev, APSR, APSR_DM, APSR_DM_RDM |
+				   APSR_DM_TDM);
+			break;
+		case PHY_INTERFACE_MODE_RGMII_RXID:
+			ravb_modify(ndev, APSR, APSR_DM, APSR_DM_RDM);
+			break;
+		case PHY_INTERFACE_MODE_RGMII_TXID:
+			ravb_modify(ndev, APSR, APSR_DM, APSR_DM_TDM);
+			break;
+		default:
+			ravb_modify(ndev, APSR, APSR_DM, 0);
+			break;
+		}
+	}
+}
+
 static int ravb_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
@@ -1984,6 +2083,11 @@ static int ravb_probe(struct platform_device *pdev)
 
 	priv->chip_id = chip_id;
 
+	if (chip_id == RCAR_GEN2)
+		priv->num_tx_desc = 2;
+	else
+		priv->num_tx_desc = 1;
+
 	/* Set function */
 	ndev->netdev_ops = &ravb_netdev_ops;
 	ndev->ethtool_ops = &ravb_ethtool_ops;
@@ -1999,6 +2103,9 @@ static int ravb_probe(struct platform_device *pdev)
 	/* Request GTI loading */
 	ravb_modify(ndev, GCCR, GCCR_LTI, GCCR_LTI);
 
+	/* Set APSR */
+	ravb_set_delay_mode(ndev);
+
 	/* Allocate descriptor base address table */
 	priv->desc_bat_size = sizeof(struct ravb_desc) * DBAT_ENTRY_NUM;
 	priv->desc_bat = dma_alloc_coherent(ndev->dev.parent, priv->desc_bat_size,
@@ -2103,6 +2210,7 @@ static int ravb_remove(struct platform_device *pdev)
 static int __maybe_unused ravb_suspend(struct device *dev)
 {
 	struct net_device *ndev = dev_get_drvdata(dev);
+	struct ravb_private *priv = netdev_priv(ndev);
 	int ret = 0;
 
 	if (netif_running(ndev)) {
@@ -2110,6 +2218,9 @@ static int __maybe_unused ravb_suspend(struct device *dev)
 		ret = ravb_close(ndev);
 	}
 
+	if (priv->chip_id != RCAR_GEN2)
+		ravb_ptp_stop(ndev);
+
 	return ret;
 }
 
@@ -2117,6 +2228,7 @@ static int __maybe_unused ravb_resume(struct device *dev)
 {
 	struct net_device *ndev = dev_get_drvdata(dev);
 	struct ravb_private *priv = netdev_priv(ndev);
+	struct platform_device *pdev = priv->pdev;
 	int ret = 0;
 
 	/* All register have been reset to default values.
@@ -2135,9 +2247,15 @@ static int __maybe_unused ravb_resume(struct device *dev)
 	/* Request GTI loading */
 	ravb_modify(ndev, GCCR, GCCR_LTI, GCCR_LTI);
 
+	/* Set APSR */
+	ravb_set_delay_mode(ndev);
+
 	/* Restore descriptor base address table */
 	ravb_write(ndev, priv->desc_bat_dma, DBAT);
 
+	if (priv->chip_id != RCAR_GEN2)
+		ravb_ptp_init(ndev, pdev);
+
 	if (netif_running(ndev)) {
 		ret = ravb_open(ndev);
 		if (ret < 0)
diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c
index 62700d1..8e3f92f 100644
--- a/drivers/pci/host/pcie-rcar.c
+++ b/drivers/pci/host/pcie-rcar.c
@@ -30,6 +30,7 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
+#include <linux/soc/renesas/s2ram_ddr_backup.h>
 
 #define PCIECAR			0x000010
 #define PCIECCTLR		0x000018
@@ -43,6 +44,7 @@
 
 /* Transfer control */
 #define PCIETCTLR		0x02000
+#define  DL_DOWN		(1 << 3)
 #define  CFINIT			1
 #define PCIETSTR		0x02004
 #define  DATA_LINK_ACTIVE	1
@@ -91,6 +93,13 @@
 #define MACCTLR			0x011058
 #define  SPEED_CHANGE		(1 << 24)
 #define  SCRAMBLE_DISABLE	(1 << 27)
+#define PMSR			0x01105c
+#define  L1FAEG			(1 << 31)
+#define  PM_ENTER_L1RX		(1 << 23)
+#define  PMSTATE		(7 << 16)
+#define  PMSTATE_L1		(3 << 16)
+#define PMCTLR			0x011060
+#define  L1_INIT		(1 << 31)
 #define MACS2R			0x011078
 #define MACCGSPSETR		0x011084
 #define  SPCNGRSN		(1 << 31)
@@ -139,6 +148,7 @@ static inline struct rcar_msi *to_rcar_msi(struct msi_controller *chip)
 	return container_of(chip, struct rcar_msi, chip);
 }
 
+
 /* Structure representing the PCIe interface */
 struct rcar_pcie {
 	struct device		*dev;
@@ -150,6 +160,150 @@ struct rcar_pcie {
 	struct			rcar_msi msi;
 };
 
+static int rcar_pcie_wait_for_dl(struct rcar_pcie *pcie);
+
+#ifdef CONFIG_RCAR_DDR_BACKUP
+
+#define PCIE_BACKUP_REGS(pcie_ip_regs)	\
+struct hw_register (pcie_ip_regs)[] = {	\
+/* PCIEC transfer control registers */	\
+	{"PCIETCTLR",	0x2000,	32, 0}, \
+	{"PCIEINTER",	0x200C,	32, 0},	\
+	{"PCIEERRFER",	0x2024,	32, 0},	\
+	{"PCIETIER",	0x2030,	32, 0},	\
+	{"PCIEPMSCIER",	0x2038,	32, 0}, \
+	{"PCIEMSIALR",	0x2048,	32, 0},	\
+	{"PCIEMSIAUR",	0x204C,	32, 0},	\
+	{"PCIEMSIIER",	0x2050,	32, 0},	\
+	{"PCIEPRAR0",	0x2080,	32, 0},	\
+	{"PCIEPRAR1",	0x2084,	32, 0},	\
+	{"PCIEPRAR2",	0x2088,	32, 0},	\
+	{"PCIEPRAR3",	0x208C,	32, 0},	\
+	{"PCIEPRAR4",	0x2090,	32, 0},	\
+	{"PCIEPRAR5",	0x2094,	32, 0},	\
+	\
+	/* Local address registers */	\
+	{"PCIELAR0",	0x2200,	32, 0},	\
+	{"PCIELAMR0",	0x2208,	32, 0},	\
+	{"PCIELAR1",	0x2220,	32, 0},	\
+	{"PCIELAMR1",	0x2228,	32, 0},	\
+	{"PCIELAR2",	0x2240,	32, 0},	\
+	{"PCIELAMR2",	0x2248,	32, 0},	\
+	{"PCIELAR3",	0x2260,	32, 0},	\
+	{"PCIELAMR3",	0x2268,	32, 0},	\
+	{"PCIELAR4",	0x2280,	32, 0},	\
+	{"PCIELAMR4",	0x2288,	32, 0},	\
+	{"PCIELAR5",	0x22A0,	32, 0},	\
+	{"PCIELAMR5",	0x22A8,	32, 0},	\
+	\
+	/* PCIEC address registers */	\
+	{"PCIEPALR0",	0x3400,	32, 0},	\
+	{"PCIEPAUR0",	0x3404,	32, 0},	\
+	{"PCIEPAMR0",	0x3408,	32, 0},	\
+	{"PCIEPTCTLR0",	0x340C,	32, 0},	\
+	{"PCIEPALR1",	0x3420,	32, 0},	\
+	{"PCIEPAUR1",	0x3424,	32, 0},	\
+	{"PCIEPAMR1",	0x3428,	32, 0},	\
+	{"PCIEPTCTLR1",	0x342C,	32, 0},	\
+	{"PCIEPALR2",	0x3440,	32, 0},	\
+	{"PCIEPAUR2",	0x3444,	32, 0},	\
+	{"PCIEPAMR2",	0x3448,	32, 0},	\
+	{"PCIEPTCTLR2",	0x344C,	32, 0},	\
+	{"PCIEPALR3",	0x3460,	32, 0},	\
+	{"PCIEPAUR3",	0x3464,	32, 0},	\
+	{"PCIEPAMR3",	0x3468,	32, 0},	\
+	{"PCIEPTCTLR3",	0x346C,	32, 0},	\
+}
+
+static PCIE_BACKUP_REGS(pcie0_ip_regs);
+static PCIE_BACKUP_REGS(pcie1_ip_regs);
+
+static struct rcar_ip pcie0_ip = {
+	.ip_name = "pcie0",
+	.base_addr = 0xFE000000,
+	.size = 0x3470,
+	.reg_count = ARRAY_SIZE(pcie0_ip_regs),
+	.ip_reg = pcie0_ip_regs,
+};
+
+static struct rcar_ip pcie1_ip = {
+	.ip_name = "pcie1",
+	.base_addr = 0xEE800000,
+	.size = 0x3470,
+	.reg_count = ARRAY_SIZE(pcie1_ip_regs),
+	.ip_reg = pcie1_ip_regs,
+};
+
+struct ip_info {
+	const char *name;
+	struct rcar_ip *ip;
+};
+
+static struct ip_info ip_info_tbl[] = {
+	{"fe000000.pcie", &pcie0_ip },
+	{"ee800000.pcie", &pcie1_ip },
+	{NULL, NULL},
+};
+
+static struct rcar_ip *rcar_pcie_get_ip(const char *name)
+{
+	struct ip_info *ip_info = ip_info_tbl;
+	struct rcar_ip *ip = NULL;
+
+	while (ip_info->name) {
+		if (!strcmp(ip_info->name, name)) {
+			ip = ip_info->ip;
+			break;
+		}
+		ip_info++;
+	}
+
+	return ip;
+}
+
+static int rcar_pcie_save_regs(struct device *dev)
+{
+	struct rcar_ip *ip = rcar_pcie_get_ip(dev_name(dev));
+	struct rcar_pcie *pcie = NULL;
+	int ret;
+
+	if (ip) {
+		if (!ip->virt_addr) {
+			pcie = dev_get_drvdata(dev);
+			ip->virt_addr = pcie->base;
+		}
+
+		ret = rcar_handle_registers(ip, DO_BACKUP);
+		if (ret)
+			pr_err("%s: %s: BACKUP failed, ret=%d\n",
+				__func__, dev_name(dev), ret);
+	} else
+		pr_err("%s: Failed to find backup of dev: %s\n\n",
+				__func__, dev_name(dev));
+
+	return 0;
+}
+
+static int rcar_pcie_restore_regs(struct device *dev)
+{
+	struct rcar_ip *ip = rcar_pcie_get_ip(dev_name(dev));
+	int ret = -ENODEV;
+
+	if (ip) {
+		ret = rcar_handle_registers(ip, DO_RESTORE);
+		if (ret)
+			pr_err("%s: %s: RESTORE failed, ret=%d\n",
+				__func__, dev_name(dev), ret);
+
+	} else
+		pr_err("%s: Failed to find backup of dev: %s\n\n",
+				__func__, dev_name(dev));
+
+	return 0;
+}
+
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+
 static void rcar_pci_write_reg(struct rcar_pcie *pcie, unsigned long val,
 			       unsigned long reg)
 {
@@ -191,6 +345,7 @@ static int rcar_pcie_config_access(struct rcar_pcie *pcie,
 		unsigned int devfn, int where, u32 *data)
 {
 	int dev, func, reg, index;
+	u32 val;
 
 	dev = PCI_SLOT(devfn);
 	func = PCI_FUNC(devfn);
@@ -232,6 +387,29 @@ static int rcar_pcie_config_access(struct rcar_pcie *pcie,
 	if (pcie->root_bus_nr < 0)
 		return PCIBIOS_DEVICE_NOT_FOUND;
 
+	/*
+	 * If we are not in L1 link state and we have received PM_ENTER_L1 DLLP,
+	 * transition to L1 link state. The HW will handle coming of of L1.
+	 */
+	val = rcar_pci_read_reg(pcie, PMSR);
+
+	if ((val == 0) || (rcar_pci_read_reg(pcie, PCIETCTLR) & DL_DOWN)) {
+		/* Wait PCI Express link is re-initialized */
+		rcar_pci_write_reg(pcie, CFINIT, PCIETCTLR);
+		rcar_pcie_wait_for_dl(pcie);
+	}
+
+	if ((val & PM_ENTER_L1RX) && ((val & PMSTATE) != PMSTATE_L1)) {
+		rcar_pci_write_reg(pcie, L1_INIT, PMCTLR);
+
+		/* Wait until we are in L1 */
+		while (!(val & L1FAEG))
+			val = rcar_pci_read_reg(pcie, PMSR);
+
+		/* Clear flags indicating link has transitioned to L1 */
+		rcar_pci_write_reg(pcie, L1FAEG | PM_ENTER_L1RX, PMSR);
+	}
+
 	/* Clear errors */
 	rcar_pci_write_reg(pcie, rcar_pci_read_reg(pcie, PCIEERRFR), PCIEERRFR);
 
@@ -472,7 +650,7 @@ static int rcar_pcie_enable(struct rcar_pcie *pcie)
 		return -ENODEV;
 	}
 
-	pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+	pci_fixup_irqs_local(bus, pci_common_swizzle, of_irq_parse_and_map_pci);
 
 	pci_bus_size_bridges(bus);
 	pci_bus_assign_resources(bus);
@@ -536,7 +714,7 @@ static int rcar_pcie_wait_for_dl(struct rcar_pcie *pcie)
 		if ((rcar_pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE))
 			return 0;
 
-		msleep(5);
+		mdelay(5);
 	}
 
 	return -ETIMEDOUT;
@@ -915,15 +1093,6 @@ static int rcar_pcie_get_resources(struct rcar_pcie *pcie)
 	if (IS_ERR(pcie->base))
 		return PTR_ERR(pcie->base);
 
-	pcie->clk = devm_clk_get(dev, "pcie");
-	if (IS_ERR(pcie->clk)) {
-		dev_err(dev, "cannot get platform clock\n");
-		return PTR_ERR(pcie->clk);
-	}
-	err = clk_prepare_enable(pcie->clk);
-	if (err)
-		return err;
-
 	pcie->bus_clk = devm_clk_get(dev, "pcie_bus");
 	if (IS_ERR(pcie->bus_clk)) {
 		dev_err(dev, "cannot get pcie bus clock\n");
@@ -955,7 +1124,6 @@ static int rcar_pcie_get_resources(struct rcar_pcie *pcie)
 err_map_reg:
 	clk_disable_unprepare(pcie->bus_clk);
 fail_clk:
-	clk_disable_unprepare(pcie->clk);
 
 	return err;
 }
@@ -1078,6 +1246,7 @@ static const struct of_device_id rcar_pcie_of_match[] = {
 	{ .compatible = "renesas,pcie-r8a7791",
 	  .data = rcar_pcie_hw_init_gen2 },
 	{ .compatible = "renesas,pcie-r8a7795", .data = rcar_pcie_hw_init },
+	{ .compatible = "renesas,pcie-r8a7796", .data = rcar_pcie_hw_init },
 	{},
 };
 
@@ -1133,11 +1302,19 @@ static int rcar_pcie_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	pcie->dev = dev;
+	platform_set_drvdata(pdev, pcie);
 
 	INIT_LIST_HEAD(&pcie->resources);
 
 	rcar_pcie_parse_request_of_pci_ranges(pcie);
 
+	pm_runtime_enable(pcie->dev);
+	err = pm_runtime_get_sync(pcie->dev);
+	if (err < 0) {
+		dev_err(pcie->dev, "pm_runtime_get_sync failed\n");
+		goto err_pm_disable;
+	}
+
 	err = rcar_pcie_get_resources(pcie);
 	if (err < 0) {
 		dev_err(dev, "failed to request resources: %d\n", err);
@@ -1153,18 +1330,11 @@ static int rcar_pcie_probe(struct platform_device *pdev)
 		return -EINVAL;
 	hw_init_fn = of_id->data;
 
-	pm_runtime_enable(dev);
-	err = pm_runtime_get_sync(dev);
-	if (err < 0) {
-		dev_err(dev, "pm_runtime_get_sync failed\n");
-		goto err_pm_disable;
-	}
-
 	/* Failure to get a link might just be that no cards are inserted */
 	err = hw_init_fn(pcie);
 	if (err) {
 		dev_info(dev, "PCIe link down\n");
-		err = 0;
+		err = -ENODEV;
 		goto err_pm_put;
 	}
 
@@ -1195,12 +1365,80 @@ static int rcar_pcie_probe(struct platform_device *pdev)
 	return err;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int rcar_pcie_suspend(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	ret = rcar_pcie_save_regs(dev);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+
+	return ret;
+}
+
+static int rcar_pcie_resume(struct device *dev)
+{
+	struct rcar_pcie *pcie = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (pcie) {
+#ifdef CONFIG_RCAR_DDR_BACKUP
+		rcar_pcie_restore_regs(dev);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+		ret = rcar_pcie_hw_init(pcie);
+		if (ret)
+			pr_debug("%s: %s: re-init hw fail, ret=%d\n",
+			__func__, dev_name(dev), ret);
+	} else
+		pr_warn("%s: %s: pcie NULL\n", __func__, dev_name(dev));
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rcar_pcie_pm_ops,
+			rcar_pcie_suspend,
+			rcar_pcie_resume);
+
+#define DEV_PM_OPS (&rcar_pcie_pm_ops)
+#else /* CONFIG_PM_SLEEP */
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
 static struct platform_driver rcar_pcie_driver = {
 	.driver = {
 		.name = "rcar-pcie",
+		.pm	= DEV_PM_OPS,
 		.of_match_table = rcar_pcie_of_match,
 		.suppress_bind_attrs = true,
 	},
 	.probe = rcar_pcie_probe,
 };
 builtin_platform_driver(rcar_pcie_driver);
+
+static int rcar_pcie_pci_notifier(struct notifier_block *nb,
+			    unsigned long action, void *data)
+{
+	struct device *dev = data;
+
+	switch (action) {
+	case BUS_NOTIFY_BOUND_DRIVER:
+		/* Force the DMA mask to lower 32-bits */
+		dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+		break;
+	default:
+		return NOTIFY_DONE;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block device_nb = {
+	.notifier_call = rcar_pcie_pci_notifier,
+};
+
+static int __init register_rcar_pcie_pci_notifier(void)
+{
+	return bus_register_notifier(&pci_bus_type, &device_nb);
+}
+
+arch_initcall(register_rcar_pcie_pci_notifier);
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
index 884bad5..1ae4c73 100644
--- a/drivers/pci/pcie/pme.c
+++ b/drivers/pci/pcie/pme.c
@@ -10,6 +10,7 @@
  * for more details.
  */
 
+#include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -448,6 +449,17 @@ static int pcie_pme_resume(struct pcie_device *srv)
 	return 0;
 }
 
+/**
+ * pcie_pme_remove - Prepare PCIe PME service device for removal.
+ * @srv - PCIe service device to remove.
+ */
+static void pcie_pme_remove(struct pcie_device *srv)
+{
+	pcie_pme_suspend(srv);
+	free_irq(srv->irq, srv);
+	kfree(get_service_data(srv));
+}
+
 static struct pcie_port_service_driver pcie_pme_driver = {
 	.name		= "pcie_pme",
 	.port_type	= PCI_EXP_TYPE_ROOT_PORT,
@@ -456,6 +468,7 @@ static struct pcie_port_service_driver pcie_pme_driver = {
 	.probe		= pcie_pme_probe,
 	.suspend	= pcie_pme_suspend,
 	.resume		= pcie_pme_resume,
+	.remove		= pcie_pme_remove,
 };
 
 /**
@@ -465,4 +478,5 @@ static int __init pcie_pme_service_init(void)
 {
 	return pcie_port_service_register(&pcie_pme_driver);
 }
-device_initcall(pcie_pme_service_init);
+
+module_init(pcie_pme_service_init);
diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c
index 95c225b..90ea8fa 100644
--- a/drivers/pci/setup-irq.c
+++ b/drivers/pci/setup-irq.c
@@ -22,7 +22,8 @@ void __weak pcibios_update_irq(struct pci_dev *dev, int irq)
 	pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
 }
 
-static void pdev_fixup_irq(struct pci_dev *dev,
+static void pdev_fixup_irq(int domain_nr,
+			   struct pci_dev *dev,
 			   u8 (*swizzle)(struct pci_dev *, u8 *),
 			   int (*map_irq)(const struct pci_dev *, u8, u8))
 {
@@ -48,8 +49,15 @@ static void pdev_fixup_irq(struct pci_dev *dev,
 		if (irq == -1)
 			irq = 0;
 	}
-	dev->irq = irq;
+	/* Since pci_fixup_irqs() can be called more than once due to multiple
+	 * host controllers, and we scan all PCI devices, not just those
+	 * attached to this controller, make sure we don't clobber dev->irq
+	 * that has nothing to do with this domain.
+	 */
+	if (domain_nr >= 0 && dev->bus->domain_nr != domain_nr)
+		return;
 
+	dev->irq = irq;
 	dev_dbg(&dev->dev, "fixup irq: got %d\n", dev->irq);
 
 	/* Always tell the device, so the driver knows what is
@@ -63,6 +71,17 @@ void pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *),
 	struct pci_dev *dev = NULL;
 
 	for_each_pci_dev(dev)
-		pdev_fixup_irq(dev, swizzle, map_irq);
+		pdev_fixup_irq(-1, dev, swizzle, map_irq);
 }
 EXPORT_SYMBOL_GPL(pci_fixup_irqs);
+
+void pci_fixup_irqs_local(struct pci_bus *bus,
+		    u8 (*swizzle)(struct pci_dev *, u8 *),
+		    int (*map_irq)(const struct pci_dev *, u8, u8))
+{
+	struct pci_dev *dev = NULL;
+
+	for_each_pci_dev(dev)
+		pdev_fixup_irq(bus->domain_nr, dev, swizzle, map_irq);
+}
+EXPORT_SYMBOL_GPL(pci_fixup_irqs_local);
diff --git a/drivers/phy/phy-rcar-gen3-usb2.c b/drivers/phy/phy-rcar-gen3-usb2.c
index 80f5bcc..baf1189 100644
--- a/drivers/phy/phy-rcar-gen3-usb2.c
+++ b/drivers/phy/phy-rcar-gen3-usb2.c
@@ -20,6 +20,7 @@
 #include <linux/of_address.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
 #include <linux/workqueue.h>
 
@@ -417,6 +418,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
 	if (IS_ERR(channel->base))
 		return PTR_ERR(channel->base);
 
+	pm_runtime_enable(dev);
 	/* call request_irq for OTG */
 	irq = platform_get_irq(pdev, 0);
 	if (irq >= 0) {
@@ -478,16 +480,45 @@ static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev)
 	if (channel->has_otg)
 		device_remove_file(&pdev->dev, &dev_attr_otg_inputs);
 
+	pm_runtime_disable(&pdev->dev);
 	return 0;
-};
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rcar_gen3_phy_suspend(struct device *dev)
+{
+	/* Empty function for now */
+	return 0;
+}
+
+static int rcar_gen3_phy_resume(struct device *dev)
+{
+	int ret = 0;
+
+	if (to_phy(dev))
+		ret = rcar_gen3_phy_usb2_init(to_phy(dev));
+	else {
+		pr_warn("%s: No phy dev\n", __func__);
+		ret = -ENODEV;
+	}
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(rcar_gen3_phy_pm_ops,
+			rcar_gen3_phy_suspend, rcar_gen3_phy_resume);
+#define DEV_PM_OPS (&rcar_gen3_phy_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
 
 static struct platform_driver rcar_gen3_phy_usb2_driver = {
 	.driver = {
 		.name		= "phy_rcar_gen3_usb2",
+		.pm		= DEV_PM_OPS,
 		.of_match_table	= rcar_gen3_phy_usb2_match_table,
 	},
 	.probe	= rcar_gen3_phy_usb2_probe,
-	.remove = rcar_gen3_phy_usb2_remove,
+	.remove	= rcar_gen3_phy_usb2_remove,
 };
 module_platform_driver(rcar_gen3_phy_usb2_driver);
 
diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c
index f3a8897..897d093 100644
--- a/drivers/pinctrl/sh-pfc/core.c
+++ b/drivers/pinctrl/sh-pfc/core.c
@@ -25,9 +25,94 @@
 #include <linux/pinctrl/machine.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/soc/renesas/s2ram_ddr_backup.h>
 
 #include "core.h"
 
+#ifdef CONFIG_RCAR_DDR_BACKUP
+static struct hw_register pfc_ip_regs[] = {
+	{"PMMR",        0x0000, 32, 0},
+	{"GPSR0",       0x0100, 32, 0},
+	{"GPSR1",       0x0104, 32, 0},
+	{"GPSR2",       0x0108, 32, 0},
+	{"GPSR3",       0x010C, 32, 0},
+	{"GPSR4",       0x0110, 32, 0},
+	{"GPSR5",       0x0114, 32, 0},
+	{"GPSR6",       0x0118, 32, 0},
+	{"GPSR7",       0x011C, 32, 0},
+	{"IPSR0",       0x0200, 32, 0},
+	{"IPSR1",       0x0204, 32, 0},
+	{"IPSR2",       0x0208, 32, 0},
+	{"IPSR3",       0x020C, 32, 0},
+	{"IPSR4",       0x0210, 32, 0},
+	{"IPSR5",       0x0214, 32, 0},
+	{"IPSR6",       0x0218, 32, 0},
+	{"IPSR7",       0x021C, 32, 0},
+	{"IPSR8",       0x0220, 32, 0},
+	{"IPSR9",       0x0224, 32, 0},
+	{"IPSR10",      0x0228, 32, 0},
+	{"IPSR11",      0x022C, 32, 0},
+	{"IPSR12",      0x0230, 32, 0},
+	{"IPSR13",      0x0234, 32, 0},
+	{"IPSR14",      0x0238, 32, 0},
+	{"IPSR15",      0x023C, 32, 0},
+	{"IPSR16",      0x0240, 32, 0},
+	{"IPSR17",      0x0244, 32, 0},
+	{"DRVCTRL0",    0x0300, 32, 0},
+	{"DRVCTRL1",    0x0304, 32, 0},
+	{"DRVCTRL2",    0x0308, 32, 0},
+	{"DRVCTRL3",    0x030C, 32, 0},
+	{"DRVCTRL4",    0x0310, 32, 0},
+	{"DRVCTRL5",    0x0314, 32, 0},
+	{"DRVCTRL6",    0x0318, 32, 0},
+	{"DRVCTRL7",    0x031C, 32, 0},
+	{"DRVCTRL8",    0x0320, 32, 0},
+	{"DRVCTRL9",    0x0324, 32, 0},
+	{"DRVCTRL10",   0x0328, 32, 0},
+	{"DRVCTRL11",   0x032C, 32, 0},
+	{"DRVCTRL12",   0x0330, 32, 0},
+	{"DRVCTRL13",   0x0334, 32, 0},
+	{"DRVCTRL14",   0x0338, 32, 0},
+	{"DRVCTRL15",   0x033C, 32, 0},
+	{"DRVCTRL16",   0x0340, 32, 0},
+	{"DRVCTRL17",   0x0344, 32, 0},
+	{"DRVCTRL18",   0x0348, 32, 0},
+	{"DRVCTRL19",   0x034C, 32, 0},
+	{"DRVCTRL20",   0x0350, 32, 0},
+	{"DRVCTRL21",   0x0354, 32, 0},
+	{"DRVCTRL22",   0x0358, 32, 0},
+	{"DRVCTRL23",   0x035C, 32, 0},
+	{"DRVCTRL24",   0x0360, 32, 0},
+	{"PCCTRL0",     0x0380, 32, 0},
+	{"TDSELCTRL0",  0x03C0, 32, 0},
+	{"PUEN0",       0x0400, 32, 0},
+	{"PUEN1",       0x0404, 32, 0},
+	{"PUEN2",       0x0408, 32, 0},
+	{"PUEN3",       0x040C, 32, 0},
+	{"PUEN4",       0x0410, 32, 0},
+	{"PUEN5",       0x0414, 32, 0},
+	{"PUEN6",       0x0418, 32, 0},
+	{"PUD0",        0x0440, 32, 0},
+	{"PUD1",        0x0444, 32, 0},
+	{"PUD2",        0x0448, 32, 0},
+	{"PUD3",        0x044C, 32, 0},
+	{"PUD4",        0x0450, 32, 0},
+	{"PUD5",        0x0454, 32, 0},
+	{"PUD6",        0x0458, 32, 0},
+	{"MOD_SEL0",    0x0500, 32, 0},
+	{"MOD_SEL1",    0x0504, 32, 0},
+	{"MOD_SEL2",    0x0508, 32, 0},
+};
+
+static struct rcar_ip pfc_ip = {
+	.ip_name   = "PFC",
+	.base_addr = 0xE6060000,
+	.size      = 0x50C,
+	.reg_count = ARRAY_SIZE(pfc_ip_regs),
+	.ip_reg    = pfc_ip_regs,
+};
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+
 static int sh_pfc_map_resources(struct sh_pfc *pfc,
 				struct platform_device *pdev)
 {
@@ -651,11 +736,46 @@ static const struct platform_device_id sh_pfc_id_table[] = {
 	{ },
 };
 
+#ifdef CONFIG_PM_SLEEP
+static int sh_pfc_suspend(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	pr_debug("%s\n", __func__);
+
+	if (!pfc_ip.virt_addr)
+		ret = rcar_handle_registers(&pfc_ip, DO_IOREMAP);
+	if (ret)
+		return ret;
+
+	ret = rcar_handle_registers(&pfc_ip, DO_BACKUP);
+#endif
+	return ret;
+}
+
+static int sh_pfc_resume(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	pr_debug("%s\n", __func__);
+	ret = rcar_handle_registers(&pfc_ip, DO_RESTORE);
+#endif
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(sh_pfc_pm_ops,
+			sh_pfc_suspend, sh_pfc_resume);
+#define DEV_PM_OPS (&sh_pfc_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
 static struct platform_driver sh_pfc_driver = {
 	.probe		= sh_pfc_probe,
 	.id_table	= sh_pfc_id_table,
 	.driver		= {
 		.name	= DRV_NAME,
+		.pm	= DEV_PM_OPS,
 		.of_match_table = of_match_ptr(sh_pfc_of_table),
 	},
 };
diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c
index 1c85ecc..0bc47fd 100644
--- a/drivers/pwm/pwm-rcar.c
+++ b/drivers/pwm/pwm-rcar.c
@@ -17,8 +17,10 @@
 #include <linux/pm_runtime.h>
 #include <linux/pwm.h>
 #include <linux/slab.h>
+#include <linux/soc/renesas/s2ram_ddr_backup.h>
 
 #define RCAR_PWM_MAX_DIVISION	24
+#define RCAR_PWM_MIN_CYCLE	2
 #define RCAR_PWM_MAX_CYCLE	1023
 
 #define RCAR_PWMCR		0x00
@@ -35,6 +37,138 @@
 #define  RCAR_PWMCNT_PH0_MASK	0x000003ff
 #define  RCAR_PWMCNT_PH0_SHIFT	0
 
+#ifdef CONFIG_RCAR_DDR_BACKUP
+/* PWM0 */
+static struct hw_register pwm0_ip_regs[] = {
+	{"PWMCNT",   0x0004, 32, 0},
+	{"PWMCR",    0x0000, 32, 0},
+};
+
+static struct rcar_ip pwm0_ip = {
+	.ip_name   = "PWM0",
+	.base_addr = 0xE6E30000,
+	.size      = 0x08,
+	.reg_count = ARRAY_SIZE(pwm0_ip_regs),
+	.ip_reg    = pwm0_ip_regs,
+};
+
+/* PWM1 */
+static struct hw_register pwm1_ip_regs[] = {
+	{"PWMCNT",   0x0004, 32, 0},
+	{"PWMCR",    0x0000, 32, 0},
+};
+
+static struct rcar_ip pwm1_ip = {
+	.ip_name   = "PWM1",
+	.base_addr = 0xE6E31000,
+	.size      = 0x08,
+	.reg_count = ARRAY_SIZE(pwm1_ip_regs),
+	.ip_reg    = pwm1_ip_regs,
+};
+
+/* PWM2 */
+static struct hw_register pwm2_ip_regs[] = {
+	{"PWMCNT",   0x0004, 32, 0},
+	{"PWMCR",    0x0000, 32, 0},
+};
+
+static struct rcar_ip pwm2_ip = {
+	.ip_name   = "PWM2",
+	.base_addr = 0xE6E32000,
+	.size      = 0x08,
+	.reg_count = ARRAY_SIZE(pwm2_ip_regs),
+	.ip_reg    = pwm2_ip_regs,
+};
+
+/* PWM3 */
+static struct hw_register pwm3_ip_regs[] = {
+	{"PWMCNT",   0x0004, 32, 0},
+	{"PWMCR",    0x0000, 32, 0},
+};
+
+static struct rcar_ip pwm3_ip = {
+	.ip_name   = "PWM3",
+	.base_addr = 0xE6E34000,
+	.size      = 0x08,
+	.reg_count = ARRAY_SIZE(pwm3_ip_regs),
+	.ip_reg    = pwm3_ip_regs,
+};
+
+/* PWM4 */
+static struct hw_register pwm4_ip_regs[] = {
+	{"PWMCNT",   0x0004, 32, 0},
+	{"PWMCR",    0x0000, 32, 0},
+};
+
+static struct rcar_ip pwm4_ip = {
+	.ip_name   = "PWM4",
+	.base_addr = 0xE6E34000,
+	.size      = 0x08,
+	.reg_count = ARRAY_SIZE(pwm4_ip_regs),
+	.ip_reg    = pwm4_ip_regs,
+};
+
+/* PWM5 */
+static struct hw_register pwm5_ip_regs[] = {
+	{"PWMCNT",   0x0004, 32, 0},
+	{"PWMCR",    0x0000, 32, 0},
+};
+
+static struct rcar_ip pwm5_ip = {
+	.ip_name   = "PWM5",
+	.base_addr = 0xE6E35000,
+	.size      = 0x08,
+	.reg_count = ARRAY_SIZE(pwm5_ip_regs),
+	.ip_reg    = pwm5_ip_regs,
+};
+
+/* PWM6 */
+static struct hw_register pwm6_ip_regs[] = {
+	{"PWMCNT",   0x0004, 32, 0},
+	{"PWMCR",    0x0000, 32, 0},
+};
+
+static struct rcar_ip pwm6_ip = {
+	.ip_name   = "PWM6",
+	.base_addr = 0xE6E36000,
+	.size      = 0x08,
+	.reg_count = ARRAY_SIZE(pwm6_ip_regs),
+	.ip_reg    = pwm6_ip_regs,
+};
+
+struct pwm_ip_info {
+	const char *name;
+	struct rcar_ip *ip;
+};
+
+static struct pwm_ip_info ip_info_tbl[] = {
+	{"e6e30000.pwm", &pwm0_ip},
+	{"e6e31000.pwm", &pwm1_ip},
+	{"e6e32000.pwm", &pwm2_ip},
+	{"e6e33000.pwm", &pwm3_ip},
+	{"e6e34000.pwm", &pwm4_ip},
+	{"e6e35000.pwm", &pwm5_ip},
+	{"e6e36000.pwm", &pwm6_ip},
+	{NULL, NULL},
+};
+
+static struct rcar_ip *rcar_pwm_get_ip(const char *name)
+{
+	struct pwm_ip_info *ip_info = ip_info_tbl;
+	struct rcar_ip *ip = NULL;
+
+	while (ip_info->name) {
+		if (!strcmp(ip_info->name, name)) {
+			ip = ip_info->ip;
+			break;
+		}
+		ip_info++;
+	}
+
+	return ip;
+}
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+
 struct rcar_pwm_chip {
 	struct pwm_chip chip;
 	void __iomem *base;
@@ -71,12 +205,17 @@ static void rcar_pwm_update(struct rcar_pwm_chip *rp, u32 mask, u32 data,
 static int rcar_pwm_get_clock_division(struct rcar_pwm_chip *rp, int period_ns)
 {
 	unsigned long clk_rate = clk_get_rate(rp->clk);
-	unsigned long long max; /* max cycle / nanoseconds */
+	unsigned long long min, max; /* min, max cycle / nanoseconds */
 	unsigned int div;
 
 	if (clk_rate == 0)
 		return -EINVAL;
 
+	min = (unsigned long long)NSEC_PER_SEC * RCAR_PWM_MIN_CYCLE;
+	do_div(min, clk_rate);
+	if (period_ns < min)
+		return -ERANGE;
+
 	for (div = 0; div <= RCAR_PWM_MAX_DIVISION; div++) {
 		max = (unsigned long long)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE *
 			(1 << div);
@@ -134,16 +273,12 @@ static int rcar_pwm_set_counter(struct rcar_pwm_chip *rp, int div, int duty_ns,
 
 static int rcar_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
-
-	return clk_prepare_enable(rp->clk);
+	return pm_runtime_get_sync(chip->dev);
 }
 
 static void rcar_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
-
-	clk_disable_unprepare(rp->clk);
+	pm_runtime_put(chip->dev);
 }
 
 static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -157,7 +292,7 @@ static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 		return div;
 
 	/* Let the core driver set pwm->period if disabled and duty_ns == 0 */
-	if (!pwm_is_enabled(pwm) && !duty_ns)
+	if (!pwm_is_enabled(pwm) && !duty_ns && !pwm->state.duty_cycle)
 		return 0;
 
 	rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
@@ -258,11 +393,62 @@ static const struct of_device_id rcar_pwm_of_table[] = {
 };
 MODULE_DEVICE_TABLE(of, rcar_pwm_of_table);
 
+#ifdef CONFIG_PM_SLEEP
+static int rcar_pwm_suspend(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	struct platform_device *pdev = to_platform_device(dev);
+	struct rcar_ip *ip = rcar_pwm_get_ip(pdev->name);
+
+	if (ip) {
+		struct rcar_pwm_chip *pwm = platform_get_drvdata(pdev);
+
+		if (!ip->virt_addr)
+			ip->virt_addr = pwm->base;
+		pm_runtime_get_sync(dev);
+		ret = rcar_handle_registers(ip, DO_BACKUP);
+		pm_runtime_put(dev);
+	} else {
+		pr_err("%s: Failed to find PWM device\n", __func__);
+		ret = -ENODEV;
+	}
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+	return ret;
+}
+
+static int rcar_pwm_resume(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	struct platform_device *pdev = to_platform_device(dev);
+	struct rcar_ip *ip = rcar_pwm_get_ip(pdev->name);
+
+	if (ip) {
+		pm_runtime_get_sync(dev);
+		ret = rcar_handle_registers(ip, DO_RESTORE);
+		pm_runtime_put(dev);
+	} else {
+		pr_err("%s: Failed to find PWM device\n", __func__);
+		ret = -ENODEV;
+	}
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(rcar_pwm_pm_ops,
+			rcar_pwm_suspend, rcar_pwm_resume);
+#define DEV_PM_OPS (&rcar_pwm_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
 static struct platform_driver rcar_pwm_driver = {
 	.probe = rcar_pwm_probe,
 	.remove = rcar_pwm_remove,
 	.driver = {
 		.name = "pwm-rcar",
+		.pm	= DEV_PM_OPS,
 		.of_match_table = of_match_ptr(rcar_pwm_of_table),
 	}
 };
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 936f7cc..70d5ba2 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -163,6 +163,13 @@
 	  BCM590xx PMUs. This will enable support for the software
 	  controllable LDO/Switching regulators.
 
+config REGULATOR_BD9571MWV
+	tristate "ROHM BD9571MWV PMIC regulator"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  This driver supports the voltage regulators of BD9571MWV-M.
+
 config REGULATOR_DA903X
 	tristate "Dialog Semiconductor DA9030/DA9034 regulators"
 	depends on PMIC_DA903X
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 2142a5d..eb179d6 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -23,6 +23,7 @@
 obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
 obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
 obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
+obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv.o
 obj-$(CONFIG_REGULATOR_DA903X)	+= da903x.o
 obj-$(CONFIG_REGULATOR_DA9052)	+= da9052-regulator.o
 obj-$(CONFIG_REGULATOR_DA9055)	+= da9055-regulator.o
diff --git a/drivers/regulator/bd9571mwv.c b/drivers/regulator/bd9571mwv.c
new file mode 100644
index 0000000..4cff812
--- /dev/null
+++ b/drivers/regulator/bd9571mwv.c
@@ -0,0 +1,140 @@
+/*
+ * Power Management IC for BD9571MWV-M.
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+struct bd9571mwv {
+	struct regulator_dev *rdev;
+	struct regmap *regmap;
+};
+
+static const struct regmap_config bd9571mwv_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static struct regulator_ops bd9571mwv_ops = {
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_time_sel = regulator_set_voltage_time_sel,
+	.list_voltage = regulator_list_voltage_linear,
+};
+
+/* Default limits measured in millivolts and milliamps */
+#define BD9571MWV_MIN_MV		600
+#define BD9571MWV_MAX_MV		1100
+#define BD9571MWV_STEP_MV		10
+#define BD9571MWV_SLEWRATE		10000
+
+/* Define Register */
+#define BD9571_DVFS_SETVID		0x54
+#define BD9571_DVFS_SETVID_MASK		0x7F
+
+static const struct regulator_desc bd9571mwv_reg = {
+	.name = "BD9571MWV",
+	.id = 0,
+	.ops = &bd9571mwv_ops,
+	.type = REGULATOR_VOLTAGE,
+	.n_voltages = BD9571MWV_MAX_MV / BD9571MWV_STEP_MV + 1,
+	.min_uV = BD9571MWV_MIN_MV * 1000,
+	.uV_step = BD9571MWV_STEP_MV * 1000,
+	.ramp_delay = BD9571MWV_SLEWRATE,
+	.vsel_reg = BD9571_DVFS_SETVID,
+	.vsel_mask = BD9571_DVFS_SETVID_MASK,
+	.linear_min_sel = BD9571MWV_MIN_MV / BD9571MWV_STEP_MV,
+	.owner = THIS_MODULE,
+};
+
+/*
+ * I2C driver interface functions
+ */
+static int bd9571mwv_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct bd9571mwv *chip;
+	struct device *dev = &i2c->dev;
+	struct regulator_dev *rdev = NULL;
+	struct regulator_config config = { };
+	int error;
+
+	chip = devm_kzalloc(&i2c->dev, sizeof(struct bd9571mwv), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->regmap = devm_regmap_init_i2c(i2c, &bd9571mwv_regmap_config);
+	if (IS_ERR(chip->regmap)) {
+		error = PTR_ERR(chip->regmap);
+		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+			error);
+		return error;
+	}
+
+	config.dev = &i2c->dev;
+	config.init_data = of_get_regulator_init_data(dev,
+				dev->of_node, &bd9571mwv_reg);
+	config.driver_data = chip;
+	config.regmap = chip->regmap;
+	config.of_node = dev->of_node;
+
+	rdev = devm_regulator_register(&i2c->dev, &bd9571mwv_reg, &config);
+	if (IS_ERR(rdev)) {
+		dev_err(&i2c->dev, "Failed to register BD9571MWV\n");
+		return PTR_ERR(rdev);
+	}
+
+	chip->rdev = rdev;
+
+	i2c_set_clientdata(i2c, chip);
+
+	dev_info(dev, "bd9571mwv probed\n");
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id bd9571mwv_dt_ids[] = {
+	{ .compatible = "rohm,bd9571mwv" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, bd9571mwv_dt_ids);
+#endif
+
+static const struct i2c_device_id bd9571mwv_i2c_id[] = {
+	{"bd9571mwv", 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(i2c, bd9571mwv_i2c_id);
+
+static struct i2c_driver bd9571mwv_regulator_driver = {
+	.driver = {
+		.name = "bd9571mwv",
+		.of_match_table	= of_match_ptr(bd9571mwv_dt_ids),
+	},
+	.probe = bd9571mwv_i2c_probe,
+	.id_table = bd9571mwv_i2c_id,
+};
+
+module_i2c_driver(bd9571mwv_regulator_driver);
+
+MODULE_AUTHOR("Keita Kobayashi <keita.kobayashi.ym@renesas.com");
+MODULE_DESCRIPTION("Power Management IC for BD9571MWV-M");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index 3090b0d..e3f986d 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -212,6 +212,22 @@ static void __ept_release(struct kref *kref)
 	kfree(ept);
 }
 
+static inline dma_addr_t msg_dma_address(struct virtproc_info *vrp, void *msg)
+{
+	unsigned long offset = msg - vrp->rbufs;
+
+	return vrp->bufs_dma + offset;
+}
+
+static inline void rpmsg_msg_sg_init(struct virtproc_info *vrp,
+				     struct scatterlist *sg,
+				     void *msg, unsigned int len)
+{
+	sg_init_table(sg, 1);
+	sg_dma_address(sg) = msg_dma_address(vrp, msg);
+	sg_dma_len(sg) = len;
+}
+
 /* for more info, see below documentation of rpmsg_create_ept() */
 static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp,
 						 struct rpmsg_device *rpdev,
@@ -604,12 +620,12 @@ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev,
 			 msg, sizeof(*msg) + msg->len, true);
 #endif
 
-	sg_init_one(&sg, msg, sizeof(*msg) + len);
+	rpmsg_msg_sg_init(vrp, &sg, msg, sizeof(*msg) + len);
 
 	mutex_lock(&vrp->tx_lock);
 
 	/* add message to the remote processor's virtqueue */
-	err = virtqueue_add_outbuf(vrp->svq, &sg, 1, msg, GFP_KERNEL);
+	err = dma_virtqueue_add_outbuf(vrp->svq, &sg, 1, msg, GFP_KERNEL);
 	if (err) {
 		/*
 		 * need to reclaim the buffer here, otherwise it's lost
@@ -729,10 +745,10 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev,
 		dev_warn(dev, "msg received with no recipient\n");
 
 	/* publish the real size of the buffer */
-	sg_init_one(&sg, msg, RPMSG_BUF_SIZE);
+	rpmsg_msg_sg_init(vrp, &sg, msg, RPMSG_BUF_SIZE);
 
 	/* add the buffer back to the remote processor's virtqueue */
-	err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, msg, GFP_KERNEL);
+	err = dma_virtqueue_add_inbuf(vrp->rvq, &sg, 1, msg, GFP_KERNEL);
 	if (err < 0) {
 		dev_err(dev, "failed to add a virtqueue buffer: %d\n", err);
 		return err;
@@ -911,9 +927,9 @@ static int rpmsg_probe(struct virtio_device *vdev)
 		struct scatterlist sg;
 		void *cpu_addr = vrp->rbufs + i * RPMSG_BUF_SIZE;
 
-		sg_init_one(&sg, cpu_addr, RPMSG_BUF_SIZE);
+		rpmsg_msg_sg_init(vrp, &sg, cpu_addr, RPMSG_BUF_SIZE);
 
-		err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr,
+		err = dma_virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr,
 					  GFP_KERNEL);
 		WARN_ON(err); /* sanity check; this can't really happen */
 	}
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index e6e90e8..b4629e9 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -12,5 +12,6 @@
 source "drivers/soc/ti/Kconfig"
 source "drivers/soc/ux500/Kconfig"
 source "drivers/soc/versatile/Kconfig"
+source "drivers/soc/renesas/Kconfig"
 
 endmenu
diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig
new file mode 100644
index 0000000..a9bdbbe
--- /dev/null
+++ b/drivers/soc/renesas/Kconfig
@@ -0,0 +1,18 @@
+#
+# Renesas SoC drivers
+#
+config RCAR_THERMAL_EMS_ENABLED
+	tristate "Renesas R-Car Gen3 Enable Emergency Shutdown"
+	depends on RCAR_GEN3_THERMAL
+	help
+	  Enable this option if you want to have support for Emergency Shutdown
+	  in R-Car Gen3.
+
+config RCAR_DDR_BACKUP
+	bool "Renesas R-Car DDR backup/restore function"
+	depends on SUSPEND
+	default y
+	help
+	  This enables DDR backup function for R-Car Gen3.
+	  It supports to backup/restore module register during suspend/resume
+	  sequence respectively when system enters S2RAM.
diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile
index 1169aba..c3baa3b 100644
--- a/drivers/soc/renesas/Makefile
+++ b/drivers/soc/renesas/Makefile
@@ -10,3 +10,10 @@
 obj-$(CONFIG_ARCH_R8A7794)	+= rcar-sysc.o r8a7794-sysc.o
 obj-$(CONFIG_ARCH_R8A7795)	+= rcar-sysc.o r8a7795-sysc.o
 obj-$(CONFIG_ARCH_R8A7796)	+= rcar-sysc.o r8a7796-sysc.o
+
+obj-$(CONFIG_ARCH_R8A7795)	+= rcar-avs.o
+obj-$(CONFIG_ARCH_R8A7796)	+= rcar-avs.o
+# EMS for R-Car Gen3
+obj-$(CONFIG_ARCH_R8A7795)	+= rcar_ems_ctrl.o
+obj-$(CONFIG_ARCH_R8A7796)	+= rcar_ems_ctrl.o
+obj-$(CONFIG_RCAR_DDR_BACKUP)	+= s2ram_ddr_backup.o
diff --git a/drivers/soc/renesas/rcar-avs.c b/drivers/soc/renesas/rcar-avs.c
new file mode 100644
index 0000000..4b673c5
--- /dev/null
+++ b/drivers/soc/renesas/rcar-avs.c
@@ -0,0 +1,104 @@
+/*
+ * Renesas R-Car AVS Support
+ *
+ *  Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+
+#ifdef CONFIG_POWER_AVS
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+/* Change the default opp_table pattern in device tree.
+ * Set opp_pattern_num is default.
+ */
+
+int change_default_opp_pattern(unsigned int opp_pattern_num)
+{
+	struct device_node *cpu_node = NULL;
+
+	__be32 *list, *pp_val;
+	int size;
+	struct property *pp;
+
+	for_each_node_with_property(cpu_node, "operating-points-v2") {
+		pp = of_find_property(cpu_node, "operating-points-v2", &size);
+		if (!pp || !pp->value)
+			return -ENOENT;
+
+		pp_val = pp->value;
+		size = size / sizeof(*pp_val);
+		if (size > opp_pattern_num) {
+			list = kzalloc(sizeof(*pp_val), GFP_KERNEL);
+			if (!list) {
+				pr_debug("%s(): kzalloc fail, return -ENOMEM\n",
+						__func__);
+				return -ENOMEM;
+			}
+			*list = *(pp_val + opp_pattern_num);
+			pp->value = list;
+		}
+		pp->length = sizeof(*list); /* opp fw only accept 1 opp_tb */
+
+		pr_info("rcar-cpufreq: %s is running with: %s\n",
+			of_node_full_name(cpu_node),
+			of_node_full_name(of_find_node_by_phandle(
+				be32_to_cpup(pp->value))));
+	}
+
+	return 0;
+}
+
+/* Get AVS value */
+#define ADVFS_BASE		0xE60A0000
+#define KSEN_ADJCNTS		(ADVFS_BASE + 0x13C)
+#define VOLCOND_MASK_0_3	0x0f	/* VOLCOND[3:0] */
+
+#define AVS_TABLE_NUM	7
+
+unsigned int get_avs_value(void)
+{
+	unsigned int ret;
+	void __iomem *ksen_adjcnts = ioremap_nocache(KSEN_ADJCNTS, 4);
+	u32 ksen_adjcnts_value = ioread32(ksen_adjcnts);
+
+	ksen_adjcnts_value &= VOLCOND_MASK_0_3;
+	if (ksen_adjcnts_value >= 0 && ksen_adjcnts_value < AVS_TABLE_NUM) {
+		ret = ksen_adjcnts_value;
+	} else {
+		ret = 0;
+		pr_debug("rcar-cpufreq: hw get invalid avs value, use avs_tb0\n");
+	}
+	pr_info("rcar-cpufreq: use avs value: %d\n", ksen_adjcnts_value);
+	iounmap(ksen_adjcnts);
+
+	return ret;
+}
+#endif /* CONFIG_POWER_AVS */
+
+int __init rcar_avs_init(void)
+{
+#ifdef CONFIG_POWER_AVS
+	int avs_val = get_avs_value();
+
+	change_default_opp_pattern(avs_val);
+#endif /* CONFIG_POWER_AVS */
+	return 0;
+}
+
+subsys_initcall(rcar_avs_init);
+
+MODULE_AUTHOR("Renesas Electronics Corporation");
+MODULE_DESCRIPTION("R-Car AVS module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/renesas/rcar_ems_ctrl.c b/drivers/soc/renesas/rcar_ems_ctrl.c
new file mode 100644
index 0000000..8295365
--- /dev/null
+++ b/drivers/soc/renesas/rcar_ems_ctrl.c
@@ -0,0 +1,308 @@
+/*
+ *  R-Car Gen3 Emergency shutdown for thermal management
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ */
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/spinlock.h>
+#include <linux/thermal.h>
+
+#include <linux/soc/renesas/rcar_ems_ctrl.h>
+
+#define EMS_THERMAL_ZONE_MAX	10
+
+static void rcar_ems_monitor(struct work_struct *ws);
+static DECLARE_DELAYED_WORK(rcar_ems_monitor_work, rcar_ems_monitor);
+
+static RAW_NOTIFIER_HEAD(rcar_ems_chain);
+
+static int ems_mode = RCAR_EMS_MODE_OFF;
+static int ems_mode_on_temp;
+static int ems_mode_off_temp;
+static int ems_poll;
+
+static int thermal_zone_num;
+static struct thermal_zone_device *thermal_zone[EMS_THERMAL_ZONE_MAX];
+
+static int rcar_ems_notify(unsigned long state, void *p)
+{
+	return raw_notifier_call_chain(&rcar_ems_chain, state, p);
+}
+
+int register_rcar_ems_notifier(struct notifier_block *nb)
+{
+	return raw_notifier_chain_register(&rcar_ems_chain, nb);
+}
+EXPORT_SYMBOL(register_rcar_ems_notifier);
+
+void unregister_rcar_ems_notifier(struct notifier_block *nb)
+{
+	raw_notifier_chain_unregister(&rcar_ems_chain, nb);
+}
+EXPORT_SYMBOL(unregister_rcar_ems_notifier);
+
+static void rcar_ems_monitor(struct work_struct *ws)
+{
+	int i, ret;
+	int temp, max_temp;
+
+	max_temp = INT_MIN;
+	for (i = 0; i < thermal_zone_num; i++) {
+		if (thermal_zone[i]) {
+			ret = thermal_zone_get_temp(
+					thermal_zone[i], &temp);
+			if (!ret) {
+				if (max_temp < temp)
+					max_temp = temp;
+			}
+		}
+	}
+
+	if (max_temp == INT_MIN)
+		goto skip;
+
+	if (ems_mode == RCAR_EMS_MODE_OFF) {
+		if (max_temp >= ems_mode_on_temp) {
+			ems_mode  = RCAR_EMS_MODE_ON;
+			rcar_ems_notify(RCAR_EMS_MODE_ON,
+					(void *)(long)max_temp);
+		}
+	} else {
+		if (max_temp <= ems_mode_off_temp) {
+			ems_mode = RCAR_EMS_MODE_OFF;
+			rcar_ems_notify(RCAR_EMS_MODE_OFF,
+					(void *)(long)max_temp);
+		}
+	}
+
+skip:
+	schedule_delayed_work(&rcar_ems_monitor_work, ems_poll);
+
+}
+
+
+int rcar_ems_get_mode(void)
+{
+	return ems_mode;
+}
+EXPORT_SYMBOL(rcar_ems_get_mode);
+
+static int __init rcar_ems_ctrl_init(void)
+{
+	struct device_node *np, *c;
+	struct thermal_zone_device *zone;
+	u32 value;
+
+	if (!IS_ENABLED(CONFIG_RCAR_THERMAL_EMS_ENABLED)) {
+		pr_err("thermal emergency: disabled\n");
+		return 0;
+	}
+
+	np = of_find_node_by_name(NULL, "thermal-zones");
+	if (!np)
+		return 0;
+
+	for_each_child_of_node(np, c) {
+		if (!strcmp(c->name, "emergency")) {
+			if (!of_property_read_u32(c,
+				"polling-delay", &value))
+				ems_poll = msecs_to_jiffies(value);
+
+			if (!of_property_read_u32(c,
+				"on-temperature", &value))
+				ems_mode_on_temp = value;
+
+			if (!of_property_read_u32(c,
+				"off-temperature", &value))
+				ems_mode_off_temp = value;
+		} else {
+			zone = thermal_zone_get_zone_by_name(c->name);
+			if (IS_ERR(zone))
+				continue;
+
+			if (thermal_zone_num < EMS_THERMAL_ZONE_MAX) {
+				thermal_zone[thermal_zone_num] = zone;
+				thermal_zone_num++;
+			}
+		}
+	}
+	of_node_put(np);
+
+	if (thermal_zone_num == 0) {
+		pr_err("thermal emergency: not find thermal_zone\n");
+		return 0;
+	}
+
+	if (ems_poll == 0 ||
+	    ems_mode_on_temp == 0 || ems_mode_off_temp == 0) {
+		pr_err("thermal emergency: not set value\n");
+		return 0;
+	}
+
+	schedule_delayed_work(&rcar_ems_monitor_work, ems_poll);
+
+	pr_info("thermal emergency: set temperature to %d celsius\n",
+		ems_mode_on_temp / 1000);
+
+	return 0;
+}
+
+static void __exit rcar_ems_ctrl_exit(void)
+{
+	cancel_delayed_work_sync(&rcar_ems_monitor_work);
+}
+
+late_initcall(rcar_ems_ctrl_init)
+module_exit(rcar_ems_ctrl_exit)
+
+
+/* emergency cpu shutdown function */
+static struct cpumask target_cpus;
+static struct cpumask freq_scaled_cpus;
+
+static int rcar_ems_cpufreq_notifier_call(struct notifier_block *nb,
+	unsigned long event, void *data)
+{
+	struct cpufreq_policy *policy = data;
+	int mode;
+
+	if (!cpumask_test_cpu(policy->cpu, &freq_scaled_cpus))
+		return NOTIFY_DONE;
+
+	switch (event) {
+	case CPUFREQ_ADJUST:
+		mode = rcar_ems_get_mode();
+		if (mode == RCAR_EMS_MODE_ON) {
+			cpufreq_verify_within_limits(policy,
+					    policy->cpuinfo.min_freq,
+					    policy->cpuinfo.min_freq);
+		}
+		break;
+
+	default:
+		return NOTIFY_DONE;
+	}
+
+	return NOTIFY_OK;
+}
+
+static int rcar_ems_thermal_notifier_call(struct notifier_block *nb,
+	unsigned long state, void *val)
+{
+	long temp = (long)val;
+	int cpu;
+
+	pr_info("thermal emergency notifier: state=%ld (temp=%ld)\n",
+		state, temp);
+
+	switch (state) {
+	case RCAR_EMS_MODE_ON:
+		for_each_cpu(cpu, &target_cpus) {
+			if (cpu_online(cpu))
+				cpu_down(cpu);
+		}
+		break;
+
+	case RCAR_EMS_MODE_OFF:
+		for_each_cpu(cpu, &target_cpus) {
+			if  (!cpu_online(cpu))
+				cpu_up(cpu);
+		}
+		break;
+
+	default:
+		return NOTIFY_DONE;
+	}
+
+	cpufreq_update_policy(cpumask_any(&freq_scaled_cpus));
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block ems_thermal_notifier_block = {
+	.notifier_call = rcar_ems_thermal_notifier_call,
+};
+static struct notifier_block ems_cpufreq_notifier_block = {
+	.notifier_call = rcar_ems_cpufreq_notifier_call,
+};
+
+static int __init rcar_ems_cpu_shutdown_init(void)
+{
+	int cpu;
+	struct device_node *cpu_node, *ems_node, *tmp_node;
+	int total_target_cpu, i;
+
+	if (!IS_ENABLED(CONFIG_RCAR_THERMAL_EMS_ENABLED))
+		return 0;
+
+	cpumask_clear(&target_cpus);
+	cpumask_clear(&freq_scaled_cpus);
+
+	ems_node = of_find_node_by_name(NULL, "emergency");
+
+	if (!ems_node)
+		return 0;
+
+	total_target_cpu = of_count_phandle_with_args(ems_node,
+						"target_cpus", 0);
+
+	for_each_online_cpu(cpu) {
+		tmp_node  = of_get_cpu_node(cpu, NULL);
+		if (!of_device_is_compatible(tmp_node, "arm,cortex-a57"))
+			continue;
+		for (i = 0; i < total_target_cpu; i++) {
+			cpu_node = of_parse_phandle(ems_node, "target_cpus", i);
+			if (tmp_node == cpu_node) {
+				cpumask_set_cpu(cpu, &target_cpus);
+				break;
+			}
+		}
+
+		if (i == total_target_cpu)
+			cpumask_set_cpu(cpu, &freq_scaled_cpus);
+	}
+
+	if (cpumask_weight(&target_cpus) == 0) {
+		pr_err("thermal emergency: shutdown cpu none\n");
+		return 0;
+	}
+
+	register_rcar_ems_notifier(&ems_thermal_notifier_block);
+	cpufreq_register_notifier(&ems_cpufreq_notifier_block,
+				  CPUFREQ_POLICY_NOTIFIER);
+
+	pr_info("thermal emergency: shutdown target cpus %*pbl\n",
+		cpumask_pr_args(&target_cpus));
+	pr_info("thermal emergency: freq scaled target cpus %*pbl\n",
+		cpumask_pr_args(&freq_scaled_cpus));
+
+	return 0;
+}
+
+static void __exit rcar_ems_cpu_shutdown_exit(void)
+{
+	unregister_rcar_ems_notifier(&ems_thermal_notifier_block);
+	cpufreq_unregister_notifier(&ems_cpufreq_notifier_block,
+				    CPUFREQ_POLICY_NOTIFIER);
+}
+
+late_initcall(rcar_ems_cpu_shutdown_init);
+module_exit(rcar_ems_cpu_shutdown_exit);
diff --git a/drivers/soc/renesas/s2ram_ddr_backup.c b/drivers/soc/renesas/s2ram_ddr_backup.c
new file mode 100644
index 0000000..818e78b
--- /dev/null
+++ b/drivers/soc/renesas/s2ram_ddr_backup.c
@@ -0,0 +1,327 @@
+/*
+ * S2RAM supports for DDR Power-Supply Backup/Restore Function
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/sizes.h>
+#include <linux/soc/renesas/s2ram_ddr_backup.h>
+#include <linux/syscore_ops.h>
+#include <linux/io.h>
+
+/* Special code for RWDT write access*/
+#define RWTCNT_CODE 0x5a5a0000
+#define RWTCSRA_CODE 0xa5a5a500
+
+/* INTC-EX */
+static struct hw_register intc_ex_ip_regs[] = {
+	{"CONFIG_00",	0x0180, 32, 0},
+	{"CONFIG_01",	0x0184, 32, 0},
+	{"CONFIG_02",	0x0188, 32, 0},
+	{"CONFIG_03",	0x018C, 32, 0},
+	{"CONFIG_04",	0x0190, 32, 0},
+	{"CONFIG_05",	0x0194, 32, 0},
+};
+
+static struct rcar_ip intc_ex_ip = {
+	.ip_name	= "INTC-SYS",
+	.base_addr	= 0xE61C0000,
+	.size		= 0x198,
+	.reg_count	= ARRAY_SIZE(intc_ex_ip_regs),
+	.ip_reg		= intc_ex_ip_regs,
+};
+
+/* SYSC */
+static struct hw_register sysc_ip_regs[] = {
+	{"SYSCIER",     0x00C, 32, 0},
+	{"SYSCIMR",     0x010, 32, 0},
+};
+
+static struct rcar_ip sysc_ip = {
+	.ip_name   = "SYSC",
+	.base_addr = 0xE6180000,
+	.size      = 0x14,
+	.reg_count = ARRAY_SIZE(sysc_ip_regs),
+	.ip_reg    = sysc_ip_regs,
+};
+
+static struct rcar_ip *common_ips[] = {
+	&intc_ex_ip,
+	&sysc_ip,
+	NULL,
+};
+
+static unsigned int read_reg(unsigned int size, void __iomem *addr)
+{
+	unsigned int ret = 0;
+
+	switch (size) {
+	case 8:
+		ret = readb_relaxed(addr);
+		break;
+	case 16:
+		ret = readw_relaxed(addr);
+		break;
+	case 32:
+		ret = readl_relaxed(addr);
+		break;
+	default:
+		pr_debug("%s: Wrong access size\n", __func__);
+		break;
+	}
+
+	return ret;
+}
+
+static void write_reg(unsigned int size, unsigned int value,
+			void __iomem *addr)
+{
+	switch (size) {
+	case 8:
+		writeb_relaxed(value, addr);
+		break;
+	case 16:
+		writew_relaxed(value, addr);
+		break;
+	case 32:
+		writel_relaxed(value, addr);
+		break;
+	default:
+		pr_debug("%s: Wrong access size\n", __func__);
+		break;
+	}
+}
+
+static int _do_ioremap(struct rcar_ip *ip)
+{
+	ip->virt_addr = ioremap_nocache(ip->base_addr, ip->size);
+
+	if (ip->virt_addr == NULL) {
+		pr_debug("s2ram ioremap: Could not remap IP register\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+static int _do_backup(struct rcar_ip *ip)
+{
+	struct hw_register *ip_reg;
+	void __iomem *virt_addr = NULL;
+	int reg_count = 0;
+	unsigned int j;
+
+	if (ip->virt_addr == NULL) {
+		pr_debug("s2ram backup: Registers have not been mapped\n");
+		return -EINVAL;
+	}
+
+	ip_reg    = ip->ip_reg;
+	reg_count = ip->reg_count;
+	virt_addr = ip->virt_addr;
+
+	pr_debug("s2ram backup:  Working with %s, size 0x%x, virt_addr 0x%p\n",
+		  ip->ip_name, ip->size, ip->virt_addr);
+
+	for (j = 0; j < reg_count; j++) {
+		pr_debug("%-20s, access_size 0x%-2x, offset 0x%-4x, value 0x%x\n",
+			  ip_reg[j].reg_name, ip_reg[j].access_size,
+			  ip_reg[j].reg_offset, ip_reg[j].reg_value);
+
+		ip_reg[j].reg_value = read_reg(ip_reg[j].access_size,
+				virt_addr + ip_reg[j].reg_offset);
+
+		pr_debug("%-20s, access_size 0x%-2x, offset 0x%-4x, value 0x%x\n",
+			  ip_reg[j].reg_name, ip_reg[j].access_size,
+			  ip_reg[j].reg_offset, ip_reg[j].reg_value);
+	}
+
+	return 0;
+}
+
+static int _do_restore(struct rcar_ip *ip)
+{
+	struct hw_register *ip_reg;
+	void __iomem *virt_addr = NULL;
+	int reg_count = 0;
+	unsigned int j;
+
+	if (ip->virt_addr == NULL) {
+		pr_debug("s2ram restore: Registers have not been mapped\n");
+		return -EINVAL;
+	}
+
+	ip_reg    = ip->ip_reg;
+	reg_count = ip->reg_count;
+	virt_addr = ip->virt_addr;
+
+	pr_debug("s2ram restore: Working with %s, size 0x%x, virt_addr 0x%p\n",
+		  ip->ip_name, ip->size, ip->virt_addr);
+
+	if (strcmp(ip->ip_name, "PFC") && strcmp(ip->ip_name, "RWDT")) {
+		for (j = 0; j < reg_count; j++) {
+			pr_debug("%-20s, access_size 0x%-2x, offset 0x%-4x, value 0x%x\n",
+				ip_reg[j].reg_name,
+				ip_reg[j].access_size,
+				ip_reg[j].reg_offset,
+				ip_reg[j].reg_value);
+
+			write_reg(ip_reg[j].access_size, ip_reg[j].reg_value,
+				virt_addr + ip_reg[j].reg_offset);
+
+			pr_debug("%-20s, access_size 0x%-2x, offset 0x%-4x, value 0x%x\n",
+				ip_reg[j].reg_name,
+				ip_reg[j].access_size,
+				ip_reg[j].reg_offset,
+				read_reg(ip_reg[j].access_size, virt_addr +
+					ip_reg[j].reg_offset));
+		}
+	} else if (!strcmp(ip->ip_name, "RWDT")) {
+		/* For RWDT registers, need special way to write */
+		for (j = 0; j < reg_count; j++) {
+			pr_debug("%-20s, access_size 0x%-2x, offset 0x%-4x, value 0x%x\n",
+				ip_reg[j].reg_name,
+				ip_reg[j].access_size,
+				ip_reg[j].reg_offset,
+				ip_reg[j].reg_value);
+
+			/* Only two registers are backup/restored.
+			 * If offset is zero, it is RWTCNT register
+			 * and RWTCNT_CODE is used for writing.
+			 * Otherwise, RWTCSRA_CODE is used for RWTCSRA register.
+			 */
+			writel_relaxed(ip_reg[j].reg_value |
+				(!ip_reg[j].reg_offset ? RWTCNT_CODE
+					: RWTCSRA_CODE),
+				virt_addr + ip_reg[j].reg_offset);
+
+			pr_debug("%-20s, access_size 0x%-2x, offset 0x%-4x, value 0x%x\n",
+				ip_reg[j].reg_name,
+				ip_reg[j].access_size,
+				ip_reg[j].reg_offset,
+				read_reg(ip_reg[j].access_size, virt_addr +
+					ip_reg[j].reg_offset));
+		}
+	} else {
+		/* For PFC registers, need to unlock before writing */
+		for (j = 0; j < reg_count; j++) {
+			pr_debug("%-20s, access_size 0x%-2x, offset 0x%-4x, value 0x%x\n",
+				ip_reg[j].reg_name,
+				ip_reg[j].access_size,
+				ip_reg[j].reg_offset,
+				ip_reg[j].reg_value);
+
+			writel_relaxed(~ip_reg[j].reg_value,
+				virt_addr + ip_reg[0].reg_offset);
+			writel_relaxed(ip_reg[j].reg_value,
+					virt_addr +
+					ip_reg[j].reg_offset);
+			pr_debug("%-20s, access_size 0x%-2x, offset 0x%-4x, value 0x%x\n",
+				ip_reg[j].reg_name,
+				ip_reg[j].access_size,
+				ip_reg[j].reg_offset,
+				readl_relaxed(virt_addr +
+					ip_reg[j].reg_offset));
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Handle backup/restore of IP register
+ *     ip: IP to be processed
+ *     handling: Flag of processing
+ *         DO_IOREMAP: ioremap
+ *         DO_BACKUP: backup
+ *         DO_RESTORE: restore
+ */
+int rcar_handle_registers(struct rcar_ip *ip, unsigned int handling)
+{
+	int ret = 0;
+
+	switch (handling) {
+	case DO_IOREMAP:
+		ret = _do_ioremap(ip);
+		break;
+	case DO_BACKUP:
+		ret = _do_backup(ip);
+		break;
+	case DO_RESTORE:
+		ret = _do_restore(ip);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(rcar_handle_registers);
+
+/*
+ * Handle backup/restore of IPs
+ *     ip: Point to address of IP list to be processed
+ *     handling: Flag of processing
+ *         DO_IOREMAP: ioremap
+ *         DO_BACKUP: backup
+ *         DO_RESTORE: restore
+ */
+int rcar_handle_ips(struct rcar_ip **ip, unsigned int handling)
+{
+	struct rcar_ip *working_ip;
+	unsigned int i = 0;
+	unsigned int ret = 0;
+
+	while (ip[i] != NULL) {
+		working_ip = ip[i];
+		ret = rcar_handle_registers(working_ip, handling);
+		i++;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(rcar_handle_ips);
+
+#ifdef CONFIG_PM_SLEEP
+static int ddr_backup_suspend(void)
+{
+	pr_debug("%s\n", __func__);
+
+	return rcar_handle_ips(common_ips, DO_BACKUP);
+}
+
+static void ddr_backup_resume(void)
+{
+	pr_debug("%s\n", __func__);
+
+	rcar_handle_ips(common_ips, DO_RESTORE);
+}
+
+static struct syscore_ops ddr_backup_syscore_ops = {
+	.suspend = ddr_backup_suspend,
+	.resume = ddr_backup_resume,
+};
+
+static int ddr_backup_init(void)
+{
+	int ret;
+
+	/* Map register for all common IPs */
+	ret = rcar_handle_ips(common_ips, DO_IOREMAP);
+
+	register_syscore_ops(&ddr_backup_syscore_ops);
+
+	return ret;
+}
+core_initcall(ddr_backup_init);
+
+#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b799547..fb418d5 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -570,6 +570,26 @@
 	help
 	  SPI driver for SuperH and SH Mobile MSIOF blocks.
 
+config SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG
+	bool "Transfer Synchronization Debug support for MSIOF"
+	depends on SPI_SH_MSIOF
+	default n
+	help
+	  In data transfer, the slave needs to have completed
+	  a transfer preparation before the master.
+	  As a test environment, it was to be able to put a sleep wait
+	  before the data transfer of the master.
+
+config SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG_MSLEEP
+	int "Master of sleep latency (msec time)"
+	default 1
+	depends on SPI_SH_MSIOF && SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG
+	help
+	  Select Sleep latency of the previous data transfer
+	  at the time of master mode.
+	  Examples:
+	    N => N msec
+
 config SPI_SH
 	tristate "SuperH SPI controller"
 	depends on SUPERH || COMPILE_TEST
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 1de3a77..9b9deb1 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -1,6 +1,7 @@
 /*
  * SuperH MSIOF SPI Master Interface
  *
+ * Copyright (C) 2016 Renesas Electronics Corporation
  * Copyright (c) 2009 Magnus Damm
  * Copyright (C) 2014 Glider bvba
  *
@@ -27,19 +28,24 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/sh_dma.h>
+#include <linux/sys_soc.h>
 
+#include <linux/soc/renesas/s2ram_ddr_backup.h>
 #include <linux/spi/sh_msiof.h>
 #include <linux/spi/spi.h>
 
 #include <asm/unaligned.h>
 
-
 struct sh_msiof_chipdata {
 	u16 tx_fifo_size;
 	u16 rx_fifo_size;
 	u16 master_flags;
 };
 
+#ifdef CONFIG_SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG
+#define TRANSFAR_SYNC_DELAY (CONFIG_SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG_MSLEEP)
+#endif /* CONFIG_SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG */
+
 struct sh_msiof_spi_priv {
 	struct spi_master *master;
 	void __iomem *mapbase;
@@ -47,8 +53,10 @@ struct sh_msiof_spi_priv {
 	struct platform_device *pdev;
 	struct sh_msiof_spi_info *info;
 	struct completion done;
+	struct completion done_dma_tx, done_dma_rx;
 	unsigned int tx_fifo_size;
 	unsigned int rx_fifo_size;
+	int mode;
 	void *tx_dma_page;
 	void *rx_dma_page;
 	dma_addr_t tx_dma_addr;
@@ -81,6 +89,7 @@ struct sh_msiof_spi_priv {
 #define MDR1_SYNCMD_LR	 0x30000000 /*   L/R mode */
 #define MDR1_SYNCAC_SHIFT	 25 /* Sync Polarity (1 = Active-low) */
 #define MDR1_BITLSB_SHIFT	 24 /* MSB/LSB First (1 = LSB first) */
+#define MDR1_DTDL_MASK   0x00700000 /* Data Pin Bit Delay Mask */
 #define MDR1_DTDL_SHIFT		 20 /* Data Pin Bit Delay for MSIOF_SYNC */
 #define MDR1_SYNCDL_SHIFT	 16 /* Frame Sync Signal Timing Delay */
 #define MDR1_FLD_MASK	 0x0000000c /* Frame Sync Signal Interval (0-3) */
@@ -179,6 +188,172 @@ struct sh_msiof_spi_priv {
 #define IER_RFUDFE	0x00000010 /* Receive FIFO Underflow Enable */
 #define IER_RFOVFE	0x00000008 /* Receive FIFO Overflow Enable */
 
+static const struct soc_device_attribute r8a7795es10[] = {
+	{ .soc_id = "r8a7795", .revision = "ES1.0" },
+	{ },
+};
+
+static const struct soc_device_attribute r8a7795es11[] = {
+	{ .soc_id = "r8a7795", .revision = "ES1.1" },
+	{ },
+};
+
+
+static int msiof_rcar_is_gen3(struct device *dev)
+{
+	struct device_node *node = dev->of_node;
+
+	return of_device_is_compatible(node, "renesas,msiof-r8a7795") ||
+		of_device_is_compatible(node, "renesas,msiof-r8a7796");
+}
+
+#ifdef CONFIG_RCAR_DDR_BACKUP
+static struct hw_register msiof0_ip_regs[] = {
+	{"SITMDR1",	0x00, 32, 0},
+	{"SITMDR2",	0x04, 32, 0},
+	{"SITMDR3",	0x08, 32, 0},
+	{"SIRMDR1",	0x10, 32, 0},
+	{"SIRMDR2",	0x14, 32, 0},
+	{"SIRMDR3",	0x18, 32, 0},
+	{"SITSCR",	0x20, 32, 0},
+	{"SICTR",	0x28, 32, 0},
+	{"SIFCTR",	0x30, 32, 0},
+	{"SIIER",	0x44, 32, 0},
+};
+
+static struct rcar_ip msiof0_ip = {
+	.ip_name = "MSIOF0",
+	.base_addr = 0xE6E90000,
+	.size = 0x48,
+	.reg_count = ARRAY_SIZE(msiof0_ip_regs),
+	.ip_reg = msiof0_ip_regs,
+};
+
+static struct hw_register msiof1_ip_regs[] = {
+	{"SITMDR1",	0x00, 32, 0},
+	{"SITMDR2",	0x04, 32, 0},
+	{"SITMDR3",	0x08, 32, 0},
+	{"SIRMDR1",	0x10, 32, 0},
+	{"SIRMDR2",	0x14, 32, 0},
+	{"SIRMDR3",	0x18, 32, 0},
+	{"SITSCR",	0x20, 32, 0},
+	{"SICTR",	0x28, 32, 0},
+	{"SIFCTR",	0x30, 32, 0},
+	{"SIIER",	0x44, 32, 0},
+};
+
+static struct rcar_ip msiof1_ip = {
+	.ip_name = "MSIOF1",
+	.base_addr = 0xE6EA0000,
+	.size = 0x48,
+	.reg_count = ARRAY_SIZE(msiof1_ip_regs),
+	.ip_reg = msiof1_ip_regs,
+};
+
+static struct hw_register msiof2_ip_regs[] = {
+	{"SITMDR1",	0x00, 32, 0},
+	{"SITMDR2",	0x04, 32, 0},
+	{"SITMDR3",	0x08, 32, 0},
+	{"SIRMDR1",	0x10, 32, 0},
+	{"SIRMDR2",	0x14, 32, 0},
+	{"SIRMDR3",	0x18, 32, 0},
+	{"SITSCR",	0x20, 32, 0},
+	{"SICTR",	0x28, 32, 0},
+	{"SIFCTR",	0x30, 32, 0},
+	{"SIIER",	0x44, 32, 0},
+};
+
+static struct rcar_ip msiof2_ip = {
+	.ip_name = "MSIO2",
+	.base_addr = 0xE6C00000,
+	.size = 0x48,
+	.reg_count = ARRAY_SIZE(msiof2_ip_regs),
+	.ip_reg = msiof2_ip_regs,
+};
+
+static struct hw_register msiof3_ip_regs[] = {
+	{"SITMDR1",	0x00, 32, 0},
+	{"SITMDR2",	0x04, 32, 0},
+	{"SITMDR3",	0x08, 32, 0},
+	{"SIRMDR1",	0x10, 32, 0},
+	{"SIRMDR2",	0x14, 32, 0},
+	{"SIRMDR3",	0x18, 32, 0},
+	{"SITSCR",	0x20, 32, 0},
+	{"SICTR",	0x28, 32, 0},
+	{"SIFCTR",	0x30, 32, 0},
+	{"SIIER",	0x44, 32, 0},
+};
+
+static struct rcar_ip msiof3_ip = {
+	.ip_name = "MSIOF3",
+	.base_addr = 0xE6C10000,
+	.size = 0x48,
+	.reg_count = ARRAY_SIZE(msiof3_ip_regs),
+	.ip_reg = msiof3_ip_regs,
+};
+
+struct msiof_ip_info {
+	const char *name;
+	struct rcar_ip *ip;
+};
+
+static struct msiof_ip_info ip_info_tbl[] = {
+	{"e6e90000.spi", &msiof0_ip},
+	{"e6ea0000.spi", &msiof1_ip},
+	{"e6c00000.spi", &msiof2_ip},
+	{"e6c10000.spi", &msiof3_ip},
+	{NULL, NULL},
+};
+
+static struct rcar_ip *msiof_get_ip(const char *name)
+{
+	struct msiof_ip_info *ip_info = ip_info_tbl;
+	struct rcar_ip *ip = NULL;
+
+	while (ip_info->name) {
+		if (!strcmp(ip_info->name, name)) {
+			ip = ip_info->ip;
+			break;
+		}
+		ip_info++;
+	}
+
+	return ip;
+}
+
+static int msiof_save_regs(struct platform_device *pdev)
+{
+	struct rcar_ip *ip = msiof_get_ip(pdev->name);
+	int ret = -ENODEV;
+
+	if (ip) {
+		struct sh_msiof_spi_priv *priv = platform_get_drvdata(pdev);
+
+		if (!ip->virt_addr)
+			ip->virt_addr = priv->mapbase;
+
+		ret = rcar_handle_registers(ip, DO_BACKUP);
+		pr_debug("%s: Backup %s register\n", __func__, ip->ip_name);
+	} else
+		pr_err("%s: Failed to find MSIOF device\n", __func__);
+
+	return ret;
+}
+
+static int msiof_restore_regs(struct platform_device *pdev)
+{
+	struct rcar_ip *ip = msiof_get_ip(pdev->name);
+	int ret = -ENODEV;
+
+	if (ip) {
+		ret = rcar_handle_registers(ip, DO_RESTORE);
+		pr_debug("%s: Restore %s register\n", __func__, ip->ip_name);
+	} else
+		pr_err("%s: Failed to find MSIOF device\n", __func__);
+
+	return ret;
+}
+#endif /* CONFIG_RCAR_DDR_BACKUP*/
 
 static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
 {
@@ -337,7 +512,34 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
 	tmp |= !cs_high << MDR1_SYNCAC_SHIFT;
 	tmp |= lsb_first << MDR1_BITLSB_SHIFT;
 	tmp |= sh_msiof_spi_get_dtdl_and_syncdl(p);
-	sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON);
+	if (soc_device_match(r8a7795es10)) {
+		if (p->mode == SPI_MSIOF_MASTER) {
+			tmp &= ~MDR1_DTDL_MASK;
+			tmp |= 0 << MDR1_DTDL_SHIFT;
+		}
+	}
+	if (soc_device_match(r8a7795es11)) {
+		if (p->mode == SPI_MSIOF_MASTER) {
+			tmp &= ~MDR1_DTDL_MASK;
+			tmp |= 1 << MDR1_DTDL_SHIFT;
+		}
+	}
+	if (p->mode == SPI_MSIOF_MASTER)
+		sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON);
+	else
+		sh_msiof_write(p, TMDR1, tmp | TMDR1_PCON);
+	if (soc_device_match(r8a7795es10)) {
+		if (p->mode == SPI_MSIOF_MASTER) {
+			tmp &= ~MDR1_DTDL_MASK;
+			tmp |= 2 << MDR1_DTDL_SHIFT;
+		}
+	}
+	if (soc_device_match(r8a7795es11)) {
+		if (p->mode == SPI_MSIOF_MASTER) {
+			tmp &= ~MDR1_DTDL_MASK;
+			tmp |= 1 << MDR1_DTDL_SHIFT;
+		}
+	}
 	if (p->master->flags & SPI_MASTER_MUST_TX) {
 		/* These bits are reserved if RX needs TX */
 		tmp &= ~0x0000ffff;
@@ -345,8 +547,18 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
 	sh_msiof_write(p, RMDR1, tmp);
 
 	tmp = 0;
-	tmp |= CTR_TSCKIZ_SCK | cpol << CTR_TSCKIZ_POL_SHIFT;
-	tmp |= CTR_RSCKIZ_SCK | cpol << CTR_RSCKIZ_POL_SHIFT;
+	if (soc_device_match(r8a7795es11)) {
+		if (p->mode == SPI_MSIOF_MASTER) {
+			tmp |= 0 << CTR_TSCKIZ_POL_SHIFT;
+			tmp |= 0 << CTR_RSCKIZ_POL_SHIFT;
+		} else {
+			tmp |= CTR_TSCKIZ_SCK | cpol << CTR_TSCKIZ_POL_SHIFT;
+			tmp |= CTR_RSCKIZ_SCK | cpol << CTR_RSCKIZ_POL_SHIFT;
+		}
+	} else {
+		tmp |= CTR_TSCKIZ_SCK | cpol << CTR_TSCKIZ_POL_SHIFT;
+		tmp |= CTR_RSCKIZ_SCK | cpol << CTR_RSCKIZ_POL_SHIFT;
+	}
 
 	edge = cpol ^ !cpha;
 
@@ -564,17 +776,18 @@ static int sh_msiof_prepare_message(struct spi_master *master,
 
 static int sh_msiof_spi_start(struct sh_msiof_spi_priv *p, void *rx_buf)
 {
-	int ret;
+	int ret = 0;
 
 	/* setup clock and rx/tx signals */
-	ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TSCKE);
+	if (p->mode == SPI_MSIOF_MASTER)
+		ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TSCKE);
 	if (rx_buf && !ret)
 		ret = sh_msiof_modify_ctr_wait(p, 0, CTR_RXE);
 	if (!ret)
 		ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TXE);
 
 	/* start by setting frame bit */
-	if (!ret)
+	if (!ret && p->mode == SPI_MSIOF_MASTER)
 		ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TFSE);
 
 	return ret;
@@ -582,15 +795,16 @@ static int sh_msiof_spi_start(struct sh_msiof_spi_priv *p, void *rx_buf)
 
 static int sh_msiof_spi_stop(struct sh_msiof_spi_priv *p, void *rx_buf)
 {
-	int ret;
+	int ret = 0;
 
 	/* shut down frame, rx/tx and clock signals */
-	ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
+	if (p->mode == SPI_MSIOF_MASTER)
+		ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
 	if (!ret)
 		ret = sh_msiof_modify_ctr_wait(p, CTR_TXE, 0);
 	if (rx_buf && !ret)
 		ret = sh_msiof_modify_ctr_wait(p, CTR_RXE, 0);
-	if (!ret)
+	if (!ret && p->mode == SPI_MSIOF_MASTER)
 		ret = sh_msiof_modify_ctr_wait(p, CTR_TSCKE, 0);
 
 	return ret;
@@ -606,6 +820,9 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
 {
 	int fifo_shift;
 	int ret;
+	unsigned long timeout;
+
+	timeout = (p->mode == SPI_MSIOF_MASTER) ? HZ : MAX_SCHEDULE_TIMEOUT;
 
 	/* limit maximum word transfer to rx/tx fifo size */
 	if (tx_buf)
@@ -636,7 +853,7 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
 	}
 
 	/* wait for tx fifo to be emptied / rx fifo to be filled */
-	if (!wait_for_completion_timeout(&p->done, HZ)) {
+	if (!wait_for_completion_timeout(&p->done, timeout)) {
 		dev_err(&p->pdev->dev, "PIO timeout\n");
 		ret = -ETIMEDOUT;
 		goto stop_reset;
@@ -665,12 +882,18 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
 	return ret;
 }
 
-static void sh_msiof_dma_complete(void *arg)
+static void sh_msiof_tx_dma_complete(void *arg)
 {
 	struct sh_msiof_spi_priv *p = arg;
 
-	sh_msiof_write(p, IER, 0);
-	complete(&p->done);
+	complete(&p->done_dma_tx);
+}
+
+static void sh_msiof_rx_dma_complete(void *arg)
+{
+	struct sh_msiof_spi_priv *p = arg;
+
+	complete(&p->done_dma_rx);
 }
 
 static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
@@ -680,6 +903,9 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
 	struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL;
 	dma_cookie_t cookie;
 	int ret;
+	unsigned long timeout;
+
+	timeout = (p->mode == SPI_MSIOF_MASTER) ? HZ : MAX_SCHEDULE_TIMEOUT;
 
 	/* First prepare and submit the DMA request(s), as this may fail */
 	if (rx) {
@@ -690,7 +916,7 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
 		if (!desc_rx)
 			return -EAGAIN;
 
-		desc_rx->callback = sh_msiof_dma_complete;
+		desc_rx->callback = sh_msiof_rx_dma_complete;
 		desc_rx->callback_param = p;
 		cookie = dmaengine_submit(desc_rx);
 		if (dma_submit_error(cookie))
@@ -709,13 +935,8 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
 			goto no_dma_tx;
 		}
 
-		if (rx) {
-			/* No callback */
-			desc_tx->callback = NULL;
-		} else {
-			desc_tx->callback = sh_msiof_dma_complete;
-			desc_tx->callback_param = p;
-		}
+		desc_tx->callback = sh_msiof_tx_dma_complete;
+		desc_tx->callback_param = p;
 		cookie = dmaengine_submit(desc_tx);
 		if (dma_submit_error(cookie)) {
 			ret = cookie;
@@ -732,6 +953,8 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
 	sh_msiof_write(p, IER, ier_bits);
 
 	reinit_completion(&p->done);
+	reinit_completion(&p->done_dma_tx);
+	reinit_completion(&p->done_dma_rx);
 
 	/* Now start DMA */
 	if (rx)
@@ -745,12 +968,37 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
 		goto stop_dma;
 	}
 
-	/* wait for tx fifo to be emptied / rx fifo to be filled */
-	if (!wait_for_completion_timeout(&p->done, HZ)) {
-		dev_err(&p->pdev->dev, "DMA timeout\n");
-		ret = -ETIMEDOUT;
-		goto stop_reset;
+	/* wait for Tx/Rx DMA completion */
+	if (tx) {
+		ret = wait_for_completion_timeout(&p->done_dma_tx, timeout);
+		if (!ret) {
+			dev_err(&p->pdev->dev, "Tx DMA timeout\n");
+			ret = -ETIMEDOUT;
+			goto stop_reset;
+		}
+		if (!rx) {
+			ier_bits = IER_TEOFE;
+			sh_msiof_write(p, IER, ier_bits);
+
+			/* wait for tx fifo to be emptied */
+			if (!wait_for_completion_timeout(&p->done, timeout)) {
+				dev_err(&p->pdev->dev,
+					"Tx fifo to be emptied timeout\n");
+				ret = -ETIMEDOUT;
+				goto stop_reset;
+			}
+		}
 	}
+	if (rx) {
+		ret = wait_for_completion_timeout(&p->done_dma_rx, timeout);
+		if (!ret) {
+			dev_err(&p->pdev->dev, "Rx DMA timeout\n");
+			ret = -ETIMEDOUT;
+			goto stop_reset;
+		}
+	}
+
+	sh_msiof_write(p, IER, 0);
 
 	/* clear status bits */
 	sh_msiof_reset_str(p);
@@ -843,7 +1091,8 @@ static int sh_msiof_transfer_one(struct spi_master *master,
 	int ret;
 
 	/* setup clocks (clock already enabled in chipselect()) */
-	sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz);
+	if (p->mode == SPI_MSIOF_MASTER)
+		sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz);
 
 	while (master->dma_tx && len > 15) {
 		/*
@@ -862,7 +1111,7 @@ static int sh_msiof_transfer_one(struct spi_master *master,
 				break;
 			copy32 = copy_bswap32;
 		} else if (bits <= 16) {
-			if (l & 1)
+			if (l & 3)
 				break;
 			copy32 = copy_wswap32;
 		} else {
@@ -872,6 +1121,11 @@ static int sh_msiof_transfer_one(struct spi_master *master,
 		if (tx_buf)
 			copy32(p->tx_dma_page, tx_buf, l / 4);
 
+#ifdef CONFIG_SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG
+		if (p->mode == SPI_MSIOF_MASTER)
+			msleep(TRANSFAR_SYNC_DELAY);
+#endif /* CONFIG_SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG */
+
 		ret = sh_msiof_dma_once(p, tx_buf, rx_buf, l);
 		if (ret == -EAGAIN) {
 			pr_warn_once("%s %s: DMA not available, falling back to PIO\n",
@@ -945,6 +1199,12 @@ static int sh_msiof_transfer_one(struct spi_master *master,
 	words = len / bytes_per_word;
 
 	while (words > 0) {
+
+#ifdef CONFIG_SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG
+		if (p->mode == SPI_MSIOF_MASTER)
+			msleep(TRANSFAR_SYNC_DELAY);
+#endif /* CONFIG_SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG */
+
 		n = sh_msiof_spi_txrx_once(p, tx_fifo, rx_fifo, tx_buf, rx_buf,
 					   words, bits);
 		if (n < 0)
@@ -980,6 +1240,8 @@ static const struct of_device_id sh_msiof_match[] = {
 	{ .compatible = "renesas,msiof-r8a7792",   .data = &r8a779x_data },
 	{ .compatible = "renesas,msiof-r8a7793",   .data = &r8a779x_data },
 	{ .compatible = "renesas,msiof-r8a7794",   .data = &r8a779x_data },
+	{ .compatible = "renesas,msiof-r8a7795",   .data = &r8a779x_data },
+	{ .compatible = "renesas,msiof-r8a7796",   .data = &r8a779x_data },
 	{},
 };
 MODULE_DEVICE_TABLE(of, sh_msiof_match);
@@ -1004,6 +1266,11 @@ static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev)
 	of_property_read_u32(np, "renesas,dtdl", &info->dtdl);
 	of_property_read_u32(np, "renesas,syncdl", &info->syncdl);
 
+	if (of_property_read_bool(np, "slave"))
+		info->mode = SPI_MSIOF_SLAVE;
+	else
+		info->mode = SPI_MSIOF_MASTER;
+
 	info->num_chipselect = num_cs;
 
 	return info;
@@ -1076,10 +1343,7 @@ static int sh_msiof_request_dma(struct sh_msiof_spi_priv *p)
 		return 0;
 	}
 
-	/* The DMA engine uses the second register set, if present */
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-	if (!res)
-		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
 	master = p->master;
 	master->dma_tx = sh_msiof_request_dma_chan(dev, DMA_MEM_TO_DEV,
@@ -1157,6 +1421,8 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
 	const struct sh_msiof_chipdata *chipdata;
 	const struct of_device_id *of_id;
 	struct sh_msiof_spi_priv *p;
+	struct clk *ref_clk;
+	u32 clk_rate = 0;
 	int i;
 	int ret;
 
@@ -1187,6 +1453,8 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
 	}
 
 	init_completion(&p->done);
+	init_completion(&p->done_dma_tx);
+	init_completion(&p->done_dma_rx);
 
 	p->clk = devm_clk_get(&pdev->dev, NULL);
 	if (IS_ERR(p->clk)) {
@@ -1226,6 +1494,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
 		p->tx_fifo_size = p->info->tx_fifo_override;
 	if (p->info->rx_fifo_override)
 		p->rx_fifo_size = p->info->rx_fifo_override;
+	p->mode = p->info->mode;
 
 	/* init master code */
 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
@@ -1250,6 +1519,17 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
 		goto err2;
 	}
 
+	if (msiof_rcar_is_gen3(&master->dev)) {
+		ref_clk = devm_clk_get(&pdev->dev, "msiof_ref_clk");
+		if (!IS_ERR(ref_clk))
+			clk_rate = clk_get_rate(ref_clk);
+		if (clk_rate) {
+			clk_prepare_enable(p->clk);
+			clk_set_rate(p->clk, clk_rate);
+			clk_disable_unprepare(p->clk);
+		}
+	}
+
 	return 0;
 
  err2:
@@ -1275,12 +1555,47 @@ static const struct platform_device_id spi_driver_ids[] = {
 };
 MODULE_DEVICE_TABLE(platform, spi_driver_ids);
 
+#ifdef CONFIG_PM_SLEEP
+static int sh_msiof_spi_suspend(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	struct platform_device *pdev = to_platform_device(dev);
+
+	pm_runtime_get_sync(dev);
+	ret = msiof_save_regs(pdev);
+	pm_runtime_put(dev);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+	return ret;
+}
+
+static int sh_msiof_spi_resume(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	struct platform_device *pdev = to_platform_device(dev);
+
+	pm_runtime_get_sync(dev);
+	ret = msiof_restore_regs(pdev);
+	pm_runtime_put(dev);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(sh_msiof_spi_pm_ops,
+			sh_msiof_spi_suspend, sh_msiof_spi_resume);
+#define DEV_PM_OPS (&sh_msiof_spi_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
 static struct platform_driver sh_msiof_spi_drv = {
 	.probe		= sh_msiof_spi_probe,
 	.remove		= sh_msiof_spi_remove,
 	.id_table	= spi_driver_ids,
 	.driver		= {
 		.name		= "spi_sh_msiof",
+		.pm		= DEV_PM_OPS,
 		.of_match_table = of_match_ptr(sh_msiof_match),
 	},
 };
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 2e05046..928c674 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -4,6 +4,7 @@
  * Copyright (C) 2006 SWAPP
  *	Andrea Paterniani <a.paterniani@swapp-eng.it>
  * Copyright (C) 2007 David Brownell (simplification, cleanup)
+ * Copyright (C) 2015 Renesas Electronics Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -696,6 +697,7 @@ static struct class *spidev_class;
 static const struct of_device_id spidev_dt_ids[] = {
 	{ .compatible = "rohm,dh2228fv" },
 	{ .compatible = "lineartechnology,ltc2488" },
+	{ .compatible = "renesas,sh-msiof" },
 	{},
 };
 MODULE_DEVICE_TABLE(of, spidev_dt_ids);
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index a13541b..92db596 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -243,6 +243,15 @@
 	  Enable this to plug the R-Car thermal sensor driver into the Linux
 	  thermal framework.
 
+config RCAR_GEN3_THERMAL
+	tristate "Renesas R-Car Gen3 thermal driver"
+	depends on ARCH_RENESAS || COMPILE_TEST
+	select SOC_BUS
+	depends on HAS_IOMEM
+	help
+	  Enable this to plug the R-Car Gen3 thermal sensor driver into the Linux
+	  thermal framework.
+
 config KIRKWOOD_THERMAL
 	tristate "Temperature sensor on Marvell Kirkwood SoCs"
 	depends on MACH_KIRKWOOD || COMPILE_TEST
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index c92eb22..1216fb3 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -30,6 +30,7 @@
 obj-$(CONFIG_SPEAR_THERMAL)	+= spear_thermal.o
 obj-$(CONFIG_ROCKCHIP_THERMAL)	+= rockchip_thermal.o
 obj-$(CONFIG_RCAR_THERMAL)	+= rcar_thermal.o
+obj-$(CONFIG_RCAR_GEN3_THERMAL)	+= rcar_gen3_thermal.o
 obj-$(CONFIG_KIRKWOOD_THERMAL)  += kirkwood_thermal.o
 obj-y				+= samsung/
 obj-$(CONFIG_DOVE_THERMAL)  	+= dove_thermal.o
diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c
new file mode 100644
index 0000000..2e5816b
--- /dev/null
+++ b/drivers/thermal/rcar_gen3_thermal.c
@@ -0,0 +1,626 @@
+/*
+ *  R-Car Gen3 THS/CIVM thermal sensor driver
+ *  Based on drivers/thermal/rcar_thermal.c
+ *
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ */
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/spinlock.h>
+#include <linux/sys_soc.h>
+#include <linux/thermal.h>
+
+/* Register offset */
+#define REG_GEN3_CTSR		0x20
+#define REG_GEN3_THCTR		0x20
+#define REG_GEN3_IRQSTR		0x04
+#define REG_GEN3_IRQMSK		0x08
+#define REG_GEN3_IRQCTL		0x0C
+#define REG_GEN3_IRQEN		0x10
+#define REG_GEN3_IRQTEMP1	0x14
+#define REG_GEN3_IRQTEMP2	0x18
+#define REG_GEN3_IRQTEMP3	0x1C
+#define REG_GEN3_TEMP		0x28
+#define REG_GEN3_THCODE1	0x50
+#define REG_GEN3_THCODE2	0x54
+#define REG_GEN3_THCODE3	0x58
+
+#define PTAT_BASE		0xE6198000
+#define REG_GEN3_PTAT1		0x5C
+#define REG_GEN3_PTAT2		0x60
+#define REG_GEN3_PTAT3		0x64
+#define PTAT_SIZE		REG_GEN3_PTAT3
+
+/* CTSR bit */
+#define PONM1            (0x1 << 8)	/* For H3 WS1.x */
+#define AOUT            (0x1 << 7)
+#define THBGR           (0x1 << 5)
+#define VMEN            (0x1 << 4)
+#define VMST            (0x1 << 1)
+#define THSST           (0x1 << 0)
+
+/* THCTR bit */
+#define PONM2            (0x1 << 6)	/* For H3 WS2.0 and M3 WS1.0 */
+
+#define CTEMP_MASK	0xFFF
+
+#define IRQ_TEMP1_BIT	(0x1 << 0)
+#define IRQ_TEMP2_BIT	(0x1 << 1)
+#define IRQ_TEMP3_BIT	(0x1 << 2)
+#define IRQ_TEMPD1_BIT	(0x1 << 3)
+#define IRQ_TEMPD2_BIT	(0x1 << 4)
+#define IRQ_TEMPD3_BIT	(0x1 << 5)
+
+#define MCELSIUS(temp)			((temp) * 1000)
+#define TEMP_IRQ_SHIFT(tsc_id)	(0x1 << tsc_id)
+#define TEMPD_IRQ_SHIFT(tsc_id)	(0x1 << (tsc_id + 3))
+#define GEN3_FUSE_MASK	0xFFF
+
+/* Attribute structs describing Salvator-X revisions */
+/* H3 WS1.0 and WS1.1 */
+static const struct soc_device_attribute r8a7795es1[]  = {
+	{ .soc_id = "r8a7795", .revision = "ES1.*" },
+	{}
+};
+
+/* M3 WS1.0 */
+static const struct soc_device_attribute r8a7796es10[]  = {
+	{ .soc_id = "r8a7796", .revision = "ES1.0" },
+	{}
+};
+
+/* Equation coefficients for thermal calculation formula.*/
+struct equation_coefs {
+	long a1;
+	long b1;
+	long a2;
+	long b2;
+};
+
+
+struct fuse_factors {
+	int thcode_1;
+	int thcode_2;
+	int thcode_3;
+	int ptat_1;
+	int ptat_2;
+	int ptat_3;
+};
+
+struct rcar_thermal_priv {
+	void __iomem *base;
+	struct device *dev;
+	struct thermal_zone_device *zone;
+	struct delayed_work work;
+	struct fuse_factors factor;
+	struct equation_coefs coef;
+	spinlock_t lock;
+	int id;
+	int irq;
+	const struct rcar_thermal_data *data;
+};
+
+struct rcar_thermal_data {
+	int (*thermal_init)(struct rcar_thermal_priv *priv);
+};
+
+#define rcar_priv_to_dev(priv)		((priv)->dev)
+#define rcar_has_irq_support(priv)	((priv)->irq)
+
+/* Temperature calculation  */
+#define CODETSD(x)		((x) * 1000)
+#define TJ_1 96000L
+#define TJ_3 (-41000L)
+
+#define rcar_thermal_read(p, r) _rcar_thermal_read(p, r)
+static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg)
+{
+	return ioread32(priv->base + reg);
+}
+
+#define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, r, d)
+static void _rcar_thermal_write(struct rcar_thermal_priv *priv,
+				u32 reg, u32 data)
+{
+	iowrite32(data, priv->base + reg);
+}
+
+static int round_temp(int temp)
+{
+	int tmp1, tmp2;
+	int result = 0;
+
+	tmp1 = abs(temp) % 1000;
+	tmp2 = abs(temp) / 1000;
+
+	if (tmp1 < 250)
+		result = CODETSD(tmp2);
+	else if (tmp1 < 750 && tmp1 >= 250)
+		result = CODETSD(tmp2) + 500;
+	else
+		result = CODETSD(tmp2) + 1000;
+
+	return ((temp < 0) ? (result * (-1)) : result);
+}
+
+static int thermal_read_fuse_factor(struct rcar_thermal_priv *priv)
+{
+	void __iomem *ptat_base;
+
+	ptat_base = ioremap_nocache(PTAT_BASE, PTAT_SIZE);
+	if (!ptat_base) {
+		dev_err(rcar_priv_to_dev(priv), "Cannot map FUSE register\n");
+		return -ENOMEM;
+	}
+
+	/* For H3 WS1.0, H3 WS1.1 and M3 ES1.0
+	 * these registers have not been programmed yet.
+	 * We will use fixed value as temporary solution.
+	 */
+	if (soc_device_match(r8a7795es1)
+		|| soc_device_match(r8a7796es10)) {
+		priv->factor.ptat_1 = 2351;
+		priv->factor.ptat_2 = 1509;
+		priv->factor.ptat_3 = 435;
+		switch (priv->id) {
+		case 0:
+			priv->factor.thcode_1 = 3248;
+			priv->factor.thcode_2 = 2800;
+			priv->factor.thcode_3 = 2221;
+			break;
+		case 1:
+			priv->factor.thcode_1 = 3245;
+			priv->factor.thcode_2 = 2795;
+			priv->factor.thcode_3 = 2216;
+			break;
+		case 2:
+			priv->factor.thcode_1 = 3250;
+			priv->factor.thcode_2 = 2805;
+			priv->factor.thcode_3 = 2237;
+			break;
+		}
+	} else {
+		priv->factor.thcode_1 = rcar_thermal_read(priv,
+						REG_GEN3_THCODE1)
+				& GEN3_FUSE_MASK;
+		priv->factor.thcode_2 = rcar_thermal_read(priv,
+						REG_GEN3_THCODE2)
+				& GEN3_FUSE_MASK;
+		priv->factor.thcode_3 = rcar_thermal_read(priv,
+						REG_GEN3_THCODE3)
+				& GEN3_FUSE_MASK;
+		priv->factor.ptat_1 = ioread32(ptat_base + REG_GEN3_PTAT1)
+				& GEN3_FUSE_MASK;
+		priv->factor.ptat_2 = ioread32(ptat_base + REG_GEN3_PTAT2)
+				& GEN3_FUSE_MASK;
+		priv->factor.ptat_3 = ioread32(ptat_base + REG_GEN3_PTAT3)
+				& GEN3_FUSE_MASK;
+	}
+
+	iounmap(ptat_base);
+
+	return 0;
+}
+
+static void thermal_coefficient_calculation(struct rcar_thermal_priv *priv)
+{
+	int tj_2 = 0;
+	long a1, b1;
+	long a2, b2;
+	long a1_num, a1_den;
+	long a2_num, a2_den;
+
+	tj_2 = (CODETSD((priv->factor.ptat_2 - priv->factor.ptat_3) * 137)
+		/ (priv->factor.ptat_1 - priv->factor.ptat_3)) - CODETSD(41);
+
+	/*
+	 * The following code is to calculate coefficients.
+	 */
+	/* Coefficient a1 and b1 */
+	a1_num = CODETSD(priv->factor.thcode_2 - priv->factor.thcode_3);
+	a1_den = tj_2 - TJ_3;
+	a1 = (10000 * a1_num) / a1_den;
+	b1 = (10000 * priv->factor.thcode_3) - ((a1 * TJ_3) / 1000);
+
+	/* Coefficient a2 and b2 */
+	a2_num = CODETSD(priv->factor.thcode_2 - priv->factor.thcode_1);
+	a2_den = tj_2 - TJ_1;
+	a2 = (10000 * a2_num) / a2_den;
+	b2 = (10000 * priv->factor.thcode_1) - ((a2 * TJ_1) / 1000);
+
+	priv->coef.a1 = DIV_ROUND_CLOSEST(a1, 10);
+	priv->coef.b1 = DIV_ROUND_CLOSEST(b1, 10);
+	priv->coef.a2 = DIV_ROUND_CLOSEST(a2, 10);
+	priv->coef.b2 = DIV_ROUND_CLOSEST(b2, 10);
+}
+
+int thermal_temp_converter(struct equation_coefs coef,
+					int temp_code)
+{
+	int temp, temp1, temp2;
+
+	temp1 = MCELSIUS((CODETSD(temp_code) - coef.b1)) / coef.a1;
+	temp2 = MCELSIUS((CODETSD(temp_code) - coef.b2)) / coef.a2;
+	temp = (temp1 + temp2) / 2;
+
+	return round_temp(temp);
+}
+
+int thermal_celsius_to_temp(struct equation_coefs coef,
+					int ctemp)
+{
+	int temp_code, temp1, temp2;
+
+	temp1 = (ctemp * coef.a1 / 1000 + coef.b1) / 1000;
+	temp2 = (ctemp * coef.a2 / 1000 + coef.b2) / 1000;
+	temp_code = (temp1 + temp2) / 2;
+
+	return temp_code;
+}
+
+/*
+ *		Zone device functions
+ */
+static int rcar_gen3_thermal_update_temp(struct rcar_thermal_priv *priv)
+{
+	u32 ctemp;
+	unsigned long flags;
+	int temp_cel, temp_code;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	ctemp = rcar_thermal_read(priv, REG_GEN3_TEMP) & CTEMP_MASK;
+	if (rcar_has_irq_support(priv)) {
+		temp_cel = thermal_temp_converter(priv->coef, ctemp);
+
+		/* set the interrupts to exceed the temperature */
+		temp_code = thermal_celsius_to_temp(priv->coef,
+						    temp_cel + MCELSIUS(1));
+		rcar_thermal_write(priv, REG_GEN3_IRQTEMP1, temp_code);
+
+		/* set the interrupts to fall below the temperature */
+		temp_code = thermal_celsius_to_temp(priv->coef,
+						    temp_cel - MCELSIUS(1));
+		rcar_thermal_write(priv, REG_GEN3_IRQTEMP2, temp_code);
+	}
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+static int rcar_gen3_thermal_get_temp(void *devdata, int *temp)
+{
+	struct rcar_thermal_priv *priv = devdata;
+	int ctemp;
+	unsigned long flags;
+	u32 ctemp_code;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	ctemp_code = rcar_thermal_read(priv, REG_GEN3_TEMP) & CTEMP_MASK;
+	ctemp = thermal_temp_converter(priv->coef, ctemp_code);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if ((ctemp < MCELSIUS(-40)) || (ctemp > MCELSIUS(125))) {
+		struct device *dev = rcar_priv_to_dev(priv);
+
+		dev_dbg(dev, "Temperature is not measured correctly!\n");
+
+		return -EIO;
+	}
+
+	*temp = ctemp;
+
+	return 0;
+}
+
+static int rcar_gen3_r8a7796_thermal_init(struct rcar_thermal_priv *priv)
+{
+	unsigned long flags;
+	unsigned long reg_val;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	reg_val = rcar_thermal_read(priv, REG_GEN3_THCTR);
+	reg_val &= ~PONM2;
+	rcar_thermal_write(priv, REG_GEN3_THCTR, reg_val);
+	udelay(1000);
+	rcar_thermal_write(priv, REG_GEN3_IRQCTL, 0x3F);
+	rcar_thermal_write(priv, REG_GEN3_IRQEN,
+			   IRQ_TEMP1_BIT | IRQ_TEMPD2_BIT);
+	reg_val = rcar_thermal_read(priv, REG_GEN3_THCTR);
+	reg_val |= THSST;
+	rcar_thermal_write(priv, REG_GEN3_THCTR, reg_val);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+static int rcar_gen3_r8a7795_thermal_init(struct rcar_thermal_priv *priv)
+{
+	unsigned long flags;
+
+	if (soc_device_match(r8a7795es1)) {
+		spin_lock_irqsave(&priv->lock, flags);
+
+		rcar_thermal_write(priv, REG_GEN3_CTSR,  THBGR);
+		rcar_thermal_write(priv, REG_GEN3_CTSR,  0x0);
+
+		udelay(1000);
+
+		rcar_thermal_write(priv, REG_GEN3_CTSR, PONM1);
+		rcar_thermal_write(priv, REG_GEN3_IRQCTL, 0x3F);
+		rcar_thermal_write(priv, REG_GEN3_IRQEN,
+				   IRQ_TEMP1_BIT | IRQ_TEMPD2_BIT);
+		rcar_thermal_write(priv, REG_GEN3_CTSR,
+				PONM1 | AOUT | THBGR | VMEN);
+		udelay(100);
+
+		rcar_thermal_write(priv, REG_GEN3_CTSR,
+				PONM1 | AOUT | THBGR | VMEN | VMST | THSST);
+
+		spin_unlock_irqrestore(&priv->lock, flags);
+	} else
+		/* H3 WS2.0 has the same init flow with M3 WS1.0 */
+		rcar_gen3_r8a7796_thermal_init(priv);
+
+	return 0;
+}
+
+/*
+ *		Interrupt
+ */
+#define rcar_thermal_irq_enable(p)	_rcar_thermal_irq_ctrl(p, 1)
+#define rcar_thermal_irq_disable(p)	_rcar_thermal_irq_ctrl(p, 0)
+static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable)
+{
+	unsigned long flags;
+
+	if (!rcar_has_irq_support(priv))
+		return;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	rcar_thermal_write(priv, REG_GEN3_IRQMSK,
+		enable ? (IRQ_TEMP1_BIT | IRQ_TEMPD2_BIT) : 0);
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void rcar_gen3_thermal_work(struct work_struct *work)
+{
+	struct rcar_thermal_priv *priv;
+
+	priv = container_of(work, struct rcar_thermal_priv, work.work);
+
+	rcar_gen3_thermal_update_temp(priv);
+	thermal_zone_device_update(priv->zone, THERMAL_EVENT_UNSPECIFIED);
+
+	rcar_thermal_irq_enable(priv);
+}
+
+static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data)
+{
+	struct rcar_thermal_priv *priv = data;
+	unsigned long flags;
+	int status;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	status = rcar_thermal_read(priv, REG_GEN3_IRQSTR);
+	rcar_thermal_write(priv, REG_GEN3_IRQSTR, 0);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (status == 0)
+		return IRQ_NONE;
+
+	if (status & (IRQ_TEMP1_BIT | IRQ_TEMPD2_BIT)) {
+		rcar_thermal_irq_disable(priv);
+		schedule_delayed_work(&priv->work, 0);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = {
+	.get_temp	= rcar_gen3_thermal_get_temp,
+};
+
+/*
+ *		Platform functions
+ */
+static int rcar_gen3_thermal_remove(struct platform_device *pdev)
+{
+	struct rcar_thermal_priv *priv = platform_get_drvdata(pdev);
+	struct device *dev = &pdev->dev;
+
+	rcar_thermal_irq_disable(priv);
+	thermal_zone_of_sensor_unregister(dev, priv->zone);
+
+	pm_runtime_put(dev);
+	pm_runtime_disable(dev);
+
+	return 0;
+}
+
+static const struct rcar_thermal_data r8a7795_data = {
+	.thermal_init = rcar_gen3_r8a7795_thermal_init,
+};
+
+static const struct rcar_thermal_data r8a7796_data = {
+	.thermal_init = rcar_gen3_r8a7796_thermal_init,
+};
+
+static const struct of_device_id rcar_thermal_dt_ids[] = {
+	{ .compatible = "renesas,thermal-r8a7795", .data = &r8a7795_data},
+	{ .compatible = "renesas,thermal-r8a7796", .data = &r8a7796_data},
+	{},
+};
+MODULE_DEVICE_TABLE(of, rcar_thermal_dt_ids);
+
+static int rcar_gen3_thermal_probe(struct platform_device *pdev)
+{
+	struct rcar_thermal_priv *priv;
+	struct device *dev = &pdev->dev;
+	struct resource *res, *irq;
+	int ret = -ENODEV;
+	int idle;
+	struct device_node *tz_nd, *tmp_nd;
+	int i, irq_cnt;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, priv);
+
+	priv->dev = dev;
+
+	pm_runtime_enable(dev);
+	pm_runtime_get_sync(dev);
+
+	priv->data = of_device_get_match_data(dev);
+	if (!priv->data)
+		goto error_unregister;
+
+	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	priv->irq = 0;
+	if (irq) {
+		priv->irq = 1;
+		for_each_node_with_property(tz_nd, "polling-delay") {
+			tmp_nd = of_parse_phandle(tz_nd,
+					"thermal-sensors", 0);
+			if (tmp_nd && !strcmp(tmp_nd->full_name,
+					dev->of_node->full_name)) {
+				of_property_read_u32(tz_nd, "polling-delay",
+					&idle);
+				(idle > 0) ? (priv->irq = 0) :
+						(priv->irq = 1);
+				break;
+			}
+		}
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		goto error_unregister;
+
+	priv->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(priv->base)) {
+		ret = PTR_ERR(priv->base);
+		goto error_unregister;
+	}
+
+	spin_lock_init(&priv->lock);
+	INIT_DELAYED_WORK(&priv->work, rcar_gen3_thermal_work);
+
+	priv->id = of_alias_get_id(dev->of_node, "tsc");
+
+	priv->zone = devm_thermal_zone_of_sensor_register(dev, 0, priv,
+				&rcar_gen3_tz_of_ops);
+
+	if (IS_ERR(priv->zone)) {
+		dev_err(dev, "Can't register thermal zone\n");
+		ret = PTR_ERR(priv->zone);
+		priv->zone = NULL;
+		goto error_unregister;
+	}
+
+	priv->data->thermal_init(priv);
+	ret = thermal_read_fuse_factor(priv);
+	if (ret)
+		goto error_unregister;
+	thermal_coefficient_calculation(priv);
+	ret = rcar_gen3_thermal_update_temp(priv);
+
+	if (ret < 0)
+		goto error_unregister;
+
+	rcar_thermal_irq_enable(priv);
+
+	/* Interrupt */
+	if (rcar_has_irq_support(priv)) {
+		irq_cnt = platform_irq_count(pdev);
+		for (i = 0; i < irq_cnt; i++) {
+			irq = platform_get_resource(pdev, IORESOURCE_IRQ, i);
+			ret = devm_request_irq(dev, irq->start,
+					       rcar_gen3_thermal_irq,
+					       IRQF_SHARED,
+					       dev_name(dev), priv);
+			if (ret) {
+				dev_err(dev, "IRQ request failed\n ");
+				goto error_unregister;
+			}
+		}
+	}
+
+	dev_info(dev, "Thermal sensor probed\n");
+
+	return 0;
+
+error_unregister:
+	rcar_gen3_thermal_remove(pdev);
+
+	return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rcar_gen3_thermal_suspend(struct device *dev)
+{
+	struct rcar_thermal_priv *priv = dev_get_drvdata(dev);
+
+	pr_debug("%s\n", __func__);
+	rcar_thermal_irq_disable(priv);
+
+	return 0;
+}
+
+static int rcar_gen3_thermal_resume(struct device *dev)
+{
+	struct rcar_thermal_priv *priv = dev_get_drvdata(dev);
+
+	pr_debug("%s\n", __func__);
+	priv->data->thermal_init(priv);
+	rcar_thermal_irq_enable(priv);
+	rcar_gen3_thermal_update_temp(priv);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops,
+			rcar_gen3_thermal_suspend,
+			rcar_gen3_thermal_resume);
+
+#define DEV_PM_OPS (&rcar_gen3_thermal_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct platform_driver rcar_gen3_thermal_driver = {
+	.driver	= {
+		.name	= "rcar_gen3_thermal",
+		.pm	= DEV_PM_OPS,
+		.of_match_table = rcar_thermal_dt_ids,
+	},
+	.probe		= rcar_gen3_thermal_probe,
+	.remove		= rcar_gen3_thermal_remove,
+};
+module_platform_driver(rcar_gen3_thermal_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("R-Car Gen3 THS/CIVM driver");
+MODULE_AUTHOR("Renesas Electronics Corporation");
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 4b26252..9ffde39 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -47,6 +47,7 @@
 #include <linux/serial_sci.h>
 #include <linux/sh_dma.h>
 #include <linux/slab.h>
+#include <linux/soc/renesas/s2ram_ddr_backup.h>
 #include <linux/string.h>
 #include <linux/sysrq.h>
 #include <linux/timer.h>
@@ -454,6 +455,307 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
 
 #define sci_getreg(up, offset)		(sci_regmap[to_sci_port(up)->cfg->regtype] + offset)
 
+#ifdef CONFIG_RCAR_DDR_BACKUP
+/* SCIF0 save/restore regs */
+static struct hw_register scif0_ip_regs[] = {
+	{"SCSMR",  0x0000, 16, 0},
+	{"SCBRR",  0x0004,  8, 0},
+	{"SCFCR",  0x0018, 16, 0},
+	{"SCSPTR", 0x0020, 16, 0},
+	{"DL",     0x0030, 16, 0},
+	{"CKS",    0x0034, 16, 0},
+	{"SCSCR",  0x0008, 16, 0},
+};
+
+static struct rcar_ip scif0_ip = {
+	.ip_name   = "SCIF0",
+	.base_addr = 0xE6E60000,
+	.size      = 0x38,
+	.reg_count = ARRAY_SIZE(scif0_ip_regs),
+	.ip_reg    = scif0_ip_regs,
+};
+
+/* SCIF1 save/restore regs */
+static struct hw_register scif1_ip_regs[] = {
+	{"SCSMR",  0x0000, 16, 0},
+	{"SCBRR",  0x0004,  8, 0},
+	{"SCFCR",  0x0018, 16, 0},
+	{"SCSPTR", 0x0020, 16, 0},
+	{"DL",     0x0030, 16, 0},
+	{"CKS",    0x0034, 16, 0},
+	{"SCSCR",  0x0008, 16, 0},
+};
+
+static struct rcar_ip scif1_ip = {
+	.ip_name   = "SCIF1",
+	.base_addr = 0xE6E68000,
+	.size      = 0x38,
+	.reg_count = ARRAY_SIZE(scif1_ip_regs),
+	.ip_reg    = scif1_ip_regs,
+};
+
+/* SCIF2 save/restore regs */
+static struct hw_register scif2_ip_regs[] = {
+	{"SCSMR",  0x0000, 16, 0},
+	{"SCBRR",  0x0004,  8, 0},
+	{"SCFCR",  0x0018, 16, 0},
+	{"SCSPTR", 0x0020, 16, 0},
+	{"DL",     0x0030, 16, 0},
+	{"CKS",    0x0034, 16, 0},
+	{"SCSCR",  0x0008, 16, 0},
+};
+
+static struct rcar_ip scif2_ip = {
+	.ip_name   = "SCIF2",
+	.base_addr = 0xE6E88000,
+	.size      = 0x38,
+	.reg_count = ARRAY_SIZE(scif2_ip_regs),
+	.ip_reg    = scif2_ip_regs,
+};
+
+/* SCIF3 save/restore regs */
+static struct hw_register scif3_ip_regs[] = {
+	{"SCSMR",  0x0000, 16, 0},
+	{"SCBRR",  0x0004,  8, 0},
+	{"SCFCR",  0x0018, 16, 0},
+	{"SCSPTR", 0x0020, 16, 0},
+	{"DL",     0x0030, 16, 0},
+	{"CKS",    0x0034, 16, 0},
+	{"SCSCR",  0x0008, 16, 0},
+};
+
+static struct rcar_ip scif3_ip = {
+	.ip_name   = "SCIF3",
+	.base_addr = 0xE6C50000,
+	.size      = 0x38,
+	.reg_count = ARRAY_SIZE(scif3_ip_regs),
+	.ip_reg    = scif3_ip_regs,
+};
+
+/* SCIF4 save/restore regs */
+static struct hw_register scif4_ip_regs[] = {
+	{"SCSMR",  0x0000, 16, 0},
+	{"SCBRR",  0x0004,  8, 0},
+	{"SCFCR",  0x0018, 16, 0},
+	{"SCSPTR", 0x0020, 16, 0},
+	{"DL",     0x0030, 16, 0},
+	{"CKS",    0x0034, 16, 0},
+	{"SCSCR",  0x0008, 16, 0},
+};
+
+static struct rcar_ip scif4_ip = {
+	.ip_name   = "SCIF4",
+	.base_addr = 0xE6C40000,
+	.size      = 0x38,
+	.reg_count = ARRAY_SIZE(scif4_ip_regs),
+	.ip_reg    = scif4_ip_regs,
+};
+
+/* SCIF5 save/restore regs */
+static struct hw_register scif5_ip_regs[] = {
+	{"SCSMR",  0x0000, 16, 0},
+	{"SCBRR",  0x0004,  8, 0},
+	{"SCFCR",  0x0018, 16, 0},
+	{"SCSPTR", 0x0020, 16, 0},
+	{"DL",     0x0030, 16, 0},
+	{"CKS",    0x0034, 16, 0},
+	{"SCSCR",  0x0008, 16, 0},
+};
+
+static struct rcar_ip scif5_ip = {
+	.ip_name   = "SCIF5",
+	.base_addr = 0xE6F30000,
+	.size      = 0x38,
+	.reg_count = ARRAY_SIZE(scif5_ip_regs),
+	.ip_reg    = scif5_ip_regs,
+};
+
+/* HSCIF0 save/restore regs */
+static struct hw_register hscif0_ip_regs[] = {
+	{"HSSMR",  0x0000, 16, 0},
+	{"HSBRR",  0x0004,  8, 0},
+	{"HSFCR",  0x0018, 16, 0},
+	{"HSSPTR", 0x0020, 16, 0},
+	{"HSSRR",  0x0040, 16, 0},
+	{"HSRTGR", 0x0050, 16, 0},
+	{"HSRTRGR", 0x0054, 16, 0},
+	{"HSTTRGR", 0x0058, 16, 0},
+	{"HSSCR",  0x0008, 16, 0},
+};
+
+static struct rcar_ip hscif0_ip = {
+	.ip_name   = "HSCIF0",
+	.base_addr = 0xE6540000,
+	.size      = 0x60,
+	.reg_count = ARRAY_SIZE(hscif0_ip_regs),
+	.ip_reg    = hscif0_ip_regs,
+};
+
+/* HSCIF1 save/restore regs */
+static struct hw_register hscif1_ip_regs[] = {
+	{"HSSMR",  0x0000, 16, 0},
+	{"HSBRR",  0x0004,  8, 0},
+	{"HSFCR",  0x0018, 16, 0},
+	{"HSSPTR", 0x0020, 16, 0},
+	{"HSSRR",  0x0040, 16, 0},
+	{"HSRTGR", 0x0050, 16, 0},
+	{"HSRTRGR", 0x0054, 16, 0},
+	{"HSTTRGR", 0x0058, 16, 0},
+	{"HSSCR",  0x0008, 16, 0},
+};
+
+static struct rcar_ip hscif1_ip = {
+	.ip_name   = "HSCIF1",
+	.base_addr = 0xE6550000,
+	.size      = 0x60,
+	.reg_count = ARRAY_SIZE(hscif1_ip_regs),
+	.ip_reg    = hscif1_ip_regs,
+};
+
+/* HSCIF2 save/restore regs */
+static struct hw_register hscif2_ip_regs[] = {
+	{"HSSMR",  0x0000, 16, 0},
+	{"HSBRR",  0x0004,  8, 0},
+	{"HSFCR",  0x0018, 16, 0},
+	{"HSSPTR", 0x0020, 16, 0},
+	{"HSSRR",  0x0040, 16, 0},
+	{"HSRTGR", 0x0050, 16, 0},
+	{"HSRTRGR", 0x0054, 16, 0},
+	{"HSTTRGR", 0x0058, 16, 0},
+	{"HSSCR",  0x0008, 16, 0},
+};
+
+static struct rcar_ip hscif2_ip = {
+	.ip_name   = "HSCIF2",
+	.base_addr = 0xE6560000,
+	.size      = 0x60,
+	.reg_count = ARRAY_SIZE(hscif2_ip_regs),
+	.ip_reg    = hscif2_ip_regs,
+};
+
+/* HSCIF3 save/restore regs */
+static struct hw_register hscif3_ip_regs[] = {
+	{"HSSMR",  0x0000, 16, 0},
+	{"HSBRR",  0x0004,  8, 0},
+	{"HSFCR",  0x0018, 16, 0},
+	{"HSSPTR", 0x0020, 16, 0},
+	{"HSSRR",  0x0040, 16, 0},
+	{"HSRTGR", 0x0050, 16, 0},
+	{"HSRTRGR", 0x0054, 16, 0},
+	{"HSTTRGR", 0x0058, 16, 0},
+	{"HSSCR",  0x0008, 16, 0},
+};
+
+static struct rcar_ip hscif3_ip = {
+	.ip_name   = "HSCIF3",
+	.base_addr = 0xE66A0000,
+	.size      = 0x60,
+	.reg_count = ARRAY_SIZE(hscif3_ip_regs),
+	.ip_reg    = hscif3_ip_regs,
+};
+
+/* HSCIF4 save/restore regs */
+static struct hw_register hscif4_ip_regs[] = {
+	{"HSSMR",  0x0000, 16, 0},
+	{"HSBRR",  0x0004,  8, 0},
+	{"HSFCR",  0x0018, 16, 0},
+	{"HSSPTR", 0x0020, 16, 0},
+	{"HSSRR",  0x0040, 16, 0},
+	{"HSRTGR", 0x0050, 16, 0},
+	{"HSRTRGR", 0x0054, 16, 0},
+	{"HSTTRGR", 0x0058, 16, 0},
+	{"HSSCR",  0x0008, 16, 0},
+};
+
+static struct rcar_ip hscif4_ip = {
+	.ip_name   = "HSCIF4",
+	.base_addr = 0xE66B0000,
+	.size      = 0x60,
+	.reg_count = ARRAY_SIZE(hscif4_ip_regs),
+	.ip_reg    = hscif4_ip_regs,
+};
+
+struct h_scif_ip_info {
+	const char *name;
+	struct rcar_ip *ip;
+};
+
+static struct h_scif_ip_info ip_info_tbl[] = {
+	/* SCIF */
+	{"e6e60000.serial", &scif0_ip},
+	{"e6e68000.serial", &scif1_ip},
+	{"e6e88000.serial", &scif2_ip},
+	{"e6c50000.serial", &scif3_ip},
+	{"e6c40000.serial", &scif4_ip},
+	{"e6c30000.serial", &scif5_ip},
+	/* HSCIF */
+	{"e6540000.serial", &hscif0_ip},
+	{"e6550000.serial", &hscif1_ip},
+	{"e6560000.serial", &hscif2_ip},
+	{"e66a0000.serial", &hscif3_ip},
+	{"e66b0000.serial", &hscif4_ip},
+	{NULL, NULL},
+};
+
+static struct rcar_ip *sci_get_ip(const char *name)
+{
+	struct h_scif_ip_info *ip_info = ip_info_tbl;
+	struct rcar_ip *ip = NULL;
+
+	while (ip_info->name) {
+		if (!strcmp(ip_info->name, name)) {
+			ip = ip_info->ip;
+			break;
+		}
+		ip_info++;
+	}
+
+	return ip;
+}
+
+static int sci_save_regs(struct device *dev)
+{
+	struct sci_port *sport = dev_get_drvdata(dev);
+	struct platform_device *pdev;
+	struct rcar_ip *h_scif_ip;
+	int ret = -ENODEV;
+
+	pdev = container_of(dev, struct platform_device, dev);
+	h_scif_ip = sci_get_ip(pdev->name);
+
+	if (h_scif_ip) {
+		if (!h_scif_ip->virt_addr)
+			h_scif_ip->virt_addr = sport->port.membase;
+
+		ret = rcar_handle_registers(h_scif_ip, DO_BACKUP);
+		pr_debug("%s: Backup %s registers\n",
+			__func__, h_scif_ip->ip_name);
+	} else
+		pr_err("%s: No serial found\n", __func__);
+
+	return ret;
+}
+
+static int sci_restore_regs(struct device *dev)
+{
+	struct platform_device *pdev;
+	struct rcar_ip *h_scif_ip;
+	int ret = -ENODEV;
+
+	pdev = container_of(dev, struct platform_device, dev);
+	h_scif_ip = sci_get_ip(pdev->name);
+
+	if (h_scif_ip) {
+		ret = rcar_handle_registers(h_scif_ip, DO_RESTORE);
+		pr_debug("%s: Restore %s registers\n",
+			__func__, h_scif_ip->ip_name);
+	} else
+		pr_err("%s: No serial found\n", __func__);
+
+	return ret;
+}
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+
 /*
  * The "offset" here is rather misleading, in that it refers to an enum
  * value relative to the port mapping rather than the fixed offset
@@ -1142,11 +1444,8 @@ static int sci_dma_rx_push(struct sci_port *s, void *buf, size_t count)
 	int copied;
 
 	copied = tty_insert_flip_string(tport, buf, count);
-	if (copied < count) {
-		dev_warn(port->dev, "Rx overrun: dropping %zu bytes\n",
-			 count - copied);
+	if (copied < count)
 		port->icount.buf_overrun++;
-	}
 
 	port->icount.rx += copied;
 
@@ -1161,8 +1460,6 @@ static int sci_dma_rx_find_active(struct sci_port *s)
 		if (s->active_rx == s->cookie_rx[i])
 			return i;
 
-	dev_err(s->port.dev, "%s: Rx cookie %d not found!\n", __func__,
-		s->active_rx);
 	return -1;
 }
 
@@ -1223,9 +1520,9 @@ static void sci_dma_rx_complete(void *arg)
 
 	dma_async_issue_pending(chan);
 
+	spin_unlock_irqrestore(&port->lock, flags);
 	dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n",
 		__func__, s->cookie_rx[active], active, s->active_rx);
-	spin_unlock_irqrestore(&port->lock, flags);
 	return;
 
 fail:
@@ -1248,8 +1545,11 @@ static void sci_tx_dma_release(struct sci_port *s, bool enable_pio)
 	dma_unmap_single(chan->device->dev, s->tx_dma_addr, UART_XMIT_SIZE,
 			 DMA_TO_DEVICE);
 	dma_release_channel(chan);
-	if (enable_pio)
+	if (enable_pio) {
+		spin_lock_irqsave(&port->lock, flags);
 		sci_start_tx(port);
+		spin_unlock_irqrestore(&port->lock, flags);
+	}
 }
 
 static void sci_submit_rx(struct sci_port *s)
@@ -1273,8 +1573,6 @@ static void sci_submit_rx(struct sci_port *s)
 		if (dma_submit_error(s->cookie_rx[i]))
 			goto fail;
 
-		dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__,
-			s->cookie_rx[i], i);
 	}
 
 	s->active_rx = s->cookie_rx[0];
@@ -1288,7 +1586,6 @@ static void sci_submit_rx(struct sci_port *s)
 	for (i = 0; i < 2; i++)
 		s->cookie_rx[i] = -EINVAL;
 	s->active_rx = -EINVAL;
-	dev_warn(s->port.dev, "Failed to re-start Rx DMA, using PIO\n");
 	sci_rx_dma_release(s, true);
 }
 
@@ -1358,10 +1655,10 @@ static void rx_timer_fn(unsigned long arg)
 	int active, count;
 	u16 scr;
 
-	spin_lock_irqsave(&port->lock, flags);
-
 	dev_dbg(port->dev, "DMA Rx timed out\n");
 
+	spin_lock_irqsave(&port->lock, flags);
+
 	active = sci_dma_rx_find_active(s);
 	if (active < 0) {
 		spin_unlock_irqrestore(&port->lock, flags);
@@ -1370,9 +1667,9 @@ static void rx_timer_fn(unsigned long arg)
 
 	status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state);
 	if (status == DMA_COMPLETE) {
+		spin_unlock_irqrestore(&port->lock, flags);
 		dev_dbg(port->dev, "Cookie %d #%d has already completed\n",
 			s->active_rx, active);
-		spin_unlock_irqrestore(&port->lock, flags);
 
 		/* Let packet complete handler take care of the packet */
 		return;
@@ -1396,8 +1693,6 @@ static void rx_timer_fn(unsigned long arg)
 	/* Handle incomplete DMA receive */
 	dmaengine_terminate_all(s->chan_rx);
 	read = sg_dma_len(&s->sg_rx[active]) - state.residue;
-	dev_dbg(port->dev, "Read %u bytes with cookie %d\n", read,
-		s->active_rx);
 
 	if (read) {
 		count = sci_dma_rx_push(s, s->rx_buf[active], read);
@@ -1469,8 +1764,7 @@ static void sci_request_dma(struct uart_port *port)
 
 	dev_dbg(port->dev, "%s: port %d\n", __func__, port->line);
 
-	if (!port->dev->of_node &&
-	    (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0))
+	if (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0)
 		return;
 
 	s->cookie_tx = -EINVAL;
@@ -1944,6 +2238,7 @@ static void sci_enable_ms(struct uart_port *port)
 static void sci_break_ctl(struct uart_port *port, int break_state)
 {
 	unsigned short scscr, scsptr;
+	unsigned long flags;
 
 	/* check wheter the port has SCSPTR */
 	if (!sci_getreg(port, SCSPTR)->size) {
@@ -1954,6 +2249,7 @@ static void sci_break_ctl(struct uart_port *port, int break_state)
 		return;
 	}
 
+	spin_lock_irqsave(&port->lock, flags);
 	scsptr = serial_port_in(port, SCSPTR);
 	scscr = serial_port_in(port, SCSCR);
 
@@ -1967,6 +2263,7 @@ static void sci_break_ctl(struct uart_port *port, int break_state)
 
 	serial_port_out(port, SCSPTR, scsptr);
 	serial_port_out(port, SCSCR, scscr);
+	spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static int sci_startup(struct uart_port *port)
@@ -2178,6 +2475,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
 	int min_err = INT_MAX, err;
 	unsigned long max_freq = 0;
 	int best_clk = -1;
+	unsigned long flags;
 
 	if ((termios->c_cflag & CSIZE) == CS7)
 		smr_val |= SCSMR_CHR;
@@ -2287,6 +2585,8 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
 		serial_port_out(port, SCCKS, sccks);
 	}
 
+	spin_lock_irqsave(&port->lock, flags);
+
 	sci_reset(port);
 
 	uart_update_timeout(port, termios->c_cflag, baud);
@@ -2304,9 +2604,6 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
 			case 27: smr_val |= SCSMR_SRC_27; break;
 			}
 		smr_val |= cks;
-		dev_dbg(port->dev,
-			 "SCR 0x%x SMR 0x%x BRR %u CKS 0x%x DL %u SRR %u\n",
-			 scr_val, smr_val, brr, sccks, dl, srr);
 		serial_port_out(port, SCSCR, scr_val);
 		serial_port_out(port, SCSMR, smr_val);
 		serial_port_out(port, SCBRR, brr);
@@ -2320,7 +2617,6 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
 		scr_val = s->cfg->scscr & (SCSCR_CKE1 | SCSCR_CKE0);
 		smr_val |= serial_port_in(port, SCSMR) &
 			   (SCSMR_CKEDG | SCSMR_SRC_MASK | SCSMR_CKS);
-		dev_dbg(port->dev, "SCR 0x%x SMR 0x%x\n", scr_val, smr_val);
 		serial_port_out(port, SCSCR, scr_val);
 		serial_port_out(port, SCSMR, smr_val);
 	}
@@ -2352,7 +2648,6 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
 	}
 
 	scr_val |= s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0);
-	dev_dbg(port->dev, "SCSCR 0x%x\n", scr_val);
 	serial_port_out(port, SCSCR, scr_val);
 	if ((srr + 1 == 5) &&
 	    (port->type == PORT_SCIFA || port->type == PORT_SCIFB)) {
@@ -2401,8 +2696,6 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
 			bits++;
 		s->rx_timeout = DIV_ROUND_UP((s->buf_len_rx * 2 * bits * HZ) /
 					     (baud / 10), 10);
-		dev_dbg(port->dev, "DMA Rx t-out %ums, tty t-out %u jiffies\n",
-			s->rx_timeout * 1000 / HZ, port->timeout);
 		if (s->rx_timeout < msecs_to_jiffies(20))
 			s->rx_timeout = msecs_to_jiffies(20);
 	}
@@ -2411,6 +2704,8 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
 	if ((termios->c_cflag & CREAD) != 0)
 		sci_start_rx(port);
 
+	spin_unlock_irqrestore(&port->lock, flags);
+
 	sci_port_disable(s);
 
 	if (UART_ENABLE_MS(port, termios->c_cflag))
@@ -2979,7 +3274,8 @@ sci_parse_dt(struct platform_device *pdev, unsigned int *dev_id)
 	struct device_node *np = pdev->dev.of_node;
 	const struct of_device_id *match;
 	struct plat_sci_port *p;
-	int id;
+	int id, index;
+	struct of_phandle_args dma_spec;
 
 	if (!IS_ENABLED(CONFIG_OF) || !np)
 		return NULL;
@@ -3009,6 +3305,20 @@ sci_parse_dt(struct platform_device *pdev, unsigned int *dev_id)
 	if (of_find_property(np, "uart-has-rtscts", NULL))
 		p->capabilities |= SCIx_HAVE_RTSCTS;
 
+	index = of_property_match_string(np, "dma-names", "tx");
+	if (index >= 0)
+		index = of_parse_phandle_with_args(np, "dmas", "#dma-cells",
+						   index, &dma_spec);
+	if (index >= 0)
+		p->dma_slave_tx = dma_spec.args[0];
+
+	index = of_property_match_string(np, "dma-names", "rx");
+	if (index >= 0)
+		index = of_parse_phandle_with_args(np, "dmas", "#dma-cells",
+						   index, &dma_spec);
+	if (index >= 0)
+		p->dma_slave_rx = dma_spec.args[0];
+
 	return p;
 }
 
@@ -3101,21 +3411,35 @@ static int sci_probe(struct platform_device *dev)
 static __maybe_unused int sci_suspend(struct device *dev)
 {
 	struct sci_port *sport = dev_get_drvdata(dev);
+	int ret = 0;
 
-	if (sport)
+	if (sport) {
+		pm_runtime_get_sync(sport->port.dev);
 		uart_suspend_port(&sci_uart_driver, &sport->port);
+#ifdef CONFIG_RCAR_DDR_BACKUP
+		ret = sci_save_regs(dev);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+		pm_runtime_put(sport->port.dev);
+	}
 
-	return 0;
+	return ret;
 }
 
 static __maybe_unused int sci_resume(struct device *dev)
 {
 	struct sci_port *sport = dev_get_drvdata(dev);
+	int ret = 0;
 
-	if (sport)
+	if (sport) {
+		pm_runtime_get_sync(sport->port.dev);
+#ifdef CONFIG_RCAR_DDR_BACKUP
+		ret = sci_restore_regs(dev);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
 		uart_resume_port(&sci_uart_driver, &sport->port);
+		pm_runtime_put(sport->port.dev);
+	}
 
-	return 0;
+	return ret;
 }
 
 static SIMPLE_DEV_PM_OPS(sci_dev_pm_ops, sci_suspend, sci_resume);
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 1700908..86612ac 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -72,7 +72,7 @@
 static const char	hcd_name [] = "ohci_hcd";
 
 #define	STATECHANGE_DELAY	msecs_to_jiffies(300)
-#define	IO_WATCHDOG_DELAY	msecs_to_jiffies(250)
+#define	IO_WATCHDOG_DELAY	msecs_to_jiffies(275)
 
 #include "ohci.h"
 #include "pci-quirks.h"
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index ed56bf9..6a07fc3 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -95,7 +95,6 @@ static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = {
 };
 
 static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
-	.firmware_name = XHCI_RCAR_FIRMWARE_NAME_V2,
 	.init_quirk = xhci_rcar_init_quirk,
 	.plat_start = xhci_rcar_start,
 };
@@ -124,6 +123,9 @@ static const struct of_device_id usb_xhci_of_match[] = {
 		.compatible = "renesas,xhci-r8a7795",
 		.data = &xhci_plat_renesas_rcar_gen3,
 	}, {
+		.compatible = "renesas,xhci-r8a7796",
+		.data = &xhci_plat_renesas_rcar_gen3,
+	}, {
 		.compatible = "renesas,rcar-gen2-xhci",
 		.data = &xhci_plat_renesas_rcar_gen2,
 	}, {
@@ -142,7 +144,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
 	struct xhci_hcd		*xhci;
 	struct resource         *res;
 	struct usb_hcd		*hcd;
+#if (!IS_ENABLED(CONFIG_USB_XHCI_RCAR))
 	struct clk              *clk;
+#endif /* CONFIG_USB_XHCI_RCAR */
 	int			ret;
 	int			irq;
 
@@ -188,6 +192,10 @@ static int xhci_plat_probe(struct platform_device *pdev)
 	 * Not all platforms have a clk so it is not an error if the
 	 * clock does not exists.
 	 */
+#if IS_ENABLED(CONFIG_USB_XHCI_RCAR)
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_get_sync(&pdev->dev);
+#else
 	clk = devm_clk_get(&pdev->dev, NULL);
 	if (!IS_ERR(clk)) {
 		ret = clk_prepare_enable(clk);
@@ -197,6 +205,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
 		ret = -EPROBE_DEFER;
 		goto put_hcd;
 	}
+#endif /* CONFIG_USB_XHCI_RCAR */
 
 	xhci = hcd_to_xhci(hcd);
 	match = of_match_node(usb_xhci_of_match, pdev->dev.of_node);
@@ -211,7 +220,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
 
 	device_wakeup_enable(hcd->self.controller);
 
+#if (!IS_ENABLED(CONFIG_USB_XHCI_RCAR))
 	xhci->clk = clk;
+#endif /* CONFIG_USB_XHCI_RCAR */
 	xhci->main_hcd = hcd;
 	xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev,
 			dev_name(&pdev->dev), hcd);
@@ -259,8 +270,13 @@ static int xhci_plat_probe(struct platform_device *pdev)
 	usb_put_hcd(xhci->shared_hcd);
 
 disable_clk:
+#if IS_ENABLED(CONFIG_USB_XHCI_RCAR)
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+#else
 	if (!IS_ERR(clk))
 		clk_disable_unprepare(clk);
+#endif /* CONFIG_USB_XHCI_RCAR */
 
 put_hcd:
 	usb_put_hcd(hcd);
@@ -272,7 +288,9 @@ static int xhci_plat_remove(struct platform_device *dev)
 {
 	struct usb_hcd	*hcd = platform_get_drvdata(dev);
 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
+#if (!IS_ENABLED(CONFIG_USB_XHCI_RCAR))
 	struct clk *clk = xhci->clk;
+#endif /* CONFIG_USB_XHCI_RCAR */
 
 	usb_remove_hcd(xhci->shared_hcd);
 	usb_phy_shutdown(hcd->usb_phy);
@@ -280,9 +298,15 @@ static int xhci_plat_remove(struct platform_device *dev)
 	usb_remove_hcd(hcd);
 	usb_put_hcd(xhci->shared_hcd);
 
+	usb_put_hcd(hcd);
+
+#if IS_ENABLED(CONFIG_USB_XHCI_RCAR)
+	pm_runtime_put_sync(&dev->dev);
+	pm_runtime_disable(&dev->dev);
+#else
 	if (!IS_ERR(clk))
 		clk_disable_unprepare(clk);
-	usb_put_hcd(hcd);
+#endif /* CONFIG_USB_XHCI_RCAR */
 
 	return 0;
 }
@@ -292,7 +316,7 @@ static int xhci_plat_suspend(struct device *dev)
 {
 	struct usb_hcd	*hcd = dev_get_drvdata(dev);
 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
-
+	int ret = 0;
 	/*
 	 * xhci_suspend() needs `do_wakeup` to know whether host is allowed
 	 * to do wakeup during suspend. Since xhci_plat_suspend is currently
@@ -301,7 +325,15 @@ static int xhci_plat_suspend(struct device *dev)
 	 * reconsider this when xhci_plat_suspend enlarges its scope, e.g.,
 	 * also applies to runtime suspend.
 	 */
-	return xhci_suspend(xhci, device_may_wakeup(dev));
+	ret = xhci_suspend(xhci, device_may_wakeup(dev));
+	if (ret)
+		pr_err("%s: xhci_suspend failed, ret: %d\n", __func__, ret);
+
+#if IS_ENABLED(CONFIG_USB_XHCI_RCAR)
+	pm_runtime_put_sync(dev);
+#endif /* CONFIG_USB_XHCI_RCAR */
+
+	return ret;
 }
 
 static int xhci_plat_resume(struct device *dev)
@@ -309,6 +341,13 @@ static int xhci_plat_resume(struct device *dev)
 	struct usb_hcd	*hcd = dev_get_drvdata(dev);
 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
 
+#if IS_ENABLED(CONFIG_USB_XHCI_RCAR)
+	pm_runtime_get_sync(dev);
+
+	hcd->driver->reset(hcd);
+	hcd->driver->start(hcd);
+#endif /* CONFIG_USB_XHCI_RCAR */
+
 	return xhci_resume(xhci, 0);
 }
 
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
index 0e4535e..d37773b 100644
--- a/drivers/usb/host/xhci-rcar.c
+++ b/drivers/usb/host/xhci-rcar.c
@@ -13,12 +13,16 @@
 #include <linux/platform_device.h>
 #include <linux/of.h>
 #include <linux/usb/phy.h>
+#include <linux/sys_soc.h>
 
 #include "xhci.h"
 #include "xhci-plat.h"
 #include "xhci-rcar.h"
 
 /*
+* - The V3 firmware is possible to use on r8a7795 es2.0 or later and
+*   r8a7796 es1.1 or later.
+* - The V2 firmware is possible to use on r8a7795 es1.x and r8a7796 es1.0.
 * - The V2 firmware is possible to use on R-Car Gen2. However, the V2 causes
 *   performance degradation. So, this driver continues to use the V1 if R-Car
 *   Gen2.
@@ -26,6 +30,7 @@
 */
 MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V1);
 MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V2);
+MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V3);
 
 /*** Register Offset ***/
 #define RCAR_USB3_INT_ENA	0x224	/* Interrupt Enable */
@@ -64,6 +69,17 @@ MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V2);
 #define RCAR_USB3_RX_POL_VAL	BIT(21)
 #define RCAR_USB3_TX_POL_VAL	BIT(4)
 
+static const struct soc_device_attribute r8a7795es1x[] = {
+	{ .soc_id = "r8a7795", .revision = "ES1.*" },
+	{ },
+};
+
+static const struct soc_device_attribute r8a7796es10[] = {
+	{ .soc_id = "r8a7796", .revision = "ES1.0" },
+	{ },
+};
+
+
 static void xhci_rcar_start_gen2(struct usb_hcd *hcd)
 {
 	/* LCLK Select */
@@ -92,6 +108,7 @@ static int xhci_rcar_is_gen3(struct device *dev)
 	struct device_node *node = dev->of_node;
 
 	return of_device_is_compatible(node, "renesas,xhci-r8a7795") ||
+		of_device_is_compatible(node, "renesas,xhci-r8a7796") ||
 		of_device_is_compatible(node, "renesas,rcar-gen3-xhci");
 }
 
@@ -120,7 +137,18 @@ static int xhci_rcar_download_firmware(struct usb_hcd *hcd)
 	u32 data, val, temp;
 
 	/* request R-Car USB3.0 firmware */
-	retval = request_firmware(&fw, priv->firmware_name, dev);
+	if (xhci_rcar_is_gen3(hcd->self.controller)) {
+		if (soc_device_match(r8a7795es1x))
+			retval = request_firmware(&fw,
+					XHCI_RCAR_FIRMWARE_NAME_V2, dev);
+		else if (soc_device_match(r8a7796es10))
+			retval = request_firmware(&fw,
+					XHCI_RCAR_FIRMWARE_NAME_V2, dev);
+		else
+			retval = request_firmware(&fw,
+					XHCI_RCAR_FIRMWARE_NAME_V3, dev);
+	} else
+		retval = request_firmware(&fw, priv->firmware_name, dev);
 	if (retval)
 		return retval;
 
@@ -192,5 +220,7 @@ int xhci_rcar_init_quirk(struct usb_hcd *hcd)
 			xhci_rcar_is_gen3(hcd->self.controller))
 		xhci->quirks |= XHCI_NO_64BIT_SUPPORT;
 
+	xhci->quirks |= XHCI_SLOW_SUSPEND;
+
 	return xhci_rcar_download_firmware(hcd);
 }
diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h
index 2941a25..d2ffe20 100644
--- a/drivers/usb/host/xhci-rcar.h
+++ b/drivers/usb/host/xhci-rcar.h
@@ -13,6 +13,7 @@
 
 #define XHCI_RCAR_FIRMWARE_NAME_V1	"r8a779x_usb3_v1.dlmem"
 #define XHCI_RCAR_FIRMWARE_NAME_V2	"r8a779x_usb3_v2.dlmem"
+#define XHCI_RCAR_FIRMWARE_NAME_V3	"r8a779x_usb3_v3.dlmem"
 
 #if IS_ENABLED(CONFIG_USB_XHCI_RCAR)
 void xhci_rcar_start(struct usb_hcd *hcd);
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 012a37a..4948740 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -735,7 +735,10 @@ static int usbhsc_suspend(struct device *dev)
 {
 	struct usbhs_priv *priv = dev_get_drvdata(dev);
 	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+	struct renesas_usbhs_platform_info *info = dev_get_platdata(dev);
+	struct renesas_usbhs_driver_callback *dfunc = &info->driver_callback;
 
+	dfunc->notify_hotplug = NULL;
 	if (mod) {
 		usbhs_mod_call(priv, stop, priv);
 		usbhs_mod_change(priv, -1);
@@ -751,11 +754,14 @@ static int usbhsc_resume(struct device *dev)
 {
 	struct usbhs_priv *priv = dev_get_drvdata(dev);
 	struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+	struct renesas_usbhs_platform_info *info = dev_get_platdata(dev);
+	struct renesas_usbhs_driver_callback *dfunc = &info->driver_callback;
 
 	if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
 		usbhsc_power_ctrl(priv, 1);
 
 	usbhs_platform_call(priv, phy_reset, pdev);
+	dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug;
 
 	usbhsc_drvcllbck_notify_hotplug(pdev);
 
@@ -775,10 +781,9 @@ static int usbhsc_runtime_nop(struct device *dev)
 }
 
 static const struct dev_pm_ops usbhsc_pm_ops = {
-	.suspend		= usbhsc_suspend,
-	.resume			= usbhsc_resume,
-	.runtime_suspend	= usbhsc_runtime_nop,
-	.runtime_resume		= usbhsc_runtime_nop,
+	SET_SYSTEM_SLEEP_PM_OPS(usbhsc_suspend, usbhsc_resume)
+	SET_RUNTIME_PM_OPS(usbhsc_runtime_nop, usbhsc_runtime_nop,
+			NULL)
 };
 
 static struct platform_driver renesas_usbhs_driver = {
diff --git a/drivers/usb/renesas_usbhs/rcar3.c b/drivers/usb/renesas_usbhs/rcar3.c
index 1d70add..d544b33 100644
--- a/drivers/usb/renesas_usbhs/rcar3.c
+++ b/drivers/usb/renesas_usbhs/rcar3.c
@@ -9,6 +9,7 @@
  *
  */
 
+#include <linux/delay.h>
 #include <linux/io.h>
 #include "common.h"
 #include "rcar3.h"
@@ -35,10 +36,13 @@ static int usbhs_rcar3_power_ctrl(struct platform_device *pdev,
 
 	usbhs_write32(priv, UGCTRL2, UGCTRL2_RESERVED_3 | UGCTRL2_USB0SEL_OTG);
 
-	if (enable)
+	if (enable) {
 		usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM);
-	else
+		/* The controller on R-Car Gen3 needs to wait up to 45 usec */
+		udelay(45);
+	} else {
 		usbhs_bset(priv, LPSTS, LPSTS_SUSPM, 0);
+	}
 
 	return 0;
 }
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index ed9c9ee..7fd180a 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -257,13 +257,42 @@ static struct vring_desc *alloc_indirect(struct virtqueue *_vq,
 	return desc;
 }
 
+static inline int vring_desc_set(struct virtqueue *_vq,
+				  struct vring_desc *desc,
+				  struct scatterlist *sg,
+				  unsigned int flags,
+				  enum dma_data_direction direction,
+				  bool dma)
+{
+	int ret = 0;
+	struct vring_virtqueue *vq = to_vvq(_vq);
+	dma_addr_t addr;
+
+	if (dma)
+		addr = sg_dma_address(sg);
+	else {
+		addr = vring_map_one_sg(vq, sg, direction);
+		ret = vring_mapping_error(vq, addr);
+		if (ret)
+			return ret;
+	}
+
+	desc->flags = cpu_to_virtio16(_vq->vdev, flags);
+	desc->addr = cpu_to_virtio64(_vq->vdev, addr);
+	desc->len = cpu_to_virtio32(_vq->vdev,
+		dma ? sg_dma_len(sg) : sg->length);
+
+	return ret;
+}
+
 static inline int virtqueue_add(struct virtqueue *_vq,
 				struct scatterlist *sgs[],
 				unsigned int total_sg,
 				unsigned int out_sgs,
 				unsigned int in_sgs,
 				void *data,
-				gfp_t gfp)
+				gfp_t gfp,
+				bool dma)
 {
 	struct vring_virtqueue *vq = to_vvq(_vq);
 	struct scatterlist *sg;
@@ -301,7 +330,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 
 	/* If the host supports indirect descriptor tables, and we have multiple
 	 * buffers, then go indirect. FIXME: tune this threshold */
-	if (vq->indirect && total_sg > 1 && vq->vq.num_free)
+	if (!dma && vq->indirect && total_sg > 1 && vq->vq.num_free)
 		desc = alloc_indirect(_vq, total_sg, gfp);
 	else
 		desc = NULL;
@@ -335,26 +364,23 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 
 	for (n = 0; n < out_sgs; n++) {
 		for (sg = sgs[n]; sg; sg = sg_next(sg)) {
-			dma_addr_t addr = vring_map_one_sg(vq, sg, DMA_TO_DEVICE);
-			if (vring_mapping_error(vq, addr))
+			int ret = vring_desc_set(_vq, desc + i, sg,
+				       VRING_DESC_F_NEXT, DMA_TO_DEVICE, dma);
+			if (ret)
 				goto unmap_release;
 
-			desc[i].flags = cpu_to_virtio16(_vq->vdev, VRING_DESC_F_NEXT);
-			desc[i].addr = cpu_to_virtio64(_vq->vdev, addr);
-			desc[i].len = cpu_to_virtio32(_vq->vdev, sg->length);
 			prev = i;
 			i = virtio16_to_cpu(_vq->vdev, desc[i].next);
 		}
 	}
 	for (; n < (out_sgs + in_sgs); n++) {
 		for (sg = sgs[n]; sg; sg = sg_next(sg)) {
-			dma_addr_t addr = vring_map_one_sg(vq, sg, DMA_FROM_DEVICE);
-			if (vring_mapping_error(vq, addr))
+			int ret = vring_desc_set(_vq, desc + i, sg,
+				       VRING_DESC_F_NEXT | VRING_DESC_F_WRITE,
+				       DMA_FROM_DEVICE, dma);
+			if (ret)
 				goto unmap_release;
 
-			desc[i].flags = cpu_to_virtio16(_vq->vdev, VRING_DESC_F_NEXT | VRING_DESC_F_WRITE);
-			desc[i].addr = cpu_to_virtio64(_vq->vdev, addr);
-			desc[i].len = cpu_to_virtio32(_vq->vdev, sg->length);
 			prev = i;
 			i = virtio16_to_cpu(_vq->vdev, desc[i].next);
 		}
@@ -461,7 +487,8 @@ int virtqueue_add_sgs(struct virtqueue *_vq,
 		for (sg = sgs[i]; sg; sg = sg_next(sg))
 			total_sg++;
 	}
-	return virtqueue_add(_vq, sgs, total_sg, out_sgs, in_sgs, data, gfp);
+	return virtqueue_add(_vq, sgs, total_sg, out_sgs, in_sgs, data, gfp,
+			     false);
 }
 EXPORT_SYMBOL_GPL(virtqueue_add_sgs);
 
@@ -483,10 +510,19 @@ int virtqueue_add_outbuf(struct virtqueue *vq,
 			 void *data,
 			 gfp_t gfp)
 {
-	return virtqueue_add(vq, &sg, num, 1, 0, data, gfp);
+	return virtqueue_add(vq, &sg, num, 1, 0, data, gfp, false);
 }
 EXPORT_SYMBOL_GPL(virtqueue_add_outbuf);
 
+int dma_virtqueue_add_outbuf(struct virtqueue *vq,
+			     struct scatterlist *sg, unsigned int num,
+			     void *data,
+			     gfp_t gfp)
+{
+	return virtqueue_add(vq, &sg, num, 1, 0, data, gfp, true);
+}
+EXPORT_SYMBOL_GPL(dma_virtqueue_add_outbuf);
+
 /**
  * virtqueue_add_inbuf - expose input buffers to other end
  * @vq: the struct virtqueue we're talking about.
@@ -505,10 +541,19 @@ int virtqueue_add_inbuf(struct virtqueue *vq,
 			void *data,
 			gfp_t gfp)
 {
-	return virtqueue_add(vq, &sg, num, 0, 1, data, gfp);
+	return virtqueue_add(vq, &sg, num, 0, 1, data, gfp, false);
 }
 EXPORT_SYMBOL_GPL(virtqueue_add_inbuf);
 
+int dma_virtqueue_add_inbuf(struct virtqueue *vq,
+			    struct scatterlist *sg, unsigned int num,
+			    void *data,
+			    gfp_t gfp)
+{
+	return virtqueue_add(vq, &sg, num, 0, 1, data, gfp, true);
+}
+EXPORT_SYMBOL_GPL(dma_virtqueue_add_inbuf);
+
 /**
  * virtqueue_kick_prepare - first half of split virtqueue_kick call.
  * @vq: the struct virtqueue
diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c
index cf61c92..96cbabf 100644
--- a/drivers/watchdog/renesas_wdt.c
+++ b/drivers/watchdog/renesas_wdt.c
@@ -16,6 +16,7 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/soc/renesas/s2ram_ddr_backup.h>
 #include <linux/watchdog.h>
 
 #define RWTCNT		0
@@ -26,6 +27,21 @@
 
 #define RWDT_DEFAULT_TIMEOUT 60U
 
+#ifdef CONFIG_RCAR_DDR_BACKUP
+static struct hw_register rwdt_ip_regs[] = {
+	{"RWTCNT",   0x00, 16, 0},
+	{"RWTCSRA",  0x04, 8, 0},
+};
+
+static struct rcar_ip rwdt_ip = {
+	.ip_name = "RWDT",
+	.base_addr = 0xE6020000,
+	.size = 0x08,
+	.reg_count = ARRAY_SIZE(rwdt_ip_regs),
+	.ip_reg = rwdt_ip_regs,
+};
+#endif /* CONFIG_RCAR_DDR_BACKUP*/
+
 static const unsigned int clk_divs[] = { 1, 4, 16, 32, 64, 128, 1024 };
 
 static bool nowayout = WATCHDOG_NOWAYOUT;
@@ -60,6 +76,15 @@ static int rwdt_init_timeout(struct watchdog_device *wdev)
 	return 0;
 }
 
+static int rwdt_set_timeout(struct watchdog_device *wdev,
+			    unsigned int new_timeout)
+{
+	wdev->timeout = new_timeout;
+	rwdt_init_timeout(wdev);
+
+	return 0;
+}
+
 static int rwdt_start(struct watchdog_device *wdev)
 {
 	struct rwdt_priv *priv = watchdog_get_drvdata(wdev);
@@ -105,6 +130,7 @@ static const struct watchdog_ops rwdt_ops = {
 	.start = rwdt_start,
 	.stop = rwdt_stop,
 	.ping = rwdt_init_timeout,
+	.set_timeout = rwdt_set_timeout,
 	.get_timeleft = rwdt_get_timeleft,
 };
 
@@ -198,9 +224,41 @@ static const struct of_device_id rwdt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, rwdt_ids);
 
+#ifdef CONFIG_PM_SLEEP
+static int rwdt_suspend(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	struct rwdt_priv *priv = dev_get_drvdata(dev);
+
+	if (!rwdt_ip.virt_addr)
+		rwdt_ip.virt_addr = priv->base;
+
+	ret = rcar_handle_registers(&rwdt_ip, DO_BACKUP);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+	return ret;
+}
+
+static int rwdt_resume(struct device *dev)
+{
+	int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+	ret = rcar_handle_registers(&rwdt_ip, DO_RESTORE);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(rwdt_pm_ops,
+			rwdt_suspend, rwdt_resume);
+#define DEV_PM_OPS (&rwdt_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
 static struct platform_driver rwdt_driver = {
 	.driver = {
 		.name = "renesas_wdt",
+		.pm	= DEV_PM_OPS,
 		.of_match_table = rwdt_ids,
 	},
 	.probe = rwdt_probe,
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index bae79f3..3c3442a 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
  * Copyright (C) 2011 Freescale Semiconductor, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
@@ -25,6 +26,7 @@ enum dw_hdmi_devtype {
 	IMX6Q_HDMI,
 	IMX6DL_HDMI,
 	RK3288_HDMI,
+	RCAR_HDMI,
 };
 
 struct dw_hdmi_mpll_config {
@@ -40,6 +42,11 @@ struct dw_hdmi_curr_ctrl {
 	u16 curr[DW_HDMI_RES_MAX];
 };
 
+struct dw_hdmi_multi_div {
+	unsigned long mpixelclock;
+	u16 multi[DW_HDMI_RES_MAX];
+};
+
 struct dw_hdmi_phy_config {
 	unsigned long mpixelclock;
 	u16 sym_ctr;    /*clock symbol and transmitter control*/
@@ -51,9 +58,11 @@ struct dw_hdmi_plat_data {
 	enum dw_hdmi_devtype dev_type;
 	const struct dw_hdmi_mpll_config *mpll_cfg;
 	const struct dw_hdmi_curr_ctrl *cur_ctr;
+	const struct dw_hdmi_multi_div *multi_div;
 	const struct dw_hdmi_phy_config *phy_config;
 	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
 					   struct drm_display_mode *mode);
+	u32 index;
 };
 
 void dw_hdmi_unbind(struct device *dev, struct device *master, void *data);
diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h
index 7a26286..2f2cc7b 100644
--- a/include/linux/mfd/tmio.h
+++ b/include/linux/mfd/tmio.h
@@ -99,6 +99,9 @@
  */
 #define TMIO_MMC_SDIO_STATUS_QUIRK	(1 << 8)
 
+/* The start or stop of SD clock don't wait 10msec. */
+#define TMIO_MMC_CLK_NO_SLEEP		(1 << 9)
+
 /*
  * Some controllers allows to set SDx actual clock
  */
@@ -125,6 +128,8 @@ struct tmio_mmc_data {
 	unsigned int			cd_gpio;
 	int				alignment_shift;
 	dma_addr_t			dma_rx_offset;
+	unsigned int			max_blk_count;
+	unsigned short			max_segs;
 	void (*set_pwr)(struct platform_device *host, int state);
 	void (*set_clk_div)(struct platform_device *host, int state);
 };
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 0e49f70..3892e35 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1139,6 +1139,9 @@ void pdev_enable_device(struct pci_dev *);
 int pci_enable_resources(struct pci_dev *, int mask);
 void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *),
 		    int (*)(const struct pci_dev *, u8, u8));
+void pci_fixup_irqs_local(struct pci_bus *,
+		    u8 (*)(struct pci_dev *, u8 *),
+		    int (*)(const struct pci_dev *, u8, u8));
 struct resource *pci_find_resource(struct pci_dev *dev, struct resource *res);
 #define HAVE_PCI_REQ_REGIONS	2
 int __must_check pci_request_regions(struct pci_dev *, const char *);
diff --git a/include/linux/soc/renesas/rcar_ems_ctrl.h b/include/linux/soc/renesas/rcar_ems_ctrl.h
new file mode 100644
index 0000000..57ef8ad
--- /dev/null
+++ b/include/linux/soc/renesas/rcar_ems_ctrl.h
@@ -0,0 +1,11 @@
+#ifndef __RCAR_EMS_CTRL_H__
+#define __RCAR_EMS_CTRL_H__
+
+#define RCAR_EMS_MODE_OFF       0
+#define RCAR_EMS_MODE_ON        1
+
+extern int register_rcar_ems_notifier(struct notifier_block *nb);
+extern void unregister_rcar_ems_notifier(struct notifier_block *nb);
+extern int rcar_ems_get_mode(void);
+
+#endif /* __RCAR_EMS_CTRL_H__  */
diff --git a/include/linux/soc/renesas/s2ram_ddr_backup.h b/include/linux/soc/renesas/s2ram_ddr_backup.h
new file mode 100644
index 0000000..f6b74df
--- /dev/null
+++ b/include/linux/soc/renesas/s2ram_ddr_backup.h
@@ -0,0 +1,45 @@
+#ifndef S2RAM_DDR_BACKUP_H
+#define S2RAM_DDR_BACKUP_H
+
+#include <linux/types.h>
+
+enum {
+	DO_IOREMAP,
+	DO_BACKUP,
+	DO_RESTORE,
+};
+
+struct hw_register {
+	char *reg_name;
+	unsigned int reg_offset;
+	unsigned int access_size;
+	unsigned int reg_value;
+};
+
+struct rcar_ip {
+	char *ip_name;
+	void __iomem *virt_addr;
+	phys_addr_t base_addr;
+	unsigned int size;
+	unsigned int reg_count;
+	struct hw_register *ip_reg;
+};
+
+#ifdef CONFIG_RCAR_DDR_BACKUP
+int rcar_handle_registers(struct rcar_ip *ip, unsigned int handling);
+int rcar_handle_ips(struct rcar_ip **ip, unsigned int handling);
+#else
+static inline int rcar_handle_registers(struct rcar_ip *ip,
+		unsigned int handling)
+{
+	return 0;
+}
+
+static inline int rcar_handle_ips(struct rcar_ip **ip,
+		unsigned int handling)
+{
+	return 0;
+}
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+
+#endif /* S2RAM_DDR_BACKUP_H */
diff --git a/include/linux/spi/sh_msiof.h b/include/linux/spi/sh_msiof.h
index b087a85..f723aa4 100644
--- a/include/linux/spi/sh_msiof.h
+++ b/include/linux/spi/sh_msiof.h
@@ -1,10 +1,16 @@
 #ifndef __SPI_SH_MSIOF_H__
 #define __SPI_SH_MSIOF_H__
 
+enum {
+	SPI_MSIOF_MASTER,
+	SPI_MSIOF_SLAVE,
+};
+
 struct sh_msiof_spi_info {
 	int tx_fifo_override;
 	int rx_fifo_override;
 	u16 num_chipselect;
+	int mode;
 	unsigned int dma_tx_id;
 	unsigned int dma_rx_id;
 	u32 dtdl;
diff --git a/include/linux/virtio.h b/include/linux/virtio.h
index d5eb547..64f45b7 100644
--- a/include/linux/virtio.h
+++ b/include/linux/virtio.h
@@ -34,6 +34,16 @@ struct virtqueue {
 	void *priv;
 };
 
+int dma_virtqueue_add_outbuf(struct virtqueue *vq,
+			     struct scatterlist sg[], unsigned int num,
+			     void *data,
+			     gfp_t gfp);
+
+int dma_virtqueue_add_inbuf(struct virtqueue *vq,
+			    struct scatterlist sg[], unsigned int num,
+			    void *data,
+			    gfp_t gfp);
+
 int virtqueue_add_outbuf(struct virtqueue *vq,
 			 struct scatterlist sg[], unsigned int num,
 			 void *data,
diff --git a/include/media/media-entity.h b/include/media/media-entity.h
index b2203ee..5fb3f06 100644
--- a/include/media/media-entity.h
+++ b/include/media/media-entity.h
@@ -181,6 +181,9 @@ struct media_pad {
  * @link_validate:	Return whether a link is valid from the entity point of
  *			view. The media_entity_pipeline_start() function
  *			validates all links by calling this operation. Optional.
+ * @has_route:		Return whether a route exists inside the entity between
+ *			two given pads. Optional. If the operation isn't
+ *			implemented all pads will be considered as connected.
  *
  * .. note::
  *
@@ -192,6 +195,8 @@ struct media_entity_operations {
 			  const struct media_pad *local,
 			  const struct media_pad *remote, u32 flags);
 	int (*link_validate)(struct media_link *link);
+	bool (*has_route)(struct media_entity *entity, unsigned int pad0,
+			  unsigned int pad1);
 };
 
 /**
@@ -846,6 +851,9 @@ void media_entity_graph_walk_cleanup(struct media_entity_graph *graph);
  */
 void media_entity_put(struct media_entity *entity);
 
+bool media_entity_has_route(struct media_entity *entity, unsigned int sink,
+			    unsigned int source);
+
 /**
  * media_entity_graph_walk_start - Start walking the media graph at a
  *	given entity
diff --git a/include/media/vsp1.h b/include/media/vsp1.h
index 8d3d07a..7459546 100644
--- a/include/media/vsp1.h
+++ b/include/media/vsp1.h
@@ -1,7 +1,7 @@
 /*
  * vsp1.h  --  R-Car VSP1 API
  *
- * Copyright (C) 2015 Renesas Electronics Corporation
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
@@ -22,7 +22,8 @@ struct device;
 int vsp1_du_init(struct device *dev);
 
 int vsp1_du_setup_lif(struct device *dev, unsigned int width,
-		      unsigned int height);
+		      unsigned int height, unsigned int lif_index,
+		      bool suspend);
 
 struct vsp1_du_atomic_config {
 	u32 pixelformat;
@@ -32,13 +33,19 @@ struct vsp1_du_atomic_config {
 	struct v4l2_rect dst;
 	unsigned int alpha;
 	unsigned int zpos;
+	bool interlaced;
 };
 
-void vsp1_du_atomic_begin(struct device *dev);
+void vsp1_du_atomic_begin(struct device *dev, unsigned int lif_index);
 int vsp1_du_atomic_update(struct device *dev, unsigned int rpf,
 			  const struct vsp1_du_atomic_config *cfg);
-void vsp1_du_atomic_flush(struct device *dev);
+void vsp1_du_atomic_flush(struct device *dev, unsigned int lif_index);
 int vsp1_du_map_sg(struct device *dev, struct sg_table *sgt);
 void vsp1_du_unmap_sg(struct device *dev, struct sg_table *sgt);
+int vsp1_du_if_set_mute(struct device *dev, bool on, unsigned int lif_index);
+int vsp1_du_setup_wb(struct device *dev, u32 pixelformat, unsigned int pitch,
+		      dma_addr_t mem[2], unsigned int lif_index);
+void vsp1_du_wait_wb(struct device *dev, u32 count, unsigned int lif_index);
+
 
 #endif /* __MEDIA_VSP1_H__ */
diff --git a/include/uapi/drm/Kbuild b/include/uapi/drm/Kbuild
index 9355dd8..061983a 100644
--- a/include/uapi/drm/Kbuild
+++ b/include/uapi/drm/Kbuild
@@ -12,6 +12,7 @@
 header-y += qxl_drm.h
 header-y += r128_drm.h
 header-y += radeon_drm.h
+header-y += rcar_du_drm.h
 header-y += savage_drm.h
 header-y += sis_drm.h
 header-y += tegra_drm.h
diff --git a/include/uapi/drm/rcar_du_drm.h b/include/uapi/drm/rcar_du_drm.h
new file mode 100644
index 0000000..43420db
--- /dev/null
+++ b/include/uapi/drm/rcar_du_drm.h
@@ -0,0 +1,42 @@
+/*
+ * rcar_du_drm.h  --  R-Car Display Unit DRM driver
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_DRM_H__
+#define __RCAR_DU_DRM_H__
+
+struct rcar_du_vmute {
+	int crtc_id;	/* CRTCs ID */
+	int on;		/* Vmute function ON/OFF */
+};
+
+/* DRM_IOCTL_RCAR_DU_SET_SCRSHOT:VSPD screen shot */
+struct rcar_du_screen_shot {
+	unsigned long	buff;
+	unsigned int	buff_len;
+	unsigned int	crtc_id;
+	unsigned int	fmt;
+	unsigned int	width;
+	unsigned int	height;
+};
+
+/* rcar-du + vspd specific ioctls */
+#define DRM_RCAR_DU_SET_VMUTE		0
+#define DRM_RCAR_DU_SCRSHOT		4
+
+#define DRM_IOCTL_DRM_RCAR_DU_SET_VMUTE \
+	DRM_IOW(DRM_COMMAND_BASE + DRM_RCAR_DU_SET_VMUTE, \
+		struct rcar_du_vmute)
+
+#define DRM_IOCTL_DRM_RCAR_DU_SCRSHOT \
+	DRM_IOW(DRM_COMMAND_BASE + DRM_RCAR_DU_SCRSHOT, \
+		struct rcar_du_screen_shot)
+
+#endif /* __RCAR_DU_DRM_H__ */
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 1cb3930..e5b80f5 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -195,9 +195,6 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_init_dai);
 
 int asoc_simple_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link)
 {
-	if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name)
-		return -EINVAL;
-
 	/* Assumes platform == cpu */
 	if (!dai_link->platform_of_node)
 		dai_link->platform_of_node = dai_link->cpu_of_node;
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index f181410..940012d 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -306,7 +306,7 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
  */
 u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
 {
-	struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+	struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io);
 	struct rsnd_mod *target;
 	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
 	u32 val = 0x76543210;
@@ -315,11 +315,11 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
 	if (rsnd_io_is_play(io)) {
 		struct rsnd_mod *src = rsnd_io_to_mod_src(io);
 
-		target = src ? src : ssi;
+		target = src ? src : ssiu;
 	} else {
 		struct rsnd_mod *cmd = rsnd_io_to_mod_cmd(io);
 
-		target = cmd ? cmd : ssi;
+		target = cmd ? cmd : ssiu;
 	}
 
 	mask <<= runtime->channels * 4;
@@ -1251,9 +1251,37 @@ static int rsnd_remove(struct platform_device *pdev)
 	return ret;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int rsnd_suspend(struct device *dev)
+{
+	struct rsnd_priv *priv = dev_get_drvdata(dev);
+
+	rsnd_adg_remove(priv);
+
+	return 0;
+}
+
+static int rsnd_resume(struct device *dev)
+{
+	struct rsnd_priv *priv = dev_get_drvdata(dev);
+	int ret;
+
+	ret = rsnd_adg_probe(priv);
+
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(rsnd_pm_ops,
+			rsnd_suspend, rsnd_resume);
+#define DEV_PM_OPS (&rsnd_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif  /* CONFIG_PM_SLEEP */
+
 static struct platform_driver rsnd_driver = {
 	.driver	= {
 		.name	= "rcar_sound",
+		.pm	= DEV_PM_OPS,
 		.of_match_table = rsnd_of_match,
 	},
 	.probe		= rsnd_probe,
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index a8f61d7..8ca1705 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -382,6 +382,7 @@ struct rsnd_dai_stream {
 };
 #define rsnd_io_to_mod(io, i)	((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL)
 #define rsnd_io_to_mod_ssi(io)	rsnd_io_to_mod((io), RSND_MOD_SSI)
+#define rsnd_io_to_mod_ssiu(io)	rsnd_io_to_mod((io), RSND_MOD_SSIU)
 #define rsnd_io_to_mod_ssip(io)	rsnd_io_to_mod((io), RSND_MOD_SSIP)
 #define rsnd_io_to_mod_src(io)	rsnd_io_to_mod((io), RSND_MOD_SRC)
 #define rsnd_io_to_mod_ctu(io)	rsnd_io_to_mod((io), RSND_MOD_CTU)
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 969a516..1e9ca57 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -189,6 +189,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
 	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
 	struct device *dev = rsnd_priv_to_dev(priv);
 	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	int use_src = 0;
 	u32 fin, fout;
 	u32 ifscr, fsrate, adinr;
 	u32 cr, route;
@@ -214,6 +215,8 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
 		return;
 	}
 
+	use_src = (fin != fout) | rsnd_src_sync_is_enabled(mod);
+
 	/*
 	 *	SRC_ADINR
 	 */
@@ -225,7 +228,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
 	 */
 	ifscr = 0;
 	fsrate = 0;
-	if (fin != fout) {
+	if (use_src) {
 		u64 n;
 
 		ifscr = 1;
@@ -239,7 +242,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
 	 */
 	cr	= 0x00011110;
 	route	= 0x0;
-	if (fin != fout) {
+	if (use_src) {
 		route	= 0x1;
 
 		if (rsnd_src_sync_is_enabled(mod)) {